Trading-Plattform-Pruefung: lauf.log und Ergebnisdatei halten die Notizzeile nicht auf (Paket NR, Task 743)

abendkontrolle.py: logge wirft nicht mehr; scheiterte lauf.log in einem Lauf, steht
am Ende eine Zeile in der Notiz von heute und der Lauf endet mit Exit 1. Laesst sich
die Ergebnisdatei eines Tages nicht schreiben, bekommt seine Notizzeile eine
Unterzeile mit dem Typ, die folgenden Tage laufen weiter, Exit 1. Treiber
abendkontrolle_dockerhost_angriffe.py: sieben neue Faelle (46), Mutanten E743-01
bis E743-13 (70, keiner ueberlebt). Kritische Pruefung NT, Befund 3, und Plankritik NR.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0126RDfXiECscXipzf67uypJ
This commit is contained in:
Pascal MichelandClaude Opus 5.5 committed 2026-10-12 22:13:36 +02:00
1 parent f73f84e3ac
commit 94f6dfeeeb
2 files changed
+409 -9

No files matched your search

@@ -39,6 +39,12 @@ Seit Paket NT (Quorum 4): Endet die Frage nach einer Summe oder die Bestaetigung
im Zeitlimit, ist der Docker-Host nicht erreichbar, und die Sicherung hoert sofort auf: eine Zeile in der
Notiz von heute, Exit 1. Bricht nur das Archiv so ab, scheitert allein diese Instanz. --tag ausser der
Form JJJJ-MM-TT oder ohne gueltiges Datum endet gleich nach dem Zerlegen mit Exit 2, ohne Notiz und ssh.
Seit Paket NR (kritische Pruefung NT, Befund 3): Laesst sich lauf.log nicht schreiben (Sperre, Rechte, volle
Platte), haelt das weder Ergebnisdatei noch Notizzeile noch Sicherung auf. Der Lauf schreibt am Ende eine
Zeile "Abendkontrolle lauf.log (automatisch): **nicht schreibbar** (<Typ>)" in die Notiz von heute und endet
mit Exit 1. Seit der Plankritik NR: Laesst sich die Ergebnisdatei eines Tages nicht schreiben, bekommt seine
Zeile in der Notiz eine Unterzeile mit dem Typ der Ausnahme, die folgenden Tage des Teils laufen weiter, und
der Lauf endet mit Exit 1.
Aufruf von Hand: python abendkontrolle.py [--tag JJJJ-MM-TT] [--vault D:\\Claude] [--ohne-wsl <json-datei>]
[--quelle wsl|dockerhost] [--quelle-ibkr wsl|dockerhost] [--sicherung-ziel <ordner>]
"""
@@ -583,10 +589,27 @@ def schreibe_notiz(vault: Path, tag: str, zeile: str) -> None:
os.replace(neu, notiz)
# Seit Paket NR (kritische Pruefung NT, Befund 3): der Typ der letzten Ausnahme, an der lauf.log in diesem
# Lauf scheiterte; None, solange es ging. main setzt ihn zu Beginn zurueck, nur logge setzt ihn.
LOG_FEHLER: str | None = None
LOG_KOPF = "Abendkontrolle lauf.log (automatisch)"
def logge(ablage: Path, text: str) -> None:
ablage.mkdir(parents=True, exist_ok=True)
with (ablage / "lauf.log").open("a", encoding="utf-8", newline="\n") as datei:
datei.write(f"{datetime.now().isoformat(timespec='seconds')} {text}\n")
"""Eine Zeile in lauf.log. Wirft seit Paket NR nicht mehr.
Vorher warf ein gesperrtes oder nicht schreibbares lauf.log in main vor fahre_teil, in fahre_teil vor
der Zeile in der Notiz und in der Sicherung vor der Zeile zum unerreichbaren Host; uebrig blieb nur
Exit 1. Jetzt merkt sich logge den Typ der Ausnahme in LOG_FEHLER, und main schreibt dazu am Ende
eine Zeile in die Notiz von heute.
"""
global LOG_FEHLER
try:
ablage.mkdir(parents=True, exist_ok=True)
with (ablage / "lauf.log").open("a", encoding="utf-8", newline="\n") as datei:
datei.write(f"{datetime.now().isoformat(timespec='seconds')} {text}\n")
except Exception as ausnahme:
LOG_FEHLER = type(ausnahme).__name__
def main(argv=None) -> int:
@@ -616,6 +639,10 @@ def main(argv=None) -> int:
if argumente.tag is not None and not tag_gueltig(argumente.tag):
print(f"abendkontrolle: --tag {argumente.tag!a} ist kein Datum JJJJ-MM-TT", file=sys.stderr)
return 2
# Seit Paket NR: Jeder Lauf beginnt ohne Fehler an lauf.log. Die Angriffsfaelle rufen main mehrmals im
# selben Prozess; ohne das Zuruecksetzen truege ein Lauf den Fehler des vorigen weiter.
global LOG_FEHLER
LOG_FEHLER = None
# Seit Paket E9: Das Zeitbudget der Sicherung zaehlt ab hier, also mit der Zeit fuer die
# Ergebnisse (bis zu zwoelf Abfragen mit je SSH_TIMEOUT_S; Plankritik Runde 2, Befund 1).
frist = monotonic() + SICHERUNG_BUDGET_S
@@ -634,8 +661,9 @@ def main(argv=None) -> int:
else:
tage = faellige_tage(jetzt, ablage)
vorsatz = "" if teil == "alpaca" else f"{teil} "
# Scheitert ein Teil ausserhalb seiner eigenen Fehlerbehandlung (Ergebnisdatei nicht
# schreibbar, lauf.log gesperrt), laeuft der naechste trotzdem (Pruefung IB2).
# Scheitert ein Teil ausserhalb seiner eigenen Fehlerbehandlung, laeuft der naechste trotzdem
# (Pruefung IB2). Seit Paket NR werfen weder ein gesperrtes lauf.log (logge) noch eine nicht
# schreibbare Ergebnisdatei (fahre_teil).
try:
logge(ablage, f"{vorsatz}start, faellig: {tage}")
# Seit Paket DH: die Quelle je Teil; die IBKR-Instanz zieht an einem eigenen Abend um.
@@ -662,6 +690,19 @@ def main(argv=None) -> int:
logge(ablage, f"sicherung abgebrochen: {type(ausnahme).__name__}: {ausnahme}")
except Exception:
pass
# Seit Paket NR (kritische Pruefung NT, Befund 3): Scheiterte lauf.log in diesem Lauf, einmal eine Zeile
# in der Notiz von heute mit dem Typ der Ausnahme, ohne Pfad und ohne Inhalt, und Exit 1. Scheitert
# auch die Notiz, bleibt es bei Exit 1.
if LOG_FEHLER is not None:
code = 1
log_zeile = (
f"- {jetzt:%H:%M} Uhr — {LOG_KOPF}: **nicht schreibbar** ({LOG_FEHLER}) #needs-review. Mindestens "
"ein Eintrag dieses Laufs fehlt dort; Ergebnisdateien, Notizzeilen und Sicherung hängen nicht davon ab."
)
try:
schreibe_notiz(vault, jetzt.date().isoformat(), log_zeile)
except Exception:
pass
return code
@@ -703,15 +744,30 @@ def fahre_teil(teil: str, tage: list[str], jetzt: datetime, vault: Path, ablage:
logge(ablage, f"{vorsatz}{tag}: auch die Notiz scheiterte {zweite!r}")
continue
# Ein Abbruch im WSL-Teil (Exit 2) wird wiederholt; alles andere gilt als gelaufen.
# Seit Paket NR (Plankritik NR): Liess sich die Ergebnisdatei nicht schreiben, flog die Ausnahme vorher
# aus fahre_teil, und es fehlten die Zeile dieses Tages und die aller folgenden Tage des Teils. Jetzt
# bekommt die Zeile eine Unterzeile mit dem Typ der Ausnahme, die folgenden Tage laufen weiter, Exit 1.
# Ob der naechste Lauf den Tag erneut prueft, entscheidet faellige_tage: nur im Fenster und nur, wenn
# unter dem Namen der Datei nichts liegt, auch keine halb geschriebene Datei.
zusatz = ""
if ergebnis.get("exit") != 2:
(ablage / f"{tag}{endung}.json").write_text(
json.dumps(ergebnis, ensure_ascii=False, indent=1), encoding="utf-8", newline="\n"
)
try:
(ablage / f"{tag}{endung}.json").write_text(
json.dumps(ergebnis, ensure_ascii=False, indent=1), encoding="utf-8", newline="\n"
)
except Exception as ausnahme:
code = 1
logge(ablage, f"{vorsatz}{tag}: Ergebnisdatei nicht geschrieben {ausnahme!r}")
zusatz = (
f"\n - Ergebnisdatei nicht geschrieben ({type(ausnahme).__name__}) #needs-review. Der nächste "
"Lauf prüft den Tag erneut, wenn er dann noch im Nachholfenster ist und unter dem Namen der "
"Datei nichts liegt, auch keine halb geschriebene."
)
else:
code = 1
logge(ablage, f"{vorsatz}{tag}: {len(ergebnis['befunde'])} Befund(e), exit {ergebnis.get('exit')}")
try:
schreibe_notiz(vault, tag, zeile_von(ergebnis, uhrzeit))
schreibe_notiz(vault, tag, zeile_von(ergebnis, uhrzeit) + zusatz)
except Exception as ausnahme:
code = 1
logge(ablage, f"{vorsatz}{tag}: Notiz nicht geschrieben {ausnahme!r}")