From fc02ccaeff72b97e55e77ef173abbea0f09b0c43 Mon Sep 17 00:00:00 2001 From: Chaim Date: Tue, 16 Jun 2026 07:37:29 +0000 Subject: [PATCH] =?UTF-8?q?feat(missing-precedents):=20=D7=A2=D7=9E=D7=95?= =?UTF-8?q?=D7=93=D7=AA=20"=D7=A6=D7=95=D7=98=D7=98=20=D7=A2"=D7=99"=20?= =?UTF-8?q?=D7=9E=D7=A6=D7=99=D7=92=D7=94=20provenance=20=D7=9C=D7=A4?= =?UTF-8?q?=D7=99=20discovery=5Fsource=20(#148)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 362 רשומות שהובאו מ-cited_only (גרף-הציטוטים) ומיומונים הציגו "—" ב"צוטט ע"י", כי העמודה קראה רק מ-cited_in_case_id (ערר חי) + cited_by_party, וה-provenance נשמר ב-notes בלבד. אושר ע"י חיים דרך שער Claude Design (mockup 09-missing-precedents). - db.list_missing_precedents + get_missing_precedent: שדות-provenance מחושבים (_MP_PROVENANCE_COLS משותף, G2): cited_by_precedents (array_agg מ- precedent_internal_citations עבור cited_only — מי-מצטט את ה-stub) + yomon_number (substring מ-notes עבור digest). discovery_source כבר הוחזר. - web-ui: MissingPrecedent type + תא "צוטט ע"י" מסתעף לפי discovery_source: cited_only→chip "פסיקה בקורפוס" + "מצוטט ע"י: <מספרים>"; digest→chip "יומון" + "מס' X"; manual→SourceChip+צד (כמו היום). טוקני plum/teal ב-globals.css (מה-mockup המאושר). אומת מול ה-DB החי: cited_only→מצטטים (רע"א 1054/21→8047-23,8126-03-25), digest→מס'-יומון (306 רשומות). tsc נקי, eslint נקי, 360 בדיקות mcp עוברות. Invariants: G2 (שדה-provenance יחיד משותף ל-list+detail), G1 (נגזר במקור), INV-IA*/UI (שינוי-עמוד דרך שער Claude Design), G12. Co-Authored-By: Claude Opus 4.8 (1M context) --- mcp-server/src/legal_mcp/services/db.py | 21 +++++++-- web-ui/src/app/globals.css | 5 +++ .../missing-precedents-table.tsx | 44 ++++++++++++++++--- web-ui/src/lib/api/missing-precedents.ts | 4 ++ 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/mcp-server/src/legal_mcp/services/db.py b/mcp-server/src/legal_mcp/services/db.py index 7575286..c0eecca 100644 --- a/mcp-server/src/legal_mcp/services/db.py +++ b/mcp-server/src/legal_mcp/services/db.py @@ -7311,6 +7311,21 @@ async def create_missing_precedent( return _row_to_missing_precedent(row) +# Provenance columns for the "צוטט ע״י" display (#148): for a non-appeal gap the +# cited_in_case_id is NULL, so we surface where it actually surfaced from — +# cited_by_precedents (corpus precedents that cite a cited_only stub, via the +# citation graph) and yomon_number (parsed from a digest gap's notes). Shared by +# the list and detail queries so both render identically (G2). +_MP_PROVENANCE_COLS = """, + (SELECT array_agg(DISTINCT src.case_number ORDER BY src.case_number) + FROM precedent_internal_citations pic + JOIN case_law src ON src.id = pic.source_case_law_id + WHERE pic.cited_case_law_id = mp.linked_case_law_id + AND COALESCE(src.case_number, '') <> '' + ) AS cited_by_precedents, + substring(mp.notes from 'מס''?\\s*([0-9]+)') AS yomon_number""" + + async def list_missing_precedents( status: str | None = None, case_id: UUID | None = None, @@ -7355,7 +7370,7 @@ async def list_missing_precedents( SELECT mp.*, c.case_number AS cited_in_case_number, cl.case_number AS linked_case_law_number, - cl.case_name AS linked_case_law_name + cl.case_name AS linked_case_law_name{_MP_PROVENANCE_COLS} FROM missing_precedents mp LEFT JOIN cases c ON c.id = mp.cited_in_case_id LEFT JOIN case_law cl ON cl.id = mp.linked_case_law_id @@ -7379,11 +7394,11 @@ async def get_missing_precedent(mp_id: UUID) -> dict | None: pool = await get_pool() async with pool.acquire() as conn: row = await conn.fetchrow( - """ + f""" SELECT mp.*, c.case_number AS cited_in_case_number, cl.case_number AS linked_case_law_number, - cl.case_name AS linked_case_law_name + cl.case_name AS linked_case_law_name{_MP_PROVENANCE_COLS} FROM missing_precedents mp LEFT JOIN cases c ON c.id = mp.cited_in_case_id LEFT JOIN case_law cl ON cl.id = mp.linked_case_law_id diff --git a/web-ui/src/app/globals.css b/web-ui/src/app/globals.css index fd6ef72..fa07e70 100644 --- a/web-ui/src/app/globals.css +++ b/web-ui/src/app/globals.css @@ -51,6 +51,11 @@ --color-danger-bg: #f5e6e6; --color-info: #4e6a8c; --color-info-bg: #e6ecf3; + /* provenance chips for missing-precedents "צוטט ע״י" (#148, approved mockup 09) */ + --color-plum: #6d5191; + --color-plum-bg: #efe9f6; + --color-teal: #3f7a7a; + --color-teal-bg: #e3efef; /* ── Typography ─────────────────────────────────────── */ --font-sans: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; diff --git a/web-ui/src/components/missing-precedents/missing-precedents-table.tsx b/web-ui/src/components/missing-precedents/missing-precedents-table.tsx index d1dc157..0ae0399 100644 --- a/web-ui/src/components/missing-precedents/missing-precedents-table.tsx +++ b/web-ui/src/components/missing-precedents/missing-precedents-table.tsx @@ -173,12 +173,44 @@ export function MissingPrecedentsTable({ status, q, legalTopic }: Props) { )} - - {mp.cited_by_party_name ? ( -
- {mp.cited_by_party_name} -
- ) : null} + {mp.discovery_source === "cited_only" ? ( + <> + + פסיקה בקורפוס + + {mp.cited_by_precedents?.length ? ( +
+ מצוטט ע״י: {mp.cited_by_precedents.join(", ")} +
+ ) : null} + + ) : mp.discovery_source === "digest" ? ( + <> + + יומון + + {mp.yomon_number ? ( +
+ מס׳ {mp.yomon_number} +
+ ) : null} + + ) : ( + <> + + {mp.cited_by_party_name ? ( +
+ {mp.cited_by_party_name} +
+ ) : null} + + )}
diff --git a/web-ui/src/lib/api/missing-precedents.ts b/web-ui/src/lib/api/missing-precedents.ts index b1f42f0..12afa3e 100644 --- a/web-ui/src/lib/api/missing-precedents.ts +++ b/web-ui/src/lib/api/missing-precedents.ts @@ -52,6 +52,10 @@ export type MissingPrecedent = { created_at: string; updated_at: string; notes: string | null; + // provenance of the gap (#148) — how it surfaced, for the "צוטט ע״י" column. + discovery_source: string | null; // manual | cited_only | digest | court_fetch + cited_by_precedents: string[] | null; // corpus precedents citing a cited_only stub + yomon_number: string | null; // digest gap's source yomon number }; export type MissingPrecedentListResponse = { -- 2.49.1