feat(plans): מרשם-תכניות קנוני (V38) + נוסח-ציטוט אחיד דטרמיניסטי לבלוק ט
מוסיף ישות קנונית לתכניות בניין-עיר (תב"ע) שחוזרות בין תיקים — SSOT לזהות+תוקף (פרסום למתן תוקף ברשומות + מס' ילקוט-הפרסומים) + משפט-ייעוד — במקום גזירה-מחדש מהשומות בכל תיק. בלוק ט מצטט את התוקף בנוסח אחיד דטרמיניסטי (format_plan_citation), כך שתאריך-פרסום/מס'-ילקוט לעולם לא מהוזים ע"י ה-LLM. - DB: טבלת plans (V38) + CRUD + _normalize_plan_number (G1) + format_plan_citation; upsert idempotent (G3) עם כלל-מיזוג: תוקף מאושר לא נדרס — סתירה נרשמת ב-discrepancies (G10 / אין בליעה שקטה). - services/plans_extractor.py: חילוץ עובדתי (claude CLI מקומי) → pending_review. - block_writer.py: _build_plans_registry_context מזריק משפטי-ציטוט מאושרים בלבד לבלוק ט; תכניות חסרות/לא-מאושרות מסומנות במפורש (לא נבלעות). - tools/plans.py + server.py: extract_plans / plan_get / plan_search / plan_list / plan_upsert / plan_review (שער-יו"ר G10), עם extract/get-symmetry (X9). - scripts/backfill_plans_registry.py: ייבוא מקורפוס-ההחלטות (טיוטות + סופיי-דפנה). - docs: block-schema (בלוק ט), SKILL, spec 02-data-model + 04. Invariants: G1/INV-DM2/X1 (מזהה מנורמל בכתיבה) · G2/INV-DM6 (מקור-אמת יחיד, appraiser_facts ללא שינוי) · G3 (upsert) · INV-DM4/G9 (provenance) · INV-DM5/G10 (review_status) · INV-AH (ציטוט דטרמיניסטי) · G5 (lookup לא קורפוס) · G11/block-schema (נוסח-הציטוט) · X9. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -196,6 +196,11 @@ BLOCK_PROMPTS = {
|
||||
1. **תכניות חלות** — מבנה הירכי: תכניות ארציות → מחוזיות → מקומיות. ציטוט ישיר מהוראות תכנית עם **הדגשה** של מילים מכריעות.
|
||||
2. **תת-פרק היתרים** — כותרת משנה "היתרים" (או "היתרי בנייה שניתנו במקרקעין"). פירוט ההיתרים הרלוונטיים על פי השומות שהוגשו לתיק.
|
||||
|
||||
## ציטוט תכנית ותוקפה (קריטי — מרשם-התכניות):
|
||||
- לכל תכנית, לחלק **הזהות והתוקף** (מספר-התכנית + מתי פורסמה למתן תוקף ברשומות + מס' ילקוט-הפרסומים) — השתמש **ככתבו** במשפט-הציטוט הקנוני המופיע תחת "מרשם-התכניות" למטה. **אל תמציא** תאריך-פרסום או מספר-ילקוט.
|
||||
- את הייעוד והניתוח התכנוני אתה רשאי לנסח בסגנון דפנה; את תאריך-התוקף ומספר-הילקוט — לעולם לא.
|
||||
- אם תכנית זוהתה בתיק אך **חסרה במרשם או טרם אושרה** — הזכר את התכנית בלי לקבוע תאריך-תוקף, ואל תנחש תאריך.
|
||||
|
||||
## כללי ציון סתירות בין שמאים (קריטי):
|
||||
- אם שני שמאים או יותר מסרו מידע שונה על אותה תכנית או היתר — חובה לסמן זאת במפורש בנוסח ניטרלי, למשל:
|
||||
> "יצוין כי שמאי הוועדה ציין כי תכנית פלונית חלה על המקרקעין במלואה, בעוד שמאי העורר סבר כי חלקה של התכנית בלבד חל"
|
||||
@@ -215,6 +220,9 @@ BLOCK_PROMPTS = {
|
||||
## תכניות שזוהו (ממטא-דאטה של מסמכים):
|
||||
{plans_context}
|
||||
|
||||
## מרשם-התכניות — משפטי-ציטוט קנוניים (מקור-אמת לתוקף; השתמש ככתבם):
|
||||
{plans_registry_context}
|
||||
|
||||
## עובדות שמאיות שחולצו (תכניות + היתרים, פרק לכל שמאי):
|
||||
{appraiser_facts_context}
|
||||
|
||||
@@ -333,6 +341,7 @@ async def write_block(
|
||||
claims_context = await _build_claims_context(case_id)
|
||||
direction_context = _build_direction_context(decision)
|
||||
plans_context = await _build_plans_context(case_id)
|
||||
plans_registry_context = await _build_plans_registry_context(case_id)
|
||||
daphna_style_exemplars, case_law_citations, _precedent_case_law_ids = (
|
||||
await _build_precedents_context(case_id, block_id)
|
||||
)
|
||||
@@ -371,6 +380,7 @@ async def write_block(
|
||||
claims_context=claims_context,
|
||||
direction_context=direction_context,
|
||||
plans_context=plans_context,
|
||||
plans_registry_context=plans_registry_context,
|
||||
daphna_style_exemplars=daphna_style_exemplars,
|
||||
case_law_citations=case_law_citations,
|
||||
style_context=style_context,
|
||||
@@ -580,6 +590,60 @@ async def _build_plans_context(case_id: UUID) -> str:
|
||||
return "(לא זוהו תכניות)"
|
||||
|
||||
|
||||
async def _build_plans_registry_context(case_id: UUID) -> str:
|
||||
"""Render chair-APPROVED canonical plan citation sentences from the registry (V38).
|
||||
|
||||
The identity+validity clause is deterministic (db.format_plan_citation), so the
|
||||
writer never invents publication dates or ילקוט numbers (INV-AH). Plans seen in
|
||||
the case but absent from the registry — or present yet not approved — are listed
|
||||
explicitly as gaps, never silently dropped (חוקה §6, אין בליעה שקטה).
|
||||
"""
|
||||
idents: set[str] = set()
|
||||
for f in await db.list_appraiser_facts(case_id, fact_type="plan"):
|
||||
if f.get("identifier"):
|
||||
idents.add(f["identifier"])
|
||||
for doc in await db.list_documents(case_id):
|
||||
metadata = doc.get("metadata") or {}
|
||||
if isinstance(metadata, str):
|
||||
metadata = json.loads(metadata)
|
||||
for p in (metadata.get("references", {}) or {}).get("plans", []):
|
||||
name = (p.get("plan_name") or "").strip()
|
||||
if name:
|
||||
idents.add(name)
|
||||
|
||||
if not idents:
|
||||
return "(לא זוהו תכניות בתיק. הרץ extract_plans ואשר אותן במרשם-התכניות.)"
|
||||
|
||||
approved: list[dict] = []
|
||||
unapproved: list[dict] = []
|
||||
missing: list[str] = []
|
||||
seen_numbers: set[str] = set()
|
||||
for ident in sorted(idents):
|
||||
plan = await db.get_plan_by_number(ident)
|
||||
if plan is None:
|
||||
missing.append(ident)
|
||||
continue
|
||||
if plan["plan_number"] in seen_numbers:
|
||||
continue
|
||||
seen_numbers.add(plan["plan_number"])
|
||||
(approved if plan["review_status"] == "approved" else unapproved).append(plan)
|
||||
|
||||
lines: list[str] = []
|
||||
if approved:
|
||||
lines.append("### משפטי-ציטוט קנוניים (מאושרים — השתמש בהם ככתבם לזהות+תוקף):")
|
||||
for p in approved:
|
||||
lines.append(f"- {p.get('citation_formatted') or db.format_plan_citation(p)}")
|
||||
if unapproved:
|
||||
lines.append('\n### תכניות במרשם שטרם אושרו (אל תצטט מהן תוקף — ממתינות לאישור-יו"ר):')
|
||||
for p in unapproved:
|
||||
lines.append(f"- {p['display_name'] or p['plan_number']} (status={p['review_status']})")
|
||||
if missing:
|
||||
lines.append("\n### תכניות שזוהו בתיק אך חסרות במרשם (הרץ extract_plans ואשר):")
|
||||
for ident in missing:
|
||||
lines.append(f"- {ident}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
APPRAISER_SIDE_LABEL_HE = {
|
||||
"committee": "שמאי הוועדה המקומית",
|
||||
"appellant": "שמאי העורר",
|
||||
@@ -1003,6 +1067,7 @@ async def get_block_context(case_id: UUID, block_id: str, instructions: str = ""
|
||||
claims_context = await _build_claims_context(case_id)
|
||||
direction_context = _build_direction_context(decision)
|
||||
plans_context = await _build_plans_context(case_id)
|
||||
plans_registry_context = await _build_plans_registry_context(case_id)
|
||||
daphna_style_exemplars, case_law_citations, _ = (
|
||||
await _build_precedents_context(case_id, block_id)
|
||||
)
|
||||
@@ -1037,6 +1102,7 @@ async def get_block_context(case_id: UUID, block_id: str, instructions: str = ""
|
||||
claims_context=claims_context,
|
||||
direction_context=direction_context,
|
||||
plans_context=plans_context,
|
||||
plans_registry_context=plans_registry_context,
|
||||
daphna_style_exemplars=daphna_style_exemplars,
|
||||
case_law_citations=case_law_citations,
|
||||
style_context=style_context,
|
||||
|
||||
Reference in New Issue
Block a user