"""Runner-level wave-5b tests: suppression path and zero-image guard.""" import backend.agents.runner as runner_mod from backend.agents.base import AgentResult from backend.agents.runner import run_agent_pipeline def _finding(sheets): return { "issue_id": "C1", "severity": "critical", "confidence": "high", "source_stage": "constructability", "sheets": sheets, "description": "HSS16x4 on (2) 2x6 STUD PACK is unbuildable", "evidence": [{"sheet": sheets[0], "source_text": "(2) 2x6 STUD PACK"}], } def _stub_agent(artifacts): return lambda usage: type("S", (), { "name": "stub", "run": lambda self, scope: AgentResult( scope_id=scope.scope_id, artifacts=list(artifacts)), })() def _patch_pipeline(monkeypatch, finding): monkeypatch.setattr( runner_mod, "convert_pdf_to_images", lambda path: [{"page_number": 1, "base64": "QUJD"}]) monkeypatch.setattr(runner_mod, "SheetExtractorAgent", _stub_agent([ {"sheet_number": "S401", "page_number": 1, "level": "roof", "discipline": "S", "assertions": [ {"text": "(2) 2x6 STUD PACK", "object_type": "framing"}, {"text": "HSS16X4 beam", "object_type": "framing"}, ]}, ])) monkeypatch.setattr(runner_mod, "SheetIndexAgent", _stub_agent([{}])) monkeypatch.setattr(runner_mod, "JurisdictionAgent", _stub_agent([{}])) monkeypatch.setattr(runner_mod, "LinkerAgent", _stub_agent([ {"key": "c1", "location": "roof beam pocket", "assertions": []}, ])) monkeypatch.setattr(runner_mod, "ConflictCriticAgent", _stub_agent([])) monkeypatch.setattr(runner_mod, "CodeAgent", _stub_agent([])) monkeypatch.setattr(runner_mod, "ConstructabilityAgent", _stub_agent([finding])) monkeypatch.setattr(runner_mod, "CompletenessAgent", _stub_agent([])) monkeypatch.setattr( runner_mod, "BrainAgent", lambda usage: type("B", (), { "run": lambda self, findings, sheet_index, jurisdiction: (list(findings), [])})()) def test_refuted_finding_is_suppressed_not_crash(monkeypatch, tmp_path): """Regression: memory.replace("suppressed", ...) must not KeyError.""" _patch_pipeline(monkeypatch, _finding(["S401"])) monkeypatch.setattr( "backend.agents.verifier.call_json", lambda **kwargs: {"verdicts": [ {"sheet": "S401", "source_text": "(2) 2x6 STUD PACK", "verdict": "corrected", "actual_text": "(5) 2x6 STUD PACK", "notes": "callout reads (5)"}, ]}) pdf = tmp_path / "dummy.pdf" pdf.write_bytes(b"%PDF-1.4\n") report = run_agent_pipeline(str(pdf), out_dir=str(tmp_path), require_review=False) assert [f["issue_id"] for f in report["suppressed_issues"]] == ["C1"] assert report["suppressed_issues"][0]["verification"]["status"] == "refuted" def test_zero_image_finding_is_not_suppressed(monkeypatch, tmp_path): """A finding whose sheets resolve to no page images must not be judged (and must never be refuted) without pixels.""" _patch_pipeline(monkeypatch, _finding(["S999"])) # no such sheet monkeypatch.setattr( "backend.agents.verifier.call_json", lambda **kwargs: {"verdicts": [ {"sheet": "S999", "source_text": "(2) 2x6 STUD PACK", "verdict": "not_found", "actual_text": None, "notes": None}, ]}) pdf = tmp_path / "dummy.pdf" pdf.write_bytes(b"%PDF-1.4\n") report = run_agent_pipeline(str(pdf), out_dir=str(tmp_path), require_review=False) assert report["suppressed_issues"] == [] validated = report.get("validated_issues") or [] assert any(f.get("issue_id") == "C1" for f in validated)