update
This commit is contained in:
@@ -103,12 +103,6 @@ async def board_env(testdb, tmp_path, monkeypatch):
|
||||
("_artefakte_block", fake_artefakte), ("_outline_block", fake_outline)]:
|
||||
monkeypatch.setattr(ba, name, fn)
|
||||
|
||||
class _EmbOff: # Cross-Block-Barrier reicht ohne Modell alle Karten durch
|
||||
@staticmethod
|
||||
def available():
|
||||
return False
|
||||
monkeypatch.setattr(ba, "embedding", _EmbOff)
|
||||
|
||||
work = tmp_path / "arbeit"
|
||||
work.mkdir()
|
||||
files = {"arbeit": work, "final": tmp_path / "blocks.md",
|
||||
@@ -973,8 +967,8 @@ async def test_qa_gate_auto_stillstand_pauses(board_env, monkeypatch):
|
||||
async def bad_qa(topic, llm=False):
|
||||
return {"note": 5.0, "topic": TOPIC, "quoten": {}, "fremd": [], "artefakte": {}}
|
||||
|
||||
async def stuck_repair(topic, ebene=None):
|
||||
return {"note": 5.0} # bewegt nichts
|
||||
async def stuck_repair(topic, ebene=None, luecken_urteil=True):
|
||||
return {"note": 5.0, "aktionen": 0} # bewegt nichts
|
||||
|
||||
monkeypatch.setattr(qa_mod, "qa_report", bad_qa)
|
||||
monkeypatch.setattr(qa_mod, "_write_report", lambda r: None)
|
||||
@@ -988,6 +982,45 @@ async def test_qa_gate_auto_stillstand_pauses(board_env, monkeypatch):
|
||||
assert await db.kanban_count(TOPIC, "done_artefact", board="artefacts") == 0
|
||||
|
||||
|
||||
async def test_qa_gate_luecken_research_runden(board_env, monkeypatch):
|
||||
"""Offene Lücken im Auto-Loop: erst LUECKEN_RESEARCH_MAX Recherche-Runden
|
||||
(Lücken-Urteil ausgesetzt), dann das Urteil — erst danach Stillstand/Pause."""
|
||||
import qa as qa_mod
|
||||
import repair as repair_mod
|
||||
db, ctx, files = board_env
|
||||
gap_report = {"note": 8.0, "topic": TOPIC, "quoten": {}, "fremd": [], "artefakte": {},
|
||||
"luecken": [{"datei": "f.txt", "abschnitt": 1, "vorschau": "x"}],
|
||||
"konzept_luecken": []}
|
||||
|
||||
async def bad_qa(topic, llm=False):
|
||||
return dict(gap_report)
|
||||
|
||||
research_calls = []
|
||||
|
||||
async def fake_producer(ctx2, flow, rep):
|
||||
research_calls.append(1)
|
||||
return 1 # Recherche hat Karten erzeugt → Runde gilt als bewegt
|
||||
|
||||
urteile = []
|
||||
|
||||
async def stuck_repair(topic, ebene=None, luecken_urteil=True):
|
||||
urteile.append(luecken_urteil)
|
||||
return {"note": 8.0, "aktionen": 0}
|
||||
|
||||
monkeypatch.setattr(qa_mod, "qa_report", bad_qa)
|
||||
monkeypatch.setattr(qa_mod, "latest_report", lambda t, guide=False: dict(gap_report))
|
||||
monkeypatch.setattr(qa_mod, "_write_report", lambda r: None)
|
||||
monkeypatch.setattr(bi, "_luecken_producer", fake_producer)
|
||||
monkeypatch.setattr(repair_mod, "repair_befunde", stuck_repair)
|
||||
monkeypatch.setattr(bi, "_QA_GATE_POLL", 0.05)
|
||||
await _seed(db)
|
||||
ok = await _run_flow(ctx, files)
|
||||
assert ok
|
||||
assert len(research_calls) == 2 # LUECKEN_RESEARCH_MAX
|
||||
assert urteile[:2] == [False, False] and urteile[2] is True
|
||||
assert await db.kanban_count(TOPIC, "done_artefact", board="artefacts") == 0 # pausiert
|
||||
|
||||
|
||||
async def test_qa_gate_auto_reaches_100(board_env, monkeypatch):
|
||||
"""Auto an, Repair hebt die Note auf 100 % → Gate öffnet, Board 2 läuft durch."""
|
||||
import qa as qa_mod
|
||||
@@ -997,8 +1030,8 @@ async def test_qa_gate_auto_reaches_100(board_env, monkeypatch):
|
||||
async def bad_qa(topic, llm=False):
|
||||
return {"note": 8.0, "topic": TOPIC, "quoten": {}, "fremd": [], "artefakte": {}}
|
||||
|
||||
async def good_repair(topic, ebene=None):
|
||||
return {"note": 10.0}
|
||||
async def good_repair(topic, ebene=None, luecken_urteil=True):
|
||||
return {"note": 10.0, "aktionen": 1}
|
||||
|
||||
monkeypatch.setattr(qa_mod, "qa_report", bad_qa)
|
||||
monkeypatch.setattr(qa_mod, "_write_report", lambda r: None)
|
||||
@@ -1019,7 +1052,7 @@ async def test_qa_gate_auto_off_pauses(board_env, monkeypatch):
|
||||
async def good_qa(topic, llm=False):
|
||||
return {"note": 10.0, "topic": TOPIC, "quoten": {}, "fremd": [], "artefakte": {}}
|
||||
|
||||
async def no_repair(topic, ebene=None):
|
||||
async def no_repair(topic, ebene=None, luecken_urteil=True):
|
||||
raise AssertionError("Auto aus darf nicht reparieren")
|
||||
|
||||
monkeypatch.setattr(qa_mod, "qa_report", good_qa)
|
||||
@@ -1094,6 +1127,11 @@ def test_qa_view_pausiert_logic(tmp_path, monkeypatch):
|
||||
counts = {"inventory": {"done_block": 3}, "artefacts": {"generate": 4}}
|
||||
v = bi._qa_view(TOPIC, counts, None)
|
||||
assert v["pausiert"] is True and v["note"] == 5.0 and v["befunde"] == ["X", "Y"]
|
||||
# 9.6 lag über QA_GATE_NOTE, pausiert den Auto-Loop aber trotzdem (Gate öffnet nur bei VOLL)
|
||||
(tmp_path / TOPIC / "r1.json").write_text(_json.dumps(
|
||||
{"note": 9.6, "quoten": {}, "fremd": [], "unecht": []}), encoding="utf-8")
|
||||
qa_mod._latest_cache.clear()
|
||||
assert bi._qa_view(TOPIC, counts, None)["pausiert"] is True
|
||||
from types import SimpleNamespace
|
||||
laufend = SimpleNamespace(state={})
|
||||
assert bi._qa_view(TOPIC, counts, laufend)["pausiert"] is False # Flow läuft noch
|
||||
@@ -1515,3 +1553,27 @@ async def test_sanierung_anker_und_beschreibung_ok_kein_judge(testdb, tmp_path,
|
||||
ctx = GenContext(topic=TOPIC, provider="claude", is_cancelled=lambda: False)
|
||||
await bi._name_one(ctx, _mk_flow(tmp_path), {"card_id": "c4", "payload": payload})
|
||||
assert (await db.kanban_get_card(TOPIC, B, "c4"))["stage"] == "naming_check"
|
||||
|
||||
|
||||
def test_themen_zielband_skaliert_mit_atomzahl():
|
||||
"""√n-Band: kleine Themen deutlich unter 15, große über dem alten 18er-Cluster-Cap."""
|
||||
lo30, hi30 = bi._themen_zielband(30)
|
||||
assert lo30 >= 2 and hi30 < 15
|
||||
lo285, hi285 = bi._themen_zielband(285)
|
||||
assert hi285 > 18
|
||||
lo4, hi4 = bi._themen_zielband(4)
|
||||
assert lo4 >= 2 and hi4 > lo4
|
||||
# Monotonie: mehr Items → nie kleineres Band
|
||||
prev = (0, 0)
|
||||
for n in (4, 30, 100, 285, 1000):
|
||||
band = bi._themen_zielband(n)
|
||||
assert band >= prev
|
||||
prev = band
|
||||
|
||||
|
||||
def test_gruppierung_prompt_rendert_zielband(tmp_path):
|
||||
"""Template↔Call-Site-Vertrag: alle Platzhalter versorgt, Band-String im Prompt."""
|
||||
from pipeline import _prompt
|
||||
p = _prompt("Blocks-Gruppierung", topic="t", candidates="x", list="1. a",
|
||||
out_path=tmp_path / "g.json", theme_lo=4, theme_hi=8, n_items=9)
|
||||
assert "~4–8 themes" in p and "(9 items)" in p
|
||||
|
||||
Reference in New Issue
Block a user