feat: refocus on drawings — code/ADA gated off, drawing-integrity wave, Brain-directed clarification
- ENABLE_CODE_REVIEW flag (default off): skips code/ADA/jurisdiction review path in both pipelines; nothing deleted, one env flag to restore. - Per-sheet Drawing Integrity QA wave (agent + classic, default on): dangling refs, on-sheet contradictions, dimension sanity, missing sheet essentials, tag hygiene. New DrawingIntegrityAgent + classic stage. - Broadened conflict critic: intra-sheet + same-discipline contradictions, not just cross-discipline. - Wave 6.5 Brain-directed clarification (bounded hub-and-spoke): Brain names uncertain findings, verify_evidence requests route through the wave-5b verifier; refuted findings suppressed. One planning call + capped verifies, single iteration. Shared _build_verify_scopes across 5b and 6.5. - Config knobs, .env.example, frontend copy, tests (182 passing).
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user