.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]>
45 KiB
type, title, status, summary, tags, created, updated, modell, effort, modell_begruendung
| type | title | status | summary | tags | created | updated | modell | effort | modell_begruendung | |||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| plan | Mutantenläufer: pytest-Konfiguration festnageln, Abbruch von pytest melden | active | Ein Task: mutanten_lauf.py sucht die pytest-Konfiguration wie pytest 9.1.1, aber nie oberhalb der Wurzel, gibt sie mit -c vor und meldet einen Abbruch von pytest (Rückgabewert 3 oder 4) mit dem Ende der Ausgabe statt FEHLT je Test; dazu fünf Treiber-Fälle, Entwurf und LIESMICH. |
|
2026-10-06 | 2026-10-06 | sonnet | low | Jeder Block steht wörtlich da, Zieldateien und Verbote sind genannt; die Blöcke für Entwurf und LIESMICH tragen typografische Anführungszeichen, Gedankenstriche und Auslassungspunkte, die Haiku still ersetzt, deshalb sonnet mit effort low. |
Übersicht: LIESMICH der Planwerkzeuge · Anlass: Fahrplan C1-Abschluss im Projekt After Install, Schritt 1 (Code Projekte/active - Linux Härtung Rocky Debian Ubuntu - After Install/docs/plans/2026-10-05-fahrplan-c1-abschluss-c4.md)
Mutantenläufer: pytest-Konfiguration festnageln, Abbruch von pytest melden
Warum so
- Der Fehler (Plankritik 2 von C1 im Projekt After Install,
docs/plans/2026-10-05-cis-paket1-system/plankritik-2/befunde.json, Linsebezeichner): Liegt die Wegwerfkopie unter dem Windows-Temp und läuft der Mutantenlauf über/mnt/c, findet pytest beim Suchen nach obenC:/Users/PASCAL~1/AppData/Local/Temp/pyproject.toml. Derenaddopts = "--new-first --failed-first"brauchen den Cache, den-p no:cacheproviderabschaltet; pytest endet mit Rückgabewert 4. Der Läufer meldete daraufhin alle 80 Tests der Grundlinie alsFEHLTmit leerer Meldung und verschluckte die Ausgabe von pytest. - Quelle der Suchregeln: pytest 9.1.1 in den WSL-Venvs
tradingundmattermost(Python 3.13.5),_pytest/config/findpaths.pymit SHA-256535d149e6f3962e44260b6645bb354976e19531f5dd248e14ee7d7fff61c8848in beiden, nachgelesen am 2026-10-06:determine_setup,locate_config(Namenpytest.toml,.pytest.toml,pytest.ini,.pytest.ini,pyproject.toml,tox.ini,setup.cfg; ohne Treffer die erstepyproject.tomlüberhaupt),get_common_ancestor,load_config_dict_from_file. Aus_pytest/config/__init__.py:confcutdirist ohne Vorgabe der Ordner der Konfigurationsdatei, sonstrootdir(Zeilen 1598 bis 1603);ExitCode3 istINTERNAL_ERROR, 4USAGE_ERROR. - Konfiguration der Wurzel bleibt gültig. Ein bloßes
-c /dev/nullnähme Headscale und Mattermost (pytest.iniin der Wurzel) und der Trading Plattform (pyproject.toml) ihre Einstellungen; Treiber-Fall 15 (Markerintegrationperaddoptsabgewählt) fiele um. Deshalb sucht der Läufer wie pytest, nur nie oberhalb der Wurzel. --confcutdir. Mit der leeren Datei aus dem Plugin-Ordner lägeconfcutdirdort, und pytest lüdeconftest.py-Dateien oberhalb der Wurzel. Ohne fremde Konfiguration warconfcutdirbisher die Wurzel;--confcutdir <rootdir>hält das fest.- Abbruch von pytest. Bei Rückgabewert 3 oder 4 hält der Läufer die letzten 20 Zeilen der Ausgabe fest. In der Grundlinie ergibt das je Interpreter einen Befund statt einer
FEHLT-Zeile je Test. Unter einem Mutanten trägt nur ein Test ohne Bericht und ohne Sammelfehler den Text; ein Sammelfehler bleibtROT, denn pytest endet bei einem Knoten in einer nicht sammelbaren Datei ebenfalls mit 4 (not found). Eine erste Fassung, die jeden Abbruch alsUNKLARzählte, fiel am 2026-10-06 im Treiber-Fall 11 durch. - Gemessen vom Koordinator am 2026-10-06 (Rohdaten unter
AI/secondbrain/linux-haertung-after-install-memory/2026-10-06-c1-abschluss/roh/, Dateien02-*): Mit dem neuen Treiber und dem alten Läufer fallen genau die Fälle 36, 38 und 40; mit dem neuen Läufer fällt keiner. Drei kaputte Varianten des neuen Läufers zeigen die übrigen Fälle rot: Konfiguration der Wurzel nie benutzt → 15, 37, 38, 40; ohne--confcutdir→ 39; Suche ohne Grenze an der Wurzel → 36.
Zieldateien
Pfade relativ zu D:/Claude/.vault-tools/plan-kopie/:
mutanten_lauf.py(geändert)mutanten_lauf_treiber.py(geändert)mutanten_lauf_ENTWURF.md(geändert)LIESMICH.md(geändert)
Verbote
- Nur die vier Zieldateien ändern.
mutanten_lauf_kopie.sh,rot_nachweis.py,plan_materialisiere.py,.claude/workflows/*und alles andere bleiben unberührt. - Kein
git add, keingit commit. - Jeden Block byte-genau übernehmen: keine Umformulierung, keine Ersetzung typografischer Zeichen, keine zusätzlichen Leerzeilen. Schreib die Blöcke mit dem Edit-Werkzeug, Aus-Block als
old_string, wird-Block alsnew_string. - Steht ein Aus-Block nicht genau einmal in seiner Datei, oder widerspricht etwas im Plan einem anderen Teil, hältst du an und meldest es. Nichts still zurechtbiegen.
- Kein Lauf gegen ein echtes Projekt; der Treiber baut alles selbst unter
/tmpin WSL.
Schritt 1 — Tests zuerst: Treiber
Wende alle Blöcke dieses Abschnitts auf mutanten_lauf_treiber.py an.
mutanten_lauf_treiber.py, Stelle 1 von 4
Aus:
#!/usr/bin/env python3
"""Treiber fuer mutanten_lauf.py: 35 Angriffsfaelle auf einer Wegwerfkopie.
wird:
#!/usr/bin/env python3
"""Treiber fuer mutanten_lauf.py: 40 Angriffsfaelle auf einer Wegwerfkopie.
mutanten_lauf_treiber.py, Stelle 2 von 4
Aus:
dafuer ein eigenes TMPDIR (tempfile respektiert die Umgebungsvariable).
"""
wird:
dafuer ein eigenes TMPDIR (tempfile respektiert die Umgebungsvariable).
Faelle 36 bis 40 (2026-10-06) pruefen die pytest-Konfiguration: Die Wurzel
liegt dafuer als k/ in einem eigenen Rahmen-Ordner, darueber wahlweise eine
fremde pyproject.toml mit addopts, die zu -p no:cacheprovider nicht passen
(wie im Windows-Temp bei der Plankritik 2 von C1), oder ein fremdes conftest.py.
Dazu ein Bedienfehler von pytest in der Grundlinie und unter einem Mutanten.
"""
mutanten_lauf_treiber.py, Stelle 3 von 4
Aus:
FAELLE = [
wird:
# --- Faelle 36 bis 40: pytest-Konfiguration ---------------------------------
FREMDE_KONFIG = (
"[tool.pytest.ini_options]\n"
"addopts = \"--new-first --failed-first\"\n"
)
FREMDES_CONFTEST = "raise RuntimeError(\"fremdes conftest oberhalb der Wurzel geladen\")\n"
def baue_rahmen(eigene_konfig, fremde_konfig, fremdes_conftest):
rahmen = Path(tempfile.mkdtemp(prefix="mutanten_lauf_treiber_rahmen_"))
if fremde_konfig:
(rahmen / "pyproject.toml").write_text(FREMDE_KONFIG, encoding="utf-8")
if fremdes_conftest:
(rahmen / "conftest.py").write_text(FREMDES_CONFTEST, encoding="utf-8")
wurzel = rahmen / "k"
dateien = baue_projekt(wurzel)
if not eigene_konfig:
(wurzel / "pyproject.toml").unlink()
dateien = [p for p in dateien if p != wurzel / "pyproject.toml"]
return rahmen, wurzel, dateien
def fall_36(basis, projektpfad):
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=False, fremde_konfig=True, fremdes_conftest=False)
try:
snap = snapshot(dateien)
toml = rahmen / "mutanten.toml"
schreibe_toml(toml, mutant_d1_d2())
ergebnis = starte_laeufer(wurzel, toml)
ok = ergebnis.returncode == 0 and "D1 OK" in ergebnis.stdout and "D2 OK" in ergebnis.stdout
melde("Fall 36", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
pruefe_unveraendert(dateien, snap, "Fall 36")
finally:
shutil.rmtree(rahmen, ignore_errors=True)
def fall_37(basis, projektpfad):
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=True, fremdes_conftest=False)
try:
snap = snapshot(dateien)
toml = rahmen / "mutanten.toml"
schreibe_toml(toml, mutant_d1_d2())
sauber = starte_laeufer(wurzel, toml)
schreibe_toml(toml, kopf() + mutant_luecke("M37", ["tests/test_faelle.py::test_wird_abgewaehlt"]))
abgewaehlt = starte_laeufer(wurzel, toml)
ausgabe = abgewaehlt.stdout + abgewaehlt.stderr
ok = (
sauber.returncode == 0
and "D1 OK" in sauber.stdout
and "D2 OK" in sauber.stdout
and abgewaehlt.returncode == 2
and "ABGEWAEHLT" in ausgabe
)
melde("Fall 37", ok, "exit={0}/{1}\n{2}{3}\n{4}".format(
sauber.returncode, abgewaehlt.returncode, sauber.stdout, sauber.stderr, ausgabe))
pruefe_unveraendert(dateien, snap, "Fall 37")
finally:
shutil.rmtree(rahmen, ignore_errors=True)
def fall_38(basis, projektpfad):
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=False, fremdes_conftest=False)
try:
(wurzel / "pyproject.toml").write_text(
"[tool.pytest.ini_options]\n"
"addopts = \"--gibt-es-nicht\"\n",
encoding="utf-8",
)
snap = snapshot(dateien)
toml = rahmen / "mutanten.toml"
schreibe_toml(toml, mutant_d1_d2())
ergebnis = starte_laeufer(wurzel, toml)
ausgabe = ergebnis.stdout + ergebnis.stderr
ok = (
ergebnis.returncode == 2
and "pytest-Bedienfehler (Rueckgabewert 4)" in ausgabe
and "--gibt-es-nicht" in ausgabe
and "ist FEHLT" not in ausgabe
)
melde("Fall 38", ok, "exit={0}\n{1}".format(ergebnis.returncode, ausgabe))
pruefe_unveraendert(dateien, snap, "Fall 38")
finally:
shutil.rmtree(rahmen, ignore_errors=True)
def fall_39(basis, projektpfad):
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=False, fremde_konfig=False, fremdes_conftest=True)
try:
snap = snapshot(dateien)
toml = rahmen / "mutanten.toml"
schreibe_toml(toml, mutant_d1_d2())
ergebnis = starte_laeufer(wurzel, toml)
ok = ergebnis.returncode == 0 and "D1 OK" in ergebnis.stdout and "D2 OK" in ergebnis.stdout
melde("Fall 39", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
pruefe_unveraendert(dateien, snap, "Fall 39")
finally:
shutil.rmtree(rahmen, ignore_errors=True)
def fall_40(basis, projektpfad):
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=False, fremdes_conftest=False)
try:
snap = snapshot(dateien)
toml = rahmen / "mutanten.toml"
schreibe_toml(toml, kopf() + (
"[[mutant]]\n"
"id = \"M40\"\n"
"datei = \"pyproject.toml\"\n"
"aus = '''\n"
"addopts = '-m \"not integration\"'\n"
"'''\n"
"wird = '''\n"
"addopts = \"--gibt-es-nicht\"\n"
"'''\n"
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
))
ergebnis = starte_laeufer(wurzel, toml)
ok = (
ergebnis.returncode == 1
and "M40 UNKLAR" in ergebnis.stdout
and "pytest-Bedienfehler (Rueckgabewert 4)" in ergebnis.stdout
)
melde("Fall 40", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
pruefe_unveraendert(dateien, snap, "Fall 40")
finally:
shutil.rmtree(rahmen, ignore_errors=True)
FAELLE = [
mutanten_lauf_treiber.py, Stelle 4 von 4
Aus:
fall_21, fall_22, fall_23, fall_24, fall_25, fall_26, fall_27, fall_28, fall_29, fall_30,
fall_31, fall_32, fall_33, fall_34, fall_35,
]
wird:
fall_21, fall_22, fall_23, fall_24, fall_25, fall_26, fall_27, fall_28, fall_29, fall_30,
fall_31, fall_32, fall_33, fall_34, fall_35, fall_36, fall_37, fall_38, fall_39, fall_40,
]
Erwarteter Fehlschlag vor der Umsetzung
Der neue Treiber läuft gegen den alten Läufer. Lege zuerst in der Git-Bash den Rot-Ordner an; git show schreibt dort die Bytes unverändert, die PowerShell würde Zeilenenden umschreiben:
# alten Laeufer aus dem letzten Commit und den neuen Treiber in einen leeren Rot-Ordner legen
R=/c/Users/PASCAL~1/AppData/Local/Temp/mlauf-rot
rm -rf "$R" && mkdir -p "$R"
git -C /d/Claude show HEAD:.vault-tools/plan-kopie/mutanten_lauf.py > "$R/mutanten_lauf.py"
cp /d/Claude/.vault-tools/plan-kopie/mutanten_lauf_treiber.py "$R/"
Dann in der PowerShell:
# Treiber in WSL gegen den alten Laeufer fahren
wsl -d Debian -- timeout 900 /home/claude/venvs/trading/bin/python /mnt/c/Users/PASCAL~1/AppData/Local/Temp/mlauf-rot/mutanten_lauf_treiber.py *> "C:\Users\PASCAL~1\AppData\Local\Temp\mlauf-rot\ausgabe.txt"
"exit=$LASTEXITCODE"
Select-String -Path "C:\Users\PASCAL~1\AppData\Local\Temp\mlauf-rot\ausgabe.txt" -Pattern "FEHLER:|gerechnete"
Erwartet: exit=1, und die Treffer von Select-String enden genau auf diese vier Zeilen:
Fall 36 FEHLER: exit=2
Fall 38 FEHLER: exit=2
Fall 40 FEHLER: exit=1
gerechnete Fehler: 3
Weicht das ab, hältst du an und meldest die Ausgabe.
Schritt 2 — Umsetzung
Wende alle Blöcke dieses Abschnitts in der Reihenfolge an.
mutanten_lauf.py, Stelle 1 von 19
Aus:
Geheimnisdateien (der Schluessel "ohne" in mutanten.toml).
wird:
Geheimnisdateien (der Schluessel "ohne" in mutanten.toml).
pytest-Konfiguration (seit 2026-10-06): Fuer jeden pytest-Aufruf bestimmt das
Skript die Konfigurationsdatei so, wie pytest 9.1.1 sie selbst suchen wuerde
(_pytest/config/findpaths.py: determine_setup, locate_config,
get_common_ancestor), sucht aber nie oberhalb der Wurzel, und gibt sie mit -c
vor. Ohne Konfiguration in der Wurzel bekommt pytest eine leere Datei aus dem
Plugin-Ordner, dazu --rootdir und --confcutdir. Grund: Unter dem Windows-Temp
fand pytest eine fremde pyproject.toml, deren addopts nicht zu
-p no:cacheprovider passten, und jeder Test der Grundlinie stand als FEHLT da
(Plankritik 2 von C1 im Projekt After Install, 2026-10-05). Benannte Luecken:
tox.ini und setup.cfg werden zeilenweise nach ihrem Abschnitt durchsucht, nicht
mit iniconfig; ein setup.cfg mit [pytest] (bei pytest selbst ein Fehler) gilt
hier als keine Konfiguration.
Endet pytest mit Rueckgabewert 3 (interner Fehler) oder 4 (Bedienfehler,
ExitCode in _pytest/config/__init__.py), steht das Ende seiner Ausgabe in der
Grundlinie als eigener Befund und unter einem Mutanten in der Meldung der
Tests ohne Bericht und ohne Sammelfehler. Ein Sammelfehler bleibt ROT, auch
wenn pytest dabei mit 4 endet: Ein Knoten in einer Datei, die sich nicht
sammeln laesst, ergibt zusaetzlich "not found" (Treiber-Fall 11).
mutanten_lauf.py, Stelle 2 von 19
Aus:
MUTANT_SCHLUESSEL = {"id", "datei", "aus", "wird", "rot_bei", "hinweis"}
wird:
MUTANT_SCHLUESSEL = {"id", "datei", "aus", "wird", "rot_bei", "hinweis"}
# Reihenfolge wie config_names in locate_config, pytest 9.1.1.
PYTEST_KONFIG_NAMEN = (
"pytest.toml",
".pytest.toml",
"pytest.ini",
".pytest.ini",
"pyproject.toml",
"tox.ini",
"setup.cfg",
)
LEERE_KONFIG_NAME = "pytest.ini"
PYTEST_ABBRUCH = {3: "pytest-Abbruch mit internem Fehler", 4: "pytest-Bedienfehler"}
AUSGABE_ENDE_ZEILEN = 20
mutanten_lauf.py, Stelle 3 von 19
Aus:
def umgebung_abdichten(home_dir, plugin_dir, ergebnis_pfad):
wird:
def _ini_hat_abschnitt(pfad, abschnitt):
"""Zeilenweise Suche nach [abschnitt]. Eine nicht lesbare Datei zaehlt als
Treffer, damit pytest sie selbst liest und den Fehler selbst meldet."""
try:
text = pfad.read_text(encoding="utf-8", errors="replace")
except OSError:
return True
kopf = "[" + abschnitt + "]"
return any(zeile.strip() == kopf for zeile in text.split("\n"))
def _ist_pytest_konfig(pfad):
"""Nachbau von load_config_dict_from_file(pfad) is not None, pytest 9.1.1."""
name = pfad.name
if name in ("pytest.toml", ".pytest.toml", "pytest.ini", ".pytest.ini"):
return True
if name == "tox.ini":
return _ini_hat_abschnitt(pfad, "pytest")
if name == "setup.cfg":
return _ini_hat_abschnitt(pfad, "tool:pytest")
if name == "pyproject.toml":
try:
daten = tomllib.loads(pfad.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, tomllib.TOMLDecodeError):
return True
werkzeug = daten.get("tool", {})
tool_pytest = werkzeug.get("pytest", {}) if isinstance(werkzeug, dict) else {}
return isinstance(tool_pytest, dict) and bool(tool_pytest)
return False
def _innerhalb(pfad, wurzel):
return pfad == wurzel or wurzel in pfad.parents
def _konfig_suchen(startordner, wurzel):
"""Nachbau von locate_config, pytest 9.1.1; die Suche endet an der Wurzel."""
erstes_pyproject = None
for start in startordner:
for basis in (start, *start.parents):
if not _innerhalb(basis, wurzel):
break
for name in PYTEST_KONFIG_NAMEN:
pfad = basis / name
if pfad.is_file():
if name == "pyproject.toml" and erstes_pyproject is None:
erstes_pyproject = pfad
if _ist_pytest_konfig(pfad):
return pfad
return erstes_pyproject
def _gemeinsamer_vorfahr(pfade, wurzel):
"""Nachbau von get_common_ancestor, pytest 9.1.1, mit der Wurzel als Aufrufordner."""
vorfahr = None
for pfad in pfade:
if not pfad.exists():
continue
if vorfahr is None:
vorfahr = pfad
elif vorfahr in pfad.parents or pfad == vorfahr:
continue
elif pfad in vorfahr.parents:
vorfahr = pfad
else:
try:
vorfahr = Path(os.path.commonpath([str(pfad), str(vorfahr)]))
except ValueError:
pass
if vorfahr is None:
return wurzel
if vorfahr.is_file():
return vorfahr.parent
return vorfahr
def pytest_konfig_bestimmen(wurzel, knoten_liste):
"""Konfiguration und rootdir eines Aufrufs, wie determine_setup in pytest
9.1.1 sie ohne -c bestimmt, aber nie oberhalb der Wurzel gesucht.
Rueckgabe (konfig, rootdir); konfig ist None, wenn die Wurzel keine hat."""
wurzel = Path(os.path.abspath(str(wurzel)))
ordner = []
for knoten in knoten_liste:
pfad = wurzel / knoten.split("::", 1)[0]
if pfad.exists():
ordner.append(pfad if pfad.is_dir() else pfad.parent)
vorfahr = _gemeinsamer_vorfahr(ordner, wurzel)
if not _innerhalb(vorfahr, wurzel):
vorfahr = wurzel
konfig = _konfig_suchen([vorfahr], wurzel)
if konfig is not None:
return konfig, konfig.parent
for basis in (vorfahr, *vorfahr.parents):
if not _innerhalb(basis, wurzel):
break
if (basis / "setup.py").is_file():
return None, basis
if ordner != [vorfahr]:
konfig = _konfig_suchen(ordner, wurzel)
if konfig is not None:
return konfig, konfig.parent
return None, wurzel
def pytest_konfig_argumente(wurzel, knoten_liste, leere_konfig):
"""Die Konfiguration der Wurzel mit -c; sonst die leere Datei mit --rootdir
und --confcutdir. Ohne --confcutdir laege confcutdir im Ordner der leeren
Datei, und pytest laede conftest.py-Dateien oberhalb der Wurzel
(_pytest/config/__init__.py, pytest 9.1.1)."""
konfig, rootdir = pytest_konfig_bestimmen(wurzel, knoten_liste)
if konfig is not None:
return ["-c", str(konfig)]
return ["-c", str(leere_konfig), "--rootdir", str(rootdir), "--confcutdir", str(rootdir)]
def pytest_konfig_beschreibung(wurzel, knoten_liste):
konfig, rootdir = pytest_konfig_bestimmen(wurzel, knoten_liste)
if konfig is not None:
return "{0} (aus der Wurzel)".format(konfig)
return "keine in der Wurzel, leere Datei; rootdir {0}".format(rootdir)
def ausgabe_ende(stdout_bytes, stderr_bytes):
"""Die letzten nicht leeren Zeilen von stdout und stderr, je hoechstens 300 Zeichen."""
text = (
(stdout_bytes or b"").decode("utf-8", errors="replace")
+ "\n"
+ (stderr_bytes or b"").decode("utf-8", errors="replace")
)
zeilen = [zeile.rstrip()[:300] for zeile in text.split("\n") if zeile.strip()]
return zeilen[-AUSGABE_ENDE_ZEILEN:]
def umgebung_abdichten(home_dir, plugin_dir, ergebnis_pfad):
mutanten_lauf.py, Stelle 4 von 19
Aus:
env = umgebung_abdichten(home_dir, plugin_dir, ergebnis_pfad)
cmd = [python_exe, "-m", "pytest", "-q", "-p", "no:cacheprovider", "-p", PLUGIN_NAME] + list(knoten_liste)
wird:
env = umgebung_abdichten(home_dir, plugin_dir, ergebnis_pfad)
konfig_argumente = pytest_konfig_argumente(wurzel, knoten_liste, Path(plugin_dir) / LEERE_KONFIG_NAME)
cmd = (
[python_exe, "-m", "pytest", "-q", "-p", "no:cacheprovider", "-p", PLUGIN_NAME]
+ konfig_argumente
+ list(knoten_liste)
)
mutanten_lauf.py, Stelle 5 von 19
Aus:
absturz = None
proc = None
wird:
absturz = None
abbruch = None
ausgabe = (b"", b"")
proc = None
mutanten_lauf.py, Stelle 6 von 19
Aus:
try:
proc.communicate(timeout=zeitlimit)
except subprocess.TimeoutExpired:
wird:
try:
ausgabe = proc.communicate(timeout=zeitlimit)
except subprocess.TimeoutExpired:
mutanten_lauf.py, Stelle 7 von 19
Aus:
pass
proc.communicate()
except BaseException:
wird:
pass
ausgabe = proc.communicate()
except BaseException:
mutanten_lauf.py, Stelle 8 von 19
Aus:
raise
wird:
raise
if not zeit_ueberschritten and proc.returncode in PYTEST_ABBRUCH:
abbruch = "{0} (Rueckgabewert {1}) mit {2}; Ende der Ausgabe:\n {3}".format(
PYTEST_ABBRUCH[proc.returncode], proc.returncode, python_exe,
"\n ".join(ausgabe_ende(*ausgabe)) or "(keine Ausgabe)",
)
mutanten_lauf.py, Stelle 9 von 19
Aus:
return zeit_ueberschritten, meldungen, absturz
finally:
wird:
return zeit_ueberschritten, meldungen, absturz, abbruch
finally:
mutanten_lauf.py, Stelle 10 von 19
Aus:
absturzmeldungen = []
for python_exe, knoten_liste in gruppen.items():
zeit, meldungen, absturz = pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimit)
if zeit:
wird:
absturzmeldungen = []
abbrueche = {}
for python_exe, knoten_liste in gruppen.items():
zeit, meldungen, absturz, abbruch = pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimit)
if zeit:
mutanten_lauf.py, Stelle 11 von 19
Aus:
absturzmeldungen.append(absturz)
for m in meldungen:
wird:
absturzmeldungen.append(absturz)
if abbruch:
abbrueche[python_exe] = abbruch
for m in meldungen:
mutanten_lauf.py, Stelle 12 von 19
Aus:
deselected.add(m.get("knoten"))
return reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen
wird:
deselected.add(m.get("knoten"))
return reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen, abbrueche
mutanten_lauf.py, Stelle 13 von 19
Aus:
gruppen = gruppiere_nach_interpreter(alle_tests, kontext["python"], kontext["python_je_datei"])
reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen = gruppen_ausfuehren(
gruppen, kontext["wurzel"], home_dir, plugin_dir, zeitlimit,
wird:
gruppen = gruppiere_nach_interpreter(alle_tests, kontext["python"], kontext["python_je_datei"])
reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen, abbrueche = gruppen_ausfuehren(
gruppen, kontext["wurzel"], home_dir, plugin_dir, zeitlimit,
mutanten_lauf.py, Stelle 14 von 19
Aus:
befunde = list(absturzmeldungen)
ergebnis_je_knoten = {}
wird:
befunde = list(absturzmeldungen)
abgebrochen = set()
for python_exe, knoten_liste in gruppen.items():
if python_exe in abbrueche:
befunde.append("Grundlinie: " + abbrueche[python_exe])
abgebrochen.update(knoten_liste)
ergebnis_je_knoten = {}
mutanten_lauf.py, Stelle 15 von 19
Aus:
status, meldung = ergebnis_bestimmen(knoten, reports, deselected)
ergebnis_je_knoten[knoten] = {"ergebnis": status, "meldung": meldung}
wird:
status, meldung = ergebnis_bestimmen(knoten, reports, deselected)
if status == "FEHLT" and knoten in abgebrochen:
ergebnis_je_knoten[knoten] = {"ergebnis": status, "meldung": "pytest abgebrochen, siehe Befunde"}
continue
ergebnis_je_knoten[knoten] = {"ergebnis": status, "meldung": meldung}
mutanten_lauf.py, Stelle 16 von 19
Aus:
gruppen = gruppiere_nach_interpreter(knoten_liste, standard_python, python_je_datei)
reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen = gruppen_ausfuehren(
gruppen, wurzel, home_dir, plugin_dir, zeitlimit,
wird:
gruppen = gruppiere_nach_interpreter(knoten_liste, standard_python, python_je_datei)
reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen, abbrueche = gruppen_ausfuehren(
gruppen, wurzel, home_dir, plugin_dir, zeitlimit,
mutanten_lauf.py, Stelle 17 von 19
Aus:
unklar = True
if ergebnis in ("GRUEN", "UEBERSPRUNGEN", "ABGEWAEHLT"):
wird:
unklar = True
abbruch = abbrueche.get(python_je_datei.get(knoten.split("::", 1)[0], standard_python))
if abbruch:
meldung = abbruch
if ergebnis in ("GRUEN", "UEBERSPRUNGEN", "ABGEWAEHLT"):
mutanten_lauf.py, Stelle 18 von 19
Aus:
print("python {0}: {1}".format(interpreter, python_version_ermitteln(interpreter)))
wird:
print("python {0}: {1}".format(interpreter, python_version_ermitteln(interpreter)))
alle_knoten = []
for mutant in ausgewaehlte_mutanten:
for _, knoten in mutant["rot_bei"]:
if knoten not in alle_knoten:
alle_knoten.append(knoten)
print("pytest-konfiguration (Grundlinie): {0}".format(pytest_konfig_beschreibung(kontext["wurzel"], alle_knoten)))
mutanten_lauf.py, Stelle 19 von 19
Aus:
(plugin_dir / (PLUGIN_NAME + ".py")).write_text(PLUGIN_QUELLTEXT, encoding="utf-8")
wird:
(plugin_dir / (PLUGIN_NAME + ".py")).write_text(PLUGIN_QUELLTEXT, encoding="utf-8")
(plugin_dir / LEERE_KONFIG_NAME).write_text("", encoding="utf-8")
mutanten_lauf_ENTWURF.md, Stelle 1 von 6
Aus:
created: 2026-09-27
updated: 2026-09-27
---
wird:
created: 2026-09-27
updated: 2026-10-06
---
mutanten_lauf_ENTWURF.md, Stelle 2 von 6
Aus:
| Hält die Abdichtung Netz und echte Zugänge fern? | Annahme, aufgelöst als Projektvorgabe: Sie ist ein Fangnetz mit benannten Lücken (Abschnitt 5), kein Nachweis. |
wird:
| Hält die Abdichtung Netz und echte Zugänge fern? | Annahme, aufgelöst als Projektvorgabe: Sie ist ein Fangnetz mit benannten Lücken (Abschnitt 5), kein Nachweis. |
| Welche pytest-Konfiguration gilt? | Quelle: pytest 9.1.1, `_pytest/config/findpaths.py` (`determine_setup`, `locate_config`, `get_common_ancestor`, `load_config_dict_from_file`; SHA-256 `535d149e6f3962e4…`, in den Venvs `trading` und `mattermost` gleich) und `_pytest/config/__init__.py` (Vorgabe von `confcutdir`, `ExitCode`), nachgelesen am 2026-10-06. Projektvorgabe seit 2026-10-06: gesucht wird wie pytest, aber nie oberhalb der Wurzel (Abschnitt 3, „Konfiguration“). |
mutanten_lauf_ENTWURF.md, Stelle 3 von 6
Aus:
**pytest-Aufruf:** `<python> -m pytest -q -p no:cacheprovider -p _mutanten_lauf_plugin <knoten…>` mit `cwd` = Wurzel. Das Plugin ist eine kleine Datei, die das Skript in einen eigenen Temp-Ordner schreibt; der Ordner kommt vorn in `PYTHONPATH`. Das Plugin schreibt je Bericht eine JSON-Zeile in die Datei aus der Umgebungsvariable `MUTANTEN_LAUF_ERGEBNIS`:
wird:
**pytest-Aufruf:** `<python> -m pytest -q -p no:cacheprovider -p _mutanten_lauf_plugin <konfiguration…> <knoten…>` mit `cwd` = Wurzel. Das Plugin ist eine kleine Datei, die das Skript in einen eigenen Temp-Ordner schreibt; der Ordner kommt vorn in `PYTHONPATH`. Das Plugin schreibt je Bericht eine JSON-Zeile in die Datei aus der Umgebungsvariable `MUTANTEN_LAUF_ERGEBNIS`:
mutanten_lauf_ENTWURF.md, Stelle 4 von 6
Aus:
Die Meldung ist `longrepr.reprcrash.message`, wenn es das gibt, sonst die erste nicht leere Zeile von `str(longrepr)`, gekürzt auf 300 Zeichen. Das Plugin schreibt jede Zeile sofort und mit `flush`.
wird:
Die Meldung ist `longrepr.reprcrash.message`, wenn es das gibt, sonst die erste nicht leere Zeile von `str(longrepr)`, gekürzt auf 300 Zeichen. Das Plugin schreibt jede Zeile sofort und mit `flush`.
**Konfiguration (seit 2026-10-06):** Ohne Vorgabe sucht pytest seine Konfiguration vom gemeinsamen Vorfahren der Testpfade aufwärts bis zur Wurzel des Dateisystems. Liegt die Kopie unter dem Windows-Temp, fand pytest dort eine fremde `pyproject.toml` mit `addopts = "--new-first --failed-first"`; die Optionen brauchen den Cache, den `-p no:cacheprovider` abschaltet, pytest brach mit Rückgabewert 4 ab, und der Läufer meldete jeden Test der Grundlinie als `FEHLT` (Plankritik 2 von C1 im Projekt After Install, 2026-10-05). Seitdem bestimmt der Läufer je Aufruf, was pytest selbst wählen würde, sucht aber nie oberhalb der Wurzel:
- Reihenfolge der Namen und Bedingungen wie in pytest 9.1.1: `pytest.toml`, `.pytest.toml`, `pytest.ini`, `.pytest.ini` immer; `pyproject.toml` mit `[tool.pytest]` oder `[tool.pytest.ini_options]`; `tox.ini` mit `[pytest]`; `setup.cfg` mit `[tool:pytest]`; ohne Treffer die erste `pyproject.toml` überhaupt. Erst vom gemeinsamen Vorfahren aus, ohne Treffer und ohne `setup.py` dazwischen noch einmal von jedem Testordner aus.
- Gefunden: `-c <datei>`. pytest setzt `rootdir` und `confcutdir` dann selbst auf ihren Ordner, wie bei der eigenen Suche.
- Nicht gefunden: `-c <leere pytest.ini im Plugin-Ordner> --rootdir <r> --confcutdir <r>`, `r` ist der Ordner mit `setup.py` oder sonst die Wurzel. Ohne `--confcutdir` läge `confcutdir` im Plugin-Ordner, und pytest lüde `conftest.py`-Dateien oberhalb der Wurzel.
- Die Kopfzeile `pytest-konfiguration (Grundlinie): …` nennt, was für die Grundlinie gilt.
- Benannte Lücken: `tox.ini` und `setup.cfg` werden zeilenweise nach ihrem Abschnitt durchsucht, nicht mit `iniconfig`; eine nicht lesbare oder kaputte Datei gilt als Treffer, damit pytest den Fehler selbst meldet; ein `setup.cfg` mit `[pytest]`, bei pytest ein Fehler, gilt hier als keine Konfiguration.
**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 diesen Text als Meldung; sie 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 „not found“ (Treiber-Fall 11, am 2026-10-06 an einer ersten Fassung gesehen, die jeden Abbruch als `UNKLAR` zählte).
mutanten_lauf_ENTWURF.md, Stelle 5 von 6
Aus:
Der Treiber baut in einem Temp-Ordner ein kleines Projekt (ein Modul, eine Testdatei, `pyproject.toml` mit `[tool.pytest.ini_options]`), schreibt je Fall eine `mutanten.toml`, startet den Läufer als Unterprozess und prüft Exit-Code und ein Meldungsstück. **Zuerst die Grundlinie:** Der saubere Fall muss Exit 0 und nur `OK` liefern, sonst misst der Rest nichts. Nach **jedem** Fall prüft der Treiber, dass jede Datei des kleinen Projekts byte-gleich mit dem Ausgangsstand ist.
wird:
Der Treiber baut in einem Temp-Ordner ein kleines Projekt (ein Modul, eine Testdatei, `pyproject.toml` mit `[tool.pytest.ini_options]`; für die Fälle 36 bis 40 je ein eigenes als Unterordner `k/` eines Rahmen-Ordners), schreibt je Fall eine `mutanten.toml`, startet den Läufer als Unterprozess und prüft Exit-Code und ein Meldungsstück. **Zuerst die Grundlinie:** Der saubere Fall muss Exit 0 und nur `OK` liefern, sonst misst der Rest nichts. Nach **jedem** Fall prüft der Treiber, dass jede Datei des kleinen Projekts byte-gleich mit dem Ausgangsstand ist.
mutanten_lauf_ENTWURF.md, Stelle 6 von 6
Aus:
| 35 | ein Mutant hängt, der Läufer bekommt nach drei Sekunden SIGTERM (eigenes `TMPDIR`), seit 2026-09-28 | Zeile `ABBRUCH: SIGTERM`, kein Prozess mit `HOME` im Fall-Tmpdir, keine `mutanten_lauf_*`-Reste, Zieldatei byte-gleich |
wird:
| 35 | ein Mutant hängt, der Läufer bekommt nach drei Sekunden SIGTERM (eigenes `TMPDIR`), seit 2026-09-28 | Zeile `ABBRUCH: SIGTERM`, kein Prozess mit `HOME` im Fall-Tmpdir, keine `mutanten_lauf_*`-Reste, Zieldatei byte-gleich |
| 36 | Wurzel ohne eigene Konfiguration, darüber eine fremde `pyproject.toml` mit `addopts = "--new-first --failed-first"` (der Fall der Plankritik 2 von C1), seit 2026-10-06 | Exit 0, zweimal `OK` |
| 37 | Wurzel mit eigener `pyproject.toml` (Marker `integration` abgewählt), darüber dieselbe fremde | sauberer Fall Exit 0; Test mit Marker in der Grundlinie Exit 2 und `ABGEWAEHLT`, die eigene Konfiguration gilt also |
| 38 | eigene Konfiguration mit `addopts = "--gibt-es-nicht"` | Exit 2, `pytest-Bedienfehler (Rueckgabewert 4)` und `--gibt-es-nicht` aus der pytest-Ausgabe, keine Zeile `ist FEHLT` |
| 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, `UNKLAR`, Meldung mit `pytest-Bedienfehler (Rueckgabewert 4)` |
LIESMICH.md, Stelle 1 von 2
Aus:
created: 2026-09-17
updated: 2026-09-27
---
wird:
created: 2026-09-17
updated: 2026-10-06
---
LIESMICH.md, Stelle 2 von 2
Aus:
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`. 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:
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“). 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.
Schritt 3 — Verifikation
# Kompilieren, nur ASCII in den Python-Dateien, keine CR in allen vier Dateien
cd /d/Claude/.vault-tools/plan-kopie
python -m py_compile mutanten_lauf.py mutanten_lauf_treiber.py && echo kompiliert
LC_ALL=C grep -cP '[^\x00-\x7F]' mutanten_lauf.py mutanten_lauf_treiber.py
for f in mutanten_lauf.py mutanten_lauf_treiber.py mutanten_lauf_ENTWURF.md LIESMICH.md; do printf '%s %s\n' "$f" "$(tr -cd '\r' < "$f" | wc -c)"; done
Erwartet: kompiliert, je Python-Datei die Zahl 0, je Datei die Zahl 0.
# Treiber gegen den neuen Laeufer in WSL
wsl -d Debian -- timeout 900 /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-rot\ausgabe-neu.txt"
"exit=$LASTEXITCODE"
Select-String -Path "C:\Users\PASCAL~1\AppData\Local\Temp\mlauf-rot\ausgabe-neu.txt" -Pattern "FEHLER:|gerechnete"
Erwartet: exit=0 und als einziger Treffer eine Zeile, die auf gerechnete Fehler: 0 endet.
Danach C:\Users\PASCAL~1\AppData\Local\Temp\mlauf-rot löschen.
Bericht
Melde: je Zieldatei die Zahl der angewendeten Aus/wird-Paare, die Ausgaben aus Schritt 1 und Schritt 3 im Wortlaut, und jede Abweichung vom Plan oder getroffene Annahme. „Keine Abweichungen“ nur, wenn es keine gab.
Fertig heißt (Koordinator)
- Die vier Zieldateien sind byte-gleich mit der Kopie, auf die dieser Plan per
plan_materialisiere.pyangewendet wurde (cmp), und sonst ist unter.vault-tools/nichts geändert (git -C D:/Claude status --porcelain -- .vault-tools). - Treiber in WSL: Exit 0,
gerechnete Fehler: 0, selbst gefahren. - Rot-Nachweis: alter Läufer → genau 36, 38, 40; die drei Varianten → 15/37/38/40, 39, 36 (vom Koordinator am 2026-10-06 gefahren, Rohdaten
02-*). - Gegenprobe an allen sechs
mutanten.tomlim Vault (Headscale Paket A und B, Mattermost Phase 1, Trading Plattform Paket WH, After Install D-40 und C1), je Kopie ausgit archive HEADunter/tmp, alter gegen neuen Läufer: gleicher Exit-Code, gleiches Ergebnis je Test der Grundlinie, gleicher Status je Mutant. Dazu C1 unter dem Windows-Temp: alter Läufer Exit 2 mitFEHLT, neuer wie unter/tmp. - Ein
prueferhat Läufer und Treiber gegen diesen Plan und die pytest-Quelle gelesen; kein offener blockierender oder wichtiger Befund.
Lauf mit echten Daten
Punkt 4 der Liste oben ist der Lauf mit echten Daten. Auffallen müsste: ein Plan, dessen Grundlinie mit dem neuen Läufer anders ausgeht als mit dem alten (dann bildet die Suche pytest nicht nach), eine Kopfzeile pytest-konfiguration (Grundlinie), die bei Headscale oder Mattermost nicht pytest.ini und bei der Trading Plattform nicht pyproject.toml nennt, und bei After Install nicht „keine in der Wurzel“.
Risiken
- Suche weicht von pytest ab.
tox.iniundsetup.cfgwerden zeilenweise gelesen, nicht mitiniconfig. Merkmal: Ein Projekt mit Konfiguration intox.iniläuft ohne seine Einstellungen. Heute hat keines der sechs Projekte eine solche Datei. - Neue pytest-Fassung. Ändert eine spätere Fassung die Suchregeln, bildet der Läufer die alte nach. Merkmal: Die Kopfzeile nennt eine andere Datei als
pytest --co -qin der Wurzel ohne-c. - Befundtext statt
FEHLT. Wer die Ausgabe der Grundlinie maschinell aufist FEHLTdurchsucht, sieht einen Abbruch von pytest jetzt alspytest-Bedienfehler.plankritik.jswertet nur den Exit-Code aus; der bleibt 2.