fix(writer): always feed canonical anti-patterns to the writer
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
Lint — undefined names / undefined-names (pull_request) Successful in 10s

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) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 21:24:24 +00:00
parent 4de555367d
commit d7201736f2

View File

@@ -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():