Commit Graph

84 Commits

Author SHA1 Message Date
e90faa9ba4 feat(settings): add Blocks tab — 12-block decision schema reference
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m35s
Read-only display of BLOCK_CONFIG from block_writer.py with CREAC role
and JWM functional-purpose annotations per block (sourced from
docs/block-schema.md).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 07:58:04 +00:00
ae35934383 feat(settings): wire frontend to Coolify SoT response shape
- McpEnvVar: infisical_value → coolify_value + has_duplicates
- McpEnvResponse: drop Infisical metadata fields
- EnvVarRow: 'Coolify:' label, 'ערוך ב-Coolify' external link
- DriftBadge: infisicalAvailable → coolifyAvailable
- EnvironmentTab: Coolify app badge, duplicates count
2026-05-04 07:53:27 +00:00
1cb832473c fix(settings): unknown drift state when Infisical unavailable + RTL drawer
- DriftBadge shows 'Unknown' (not 'Synced') when infisical_available=false
- Plumb infisicalAvailable from EnvironmentTab through EnvVarRow → DriftBadge
- Add dir='rtl' to ToolDetailDrawer SheetContent for Hebrew descriptions
2026-05-04 07:01:42 +00:00
89ce6c79d7 feat(settings): implement Registrations tab
Replaces stub RegistrationsTab with a full read-only view grouped by client.
Handles all 4 states: loading skeleton, fetch error, host_path_unavailable,
empty list, and populated data with per-registration detail rows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 06:50:12 +00:00
7e3c912899 feat(settings): implement Tools tab with detail drawer
Replaces stub ToolsTab with a grouped-by-module grid of clickable tool cards.
Adds ToolDetailDrawer (Sheet) showing name, description, module, source_location,
and params_schema for the selected tool.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 06:50:08 +00:00
f418686724 feat(settings): implement Environment tab with edit + drift detection
Add drift-badge, env-var-editor, env-var-row components and replace the
environment-tab stub; install shadcn Switch which was missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 06:47:40 +00:00
8289b4d643 refactor(settings): split into tabs (paperclip + 3 stubs)
Extracts Paperclip companies + tag-mappings UI into PaperclipTab component,
adds stub tabs for Environment / Tools / Registrations, and replaces the flat
page.tsx with a shadcn Tabs layout to make room for Tasks 8-10.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 06:44:27 +00:00
6c129a1350 feat(settings): add MCP API hooks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 06:41:30 +00:00
2f05cdea2e feat(precedents): add /precedents/[id] read-only detail page
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 34s
Global search rows linked to /precedents/<case_law_id> but no route
existed, so clicking a result hit a Next 404 and React threw hydration
error #418. New page reads /api/precedent-library/{id} and shows
metadata, summary/headnote/key_quote, subject tags, and the full
halachot roll-up. "ערוך פרטים" opens the existing PrecedentEditSheet
(no duplicate edit UX).

Extracted ExtractedHalachotSection + ReviewStatusPill from the edit
sheet into a shared component so both surfaces render the same block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 05:36:43 +00:00
bd1fb61655 feat(precedents): show extracted halachot in library edit sheet
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 35s
The "ספרייה" tab only exposed approved/total counts in a status pill;
to inspect the actual extracted halachot per case the chair had to use
the global "ממתין לאישור" tab, which only surfaces pending items, or
the MCP tool. Now the per-precedent edit sheet renders a read-only
roll-up of every halacha (approved + pending + rejected) with status
filter tabs and counts. Review actions intentionally stay in the
review tab to avoid duplicate approve/reject UX.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 05:24:25 +00:00
b9cdcf980d fix(precedents): translate practice_area slugs to Hebrew in halacha review
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 35s
The halacha-review panel was rendering raw slugs (`betterment_levy`,
`rishuy_uvniya`, `compensation_197`) as English badges. Pipe them through
the existing `practiceAreaLabel()` helper so the chair sees
"היטל השבחה", "רישוי ובניה", "פיצויים לפי ס' 197".

All other UI sites (library-list-panel, library-stats-panel,
precedent-edit-sheet) were already using the helper — this was the
sole place left rendering the raw slug.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:13:48 +00:00
688ba37d9c fix(ui): reorder + center the agent dropdown label
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 34s
In an RTL paragraph the bidi algorithm puts the *first* logical token
on the right, so "פתח דאשבורד Paperclip" rendered visually as
"Paperclip" on the LEFT — which reads as the *last* word in Hebrew
and looks like an afterthought rather than the brand name the menu
opens. Reorders to "Paperclip פתח דאשבורד" so Paperclip sits on the
right (read first) and centers the label so it sits above both items
instead of hugging the inline-start edge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:38:05 +00:00
b2985f88de fix(ui): use 3-column grid in header Row 1 for true viewport-centered search
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 33s
The previous flex layout used `flex-1` on the search wrapper, which
centers the search relative to the *remaining* space — so as the brand
subtitle grows ("עוזר משפטי · ערר 8137-24 · ניסוח") or the agent
trigger label changes, the search drifts off-center.

Switches Row 1 to `grid-cols-[minmax(0,1fr)_minmax(280px,460px)_minmax(0,1fr)]`:
brand on the right, search in the middle (anchored to the viewport
midpoint), agent dropdown on the left. The side cells flex equally so
the center stays put regardless of side content width.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:32:31 +00:00
01ea902156 fix(ui): stack agent dropdown items vertically to stop multi-line wrapping
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 34s
The previous layout used `justify-between` with the board name and the
prefix·hint hint on the same row. With Hebrew labels + the long hint
"תיקי 8xxx / 9xxx" the row overflowed the 220px content and wrapped the
hint into 2-3 lines, breaking visual alignment.

Stacks each item now: bold board name on top, dim prefix·hint underneath.
Adds whitespace-nowrap to both lines and bumps min-width to 240px so the
content drives the dropdown width instead of fighting it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:21:48 +00:00
cca17689de feat(ui): redesign header to two rows with grouped nav (Phase B)
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 32s
Splits the AppShell header into:
  Row 1 — brand: logo + dynamic context subtitle (route-aware) +
          global search + agent boards dropdown
  Row 2 — nav:   work group (בית · ארכיון) | knowledge group (ספריית
          פסיקה · אימון · מתודולוגיה) + admin dropdown (⚙) on the left

Three changes from the previous flat 8-item nav:

1. Grouping reflects intent. Daily-driver pages are in "work", corpus
   pages in "knowledge"; system pages (skills · diagnostics · settings)
   move into a single ⚙ dropdown so they stop competing for attention.

2. Subtitle is now dynamic. `headerSubtitle(pathname)` resolves the
   current section so the user always sees where they are without
   scanning the nav row. Case routes show the case number explicitly
   ("ערר 1234-24" / "ערר 1234-24 · ניסוח").

3. The gold-underline active state is preserved and the admin trigger
   inherits it whenever any admin route is active.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:15:20 +00:00
deb1a1eaf4 chore(api-types): regenerate after /api/search/cases
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 37s
Captures accumulated backend drift since last regeneration. Triggered
by the new /api/search/cases endpoint added for header global search,
but the diff also picks up many other endpoints that had been added
without re-running api:types.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:10:57 +00:00
f722fa45bd feat(search): add header global search (Phase A) — cases + precedents + docs
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 41s
Adds an always-visible debounced search input in the AppShell header
that fans out to three independent sources in parallel and renders
per-source result groups with their own loading/empty/error states:

- /api/search/cases (NEW): SQL ILIKE on case_number, address, parties,
  title, subject. Returns small projections, no embeddings needed.
- /api/precedent-library/search (existing): semantic over case-law
  halachot + passages.
- /api/search (existing): semantic over case documents + past decisions.

Cmd/Ctrl+K focuses the input; Esc and click-outside close the panel.
This is Phase A of the header redesign — the bar layout itself is
unchanged; row grouping + dynamic context follow in Phase B.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:05:51 +00:00
cbdbc522a0 feat(ui): convert agent-mgmt link to dropdown for both Paperclip boards
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 35s
Replaces the hardcoded CMPA link with a dropdown listing both
Paperclip boards (CMP = רישוי ובניה, CMPA = היטלי השבחה). Fixes the
mislabeling where the original link pointed to the wrong board, and
gives the user a single entry point that scales if a third board is
added later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:37:02 +00:00
6c727cb5d0 feat(ui): add CMPA agent dashboard link to header
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 32s
Adds a "ניהול סוכנים" link on the opposite side of the "עוזר משפטי"
title in the app shell header. Opens the Paperclip CMPA dashboard
(pc.nautilus.marcusgroup.org/CMPA/dashboard) in a new tab for quick
cross-tool navigation between the legal-ai workspace and agent ops.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:24:02 +00:00
789cc273ee fix(precedents): allow delete when extraction completed but timestamp stale
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 32s
The local MCP worker is supposed to NULL `*_extraction_requested_at` after
a successful run, but in practice these timestamps linger. The previous
isPrecedentActive logic treated any non-null timestamp as "still active",
which left completed rows permanently undeletable.

Now only "processing" status (or genuinely queued: pending + timestamp)
counts as active. Once a row is "completed"/"failed", stale timestamps
no longer block the delete button.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:24:16 +00:00
1f17419ee9 ui(precedents): live status pill with shimmer + auto-queue + auto-refresh
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m44s
The chair pointed out three UX gaps after uploading a new precedent:

1. The status said "מחלץ הלכות" but nothing was actually running — the
   field only meant "halacha_extraction_status != completed", which
   includes the post-upload "pending" state where the local MCP worker
   hasn't been told to drain anything yet. Misleading.

2. The page didn't refresh on its own. The chair had to F5 to see new
   counts after extraction completed.

3. Clicking the trash icon mid-extraction would cascade-delete the row
   while the extractor was still using it (FK errors, partial writes).

Fixes:

- ingest_precedent now auto-queues both metadata and halacha extraction
  on upload by stamping the request timestamps. The chair (or me) drains
  the queue with one `precedent_process_pending` call from chat —
  no need to click any button before that.

- StatusPill is now five-state with proper labels:
    "נכשל" (extraction_status=failed) — red
    "מעבד טקסט" — shimmer (extraction_status=processing)
    "בתור" — neutral (chunks queued, not yet running)
    "מחלץ הלכות" — shimmer (halacha_extraction_status=processing)
    "ממתין לחילוץ" — neutral (queued for local MCP worker)
    "לא חולץ" — neutral (pending without queue stamp — shouldn't happen)
    "X/Y מאושרות" — gold (done, with halachot count)
  The shimmer is a CSS-only sliding-stripe animation defined in globals.

- usePrecedents has a conditional refetchInterval — polls every 5s while
  any row is mid-extraction or queued, then stops once everything settles
  to completed/failed. New helper isPrecedentActive() centralises the
  "is this row mid-something" check so the UI and the destructive-action
  guard agree.

- Trash button is disabled (opacity 30%, tooltip explains) while the row
  is active. Pencil/edit stays enabled — editing metadata fields during
  extraction is safe (last write wins, low-stakes race).

Schema: list_external_case_law now exposes the two *_requested_at
timestamps so the UI can distinguish "queued" from "never asked".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:47:31 +00:00
4a9a6b7970 feat(precedents): UI button queues extraction for local MCP worker
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m27s
The chair wanted a one-click "extract metadata" button on the edit sheet.
The constraint stays the same — claude_session needs the local CLI which
the container doesn't have, so the button can't run the extractor itself.
Compromise: button stamps a queue marker; the local MCP server drains the
queue on demand.

DB (V8): two nullable timestamps on case_law,
metadata_extraction_requested_at and halacha_extraction_requested_at,
with partial indexes for cheap "find pending" scans.

API:
  POST /api/precedent-library/{id}/request-metadata   → stamp the row
  POST /api/precedent-library/{id}/request-halachot   → same for halacha
  GET  /api/precedent-library/queue/pending?kind=...  → read-only view

UI: Sparkles button in the edit sheet header. Click → toast tells the
chair what to run from Claude Code. The button never triggers the
extractor directly from the container.

MCP tool: precedent_process_pending(kind, limit) — runs from Claude Code
with the local CLI, picks up everything stamped, calls the extractor for
each, clears the timestamp on success. Failures keep the timestamp so the
next invocation retries them.

Architectural rule (claude_session local-only) is preserved end-to-end
and called out in the new endpoint comment + tool docstring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:32:25 +00:00
8e1384b897 fix(precedents): wrap citation column + extractor fills source_type
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m27s
Two follow-ups after running the metadata extractor on 403-17:

1. Library table: shadcn TableCell defaults to whitespace-nowrap and
   the table wrapper has overflow-x-auto, so the long citation forced
   a horizontal scrollbar inside the row. Override on the citation
   cell only — whitespace-normal + break-words + min/max-w to keep the
   column readable. Same for the case-name cell. Row aligns to top so
   wrapping doesn't push neighbours up.

2. Extractor now also fills source_type (court_ruling /
   appeals_committee). The previous round added decision_date_iso,
   precedent_level, and court but left source_type empty. Same
   closed-enum + merge-only-if-empty policy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:28:35 +00:00
fc3b6b6cae ui(precedents): collapsible groups by precedent + Hebrew labels + RTL fixes
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 33s
After running the dual-mode halacha extractor on a real appeals committee
decision (403-17), the pending-review tab surfaced 351 halachot in a
single flat list — the chair correctly pointed out that this is unusable
without grouping. Three fixes:

1. Group pending halachot by precedent (case_law_id). Each group shows
   the citation, court, date, level and item count; default state is
   collapsed so the chair picks one ruling at a time. Within a group,
   items still sort by confidence ascending so the doubtful ones surface
   first. J/K/A/R/E now scope to currently-expanded groups; toggling
   open auto-focuses the first item.

2. Translate the badges that were leaking English: rule_type values
   (`persuasive`, `interpretive`, `binding`, `application`, `procedural`,
   `obiter`) now render as Hebrew labels, and `confidence X.XX` becomes
   `ביטחון X.XX`. The card header no longer repeats the citation since
   it's already in the group header.

3. Strip Unicode bidi marks (U+200E/F/202A-E/2066-9) from displayed
   citations. Nevo PDFs and the upload form embed these in the
   case_number; they render as zero-width but visually push the text
   away from the right edge of the table cell. Also: hide the empty
   court line under the case name in the list (was rendering as a
   stray em-dash), and use a muted em-dash for empty date/level rather
   than blank/dash inconsistency across columns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:05:40 +00:00
2cfdf35191 refactor(precedents): keep all LLM calls on the local-MCP path
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m28s
Architectural correction: every claude_session caller in this project
runs through the local MCP server (~/.claude.json points at
/home/chaim/legal-ai/mcp-server/.venv/bin/python). The Coolify container
has no `claude` CLI and no claude.ai session, so any LLM call originating
from web/ FastAPI fails with "Claude CLI not found" — which is exactly
what we hit on 403-17.

The earlier Anthropic SDK fallback would have made it work, but at
direct API cost. The chair's preference is to stay on the claude.ai
session for everything. So:

- claude_session.py: removed the SDK fallback, restored CLI-only.
  The error message now points the next person at the architectural
  rule in the module docstring instead of papering over it.
- precedent_library.py:ingest_precedent (called from FastAPI on upload)
  now does only the non-LLM half: extract → chunk → embed → store.
  Sets halacha_extraction_status='pending' for the chair to act on.
- reextract_halachot / reextract_metadata kept, but lazy-import their
  extractors so the FastAPI path can't accidentally pull them in. They
  are reachable only via the MCP tools precedent_extract_halachot /
  precedent_extract_metadata, which run locally with CLI.
- Removed POST /api/precedent-library/{id}/extract-halachot and
  /extract-metadata — they were dead ends from the container.
- Dropped the `anthropic` Python dep that the SDK fallback required.
- UI: removed the "refresh halachot" and "sparkles metadata" buttons
  that called those endpoints. Edit sheet now points the chair at the
  MCP tool names instead.

Halacha and metadata extraction for an uploaded precedent now happen
when the chair (via Claude Code) runs:
  mcp__legal-ai__precedent_extract_metadata <case_law_id>
  mcp__legal-ai__precedent_extract_halachot <case_law_id>

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 11:06:08 +00:00
5d836ca414 fix(precedents): Anthropic SDK fallback, format() crash, UI refresh
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m31s
Three fixes to the precedent library after the first end-to-end test on
403-17 surfaced runtime issues:

1. Anthropic SDK fallback in claude_session. The legal-ai Docker container
   does not ship the `claude` CLI, so every halacha and metadata extraction
   was failing with "Claude CLI not found." Module now tries the CLI first
   (zero-cost local path) and falls back to the Anthropic SDK with
   ANTHROPIC_API_KEY when the binary is absent. Default model is
   claude-sonnet-4-6, overridable via CLAUDE_SDK_MODEL env. The system
   message gets cache_control: ephemeral so multi-chunk runs reuse the
   cached instruction prefix at ~10% read cost. Adds `anthropic` to
   pyproject deps.

2. precedent_metadata_extractor crashed with KeyError because the JSON
   example inside the prompt template contained literal { } characters
   that str.format() interpreted as placeholders. Switched to f-string
   concatenation; the prompt template no longer needs format() at all.

3. Library list query stays stale after upload because the upload
   mutation's onSuccess fires when the POST returns task_id, not when
   SSE reports completion. Added a second invalidate inside the SSE
   watcher in PrecedentUploadSheet so the new row appears with up-to-date
   chunk and halachot counts the moment processing finishes.

Halacha and metadata extractors now route the long static prompt through
the new `system=` parameter so the SDK path actually caches it; the CLI
path concatenates and behaves as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:52:31 +00:00
73a79ea7e8 feat(precedents): metadata auto-fill, edit sheet, persuasive extraction
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m28s
Three improvements to the precedent library based on usage feedback:

1. Auto-fill metadata at upload time. New service
   precedent_metadata_extractor reads the ruling's full_text and
   suggests case_name (short), summary, headnote, key_quote,
   subject_tags, appeal_subtype. The merge policy fills only empty
   fields, preserving everything the chair typed in the upload form.
   Wired into the ingest pipeline; also exposed as a re-run endpoint
   POST /api/precedent-library/{id}/extract-metadata for existing
   records.

2. Edit sheet in the UI. Pencil icon on each library row opens a
   pre-populated form covering every field. A Sparkles button on the
   sheet runs the metadata extractor on demand and refreshes the
   form. The case_number is read-only because halachot are FK'd to
   it; renaming requires delete + re-upload.

3. Halacha extractor branches on is_binding. Sources marked binding
   (Supreme/Administrative) keep the strict halacha prompt. Non-binding
   sources (other appeals committees, district courts on planning
   matters) get a different prompt that extracts applications,
   interpretive principles, and persuasive conclusions — labeled with
   new rule_types 'application' and 'persuasive'. The fallback also
   widens chunk selection: if the chunker labeled nothing as
   legal_analysis/ruling/conclusion, we now run on all chunks rather
   than returning zero halachot for a usable ruling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:19:35 +00:00
b51163b67c web-ui: shrink KPI card height on home dashboard
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 35s
Reduce vertical padding, number font size, and inter-element gaps so
the four counters take less vertical real estate. Width unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 08:46:27 +00:00
7ee90dce31 feat: external precedent library with auto halacha extraction
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m27s
Adds a third corpus of legal authority distinct from style_corpus
(Daphna's prior decisions for voice) and case_precedents (chair-attached
quotes per case). The new corpus holds chair-uploaded court rulings and
other appeals committee decisions, with binding rules (הלכות) extracted
automatically and queued for chair approval.

Pipeline (web/app.py + services/precedent_library.py):
file → extract → chunk → Voyage embed → halacha_extractor → store +
publish progress over the existing Redis SSE channel.

Schema V7 (services/db.py): extends case_law with source_kind +
extraction status fields under a CHECK constraint pinning practice_area
to the three appeals committee domains (rishuy_uvniya, betterment_levy,
compensation_197). New precedent_chunks (vector(1024)) and halachot
tables (vector(1024) over rule_statement, IVFFlat indexes, gin on
practice_areas/subject_tags). Halachot start as pending_review; only
approved/published rows are visible to search_precedent_library.

Agents: legal-writer, legal-researcher, legal-analyst, legal-ceo,
legal-qa get search_precedent_library. legal-writer prompt explains
the three-corpus distinction and CREAC use; legal-qa now verifies that
every cited halacha resolves to an approved row in the corpus.

UI: /precedents page with four tabs — library / semantic search /
pending review (J/K nav, A/R/E shortcuts, badge count) / stats.
Reuses the existing upload-sheet progress + SSE pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 08:38:18 +00:00
a6edb75bbf web-ui: hide spurious horizontal scrollbar on case documents list
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m3s
The list's scroll container had only overflow-y:auto, which CSS computes
overflow-x to auto too. Combined with the row's -mx-2 hover-background
extension, this surfaced an unwanted horizontal scrollbar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 07:52:41 +00:00
e849285806 home: split cases table by appeal type + add appeal-type chart
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 32s
Backend (cases listing)
- /api/cases: also return updated_at, created_at, practice_area,
  appeal_subtype, subject. The detail-mode response was previously
  dropping these even though db.list_cases reads them, leaving the
  UI's "תחום" and "עודכן" columns blank.

Frontend
- Split the home table into two: רישוי (1xxx) and היטל השבחה ופיצויים
  (8xxx + 9xxx), bucketing on appeal_subtype with a case-number-prefix
  fallback. The "תחום" column is now redundant and removed.
- New AppealTypeBars chart in the right rail next to the existing
  status donut.
- Donut: switch to a vertical layout (donut on top, legend below in a
  3-col grid) so labels like "חדש / בעיבוד" no longer wrap inside the
  320px sidebar; counts now align in a tabular column.
- CasesTable accepts emptyText/searchPlaceholder so each split table
  has its own copy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 15:44:41 +00:00
817d6e6d8d web-ui: raise proxy body limit to 100mb for large document uploads
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m9s
Default 10mb caused upload-tagged 500s on scanned PDFs in case 1027-26
(Next 16 truncates body, FastAPI sees broken multipart, socket hang up).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:12:41 +00:00
fa70944ed4 case-create: surface Gitea repo result + UI retry button
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m29s
The auto-creation in case_create had two failure modes that combined to
make repos silently missing: a stale GITEA_TOKEN returning 401, and the
outer try/except in case_create that swallowed every exception with a
bare pass. Result: cases like 8174-24 ended up with a local git repo and
Paperclip project but no Gitea repo, with no signal anywhere.

_setup_gitea_remote now returns {ok, url, error} and never raises; the
result is attached to the case JSON and the FastAPI endpoint logs a
warning when ok=false. The UI gets a "צור ריפו ב-Gitea" button on the
case header that appears only when the repo or remote is missing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:12:05 +00:00
9bdfb05350 Upload progress: Redis-backed store + flushed SSE + client fallback
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m24s
The previous in-memory _progress dict + polling SSE handler had a 30s silent
tail after completion. HTTP/2 framing in the proxy chain (Traefik) buffered
the small chunks until the stream closed, so when a transient blip caused
EventSource to reconnect, the server returned 404 and the UI stuck on the
"מתחיל…" placeholder forever. Reproduced live: 445 bytes withheld 31s.

Changes:
  • web/progress_store.py — ProgressStore wraps Redis with TTL (5m), atomic
    GETDEL, dict-like API. Best-effort: Redis errors are logged and swallowed
    so observability outages don't break uploads.
  • web/app.py — _progress is now Redis-backed; every set/get/active/pop is
    awaited. SSE handler emits a heartbeat each tick (forces HTTP/2 flush),
    drops the 30s post-completion sleep, and returns a terminal
    {"status":"unknown"} payload instead of 404 when the task is gone — so
    EventSource closes cleanly instead of reconnect-looping. New _SSE_HEADERS
    set X-Accel-Buffering: no.
  • web-ui useProgress(taskId, caseNumber) — 10s fallback that invalidates
    the case detail if no SSE message arrived; treats "unknown" as terminal
    and triggers a refetch from the source of truth.
  • upload-sheet wires caseNumber through and renders "unknown" as completed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:53:23 +00:00
3a1760b4cd Agent feed: don't show "waiting for report" when all issues closed
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 30s
The AgentActivityFeed showed a spinner with "הסוכנים התחילו לעבוד,
ממתין לדיווח ראשון..." whenever the case had any issues but no
comments — including cases where all issues had ended in 'done' or
'cancelled' (like 1130-25 after archive). The widget mistook a
finished case for an in-flight workflow.

Now compute hasActiveIssue = some(issues, status !== done && cancelled)
and pick the message accordingly: spinner only while there's still
real work; otherwise a quiet "אין משימות פעילות בתיק. כל המשימות
הסתיימו או בוטלו." with the static MessageSquare icon.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:22:20 +00:00
7d86ed4a62 Archive: also cancel open Paperclip issues to clear agent widget
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 32s
When a case is archived, the legal-ai UI's AgentStatusWidget kept showing
"agents started working, waiting for first report" because related
Paperclip issues remained in 'todo' / 'in_progress' status. Concrete
example: case 1130-25 had two open issues (CMP-15 ניתוח תכנוני, CMP-21
כתיבת החלטה) that lingered after the case was finalized; 1194-25 had
two more (CMP-37, CMP-44).

Extended pc_archive_project to also UPDATE issues SET status='cancelled',
cancelled_at=now() WHERE project_id matches AND status IN
('backlog','todo','in_progress','blocked','in_review'). Returns the list
of cancelled issues so the toast can announce the count.

Updated cases.ts ArchiveResult.paperclip.issues_cancelled type and the
toast message in case-archive-action to surface "(N משימות פתוחות בוטלו)"
when relevant.

Restore is intentionally unchanged — we don't auto-recreate cancelled
issues; if work needs to resume, a fresh issue should be created.

Stale issues for 1130-25 / 1194-25 cancelled directly in DB as a one-off
cleanup (CMP-15, CMP-21, CMP-37, CMP-44).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:14:12 +00:00
2b7f291928 Case archive/restore with Paperclip sync
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m27s
Adds a comprehensive archive flow for closed cases — separate /archive
screen in the UI, archive/restore actions on the case detail page, and
automatic two-way sync with Paperclip.

Backend (web/app.py + mcp-server/services/db.py):
- New SCHEMA_V6 migration: cases.archived_at TIMESTAMPTZ + partial index
- list_cases gains include_archived/archived_only flags; default excludes
  archived rows so the main /api/cases list hides closed cases
- archive_case / restore_case helpers in db.py
- POST /api/cases/{n}/archive sets archived_at and calls
  pc_archive_project (sets Paperclip projects.archived_at via direct DB)
- POST /api/cases/{n}/restore clears archived_at and calls
  pc_restore_project (clears Paperclip archived_at)
- archive_project / restore_project in paperclip_client.py — name-based
  match consistent with create_project's lookup

Frontend (web-ui):
- cases.ts: scope param ("active"|"archived"|"all") on useCases;
  useArchiveCase / useRestoreCase mutations
- /archive page (new): table of archived cases with restore button +
  search, sort, empty state matching the editorial aesthetic of /
- case-archive-action.tsx: button on case detail header. Active case →
  confirm dialog → archive. Archived case → restore (no confirm).
  Toast announces both legal-ai and Paperclip outcomes (synced, not
  found in pc, error)
- case-header shows "בארכיון" badge when archived_at is set
- Nav: ארכיון link added to AppShell after בית

Tested end-to-end against the live DB:
- 1130-25 archive → list_cases(include_archived=False) excludes it,
  list_cases(archived_only=True) includes it, restore reverses
- pc archive/restore on 1194-25 verified via direct DB lookup
- TypeScript compiles clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:54:52 +00:00
36ca713dfa Retrofit: tighten yod-bet pattern, add cover-block fallback
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 6s
The "על כן" pattern for block-yod-bet was too greedy and matched mid-discussion
transitional sentences (e.g. "על כן, במקום בו..."), which caused forward-scan
to skip block-yod-alef ("סוף דבר") via the pointer advance.

Tightened to require an operative subject (אנו / הערר / הוועדה / ועדת הערר)
so terminal "על כן, אנו מחליטים" still matches but mid-block transitions don't.

Added structural_fallback for cover blocks (alef/bet/gimel/dalet) — these are
template metadata not present in user-edited DOCX bodies. Inject zero-content
anchors so apply_user_edit can still target them later. The frontend toast
distinguishes real content gaps from fallback anchors.

Also expanded heading patterns based on training corpus inspection:
- block-vav: על המקרקעין חלות / במצב התכנוני / התכניות החלות
- block-zayin: טענות העוררת
- block-chet: עיקר תגובת המשיב
- block-tet: הדיון בוועדת הערר

For case 1130-25, this raises detection from 6/12 to 11/12 blocks — only
block-yod-bet remains missing (Daphna's edit ends at "סוף דבר" + numbered
ruling, no terminal "ההחלטה" or "על כן אנו מחליטים" paragraph).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 06:57:41 +00:00
eac7784b87 Trigger appraiser-facts extraction from the UI
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 36s
Extraction is expensive (multi-minute LLM calls) and runs across every
appraisal in the case at once, so we don't kick it off silently on every
tag save. The chair tags the appraisals, then runs extraction once when
they're ready.

- New POST /api/cases/{n}/extract-appraiser-facts endpoint returns the
  extractor's summary as-is: status=completed with fact counts and
  conflicts, or status=sides_missing with the list of still-untagged
  appraisal docs.
- DocumentTypeEditor now has a two-phase popover. After a successful
  save on an appraisal doc, the body switches to a confirmation view
  with a "חלץ עובדות שמאיות עכשיו" button. The result (completed /
  sides_missing / no_appraisals / error) renders in the same popover
  so the chair sees exactly which appraisals still need tagging
  without closing and reopening anything.
- useExtractAppraiserFacts React-Query mutation invalidates the case
  detail on success so downstream views (conflict rendering in
  block-tet context) pick up the new facts.
2026-04-19 09:42:49 +00:00
c536ed0e63 Edit document doc_type and appraiser side from the case UI
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m26s
Until now changing a document's doc_type required a manual SQL update.
Adds an inline editor on the document badge so the chair can retag
without leaving the case page, and threads an appraiser_side tag
(committee / appellant / deciding) through the appraisal pipeline so
betterment-levy cases — which usually have 2-3 appraisers — render
conflicts with the deciding appraiser's view marked as governing.

Backend
- New appraiser_facts.appraiser_side column (V5.1) populated from
  documents.metadata.appraiser_side at extraction time.
- extract_appraiser_facts now returns status='sides_missing' with the
  list of untagged appraisals instead of running with empty side
  labels — chair must tag every appraisal first via the UI.
- Conflict detection orders entries committee → appellant → deciding so
  the deciding appraiser appears last; block-tet's prompt instructs the
  writer to phrase the deciding appraiser's view as the governing
  factual finding ("ואולם, השמאי המכריע קבע...").
- New PATCH /api/cases/{n}/documents/{doc_id} (Pydantic model with
  whitelist validation) and matching document_update MCP tool. Both
  merge appraiser_side into metadata JSONB instead of touching the
  schema.

UI
- New shared doc-types module exports the canonical 11 doc_type
  options plus the 3 appraiser-side options; both upload-sheet and
  the document badge now read from it instead of duplicating Hebrew
  labels.
- New DocumentTypeEditor renders a Popover off the doc-type Badge
  with two Selects. The save button stays disabled while doc_type is
  appraisal but no side has been picked, mirroring the backend
  enforcement so the user finds out before triggering extraction.
- usePatchDocument React-Query mutation invalidates the case detail
  on success so the badge updates without a manual refresh.
2026-04-19 06:26:51 +00:00
110901a66c web-ui: add שומה label for appraisal doc type
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 30s
The backend (app.py, documents.py, models.py) already maps appraisal→שומה
but the frontend DOC_TYPE_LABELS and upload DOC_TYPES dropdowns were
missing the entry, so appraisal documents rendered as the raw English
string instead of the Hebrew label.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 06:00:17 +00:00
2b40e02a65 Merge Documents tab into Overview, promote action buttons to header
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 4m0s
- Remove "מסמכים" tab; render DocumentsPanel at the bottom of the Overview tab
- Move "פתח בעורך ההחלטה" and "עריכת פרטי תיק" into the top row, right of tabs, before "העלאת מסמכים"
- Drop the redundant document count from the quick-summary grid (list is visible below)
- Add flex-wrap to the header row so the extra buttons flow onto a second line on narrow screens

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 06:53:59 +00:00
726498126d Add Track Changes architecture for draft revisions (CMP + CMPA)
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m29s
Fixes critical bug in 1033-25: user-uploaded עריכה-*.docx files were
orphaned on disk while exports kept rebuilding from stale DB blocks.

New architecture:
- User-uploaded DOCX becomes the source of truth (cases.active_draft_path)
- System edits via XML surgery with real Word <w:ins>/<w:del> revisions
- User can Accept/Reject each change from within Word

Components:
- docx_reviser.py: XML surgery for Track Changes (15 tests)
- docx_retrofit.py: retroactive bookmark injection with Hebrew marker
  detection + heading heuristic (9 tests)
- docx_exporter.py: emits bookmarks around each of the 12 blocks
- 3 new MCP tools: apply_user_edit, list_bookmarks, revise_draft
- 4 new/updated endpoints: upload (auto-registers active draft),
  /exports/revise, /exports/bookmarks, /exports/{filename}/retrofit,
  /active-draft
- DB migration: cases.active_draft_path column
- UI: correct banner using real v-numbers, "מקור האמת" badge,
  detailed upload toast with bookmarks_added/missing_blocks
- agents: legal-exporter (3 export modes), legal-ceo (stage G for
  revision handling), legal-writer (revision mode)

Multi-tenancy:
- Works for both CMP (1xxx cases) and CMPA (8xxx/9xxx cases)
- New revise-draft skill added to both companies
- deploy-track-changes.sh syncs skills CMP ↔ CMPA
- retrofit_case.py: one-off retrofit of existing files

Tests: 34 passing (15 reviser + 9 retrofit + 4 exporter bookmarks + 6 e2e)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 18:49:30 +00:00
28daff58be Pre-existing agent updates + analysis DOCX export
Updates accumulated from prior sessions:
- HEARTBEAT: company-based filtering (CMP/CMPA) rules
- legal-qa, legal-researcher: routine updates
- analysis_docx_exporter: new service for analysis DOCX export
- compose page: "הורד כ-DOCX" button for analysis
- decision_template.docx: template for exporter

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 18:49:10 +00:00
d7a79cf5ec Show per-case agent status instead of global — fix Hebrew translation of "running"
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m41s
Agent status widget now checks heartbeat_runs + wakeup_requests to determine
if an agent is running on *this* case. Agents running on other cases show as idle.
Added "running" to STATUS_DOT/STATUS_LABEL maps so it displays in Hebrew.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:47:42 +00:00
3288624349 Add methodology settings page with golden ratios, discussion rules, and checklists
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m29s
New /methodology page with 3 tabs for viewing and editing decision
writing methodology. Uses DB override pattern: hardcoded Python
constants serve as defaults, edits saved to appeal_type_rules table,
delete restores default.

Backend: 3 generic endpoints (GET/PUT/DELETE /api/methodology/{category}/{key})
with validation per category type.

Frontend: methodology.ts hooks, GoldenRatiosPanel (number inputs per
outcome/section), DiscussionRulesPanel (accordion with textarea per
rule), ContentChecklistsPanel (markdown editor with preview toggle).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:30:39 +00:00
c9f3fcd012 Translate agent role badges and issue status to Hebrew
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 31s
Role labels: ceo→מנהל, researcher→חוקר, engineer→מהנדס, qa→בודק איכות
Issue status: in_progress→בביצוע, done→הושלם, todo→לביצוע, etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:55:31 +00:00
1e4c5c1518 Add Paperclip agent activity mirror to case detail page
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m16s
New "Agents" tab in case detail shows all Paperclip agent comments,
issue status, and agent status for each case — eliminating the need
to switch between Legal-AI and Paperclip UIs.

Backend: 4 new DB query functions in paperclip_client.py (issues,
comments, agents, post_comment) + 2 new API endpoints (GET/POST
/api/cases/{case_number}/agents). Comment posting uses Board API
with DB+wakeup fallback to ensure CEO routing.

Frontend: agents.ts hooks (10s polling), AgentActivityFeed component
(markdown timeline + comment input), AgentStatusWidget (sidebar),
4th tab in case detail page.

Also includes new-company-setup-guide.md documenting the process
for setting up the betterment levy (CMPA) company.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:44:42 +00:00
6228846223 Add "Start Workflow" button to trigger CEO agent from web UI
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 49s
New endpoint POST /api/cases/{case_number}/start-workflow creates a
Paperclip issue, wakes the CEO agent via wakeup API, and transitions
case status to "processing". Button appears on case page only when
status is "new" or "documents_ready".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:51:23 +00:00
82ba4663ba Fix case repo sync + auto-create Gitea repos + add sync indicator
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m30s
- auto-sync-cases.sh: fix broken directory scan (was looking for
  status subdirs that don't exist), fix env var word-splitting bug,
  add safe.directory handling and error logging
- cases.py: auto-create Gitea repo on case_create, fix
  documents/original → documents/originals naming mismatch
- app.py: add GET /api/cases/{case_number}/git-status endpoint
- web-ui: add SyncIndicator component in case header showing
  sync status (synced/pending/no remote) with last commit time
- pyproject.toml: add httpx dependency
- CLAUDE.md: update Paperclip wakeup API docs
- settings page: switch tag input from Select to free-text with datalist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:28:16 +00:00