feat(halachot): Phase 5 — canonical panel UI + instances accordion (V41)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
Lint — undefined names / undefined-names (pull_request) Successful in 10s

UI changes to halacha-review-panel.tsx:
- instance_type badge (עיקרון מקורי / ציטוט / יישום) in meta row
- "מוזכר ב-N פסיקות" pill when instance_count > 1
- CanonicalSection component: canonical_statement (view + edit), instances accordion

Backend:
- list_halachot SQL: adds canonical_id, instance_type, canonical_statement,
  instance_count via LEFT JOIN canonical_halachot
- list_canonical_instances(canonical_id) → compact rows for accordion
- GET /api/canonical-halachot/{canonical_id}/instances endpoint
- PATCH /api/halachot/{id}: canonical_statement propagates to canonical_halachot
- HalachaUpdateRequest: canonical_statement field
- useCanonicalInstances hook + CanonicalInstance type in precedent-library.ts

INV-G10 (chair gate): only the chair can edit canonical_statement (same
flow as rule_statement — PATCH /api/halachot/{id} with reviewer="דפנה").
G2: canonical data flows through canonical_halachot, not a parallel store.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 05:41:24 +00:00
parent 75f40cc778
commit dd2e12f902
4 changed files with 218 additions and 1 deletions

View File

@@ -5494,12 +5494,15 @@ async def list_halachot(
h.cites, h.confidence, h.quote_verified, h.quality_flags,
h.review_status,
h.reviewer, h.reviewed_at, h.created_at, h.updated_at,
h.canonical_id, h.instance_type,
ch.canonical_statement, ch.instance_count,
cl.case_number, cl.case_name, cl.court, cl.date AS decision_date,
cl.precedent_level,
COALESCE(cor.corroboration_count, 0)::int AS corroboration_count,
COALESCE(cor.corroboration_negative, false) AS corroboration_negative,
pr.verdict AS panel_verdict
FROM halachot h
LEFT JOIN canonical_halachot ch ON ch.id = h.canonical_id
LEFT JOIN case_law cl ON cl.id = h.case_law_id
LEFT JOIN (
SELECT halacha_id,
@@ -6144,6 +6147,21 @@ async def update_canonical_statement(
return result.split()[-1] != "0"
async def list_canonical_instances(canonical_id: "UUID") -> list[dict]:
"""List all halachot (instances) sharing a canonical_id — used by the UI accordion."""
pool = await get_pool()
rows = await pool.fetch(
"""SELECT h.id, h.instance_type, h.confidence, h.rule_statement,
cl.case_number, cl.case_name
FROM halachot h
LEFT JOIN case_law cl ON cl.id = h.case_law_id
WHERE h.canonical_id = $1
ORDER BY h.instance_type, cl.case_number""",
canonical_id,
)
return [dict(r) for r in rows]
async def _annotate_equivalents(pool, out: list[dict]) -> None:
"""Attach an `equivalents` list to each row (#84.2) — parallel-authority links.