diff --git a/backend/agents/linker.py b/backend/agents/linker.py index 0eb6635..2b25a7b 100644 --- a/backend/agents/linker.py +++ b/backend/agents/linker.py @@ -38,7 +38,7 @@ def _xref_keys(assertion: Dict) -> List[str]: if ref: keys.append(f"detail:{ref}") tag = re.sub(r"\s+", "", str(location.get("tag") or "")).upper() - if re.match(r"^[A-Z]{2,}\d", tag): # member marks: HSS16X4X5/8, W12X26, ... + if re.match(r"^[A-Z]+\d", tag): # member marks: W12X26, HSS16X4X5/8, ... keys.append(f"tag:{tag}") return keys @@ -76,9 +76,12 @@ def build_link_scopes(sheets: List[Dict]) -> List[AgentScope]: sheets_present = {a.get("sheet_number") for a in assertions} if len(assertions) < 2 or len(sheets_present) < 2: continue + chunk = assertions[:cap] + if len({a.get("sheet_number") for a in chunk}) < 2: + continue # cap landed on a single sheet — xref adds nothing scopes.append(AgentScope( scope_id=f"xref:{key}", - payload={"assertions": assertions[:cap], + payload={"assertions": chunk, "level": "xref", "family": key}, )) return scopes diff --git a/tests/agents/test_linker_xref.py b/tests/agents/test_linker_xref.py index a236934..031c395 100644 --- a/tests/agents/test_linker_xref.py +++ b/tests/agents/test_linker_xref.py @@ -45,3 +45,30 @@ def test_xref_scope_joins_shared_member_tag(): 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"