"""Flag-gating tests: ENABLE_CODE_REVIEW off skips code, drawing_integrity runs. Runner-level smoke tests using stubbed agents (same pattern as test_wave5b_suppression). Verifies the code/ADA wave is skipped when ENABLE_CODE_REVIEW is false and the Drawing Integrity wave feeds findings into the report by_stage counters. """ import backend.agents.runner as runner_mod from backend import config from backend.agents.base import AgentResult from backend.agents.runner import run_agent_pipeline 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 _integrity_finding(): return { "issue_id": "DI-1", "severity": "high", "confidence": "high", "source_stage": "drawing_integrity", "sheets": ["A101"], "category": "dangling_reference", "description": "Detail callout 5/A101 has no detail 5 on this sheet", "evidence": [{"sheet": "A101", "source_text": "5/A101"}], } def _patch(monkeypatch, code_should_raise): monkeypatch.setattr( runner_mod, "convert_pdf_to_images", lambda path: [{"page_number": 1, "base64": "QUJD"}]) # Sheet has 3+ assertions so the integrity wave does NOT skip it. monkeypatch.setattr(runner_mod, "SheetExtractorAgent", _stub_agent([ {"sheet_number": "A101", "page_number": 1, "level": "1", "discipline": "A", "assertions": [ {"text": "5/A101", "object_type": "detail_marker"}, {"text": "ROOM 101", "object_type": "room"}, {"text": "DOOR 101A", "object_type": "door"}, ]}, ])) monkeypatch.setattr(runner_mod, "SheetIndexAgent", _stub_agent([{}])) monkeypatch.setattr(runner_mod, "JurisdictionAgent", _stub_agent([{}])) monkeypatch.setattr(runner_mod, "LinkerAgent", _stub_agent([])) monkeypatch.setattr(runner_mod, "ConflictCriticAgent", _stub_agent([])) def code_boom(usage): if code_should_raise: raise AssertionError("CodeAgent must not run when gated off") return _stub_agent([])(usage) monkeypatch.setattr(runner_mod, "CodeAgent", code_boom) monkeypatch.setattr(runner_mod, "DrawingIntegrityAgent", _stub_agent([_integrity_finding()])) monkeypatch.setattr(runner_mod, "ConstructabilityAgent", _stub_agent([])) monkeypatch.setattr(runner_mod, "CompletenessAgent", _stub_agent([])) monkeypatch.setattr( runner_mod, "BrainAgent", lambda usage: type("B", (), { "run": lambda self, findings, si, ju: (list(findings), []), "plan_clarifications": lambda self, prioritized: []})()) # Stub the wave-5b verifier so the high-severity integrity finding is # confirmed (never a live network call). monkeypatch.setattr( "backend.agents.verifier.call_json", lambda **kwargs: {"verdicts": [ {"sheet": "A101", "source_text": "5/A101", "verdict": "confirmed", "actual_text": None, "notes": None}, ]}) def test_code_gated_off_integrity_on(monkeypatch, tmp_path): monkeypatch.setattr(config, "ENABLE_CODE_REVIEW", False) monkeypatch.setattr(config, "ENABLE_DRAWING_INTEGRITY", True) _patch(monkeypatch, code_should_raise=True) pdf = tmp_path / "d.pdf" pdf.write_bytes(b"%PDF-1.4\n") report = run_agent_pipeline(str(pdf), out_dir=str(tmp_path), require_review=False) by_stage = report["summary"]["by_stage"] assert by_stage["code"] == 0 assert by_stage["drawing_integrity"] == 1 # The integrity finding survived into the validated set. assert any(f.get("issue_id") == "DI-1" for f in report.get("validated_issues") or []) def test_code_enabled_runs(monkeypatch, tmp_path): monkeypatch.setattr(config, "ENABLE_CODE_REVIEW", True) monkeypatch.setattr(config, "ENABLE_DRAWING_INTEGRITY", True) _patch(monkeypatch, code_should_raise=False) # build_code_scopes runs on the real sheet; CodeAgent is stubbed to [] pdf = tmp_path / "d.pdf" pdf.write_bytes(b"%PDF-1.4\n") report = run_agent_pipeline(str(pdf), out_dir=str(tmp_path), require_review=False) # No crash; integrity still reported. assert report["summary"]["by_stage"]["drawing_integrity"] == 1