"""The style invariants must actually REACH the writer. Both tests here cover defects found by the 2026-07-28 model×prompt A/B over the signed finals: the canonical anti-patterns were present in the prompt but buried ~47K chars into style_context (where they measurably failed to bind), and they were rendered inside a try/except that an unrelated DB failure could abort. """ import pytest from legal_mcp.services import block_writer from legal_mcp.services.lessons import ANTI_PATTERNS, anti_pattern_directive def test_directive_renders_every_canonical_anti_pattern(): """One source, two renderings — the directive may not drift from the list style_distance scores against.""" text = anti_pattern_directive() for ap in ANTI_PATTERNS: assert ap["note"] in text, f"missing anti-pattern in directive: {ap['name']}" def test_both_writer_paths_append_the_directive_last(): """write_block (in-process) and get_block_context (legal-writer agent) must both close with the directive — otherwise the two writers drift (G2).""" import inspect src = inspect.getsource(block_writer) for fn in ("async def write_block(", "async def get_block_context("): start = src.index(fn) # bound the search to this function: up to the next top-level def rest = src[start + len(fn):] nxt = rest.find("\nasync def ") body = rest[: nxt if nxt != -1 else len(rest)] assert "anti_pattern_directive()" in body, f"{fn} does not append the style directive" @pytest.mark.asyncio async def test_style_context_keeps_anti_patterns_when_overrides_fail(monkeypatch): """A chair-override outage must not silently un-teach the structural style. Regression: the canonical list used to be emitted inside the overrides loop, so a throw on an EARLIER category (golden_ratios) dropped it entirely. """ async def _boom(*a, **k): raise RuntimeError("methodology table unavailable") async def _empty(*a, **k): return [] # Every DB accessor this function touches is stubbed — the test must not open # a real connection (a live pool here leaks across the shared event loop and # breaks unrelated tests later in the run). monkeypatch.setattr(block_writer.db, "get_style_patterns", _empty) monkeypatch.setattr(block_writer.db, "get_methodology_overrides", _boom) monkeypatch.setattr(block_writer.db, "get_recent_decision_lessons", _empty) ctx = await block_writer._build_style_context("היטל השבחה") assert "נרטיב משפטי רציף" in ctx for ap in ANTI_PATTERNS: assert ap["note"] in ctx, f"anti-pattern dropped on override failure: {ap['name']}"