Regelwerk, Agenten, Skills und Werkzeuge veröffentlichen
Erster Stand des öffentlichen Ausschnitts aus dem Vault. Die .gitignore arbeitet als Positivliste: ausgeschlossen ist alles, eingeschlossen nur, was ausdrücklich wieder aufgenommen wird. Notizen, Projekte, Finanzen, das Sitzungsgedächtnis und settings.local.json bleiben damit außen vor. Der Kundenname in AI/Regelwerk/seo.md ist durch eine neutrale Bezeichnung ersetzt; die Regeln selbst sind unverändert. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
commit
2c9a4475fc
97 files changed
+14627
No files matched your search
@@ -0,0 +1,165 @@
|
||||
"""Mutantenlauf auf der materialisierten Kopie: Schlaegt jeder neue Test an, wenn sein Gegenstand kaputt ist?
|
||||
|
||||
Laeuft in WSL; Aufruf: python mutanten_py.py <wsl-pfad-der-kopie>. Je Mutant: Datei als Bytes sichern, genau eine Stelle ersetzen, die genannten Tests
|
||||
fahren, Datei aus der Sicherung zurueckspielen. Erwartet ist ROT; ein gruener Mutant ist ein Befund.
|
||||
Zuerst die Grundlinie: Ohne Mutant muessen dieselben Tests gruen sein.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
MAT = Path(sys.argv[1]) # WSL-Pfad der Kopie mit dem angewendeten Plan
|
||||
PY = str(Path.home() / "venvs/trading/bin/python")
|
||||
|
||||
MUTANTEN = [
|
||||
(
|
||||
"lies_lauf trennt mit der Zeilentrennung von str",
|
||||
"trading/dashboard/laeufe.py",
|
||||
' with pfad.open("rb") as strom:\n for roh in strom:\n if not roh.strip():',
|
||||
' with pfad.open("rb") as strom:\n for roh in [z.encode("utf-8") for z in strom.read().decode("utf-8").splitlines()]:\n if not roh.strip():',
|
||||
["tests/test_dashboard_laeufe.py::test_die_einzelansicht_trennt_nicht_an_u2028"],
|
||||
),
|
||||
(
|
||||
"_ist_protokolldatei laesst alles durch",
|
||||
"trading/dashboard/laeufe.py",
|
||||
" return pfad.is_file() and not pfad.is_symlink()",
|
||||
" return True",
|
||||
[
|
||||
"tests/test_dashboard_laeufe.py::test_ein_ordner_mit_der_endung_jsonl_ist_kein_lauf",
|
||||
"tests/test_dashboard_laeufe.py::test_eine_symbolische_verknuepfung_ist_kein_lauf",
|
||||
],
|
||||
),
|
||||
(
|
||||
"_ist_protokolldatei folgt Verknuepfungen",
|
||||
"trading/dashboard/laeufe.py",
|
||||
" return pfad.is_file() and not pfad.is_symlink()",
|
||||
" return pfad.is_file()",
|
||||
["tests/test_dashboard_laeufe.py::test_eine_symbolische_verknuepfung_ist_kein_lauf"],
|
||||
),
|
||||
(
|
||||
"letzte_zeilen gibt ein Bruchstueck ohne Trenner zurueck",
|
||||
"trading/dashboard/laeufe.py",
|
||||
" if not trenner:\n return []",
|
||||
" if not trenner:\n return [_bruchstueck]",
|
||||
["tests/test_dashboard_laeufe.py::test_eine_letzte_zeile_ueber_dem_block_ohne_zeilenende_liefert_nichts"],
|
||||
),
|
||||
(
|
||||
"Eimeranfang mit festem Stundenschritt",
|
||||
"trading/dashboard/markt.py",
|
||||
" anfang_min = HANDELSSTART_MIN + nummer * aufloesung_min",
|
||||
" anfang_min = HANDELSSTART_MIN + nummer * 60",
|
||||
["tests/test_dashboard_markt.py::test_15_und_5_minuten_teilen_den_handelstag_ohne_rest"],
|
||||
),
|
||||
(
|
||||
"Tagesachse in der Achsenzone statt in der Handelszone",
|
||||
"trading/dashboard/markt.py",
|
||||
" return zeitpunkt.tz_convert(HANDELSZONE).date().isoformat()",
|
||||
" return zeitpunkt.tz_convert(zone).date().isoformat()",
|
||||
["tests/test_dashboard_markt.py::test_eine_tageskerze_bekommt_den_new_yorker_kalendertag_als_string"],
|
||||
),
|
||||
(
|
||||
"Tagesachse in UTC",
|
||||
"trading/dashboard/markt.py",
|
||||
" return zeitpunkt.tz_convert(HANDELSZONE).date().isoformat()",
|
||||
" return zeitpunkt.date().isoformat()",
|
||||
["tests/test_dashboard_markt.py::test_eine_tageskerze_bekommt_den_new_yorker_kalendertag_als_string"],
|
||||
),
|
||||
(
|
||||
"Handelstag mit 391 Kerzen",
|
||||
"trading/dashboard/markt.py",
|
||||
"KERZEN_JE_HANDELSTAG = 390",
|
||||
"KERZEN_JE_HANDELSTAG = 391",
|
||||
["tests/test_dashboard_markt.py::test_der_handelstag_hat_390_minuten_und_die_lesespanne_folgt_daraus"],
|
||||
),
|
||||
(
|
||||
"Kalenderfaktor 1,5 statt 1,6",
|
||||
"trading/dashboard/markt.py",
|
||||
"KALENDERFAKTOR_MINUTEN = 1.6",
|
||||
"KALENDERFAKTOR_MINUTEN = 1.5",
|
||||
["tests/test_dashboard_markt.py::test_der_handelstag_hat_390_minuten_und_die_lesespanne_folgt_daraus"],
|
||||
),
|
||||
(
|
||||
"lies_kerzen liest die Metadaten ungeschuetzt",
|
||||
"trading/dashboard/markt.py",
|
||||
" meta = _meta(katalog, bar_type)\n bis = _bis(ende, meta)",
|
||||
" meta = katalog.meta(bar_type)\n bis = _bis(ende, meta)",
|
||||
["tests/test_dashboard_markt.py::test_lies_kerzen_uebersteht_eine_unlesbare_metadatei"],
|
||||
),
|
||||
(
|
||||
"katalog() prueft nur die Wurzel",
|
||||
"trading/dashboard/app.py",
|
||||
' if not (wurzel / "_meta").is_dir():',
|
||||
" if not wurzel.is_dir():",
|
||||
["tests/test_dashboard_endpunkte.py::test_ein_ordner_ohne_meta_ist_kein_katalog_und_bekommt_keines"],
|
||||
),
|
||||
(
|
||||
"_ersetze laesst den alten Snapshot liegen",
|
||||
"trading/dashboard/verteiler.py",
|
||||
" if warteschlange.full():\n try:",
|
||||
" if warteschlange.full():\n return\n if warteschlange.full():\n try:",
|
||||
["tests/test_dashboard_verteiler.py::test_eine_volle_warteschlange_bekommt_den_neuen_snapshot"],
|
||||
),
|
||||
(
|
||||
"killswitch_uebung prueft den Schalter erst nach der Konfiguration",
|
||||
"scripts/killswitch_uebung.py",
|
||||
" if not parse_args().ausfuehren:\n print(OHNE_SCHALTER, file=sys.stderr)\n return 2\n\n limits = lade_limits(\"config/risk.toml\")",
|
||||
" limits = lade_limits(\"config/risk.toml\")\n if not parse_args().ausfuehren:\n print(OHNE_SCHALTER, file=sys.stderr)\n return 2\n",
|
||||
[
|
||||
"tests/test_skripte_starten.py::test_die_killswitch_uebung_ohne_schalter_endet_mit_exit_2",
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
GRUND = ""
|
||||
|
||||
|
||||
def fahre(tests: list[str]) -> bool:
|
||||
"""Exitcode allein ist kein Beleg: Ein Mutant mit Syntaxfehler waere genauso rot.
|
||||
Deshalb wird die erste Fehlerzeile mitgeschrieben und in der Ausgabe gezeigt."""
|
||||
global GRUND
|
||||
ergebnis = subprocess.run(
|
||||
[PY, "-m", "pytest", "-q", "-p", "no:cacheprovider", "-x", "--tb=short", *tests],
|
||||
cwd=MAT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
stdin=subprocess.DEVNULL,
|
||||
)
|
||||
zeilen = [z.strip() for z in ergebnis.stdout.splitlines() if z.startswith("E ")]
|
||||
GRUND = " | ".join(zeilen[:2])[:260]
|
||||
return ergebnis.returncode == 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
befunde = 0
|
||||
alle = sorted({t for m in MUTANTEN for t in m[4]})
|
||||
if not fahre(alle):
|
||||
print("BEFUND Grundlinie: ohne Mutant sind nicht alle Tests gruen -- der Lauf misst nichts.")
|
||||
return 2
|
||||
print(f"Grundlinie gruen ({len(alle)} Tests).")
|
||||
for name, datei, alt, neu, tests in MUTANTEN:
|
||||
pfad = MAT / datei
|
||||
sicherung = pfad.read_bytes()
|
||||
text = sicherung.decode("utf-8")
|
||||
anzahl = text.count(alt)
|
||||
if anzahl != 1:
|
||||
print(f"BEFUND Mutant '{name}': Stelle steht {anzahl}-mal in {datei}")
|
||||
befunde += 1
|
||||
continue
|
||||
try:
|
||||
pfad.write_bytes(text.replace(alt, neu).encode("utf-8"))
|
||||
gruen = fahre(tests)
|
||||
finally:
|
||||
pfad.write_bytes(sicherung)
|
||||
if gruen:
|
||||
print(f"BEFUND Mutant '{name}' bleibt GRUEN: {tests}")
|
||||
befunde += 1
|
||||
else:
|
||||
print(f"rot {name}\n {GRUND}")
|
||||
print(f"{befunde} Befund(e).")
|
||||
return 2 if befunde else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user