From d7201736f2075ccd3390165e65a2babea6691b5f Mon Sep 17 00:00:00 2001 From: Chaim Date: Sun, 28 Jun 2026 21:24:24 +0000 Subject: [PATCH] fix(writer): always feed canonical anti-patterns to the writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The learning loop measured anti-patterns but never corrected them. style_distance detects markdown headers / bullet lists / mid-paragraph mini-lists (from the canonical lessons.ANTI_PATTERNS), but the writer only received anti-pattern guidance if a chair `anti_patterns` override existed in appeal_type_rules — and none does. So _build_style_context's `if ov:` branch injected nothing, the writer was never told to avoid them, and drafts kept emitting them (8137: 28 hits, the worst, newest — anti-patterns were trending UP, not down). Anti-patterns are structural invariants of Dafna's voice (continuous legal narrative — no markdown, no bullets), not overridable preferences. So inject the canonical ANTI_PATTERNS notes ALWAYS, from the same list style_distance measures against (single source of truth), with any chair additions layered on top. This closes the measure-but-don't-correct gap: the next draft should show the markdown/ bullet anti-patterns drop, and Path A (style_distance_history) will confirm it. Invariants: G1 (correct at source — the writer, not a post-hoc stripper), G2 (one canonical anti-pattern list shared by detection and instruction — no parallel list), INV-LRN4 (closes the feedback half of the draft↔final loop). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/legal_mcp/services/block_writer.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mcp-server/src/legal_mcp/services/block_writer.py b/mcp-server/src/legal_mcp/services/block_writer.py index 1026a7a..38caccb 100644 --- a/mcp-server/src/legal_mcp/services/block_writer.py +++ b/mcp-server/src/legal_mcp/services/block_writer.py @@ -983,6 +983,22 @@ async def _build_style_context(practice_area: str = "") -> str: ("anti_patterns", "אנטי-דפוסים (להימנע)"), ): ov = await db.get_methodology_overrides(cat) + if cat == "anti_patterns": + # Anti-patterns are STRUCTURAL INVARIANTS of Dafna's style (no + # markdown headers, no bullet lists, no mid-paragraph mini-lists — + # she writes continuous legal narrative). They must reach the writer + # ALWAYS, from the SAME canonical list style_distance measures against + # (lessons.ANTI_PATTERNS) — otherwise the loop detects them but never + # corrects them, and drafts keep emitting them (the gap that left + # 8137 with 28 hits). Chair additions layer on top; they never + # remove the canonical ones. + from legal_mcp.services.lessons import ANTI_PATTERNS as _ANTI + learned.append(f"\n**{label} — כתוב נרטיב משפטי רציף; הימנע מ:**") + for ap in _ANTI: + learned.append(f"- {ap['note']}") + for k, v in (ov or {}).items(): + learned.append(f"- (יו\"ר) {k}: {json.dumps(v, ensure_ascii=False)}") + continue if ov: learned.append(f"\n**{label} — ערכי היו\"ר (גוברים על ברירת-המחדל):**") for k, v in ov.items():