Pipeline: zwei neue Pruefstufen vor dem Commit (Typaritaet + JS-Render)

User: "baue alle und integriere in die pipeline vor dem commit". Beide Stufen
schliessen Luecken, die HEUTE real zugeschlagen haben.

A3) TYPPRUEFUNG (ty 0.0.73, Konfiguration in ty.toml)
Anlass: `paarweise(d)` wurde mit 1 von 2 Pflichtargumenten gerufen. py_compile
sah nichts (Syntax ok), ruff F821 auch nicht (der Name IST definiert), kein Test
deckte den Pfad ab - der Fehler schlug erst zur Laufzeit zu.
⚠ BEWUSST ENGES REGELSET: der volle ty-Lauf meldet 172 Befunde, davon 97
`unresolved-attribute` aus fehlenden MetaTrader5-Stubs. Eine Stufe mit
Dauer-Treffern wird ignoriert, und mit ihr die eine echte Meldung (die Lehre vom
07.08.). Aktiv sind nur die Aritaets-Regeln. `call-non-callable` und
`not-iterable` sind AUS - beide melden hier ausschliesslich Falsch-Positive aus
heterogenen dicts (`c["have"]()`, `"up" in c`); geprueft, nicht angenommen.
⚠ Der dokumentierte Einwand "Pruefumgebung != Laufumgebung" greift NICHT: ein
Typchecker fuehrt nichts aus, er kann also nicht divergieren. Er braucht auch
kein MT5.
⚠ Nebenbefund: die CLAUDE.md-Begruendung gegen mypy ("kaum Annotationen
vorhanden") ist veraltet - gemessen sind 305 von 443 Funktionen annotiert (69 %).

B2) RENDER-PROBE (tools/render_check.mjs gegen tests/fixtures/snapshot.json)
Anlass: `node --check` prueft nur SYNTAX. Der ReferenceError von heute Abend
(RSI-Zeile griff in renderM15(s) auf d.market zu) war syntaktisch einwandfrei
und brach im Browser den GESAMTEN Render ab - sechs leere Zeilen und eine leere
Breakout-Karte. Fuer Python gibt es ruff F821, fuer JS gab es nichts.
⚠ Laeuft gegen einen GESPEICHERTEN Snapshot, nicht gegen den laufenden Server -
der Hook darf nicht davon abhaengen, ob gerade ein Server laeuft.
⚠ KEIN ESLint: das braeuchte npm und node_modules, gegen die dokumentierte
Entscheidung ("kein npm, keine package.json").

BEIDE RICHTUNGEN VERIFIZIERT, je Stufe:
  A3  Aritaetsfehler eingebaut -> "FEHLER too-many-positional-arguments",
      1 Befund; zurueck -> gruen
  B2  d.market zurueckgebaut   -> " EXCEPTION: d is not defined",
      1 Befund; zurueck -> gruen
Beide haengen an tools/check_nfalle.py und laufen damit im pre-commit-Hook mit.
Fehlt das Werkzeug (ty nicht installiert, node nicht im PATH, Fixture fehlt),
wird die Stufe uebersprungen statt zu blockieren - eine frische Maschine darf am
Hook nicht haengenbleiben. Ein WERKZEUGFEHLER (Exit != 0/1) gilt dagegen
ausdruecklich NICHT als sauber.

ty in requirements-dev.txt gepinnt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-19 23:14:43 +02:00
co-authored by Claude Opus 5
parent 95b22e2add
commit 0fe51dea48
4 changed files with 127 additions and 0 deletions
+2
View File
@@ -19,3 +19,5 @@ numpy==2.2.6
scipy==1.18.0 # `backtest_cost_gate.ci95` (Bootstrap-Konfidenzintervalle)
scikit-learn==1.9.0 # bislang ungenutzt; Kandidat fuer P(break)-Nachbau
pandas==3.0.2 # bislang ungenutzt
# Typpruefung (Aritaet) — Stufe A3 in tools/check_nfalle.py, seit 2026-08-19
ty==0.0.73
+1
View File
File diff suppressed because one or more lines are too long
+90
View File
@@ -81,6 +81,91 @@ print(f" {npy} Dateien geprueft.")
SCHWER = ("undefined name", "undefined local", "syntax error")
print()
print("=" * 88)
# ── A3) TYPPRUEFUNG: falsche Aufrufe ─────────────────────────────────────
# ⚠⚠ WARUM ES DAS BRAUCHT (2026-08-19): `paarweise(d)` wurde mit 1 von 2
# Pflichtargumenten gerufen. py_compile sah nichts (Syntax ok), ruff F821 auch
# nicht (der Name IST definiert), und kein Test deckte den Pfad ab. Der Fehler
# schlug erst zur Laufzeit zu. Genau diese Luecke schliesst `ty`.
# ⚠ BEWUSST ENGES REGELSET (`ty.toml`): der volle Lauf meldet 172 Befunde, davon
# 97 `unresolved-attribute` aus fehlenden MetaTrader5-Stubs. Eine Stufe mit
# Dauer-Treffern wird ignoriert, und mit ihr die eine echte Meldung. Aktiv
# sind nur die Aritaets-Regeln; `call-non-callable`/`not-iterable` sind AUS,
# weil sie hier nur Falsch-Positive aus heterogenen dicts liefern (geprueft).
# ⚠ EXIT-CODES wie bei ruff: 0 sauber · 1 Befunde · alles andere = Werkzeugfehler
# und darf NIEMALS als "sauber" durchgehen (die Lehre vom 07.08.).
def _stufe_typen():
print("")
print("=" * 88)
print(" A3) TYPPRUEFUNG — falsche Aufrufe (Aritaet)")
print("=" * 88)
cfg = WURZEL / "ty.toml"
if not cfg.exists():
print(" ty.toml fehlt — Stufe uebersprungen.")
return
try:
r = subprocess.run([sys.executable, "-m", "ty", "check",
"--config-file", str(cfg),
str(WURZEL / "core"), str(WURZEL / "server.py")],
capture_output=True, text=True, timeout=300)
except FileNotFoundError:
print(" `ty` nicht installiert — Stufe uebersprungen "
"(pip install ty). Eine frische Maschine darf hier nicht haengen.")
return
except Exception as e:
print(f" ⚠ ty nicht ausfuehrbar ({e}) — Stufe uebersprungen.")
return
if r.returncode not in (0, 1):
print(f" ⚠ ty meldet einen WERKZEUGFEHLER (Exit {r.returncode}) — "
f"NICHT als sauber gewertet:")
for z in (r.stderr or r.stdout).strip().splitlines()[:3]:
print(f" {z}")
befunde.append("TY Werkzeugfehler")
return
treffer = [z for z in (r.stdout or "").splitlines() if z.startswith("error[")]
for z in treffer:
befunde.append(f"TY {z.strip()}")
print(f" FEHLER {z.strip()}")
if not treffer:
print(" keine falschen Aufrufe.")
# ── B2) RENDER-PROBE: JS-Laufzeitfehler im Dashboard ─────────────────────
# ⚠⚠ `node --check` prueft nur SYNTAX. Ein ReferenceError (falsche Variable im
# falschen Scope) ist syntaktisch einwandfrei und bricht im Browser den
# GESAMTEN Render ab — alles ab der Fehlerstelle bleibt auf "—" stehen.
# Real am 19.08.: die RSI-Zeile griff in `renderM15(s)` auf `d.market` zu;
# sechs leere Zeilen und eine komplett leere Breakout-Karte.
# ⚠ Laeuft gegen einen GESPEICHERTEN Snapshot (`tests/fixtures/snapshot.json`),
# NICHT gegen den laufenden Server — der Hook darf nicht davon abhaengen, ob
# gerade ein Server laeuft.
def _stufe_render():
print("")
print("=" * 88)
print(" B2) RENDER-PROBE — JS-Laufzeitfehler (was `node --check` nicht sieht)")
print("=" * 88)
werkzeug = WURZEL / "tools" / "render_check.mjs"
fixture = WURZEL / "tests" / "fixtures" / "snapshot.json"
if not werkzeug.exists() or not fixture.exists():
print(" Werkzeug oder Fixture fehlt — Stufe uebersprungen.")
return
try:
r = subprocess.run(["node", str(werkzeug), str(fixture)],
capture_output=True, text=True, timeout=120)
except FileNotFoundError:
print(" node nicht im PATH — Stufe uebersprungen.")
return
except Exception as e:
print(f" ⚠ Render-Probe nicht ausfuehrbar ({e}) — uebersprungen.")
return
aus = (r.stdout or "") + (r.stderr or "")
if r.returncode != 0 or "EXCEPTION" in aus:
for z in aus.strip().splitlines()[:6]:
print(f" {z}")
befunde.append("RENDER " + aus.strip().splitlines()[0][:80])
else:
print(" alle Render-Funktionen laufen ohne Exception durch.")
print(" A2) LINTER — undefinierte Namen (was py_compile nicht sieht)")
print("=" * 88)
# ⚠ RUFF statt pyflakes (2026-08-07, Web-Recherche zum 2026er-Standard): gleiche
@@ -335,6 +420,11 @@ print(" keine gefunden." if not sig else f" {sig} Stellen.")
print()
print("=" * 88)
_stufe_typen()
_stufe_render()
print("")
print("=" * 88)
print(" ERGEBNIS: " + ("keine \\n-Falle gefunden" if not befunde
else f"{len(befunde)} Befund(e)"))
print("=" * 88)
+34
View File
@@ -0,0 +1,34 @@
# Typpruefung — BEWUSST ENGES Regelset (2026-08-19).
#
# ⚠⚠ Der volle Lauf meldet 172 Befunde, davon 97 `unresolved-attribute` aus
# fehlenden MetaTrader5-Stubs. Eine Stufe mit Dauer-Treffern wird ignoriert,
# und mit ihr die eine echte Meldung — das ist im Projekt dokumentiert
# (ruff-Schweregrad-Trennung, 07.08.). Deshalb blockieren nur die Regeln,
# die ECHTE Aufruffehler finden und hier nachweislich rauschfrei sind.
#
# Anlass ist ein realer Fehler vom 19.08.: `paarweise(d)` mit 1 von 2
# Pflichtargumenten. py_compile, ruff F821 und pytest liessen ihn durch; er
# schlug erst zur Laufzeit zu.
[src]
exclude = ["backtest_*.py", "analyze_*.py", "tests/**"]
[rules]
# ── AKTIV: echte Aufruffehler ────────────────────────────────────────────
missing-argument = "error"
too-many-positional-arguments = "error"
unknown-argument = "error"
# ⚠ call-non-callable / not-iterable BEWUSST AUS: beide melden hier nur
# Falsch-Positive aus heterogenen dicts (`c["have"]()` in den CHECKS,
# `"up" in c` in news.py). Geprueft, nicht angenommen.
call-non-callable = "ignore"
not-iterable = "ignore"
unresolved-import = "ignore"
# ── AUS: braucht vollstaendige Stubs/Annotationen, sonst Dauer-Rauschen ──
unresolved-attribute = "ignore"
unsupported-operator = "ignore"
invalid-argument-type = "ignore"
no-matching-overload = "ignore"
not-subscriptable = "ignore"
possibly-missing-attribute = "ignore"
invalid-assignment = "ignore"
invalid-return-type = "ignore"