"""Classic pipeline path must also satisfy the {disputes} placeholder added to CONSTRUCTABILITY_USER_INSTRUCTION (agent path substitutes it in construct_agent.py; the classic stage builds its own subs dict).""" from unittest.mock import patch from backend.pipeline._stage import render from backend.pipeline.constructability import constructability_review from backend.prompts import CONSTRUCTABILITY_USER_INSTRUCTION def _cluster_with_dispute(): return { "key": "c1", "assertions": [], "disputed_attributes": [{ "attribute": "stud_pack_size", "values": ["(2) 2x6 STUD PACK", "(5) 2x6 STUD PACK"], "assertion_ids": ["a1", "a2"], }], } def test_classic_constructability_supplies_disputes_sub(): captured = {} def fake_call_stage(system_prompt, user_instruction, subs=None, **kwargs): captured["subs"] = subs or {} return {"issues": []} with patch("backend.pipeline.constructability.call_stage", fake_call_stage): constructability_review([], [_cluster_with_dispute()], []) assert "disputes" in captured["subs"], "classic path must substitute {disputes}" rendered = render(CONSTRUCTABILITY_USER_INSTRUCTION, captured["subs"]) assert "{disputes}" not in rendered assert "(5) 2x6 STUD PACK" in rendered def test_classic_constructability_disputes_defaults_empty(): captured = {} def fake_call_stage(system_prompt, user_instruction, subs=None, **kwargs): captured["subs"] = subs or {} return {"issues": []} with patch("backend.pipeline.constructability.call_stage", fake_call_stage): constructability_review([], [{"key": "c2", "assertions": []}], []) rendered = render(CONSTRUCTABILITY_USER_INSTRUCTION, captured["subs"]) assert "{disputes}" not in rendered