From 632fe73857496c914b3716d9ed125b0ee300f2cc Mon Sep 17 00:00:00 2001 From: Chaim Date: Sun, 7 Jun 2026 13:55:06 +0000 Subject: [PATCH] feat(goldset): separate court rulings from committee decisions in tagging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tagging is easier one source-type at a time. goldset_list now returns case_law.source_type; the page adds: - a filter (הכל / פסקי דין / ועדת ערר) with live counts, - a group-sort so even in "הכל" all court rulings come first, then all committee decisions, - a per-card source badge (פסק-דין / ועדת ערר). Verified: tsc --noEmit 0; source_type splits the live batch 58 court / 92 committee. Co-Authored-By: Claude Opus 4.8 (1M context) --- mcp-server/src/legal_mcp/services/db.py | 2 +- .../src/components/goldset/goldset-panel.tsx | 50 +++++++++++++++++-- web-ui/src/lib/api/goldset.ts | 1 + 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/mcp-server/src/legal_mcp/services/db.py b/mcp-server/src/legal_mcp/services/db.py index ca38047..2f6369f 100644 --- a/mcp-server/src/legal_mcp/services/db.py +++ b/mcp-server/src/legal_mcp/services/db.py @@ -4340,7 +4340,7 @@ async def goldset_list(batch: str = "default") -> list[dict]: " g.correct_type, g.quote_complete, g.tagged_by, g.tagged_at, " " h.rule_statement, h.supporting_quote, h.reasoning_summary, " " h.rule_type, h.confidence, h.quality_flags, h.review_status, " - " cl.case_number, cl.case_name " + " cl.case_number, cl.case_name, cl.source_type " "FROM halacha_goldset g JOIN halachot h ON h.id = g.halacha_id " "LEFT JOIN case_law cl ON cl.id = h.case_law_id " "WHERE g.batch = $1 ORDER BY g.created_at, g.id", batch, diff --git a/web-ui/src/components/goldset/goldset-panel.tsx b/web-ui/src/components/goldset/goldset-panel.tsx index 280db66..1daa656 100644 --- a/web-ui/src/components/goldset/goldset-panel.tsx +++ b/web-ui/src/components/goldset/goldset-panel.tsx @@ -49,6 +49,17 @@ function cleanCitation(s: string | null | undefined): string { return s.replace(/[‎‏‪-‮⁦-⁩]/g, "").trim(); } +// Source separation (פסקי-דין מול החלטות ועדת-ערר) for convenient tagging. +function sourceLabel(s: string | null): string { + return s === "court_ruling" ? "פסק-דין" + : s === "appeals_committee" ? "ועדת ערר" : "אחר"; +} +const SOURCE_FILTERS: { value: "all" | "court_ruling" | "appeals_committee"; label: string }[] = [ + { value: "all", label: "הכל" }, + { value: "court_ruling", label: "פסקי דין" }, + { value: "appeals_committee", label: "ועדת ערר" }, +]; + function isTagged(it: GoldsetItem): boolean { // Fully tagged only when ALL THREE answers are set — otherwise, in // "hide tagged" mode, a card would vanish the moment is_holding is clicked, @@ -201,6 +212,12 @@ function TagCard({ >
{cleanCitation(it.case_number)} + + {sourceLabel(it.source_type)} + מכונה: {it.rule_type} {it.confidence != null && ( ביטחון {it.confidence.toFixed(2)} @@ -282,13 +299,24 @@ export function GoldsetPanel() { const createSample = useCreateGoldsetSample(batch); const [focusedId, setFocusedId] = useState(null); const [hideTagged, setHideTagged] = useState(false); + const [sourceFilter, setSourceFilter] = + useState<"all" | "court_ruling" | "appeals_committee">("all"); const items = useMemo(() => data?.items ?? [], [data]); const taggedCount = items.filter(isTagged).length; - const visible = useMemo( - () => (hideTagged ? items.filter((i) => !isTagged(i)) : items), - [items, hideTagged], - ); + const sourceCounts = useMemo(() => ({ + court_ruling: items.filter((i) => i.source_type === "court_ruling").length, + appeals_committee: items.filter((i) => i.source_type === "appeals_committee").length, + }), [items]); + const visible = useMemo(() => { + let v = items; + if (sourceFilter !== "all") v = v.filter((i) => i.source_type === sourceFilter); + if (hideTagged) v = v.filter((i) => !isTagged(i)); + // group-sort: כל פסקי-הדין יחד, ואז כל החלטות ועדת-הערר (הפרדה ברורה). + const order = (s: string | null) => + s === "court_ruling" ? 0 : s === "appeals_committee" ? 1 : 2; + return [...v].sort((a, b) => order(a.source_type) - order(b.source_type)); + }, [items, hideTagged, sourceFilter]); const focused = focusedId ? visible.find((i) => i.id === focusedId) ?? null : null; @@ -363,6 +391,20 @@ export function GoldsetPanel() {
+ {/* source separation — פסקי-דין מול החלטות ועדת-ערר */} +
+ {SOURCE_FILTERS.map((s) => ( + + ))} +
+
{taggedCount}/{items.length} תויגו
diff --git a/web-ui/src/lib/api/goldset.ts b/web-ui/src/lib/api/goldset.ts index 7e389ad..060b03c 100644 --- a/web-ui/src/lib/api/goldset.ts +++ b/web-ui/src/lib/api/goldset.ts @@ -28,6 +28,7 @@ export type GoldsetItem = { review_status: string; case_number: string | null; case_name: string | null; + source_type: string | null; // 'court_ruling' | 'appeals_committee' | '' }; export type GoldsetScore = { -- 2.49.1