Erste Fassung: Claude-Werkzeuge aus dem Vault

.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]>
This commit is contained in:
Pascal MichelandClaude Opus 5.5 committed 2026-10-14 11:27:59 +02:00
commit fa1c26b407
137 files changed
+43926

No files matched your search

@@ -0,0 +1,304 @@
"""Faelle fuer verbrauch_reihe.py: baut je Fall einen kuenstlichen Session-Speicher und faehrt das Skript als eigenen Prozess.
Die Grundlinie laeuft zuerst; scheitert sie, messen die uebrigen Faelle nichts. Der Prozess laeuft
ohne PYTHONIOENCODING, damit eine Ausgabe ausserhalb von ASCII auf cp1252 auffaellt.
Aufruf: python faelle_verbrauch_reihe.py
Exit 0, wenn alle Faelle bestehen, sonst 1.
"""
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path
SKRIPT = Path(__file__).with_name("verbrauch_reihe.py")
def fenster(fuenf, woche, reset5="2026-09-24T18:30:00Z", resetw="2026-09-28T00:00:00Z"):
return [
{"label": "5-hour limit", "percentUsed": fuenf, "resetsAt": reset5},
{"label": "Weekly · all models", "percentUsed": woche, "resetsAt": resetw},
]
def baue(speicher, laeufe):
"""laeufe: {lauf: [agent]}; agent: {id, label, start, ende, modell, ergebnis, ohne_zeit, kaputt}."""
for lauf, agenten in laeufe.items():
ordner = speicher / "sitzung-1" / "subagents" / "workflows" / lauf
ordner.mkdir(parents=True)
with (ordner / "journal.jsonl").open("w", encoding="utf-8") as journal:
journal.write(json.dumps({"type": "launched"}) + "\n")
for a in agenten:
journal.write(json.dumps({"type": "started", "agentId": a["id"], "label": a["label"]}) + "\n")
if a.get("kaputt"):
journal.write("{kein json\n")
if "ergebnis" in a:
journal.write(json.dumps({"type": "result", "agentId": a["id"], "result": a["ergebnis"]}) + "\n")
for a in agenten:
(ordner / f"agent-{a['id']}.meta.json").write_text(json.dumps({"model": a.get("modell", "haiku")}), encoding="utf-8")
if a.get("ohne_zeit"):
continue
with (ordner / f"agent-{a['id']}.jsonl").open("w", encoding="utf-8") as d:
d.write(json.dumps({"timestamp": a["start"], "type": "user"}) + "\n")
d.write(json.dumps({"timestamp": a["ende"], "type": "assistant"}) + "\n")
def ablesung(nr, anlass, zeitpunkt, fuenf, woche, **extra):
ende = extra.pop("bis", None) or zeitpunkt
return {"id": f"v{nr}", "label": "verbrauch:" + anlass, "start": zeitpunkt, "ende": ende, "modell": "haiku",
"ergebnis": {"verfuegbar": True, "status": "ok", "plan": "Pro", "fenster": extra.pop("liste", None) or fenster(fuenf, woche, **extra), "fehler": ""}}
# resetsAt schwankt bei echten Ablesungen um Millisekunden und springt ueber Sekunden- und
# Datumsgrenzen (gemessen 2026-09-24); die Grundlinie traegt deshalb solche Werte.
ZITTER = {"reset5": "2026-09-24T18:29:59.664Z", "resetw": "2026-09-27T23:59:59.664Z"}
def opus(nr, start, ende, modell="opus"):
return {"id": f"o{nr}", "label": f"plankritik:linse{nr}", "start": start, "ende": ende, "modell": modell, "ergebnis": {}}
def fahre(speicher, *zusatz):
umgebung = {k: v for k, v in os.environ.items() if k not in ("PYTHONIOENCODING", "PYTHONUTF8")}
lauf = subprocess.run([sys.executable, str(SKRIPT), "--speicher", str(speicher), *zusatz],
capture_output=True, env=umgebung)
return lauf.returncode, lauf.stdout.decode("ascii", "replace"), lauf.stderr.decode("utf-8", "replace")
GRUND = {
"wf_a": [
ablesung(1, "plankritik:vor Linsen", "2026-09-24T10:00:00Z", 10, 5),
opus(1, "2026-09-24T10:00:10Z", "2026-09-24T10:30:10Z"),
ablesung(2, "plankritik:Ende", "2026-09-24T10:30:10Z", 14, 6, **ZITTER),
]
}
def f_grundlinie(tmp):
baue(tmp, GRUND)
code, aus, err = fahre(tmp)
fehler = []
if code != 0:
fehler.append(f"Exit {code}: {err[-300:]}")
for erwartet in ["2 Ablesungen, 1 Paare", "| wf_a | plankritik:vor Linsen -> plankritik:Ende | 30.2 | opus 30.0 | 4.0 | 1.0 |",
"Naeherung: 8.0 % des 5-Stunden-Fensters je Opus-Agentenstunde (1 Paare, 0.5 Opus-Stunden"]:
if erwartet not in aus:
fehler.append("fehlt: " + erwartet)
if "Hinweise" in aus:
fehler.append("unerwartete Hinweise")
return fehler
def f_reset(tmp):
baue(tmp, {"wf_r": [ablesung(1, "a", "2026-09-24T10:00:00Z", 80, 5), opus(1, "2026-09-24T10:01:00Z", "2026-09-24T10:20:00Z"),
ablesung(2, "b", "2026-09-24T10:30:00Z", 3, 6, reset5="2026-09-24T15:30:00Z")]})
code, aus, _ = fahre(tmp)
fehler = []
if code != 0 or "| Reset | 1.0 |" not in aus:
fehler.append("Reset nicht erkannt: " + aus[-300:])
if "Keine auswertbaren Paare mit Opus-Agenten." not in aus:
fehler.append("Reset-Paar wurde mitgerechnet")
return fehler
def f_speicher_fehlt(tmp):
code, aus, _ = fahre(tmp / "gibt-es-nicht")
return [] if code == 2 and "Speicher fehlt" in aus else [f"Exit {code}, Ausgabe {aus!r}"]
def f_null_ablesungen(tmp):
baue(tmp, {"wf_n": [opus(1, "2026-09-24T10:00:00Z", "2026-09-24T10:10:00Z")]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and "0 Ablesungen, 0 Paare" in aus else [f"Exit {code}, Ausgabe {aus!r}"]
def f_kaputte_zeile(tmp):
agenten = [dict(GRUND["wf_a"][0], kaputt=True), GRUND["wf_a"][1], GRUND["wf_a"][2]]
baue(tmp, {"wf_k": agenten})
code, aus, _ = fahre(tmp)
fehler = []
if code != 0 or "1 Paare" not in aus:
fehler.append("Auswertung trotz kaputter Zeile gescheitert: " + aus[-300:])
if "ist kein JSON" not in aus:
fehler.append("kaputte Zeile nicht als Hinweis gemeldet")
return fehler
def f_ohne_zeitstempel(tmp, position):
agenten = [dict(a) for a in GRUND["wf_a"]]
agenten[position]["ohne_zeit"] = True
baue(tmp, {"wf_z": agenten})
code, aus, _ = fahre(tmp)
fehler = []
if code != 0 or "1 Ablesungen, 0 Paare" not in aus:
fehler.append(f"Exit {code}, Ausgabe {aus[-300:]!r}")
if "ohne Zeitstempel" not in aus:
fehler.append("fehlender Zeitstempel nicht gemeldet")
return fehler
def f_fenster_kaputt(tmp, liste, erwartet="| unbekannt | unbekannt |"):
baue(tmp, {"wf_f": [ablesung(1, "a", "2026-09-24T10:00:00Z", 10, 5, liste=liste), opus(1, "2026-09-24T10:01:00Z", "2026-09-24T10:20:00Z"),
ablesung(2, "b", "2026-09-24T10:30:00Z", 12, 6)]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and erwartet in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
def f_umlaut_im_anlass(tmp):
baue(tmp, {"wf_u": [ablesung(1, "pruefung:vor Prüfung", "2026-09-24T10:00:00Z", 10, 5), ablesung(2, "pruefung:nach Prüfung", "2026-09-24T10:30:00Z", 12, 5)]})
code, aus, err = fahre(tmp)
return [] if code == 0 and "pruefung:vor Pruefung -> pruefung:nach Pruefung" in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}, stderr {err[-200:]!r}"]
def f_seit(tmp):
baue(tmp, {"wf_s": [ablesung(1, "alt", "2026-09-20T10:00:00Z", 10, 5), ablesung(2, "a", "2026-09-24T10:00:00Z", 10, 5),
ablesung(3, "b", "2026-09-24T11:00:00Z", 12, 5)]})
code, aus, _ = fahre(tmp, "--seit", "2026-09-24")
return [] if code == 0 and "2 Ablesungen, 1 Paare" in aus and "alt ->" not in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
def f_seit_ungueltig(tmp):
baue(tmp, GRUND)
code, aus, _ = fahre(tmp, "--seit", "gestern")
return [] if code == 2 and "kein Datum" in aus else [f"Exit {code}, Ausgabe {aus!r}"]
def f_voller_modellname(tmp):
baue(tmp, {"wf_m": [GRUND["wf_a"][0], opus(1, "2026-09-24T10:00:10Z", "2026-09-24T10:30:10Z", modell="claude-opus-5-5"), GRUND["wf_a"][2]]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and "opus 30.0" in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
def f_agent_ausserhalb(tmp):
# Ein Agent, der vor der ersten Ablesung endet, zaehlt nicht mit; einer, der halb hineinreicht, anteilig.
baue(tmp, {"wf_x": [opus(1, "2026-09-24T09:00:00Z", "2026-09-24T09:50:00Z"), GRUND["wf_a"][0],
opus(2, "2026-09-24T09:55:00Z", "2026-09-24T10:10:00Z"), GRUND["wf_a"][2]]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and "| opus 10.0 |" in aus else [f"Exit {code}, Ausgabe {aus[-400:]!r}"]
def f_absturz_ist_befund(tmp):
# Ein Zeitstempel ohne Zeitzone neben einem mit ihr laesst den Vergleich mit TypeError scheitern.
# Der Lauf muss das als Hinweis melden und die anderen Laeufe trotzdem auswerten.
kaputt = [dict(GRUND["wf_a"][0]), dict(GRUND["wf_a"][1], start="2026-09-24T10:00:10"), dict(GRUND["wf_a"][2])]
baue(tmp, {"wf_a": GRUND["wf_a"], "wf_b": kaputt})
code, aus, err = fahre(tmp)
fehler = []
if code != 0:
fehler.append(f"Exit {code}: {err[-300:]}")
if "wf_b: Auswertung abgebrochen (TypeError" not in aus:
fehler.append("Absturz nicht als Hinweis gemeldet")
if "| wf_a |" not in aus:
fehler.append("der gesunde Lauf fehlt")
return fehler
def f_verschiebung(tmp, reset5, erwartet):
# Nur resetsAt verschiebt sich, der Wert steigt; entscheidend ist allein die Toleranz.
baue(tmp, {"wf_v": [ablesung(1, "a", "2026-09-24T10:00:00Z", 10, 5), ablesung(2, "b", "2026-09-24T10:20:00Z", 12, 5, reset5=reset5)]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and erwartet in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
def f_fallender_wert(tmp):
baue(tmp, {"wf_d": [ablesung(1, "a", "2026-09-24T10:00:00Z", 30, 5), ablesung(2, "b", "2026-09-24T10:20:00Z", 5, 5)]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and "| Reset | 0.0 |" in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
def f_reset_unlesbar(tmp):
baue(tmp, {"wf_u": [ablesung(1, "a", "2026-09-24T10:00:00Z", 10, 5, reset5="bald"), ablesung(2, "b", "2026-09-24T10:20:00Z", 12, 6)]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and "| unbekannt | 1.0 |" in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
def f_hoechstes_fenster(tmp):
liste = [{"label": "5-hour limit", "percentUsed": 10, "resetsAt": "2026-09-24T18:30:00Z"},
{"label": "5-hour limit", "percentUsed": 30, "resetsAt": "2026-09-24T18:30:00Z"},
{"label": "Weekly · all models", "percentUsed": 5, "resetsAt": "2026-09-28T00:00:00Z"}]
baue(tmp, {"wf_h": [ablesung(1, "a", "2026-09-24T10:00:00Z", 0, 0, liste=liste), ablesung(2, "b", "2026-09-24T10:20:00Z", 35, 6)]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and "| 5.0 | 1.0 |" in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
def f_ablesezeit_ist_ende(tmp):
# Der Opus-Agent laeuft ganz innerhalb des ersten Ableseagenten, also vor der Ablesung.
baue(tmp, {"wf_t": [ablesung(1, "a", "2026-09-24T10:00:00Z", 10, 5, bis="2026-09-24T10:00:10Z"),
opus(1, "2026-09-24T10:00:02Z", "2026-09-24T10:00:08Z"),
ablesung(2, "b", "2026-09-24T10:30:00Z", 12, 5)]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and "| 29.8 | - | 2.0 | 0.0 |" in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
def f_agent_ueber_das_paar_hinaus(tmp):
baue(tmp, {"wf_b": [ablesung(1, "a", "2026-09-24T10:00:00Z", 10, 5), ablesung(2, "b", "2026-09-24T10:30:00Z", 12, 5),
opus(1, "2026-09-24T10:20:00Z", "2026-09-24T10:50:00Z"), ablesung(3, "c", "2026-09-24T11:00:00Z", 15, 5)]})
code, aus, _ = fahre(tmp)
fehler = []
if code != 0 or "| a -> b | 30.0 | opus 10.0 |" not in aus:
fehler.append("erstes Paar nicht am Ende gekappt: " + aus[-400:])
if "| b -> c | 30.0 | opus 20.0 |" not in aus:
fehler.append("zweites Paar falsch: " + aus[-400:])
return fehler
def f_ueber_hundert(tmp):
baue(tmp, {"wf_g": [ablesung(1, "a", "2026-09-24T10:00:00Z", 98, 5), ablesung(2, "b", "2026-09-24T10:20:00Z", 103, 5)]})
code, aus, _ = fahre(tmp)
return [] if code == 0 and "| 5.0 | 0.0 |" in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"]
FAELLE = [
("Grundlinie: zwei Ablesungen, ein Opus-Agent dazwischen", f_grundlinie),
("Reset zwischen zwei Ablesungen", f_reset),
("Speicher fehlt: Exit 2", f_speicher_fehlt),
("null Ablesungen: Exit 0 mit Zaehlung", f_null_ablesungen),
("kaputte Journalzeile: Hinweis, kein Absturz", f_kaputte_zeile),
("erste Ablesung ohne Zeitstempel", lambda t: f_ohne_zeitstempel(t, 0)),
("letzte Ablesung ohne Zeitstempel", lambda t: f_ohne_zeitstempel(t, 2)),
("5-Stunden-Fenster fehlt", lambda t: f_fenster_kaputt(t, [{"label": "Weekly · all models", "percentUsed": 5, "resetsAt": "2026-09-28T00:00:00Z"}], "| unbekannt | 1.0 |")),
("Umlaut im Anlass auf cp1252", f_umlaut_im_anlass),
("Prozent als Text", lambda t: f_fenster_kaputt(t, [{"label": "5-hour limit", "percentUsed": "10", "resetsAt": "2026-09-24T18:30:00Z"}, {"label": "Weekly · all models", "percentUsed": 5, "resetsAt": "2026-09-28T00:00:00Z"}])),
("Prozent als Wahrheitswert", lambda t: f_fenster_kaputt(t, [{"label": "5-hour limit", "percentUsed": True, "resetsAt": "2026-09-24T18:30:00Z"}, {"label": "Weekly · all models", "percentUsed": 5, "resetsAt": "2026-09-28T00:00:00Z"}])),
("fenster kein Array", lambda t: f_fenster_kaputt(t, {"a": 1})),
("--seit filtert aeltere Ablesungen", f_seit),
("--seit ungueltig: Exit 2", f_seit_ungueltig),
("voller Modellname zaehlt als opus", f_voller_modellname),
("Agent ausserhalb und halb innerhalb des Paars", f_agent_ausserhalb),
("Absturz in einem Lauf ist ein Hinweis", f_absturz_ist_befund),
("resetsAt 5 Stunden weiter, Wert steigt: Reset", lambda t: f_verschiebung(t, "2026-09-24T23:30:00Z", "| Reset | 0.0 |")),
("resetsAt 4:59 verschoben: dasselbe Fenster", lambda t: f_verschiebung(t, "2026-09-24T18:34:59Z", "| 2.0 | 0.0 |")),
("resetsAt 5:01 verschoben: Reset", lambda t: f_verschiebung(t, "2026-09-24T18:35:01Z", "| Reset | 0.0 |")),
("resetsAt 5:01 frueher: Reset", lambda t: f_verschiebung(t, "2026-09-24T18:24:59Z", "| Reset | 0.0 |")),
("fallender Wert im selben Fenster ist ein Reset", f_fallender_wert),
("resetsAt ohne Datum: unbekannt", f_reset_unlesbar),
("doppeltes 5-Stunden-Fenster: das hoechste zaehlt", f_hoechstes_fenster),
("Ablesezeit ist das Ende des Ableseagenten", f_ablesezeit_ist_ende),
("Agent ueber das Paar hinaus wird am Ende gekappt", f_agent_ueber_das_paar_hinaus),
("ueber 100 Prozent ist ein Wert, kein Fehler", f_ueber_hundert),
]
def main():
# Fehlertexte koennen Zeichen ausserhalb der Konsolenkodierung tragen; der Treiber darf daran nicht sterben.
sys.stdout.reconfigure(errors="replace")
gescheitert = 0
for name, fall in FAELLE:
with tempfile.TemporaryDirectory() as tmp:
try:
fehler = fall(Path(tmp))
except Exception as e: # ein Absturz im Fall ist selbst ein Befund
fehler = [f"Absturz im Fall: {type(e).__name__}: {e}"]
if fehler:
gescheitert += 1
print(f"FEHLER {name}\n " + "\n ".join(fehler))
else:
print(f"OK {name}")
print(f"{len(FAELLE)} Faelle, {gescheitert} gescheitert")
return 1 if gescheitert else 0
if __name__ == "__main__":
sys.exit(main())