fix(writer): כלל-הסגנון בסוף הפרומפט — הוא היה שם, במקום שבו הוא לא תופס
הרשימה הקנונית של lessons.ANTI_PATTERNS כבר הוזרקה לכותב, אבל בתו ~46,781 מתוך 46,950 של style_context — שהוא עצמו מקטע אחד מתוך ~12 בפרומפט. הטיוטות המשיכו לפלוט בדיוק את מה שהיא אוסרת. A/B מדוד מול הסופיים החתומים (9 תיקים, 60 ייצורים, 2026-07-28) הראה שאותו כלל, בסוף הפרומפט, חותך anti_pattern_total ב-72–93%: block-vav opus-4-8 1.75→0.12 · opus-5 2.25→0.62 block-zayin opus-4-8 4.57→0.43 · opus-5 4.43→0.43 וב-distance: −12%/−29% (4-8), −9%/−27% (5). זה שיפור גדול פי-3 מכל הבדל שנמדד בין המודלים עצמם. - `lessons.anti_pattern_directive()` — רינדור שני של אותה רשימה קנונית (מקור אחד, שתי תצוגות — לא שני כללים). - מתווסף **אחרון** בשני מסלולי-הכתיבה: `write_block` (בתהליך) ו- `get_block_context` (סוכן legal-writer). אילו הוחל רק באחד, שני הכותבים היו נפרדים בסגנון (G2). - **תיקון בליעה-שקטה (§6):** הרשימה הקנונית רונדרה בתוך לולאת-ה-overrides, כך שכשל-DB בקטגוריה מוקדמת (golden_ratios) הפיל את הלולאה והשמיט את אינווריאנטי-הסגנון כליל — עם אזהרה גנרית בלבד. עכשיו היא מרונדרת ללא תנאי, לפני כל קריאת-DB; הערות-היו"ר מתווספות מעליה. invariants: G11 (תוכן משפטי — סגנון דפנה) · G2 (מקור-אמת יחיד לכלל, ושני מסלולי-הכתיבה מיושרים) · §6 (אין בליעה שקטה). בדיקות: 473 passed (3 חדשות — רינדור מלא, שני המסלולים, שרידות לכשל-DB). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
60
mcp-server/tests/test_anti_pattern_directive.py
Normal file
60
mcp-server/tests/test_anti_pattern_directive.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""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']}"
|
||||
Reference in New Issue
Block a user