from backend.agents.base import AgentScope from backend.agents.linker import build_link_scopes def _sheet(number, page, level, assertions): return {"sheet_number": number, "page_number": page, "discipline": "Structural", "level": level, "assertions": assertions} def _assertion(id_, ref=None, tag=None, level=None): return {"id": id_, "attribute": "stud_pack_size", "value": "(5) 2x6", "source_text": "(5) 2x6 STUD PACK", "location_key": {"detail_reference": ref, "tag": tag, "level": level}} def test_xref_scope_joins_same_detail_reference_across_levels(): sheets = [ _sheet("S101", 10, "foundation", [_assertion("a1", ref="A/S205")]), _sheet("S205", 20, "roof", [_assertion("a2", ref="A/S205")]), _sheet("S401", 30, "roof", [_assertion("a3", ref="A/S205")]), ] scopes = build_link_scopes(sheets) xref = [s for s in scopes if s.scope_id.startswith("xref:")] assert xref, "expected a cross-level detail-reference scope" ids = {a["id"] for s in xref for a in s.payload["assertions"]} assert ids == {"a1", "a2", "a3"} def test_xref_scope_requires_two_distinct_sheets(): sheets = [ _sheet("S401", 30, "roof", [_assertion("a1", ref="A/S205"), _assertion("a2", ref="A/S205")]), ] scopes = build_link_scopes(sheets) assert not [s for s in scopes if s.scope_id.startswith("xref:")] def test_xref_scope_joins_shared_member_tag(): sheets = [ _sheet("S102", 5, "roof", [_assertion("a1", tag="HSS16X4X5/8")]), _sheet("S401", 30, "unknown", [_assertion("a2", tag="HSS16X4X5/8")]), ] scopes = build_link_scopes(sheets) xref = [s for s in scopes if s.scope_id.startswith("xref:")] assert xref def test_xref_scope_joins_single_letter_member_mark(): # W-shapes (W12X26) are the most common steel marks and have one leading letter sheets = [ _sheet("S102", 5, "roof", [_assertion("a1", tag="W12X26")]), _sheet("S401", 30, "unknown", [_assertion("a2", tag="W12X26")]), ] scopes = build_link_scopes(sheets) xref = [s for s in scopes if s.scope_id.startswith("xref:")] assert xref, "single-letter member marks (W12X26) must join xref scopes" def test_xref_scope_rechecks_sheet_diversity_after_cap(monkeypatch): from backend import config monkeypatch.setattr(config, "AGENT_LINK_MAX_ASSERTIONS", 2) sheets = [ _sheet("S401", 30, "roof", [_assertion("a1", ref="A/S205"), _assertion("a2", ref="A/S205")]), _sheet("S205", 20, "roof", [_assertion("a3", ref="A/S205")]), ] scopes = build_link_scopes(sheets) xref = [s for s in scopes if s.scope_id.startswith("xref:")] for scope in xref: sheets_in_scope = {a["sheet_number"] for a in scope.payload["assertions"]} assert len(sheets_in_scope) >= 2, \ "capped xref scope must still span two sheets"