This commit is contained in:
team3
2026-07-06 17:32:52 +02:00
parent cc27e53b9e
commit 73ac3e532f
19 changed files with 565 additions and 523 deletions

39
backend/auto_loop.py Normal file
View File

@@ -0,0 +1,39 @@
"""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: behebt die Befunde UND gibt die neu gemessene Note zurück
(die Repair-Bausteine messen ohnehin am Ende — keine Doppelmessung).
Stopp bei: Note == VOLL (fertig), Note verbessert sich nicht mehr (stillstand),
oder max_iter Runden (limit). Returns {ebene, note, runden, grund} mit
grund ∈ {"fertig","stillstand","limit"} — der Aufrufer nutzt grund für die
Fortschritts-/Systemfehler-Meldung.
"""
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 = await reparieren()
log.info("[%s] Auto-Loop Runde %d: %.1f%.1f", ebene, runde, note, neu)
if neu >= VOLL:
return {"ebene": ebene, "note": neu, "runden": runde, "grund": "fertig"}
if neu <= note: # keine Verbesserung ⇒ weitere Runden zwecklos (kein Repair-Pfad)
return {"ebene": ebene, "note": neu, "runden": runde, "grund": "stillstand"}
note = neu
return {"ebene": ebene, "note": note, "runden": max_iter, "grund": "limit"}