Files
creator/backend/auto_loop.py
2026-07-08 21:14:33 +02:00

44 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Auto-Repair-Loop einer Ebene: QA → Befunde beheben, bis 100 % (10.0/10),
Stillstand (Repair verbessert die Note nicht mehr) oder max_iter erreicht ist.
Reine Primitive — keine Kanban-/DB-Kopplung. Der Aufrufer übergibt zwei Coroutinen:
`messen()` liefert die aktuelle QA-Note (010), `reparieren()` behebt die Befunde.
So bleibt der Loop pro Ebene (Inventar/Artefakte/Guide) identisch testbar.
"""
import logging
log = logging.getLogger("creator.auto_loop")
VOLL = 10.0 # 100 % = 10.0/10
MAX_ITER = 10 # Sicherheitslimit: mehr Runden ⇒ etwas im System stimmt nicht
async def auto_repair_loop(ebene: str, erst_note: float, reparieren, max_iter: int = MAX_ITER) -> dict:
"""Loop QA→Repair für eine Ebene.
erst_note : float — bereits gemessene QA-Note vor dem ersten Repair (010).
reparieren() -> (float, bool): behebt die Befunde UND gibt die neu gemessene Note
zurück (die Repair-Bausteine messen ohnehin am Ende — keine Doppel-
messung) plus „bewegt": ob die Runde etwas getan hat (Fixes, Merges,
Freisprüche, Nach-Recherche). Eine bewegte Runde darf die Note auch
mal transient senken (neue Blöcke → neue Verdachte) — Stillstand ist
erst, wenn NICHTS mehr passiert UND die Note nicht steigt.
Stopp bei: Note == VOLL (fertig), unbewegt ohne Verbesserung (stillstand),
oder max_iter Runden (limit — begrenzt „bewegt ohne Notengewinn"-Livelocks).
Returns {ebene, note, runden, grund} mit grund ∈ {"fertig","stillstand","limit"}.
"""
if erst_note >= VOLL:
return {"ebene": ebene, "note": erst_note, "runden": 0, "grund": "fertig"}
note = erst_note
for runde in range(1, max_iter + 1):
neu, bewegt = await reparieren()
log.info("[%s] Auto-Loop Runde %d: %.1f%.1f (%s)",
ebene, runde, note, neu, "bewegt" if bewegt else "unbewegt")
if neu >= VOLL:
return {"ebene": ebene, "note": neu, "runden": runde, "grund": "fertig"}
if not bewegt and neu <= note: # nichts getan UND keine Verbesserung ⇒ kein Repair-Pfad
return {"ebene": ebene, "note": neu, "runden": runde, "grund": "stillstand"}
note = max(note, neu)
return {"ebene": ebene, "note": note, "runden": max_iter, "grund": "limit"}