"""Step 9 tests — fresh_workflow isolation + maker-checker round cap (FakeChatClient, no LLM). Isolation is asserted on received-message CONTENT (not a call counter — the Fase 1 round-2 fix): a fresh workflow per run gives each run a clean thread, so a participant sees ONLY its own project. The round cap is pinned to the EXACT observed count (retiring the 1-turn=1-round off-by-one assumption). Pattern: tests/spikes/test_b_footguns.py + tests/spikes/test_harness.py. """ from collections.abc import Callable from agent_framework import BaseChatClient from spikes._harness import FakeChatClient from portfolio_optimiser.reference_domain import load_reference_projects from portfolio_optimiser.workflow import fresh_workflow def _recording_factory(created: list[FakeChatClient]) -> Callable[[str], BaseChatClient]: def factory(role: str) -> BaseChatClient: client = FakeChatClient(default_reply=f"{role} view") created.append(client) return client return factory async def test_participant_sees_only_its_own_project() -> None: ids = [p.id for p in load_reference_projects()] seen_per_run: list[list[str]] = [] for pid in ids: created: list[FakeChatClient] = [] wf = fresh_workflow(_recording_factory(created), max_rounds=2) await wf.run(f"Evaluate project {pid}.") blob = " ".join(t for c in created for call in c.received_texts for t in call) seen_per_run.append([q for q in ids if q in blob]) # A fresh instance per run -> each participant sees ONLY its own project (B7 mitigation). assert seen_per_run == [[pid] for pid in ids] async def test_never_converging_debate_halts_at_exactly_max_rounds() -> None: created: list[FakeChatClient] = [] def factory(role: str) -> BaseChatClient: client = FakeChatClient(default_reply="still disagreeing, keep debating") created.append(client) return client wf = fresh_workflow(factory, max_rounds=3) await wf.run("Debate a never-ending question.") # with_max_rounds(3) is the hard cap; the debate halts at EXACTLY 3 turns, not <=. assert sum(c.call_count for c in created) == 3 def test_layer1_hitl_option_builds() -> None: def factory(role: str) -> BaseChatClient: return FakeChatClient(default_reply="ok") wf = fresh_workflow(factory, max_rounds=2, enable_layer1_hitl=True) assert wf is not None