Files
legal-ai/web-ui/src/components/precedents/practice-area.ts
Chaim f3cc9ca9d4
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
feat: Stage A finalizers + #35/#36/#37 — critical-gap closure
Four parallel sub-agents closed the remaining critical gaps from the
26/05 Stage A/B sprint. Each block independently tested; aggregated here.

## #30/#31 finalizers (sub-agent A)
* Auto-derive practice_area in case_create from case_number prefix
  (1xxx→rishuy_uvniya, 8xxx→betterment_levy, 9xxx→compensation_197);
  default for CaseCreateRequest is now "" (the DB constraint catches
  any stray "appeals_committee").
* practice_area.py: derive_subtype now handles axis-B domain values
  (rishuy_uvniya/betterment_levy/compensation_197) without parsing the
  case number; new helper derive_domain_practice_area().
* Halacha re-extraction verified unnecessary — all 6 reclassified
  records already had is_binding=false and approved halachot.
* Regression tests: 6 cases in tests/test_corpus_constraints.py
  covering practice_area enum, internal-committee chair/district,
  external-upload arar prefix, MCP guard.
* UI: district input → Select dropdown (7 districts) in
  precedent-edit-sheet.tsx, preserving legacy free-text values.

## #37 בל"מ subtypes (sub-agent B)
* 3 new appeal_subtypes: extension_request_{building_permit,
  betterment_levy,compensation}. APPEALS_COMMITTEE_SUBTYPES extended,
  SUBTYPES_BY_AREA mappings added.
* New helpers: is_blam_subject(), is_blam_subtype(),
  derive_subtype_with_blam(case_number, subject, practice_area).
  case_create now uses it to auto-detect "בקשה להארכת מועד" subjects.
* 3 methodology templates under docs/methodology/extension-request-*.md.
* paperclip_client.py mapping updated for the 3 new subtypes
  (extension_request_building_permit→CMP, the other two→CMPA).
* Frontend: bilingual "בל"מ" badge + filter dropdown on cases list +
  detail header; appeal-type-bars collapseBlam() merges בל"מ into its
  parent domain for aggregate bars.
* Wizard auto-detects בל"מ from subject during case creation.
* 3 Berlinger cases (1017/1018/1019-03-26) migrated to
  appeal_subtype=extension_request_building_permit via psql.

## #35 missing_precedents feature (sub-agent C)
* Schema V13: missing_precedents table (citation, case_id, party,
  legal_topic, status, linked_case_law_id, claim_quote, ...) +
  FK constraints + 3 indexes. Applied via psql + idempotent migration.
* 6 db.py service functions, 3 MCP tools, 6 FastAPI endpoints
  (POST/GET/PATCH/DELETE/upload — upload routes by citation prefix
  to ingest_internal_decision or ingest_precedent).
* Next.js page /missing-precedents with 5 status tabs + filters +
  sidebar badge counter + detail drawer with metadata edit + smart
  upload form that switches fields per committee/court.
* Bootstrap: 7 rows imported from the JSON file
  (3 citations × cases, all status=closed with linked_case_law_id).
* legal-researcher.md: new §2ב.5 with missing_precedent_create
  usage + dedup semantics + tool grant.

## #36 legal_arguments aggregation (sub-agent D)
* Schema V14: legal_arguments + legal_argument_propositions M:M.
  Applied via psql.
* New service argument_aggregator.py with two functions —
  aggregate_claims_to_arguments() (Claude CLI / claude_session) and
  get_legal_arguments(). Graceful llm_unavailable handling when CLI
  is missing (containers).
* 2 MCP tools + 2 API endpoints (POST .../aggregate-arguments as
  BackgroundTask, GET .../legal-arguments).
* Frontend: shadcn Accordion + new legal-arguments-panel.tsx with
  hierarchical (party → priority badge → arguments) display, "טיעונים"
  tab on the case page, "חשב/חשב מחדש" buttons.
* scripts/backfill_legal_arguments.py + SCRIPTS.md entry — dry-run
  found 8 candidate cases including 1017/1018/1019.

## Open follow-ups (intentionally deferred)
* npm run api:types in web-ui (CLAUDE.md flow) — recommended before
  the next UI commit; not required for backend deployment.
* Run backfill_legal_arguments.py --apply once the container picks up
  the new aggregator service.
* webhook on missing-precedents upload-close to Paperclip (optional).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 08:34:40 +00:00

67 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Practice-area constants for the precedent library.
*
* The chair confined the library to the three appeals committee
* domains — no national-insurance corpus. The DB enforces this
* via a CHECK constraint on case_law.practice_area.
*/
export const PRACTICE_AREAS = [
{ value: "rishuy_uvniya", label: "רישוי ובניה", short: "רישוי" },
{ value: "betterment_levy", label: "היטל השבחה", short: "השבחה" },
{ value: "compensation_197", label: "פיצויים לפי ס' 197", short: "פיצויים" },
] as const;
export const PRECEDENT_LEVELS = [
{ value: "עליון", label: "עליון" },
{ value: "מנהלי", label: "מנהלי" },
{ value: "ועדת_ערר_ארצית", label: "ועדת ערר ארצית" },
{ value: "ועדת_ערר_מחוזית", label: "ועדת ערר מחוזית" },
] as const;
export const SOURCE_TYPES = [
{ value: "court_ruling", label: "פסק דין" },
{ value: "appeals_committee", label: "החלטת ועדת ערר" },
] as const;
/**
* Districts for ועדות ערר. The chair's committee is ירושלים; the rest
* are listed so that uploaded precedents from peer committees can be
* filed correctly. Order matches what's displayed in the UI dropdown.
*/
export const DISTRICTS = [
{ value: "ירושלים", label: "ירושלים" },
{ value: "מרכז", label: "מרכז" },
{ value: "תל אביב", label: "תל אביב" },
{ value: "צפון", label: "צפון" },
{ value: "דרום", label: "דרום" },
{ value: "חיפה", label: "חיפה" },
{ value: "ארצי", label: "ארצי" },
] as const;
export function practiceAreaLabel(value: string | null | undefined): string {
if (!value) return "—";
const match = PRACTICE_AREAS.find((p) => p.value === value);
return match ? match.label : value;
}
export function practiceAreaShort(value: string | null | undefined): string {
if (!value) return "—";
const match = PRACTICE_AREAS.find((p) => p.value === value);
return match ? match.short : value;
}
/**
* Display label for ``appeal_subtype``. The field is free text — the chair
* usually types specific subjects like "תכנית רחביה" or "סופיות ההחלטה" —
* but the LLM extractor sometimes mistakenly stuffs the practice_area
* enum value (``betterment_levy``, ``rishuy_uvniya``, ``compensation_197``)
* in there. When that happens, translate to Hebrew so the UI doesn't
* leak English. Otherwise return the value unchanged.
*/
export function appealSubtypeLabel(value: string | null | undefined): string {
if (!value) return "";
const enumMatch = PRACTICE_AREAS.find((p) => p.value === value);
return enumMatch ? enumMatch.label : value;
}