Regenerate web-ui/src/lib/api/types.ts from the PROD OpenAPI schema
(https://legal-ai.nautilus.marcusgroup.org/openapi.json) via `npm run api:types`.
Adds 3 drifted paths (pure additions, generated — not hand-edited):
- POST /api/cases/{case_number}/generate/party-claims-summary (#214)
- POST /api/cases/{case_number}/generate/interim-draft (#214)
- /api/status-model (drift)
Invariants:
- X6 (UI<->API contract in sync) — generated types now match the deployed surface.
- G2 (single generated client) — openapi-typescript output only, no hand edits.
Verified: `npx tsc --noEmit` passes clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
מוסיף כרטיס "הפקת מסמכים" בראש טאב "טיוטות והערות" (mockup 26 — תוספת ל-18h),
תוספת לעמוד הקיים, כל הסקשנים נשמרים.
- כפתור 1 "הפק סיכום מנהלים" → endpoint חדש POST /generate/party-claims-summary
→ CEO wakeup סטרוקטורלי action="party_claims_summary" (שלב H2, host-side).
Polling: GET /research/party-claims-summary (200=מוכן → פתח/DOCX).
- כפתור 2 "הפק טיוטה של טענות הצדדים" → endpoint חדש POST /generate/interim-draft
→ CEO wakeup action="interim_draft" (שלב H, host-side).
Polling: רשימת ה-exports עד שמופיע טיוטת-ביניים-{case}-vN.docx.
- כפתור 3 "הפק טיוטת החלטה מלאה" → ה-useExportDocx הקיים (in-container, ללא LLM);
הועבר מהבאנר (כפתור "הפק DOCX" הוסר משם — אין כפילות). disabled+title כשאין
בלוקים כתובים (isDraftReady).
מסלול-עירור יחיד דרך ה-Platform Port (pc_wake_ceo_for_action) → API helper
/api/agents/{id}/wakeup, לעולם לא DB insert. Israel-time + cache-invalidation.
Invariants: G12 (מגע-Paperclip רק ב-paperclip_client + agent_platform_port;
leak-guard ירוק) · G2 (אין מסלול-עירור מקביל — שימוש חוזר ב-pc_request הקיים) ·
G10 (הפקה מגודרת-יו"ר/CEO) · INV-UI9 (Asia/Jerusalem) · X6 (UI↔API contract).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 3 drafts cards rendered final-decision before draft-files; mockup 18h has
the draft-files card first, then final+learning, then feedback. Switched the
container to flex-col and gave each card an explicit `order` so the visual order
matches the mockup without reordering any JSX (zero-risk).
tsc --noEmit + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes mockup 18i beyond the earlier minimal version:
- AgentRoster — a 5-up grid of agents, each with a status dot, name, role label
and its current activity (derived from the agent's most recent comment).
Replaces the simple AgentStatusWidget list at the top of the feed (reset still
lives in the banner status-rail popover).
- "ממתין לתשובתך" now renders the actual pending InteractionCards (the
approve/reject/answer forms) at the top, not just a count pointer — and those
pending interactions are excluded from the timeline below so they aren't shown
twice.
All interaction logic / mutations preserved. tsc --noEmit + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes the case where the H1 chip, the pipeline stepper and the manual-changer
dropdown disagreed (e.g. 1043-02-26 on analyst_verified): the status sat between
the canonical 10 and a legacy bucket, so each surface read a different list.
Root fix — ONE authoritative status model + canonicalize the real intermediate
states (chair-approved):
- New mcp-server/.../case_status_model.py — the single registry: ordered
StatusDef list (key/label/description/phase/selectable/terminal/on_enter),
the 5 phases, STATUS_ORDER, phase_of/label_of, and a drift assertion that the
CaseStatus enum matches it. `on_enter` is the seam for a status to *do*
something on entry, declared next to its definition (no dispatcher wired yet).
- models.CaseStatus enum: analyst_verified + research_complete promoted to
first-class canonical statuses (the agents set them — they belong in the
model, not a legacy fallback).
- tools/cases.py: the forward-only STATUS_ORDER guard now derives from the
registry instead of an inline list.
- GET /api/status-model exposes the model so the frontend mirror can be
generated against it.
- web-ui case-status.ts mirrors it: the two intermediates moved from the
legacy map into CASE_STATUSES / PHASES(thinking) / STATUS_LABELS /
STATUS_DESCRIPTIONS; status-badge icons+tones extended. So the chip (status),
stepper (its phase) and dropdown (now includes it) all agree.
Invariants: G2 — collapses the scattered status definitions (enum, inline
STATUS_ORDER, two frontend label maps) onto one backend authority + a documented
frontend mirror; no parallel status list remains. Agent-prompt alignment +
backfill follow in a separate change. py_compile + tsc + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Groups the drafts/comments tab into distinct cards instead of one long scroll:
- "פסיקה שצוטטה" (CitationsSection) moved inside the chair's signed-final card
as its sub-section (was floating between final and the files table).
- "קבצי טיוטה" and "הערות יו״ר" sections each wrapped in a bordered surface card.
Result: draft-ready banner + active-draft badge at top, then three cards —
final-decision+learning (gold), draft files, chair feedback. All logic,
mutations, dialogs and the feedback list preserved verbatim.
tsc --noEmit + eslint clean. Design-gate: mockup 18h approved in Claude Design X17.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Agents tab: render the AgentStatusWidget roster at the top of the activity
feed ("who's doing what right now") and surface pending agent interactions in
an amber "ממתין לתשובתך" banner above the timeline (mockup 18i key wins).
- Drafts tab: Hebraize the drafts-table headers File/Size/Date → קובץ/גודל/תאריך
(mockup 18h).
All existing feed logic, mutations and interaction forms preserved.
tsc --noEmit + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chair-requested refinements to the case banner:
- Tabs now span the full band width via justify-between — wide gaps with the
current 6 tabs, tightening as more are added (gap-2 no-overlap floor).
- Parties moved out of the always-on header line into a "צדדים" Popover, so the
band is shorter and the tab content sits higher on the page.
- Case subject (title) moved up onto the case-number line in a smaller font,
instead of its own line below.
tsc --noEmit + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary with a verification progress bar, status filter chips + only-unverified
toggle, per-argument collapsible accordion with a v/t verify-progress chip, and
compact precedent rows (clamped/expandable quote, collapsible chair note). All
verify/saveNote mutations + types preserved verbatim.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folds the standalone /compose decision-editor route into the main case page so
there is no more "sub-page" (mockup 18f), with the final 6-tab workflow order:
סקירה · טיעונים ועמדות · אימות פסיקה · ההחלטה · טיוטות והערות · סוכנים.
- New PositionsPanel — the chair's editing surface (threshold claims + issues
SubsectionCards + ChairEditor + background prose + case precedents + finish
rail), extracted verbatim from the former /compose "עמדות וטענות" tab. Drops
only the doc-strip (the overview DocumentsPanel is the sole owner — no dup).
- "טיעונים ועמדות" tab = PositionsPanel above + the aggregated by-party
LegalArgumentsPanel in a collapsible below.
- "אימות פסיקה" promoted to its own top-level tab (CitationVerificationPanel,
was /compose tab 3); placed BEFORE "ההחלטה" — verify citations, then write
(INV-AH: the writer cites only verified precedents).
- "ההחלטה" stays the single 12-block editor (DecisionBlocksPanel) — no longer
duplicated between /compose and the case page.
- Tabs are now controlled; the "פתח עורך החלטה" CTA switches to the ההחלטה tab
instead of navigating to the deleted route.
- /compose route deleted; header-context /compose special-case removed.
No duplication remains (G2): blocks editor single, docs single (overview),
citation-verify single. Design-gate: mockup 18f approved in Claude Design X17.
tsc --noEmit + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the approved X17 case-detail redesign foundation (mockups 18c "A" +
18e "V2"):
- New StatusRail component — the integrated status strip (current-phase chip,
expected-outcome semantic tag, hearing/updated/sync meta inline, agents
Popover, manual-changer Popover) over the 5-phase pipeline stepper. Lives
INSIDE the parchment band (CaseHeader), so status is now visible on EVERY tab
(supersedes the overview-only StatusHero / reverses the prior INV-IA1
overview-only decision, deliberately).
- CaseHeader absorbs the rail and drops its separate hearing/updated/sync dl
(those moved into the rail — no duplication).
- Tabs restyled to the V2 "segmented" look (each tab a pill, active = raised
white card) with per-tab icons.
- Overview tab body is now just documents + agent-activity preview (status left
the body).
- StatusHero deleted — its content moved wholesale into StatusRail, so no
parallel status surface remains (G2).
Reuses the shared StatusChanger / StatusGuide / AgentStatusWidget verbatim
(G2). Design-gate: mockups 18c + 18e approved in Claude Design X17.
tsc --noEmit + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A case parked on a status the trimmed PHASES list dropped — but the agents
still set, e.g. `analyst_verified` (HEARTBEAT.md), `research_complete`
(legal-researcher) — rendered with NO active phase: phaseIndexOf returned -1,
so the pipeline stepper went fully blank and the chair couldn't see where the
case stood (reported on 1043-02-26, currently at analyst_verified).
Add LEGACY_STATUS_PHASE in case-status.ts mapping each legacy/intermediate
status to its display phase (analyst_verified/research_complete → "ניתוח וכיוון"),
and a single phaseIndexOf() exported from the SSoT. status-hero.tsx and
workflow-timeline.tsx both dropped their identical local phaseIndexOf (which had
the bug) and import the shared one — no parallel path (G2).
Display-only data-mapping fix; the stepper design is unchanged. Does NOT address
the root drift (agents still set analyst_verified) — that prompt-alignment is a
separate, carefully-sequenced follow-up needing cross-company agent sync.
Invariants: G2 (single phase-resolution path, legacy handled in the SSoT, no
new parallel logic). No backend / status-model change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "אימות פסיקה" tab reloaded its data on every tab revisit and the first
load took 1.5–3s. Two independent causes, two fixes:
Frontend (web-ui/src/lib/api/citation-verification.ts):
- staleTime 10s → 5min and refetchOnMount: false. Radix Tabs unmounts inactive
TabsContent, so switching back used to trigger a full rebuild once the 10s
window elapsed. Verify mutations still invalidate the query, so edits pull
fresh data.
Backend (mcp-server/.../case_citation_verification.py):
- The per-argument loop ran a Voyage embed + vector search + batched authority
query sequentially (N args × ~300ms waterfall). Extracted to _fetch() and
fanned out with asyncio.gather — order preserved, n_verified counter and
per-argument seen-sets unchanged. First load ~5× faster on multi-argument cases.
Pure read/assembly — no new parallel path (G2), no silent error swallowing
(search/radar still log warnings). Invariants: INV-DIG1/INV-AH unaffected
(still read-only, never cites).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Storage stays UTC (DB TIMESTAMPTZ, API ISO-UTC) — only the display layer is
localized, and now deterministically: every timestamp renders pinned to
Asia/Jerusalem via a single Intl-based formatter, so SSR (UTC container) and
the browser agree on any runtime. No layout/visible-format change — only the tz.
- New single date formatter web-ui/src/lib/format-date.ts (G2): formatDate /
formatDateShort / formatDateLong / formatDateTime / formatDateTimeFull /
formatTime / formatIsoDate + Israel helpers getIsraelYear / israelDayKey /
israelMidnightMs / israelParts + formatRelative (long/short/tight wording).
- Routed all ad-hoc toLocaleDateString/toLocaleTimeString/toLocaleString +
hand-rolled new Date(...).get*() / toISOString().slice(0,10) timestamp call
sites (30 files) through it. Number toLocaleString left untouched.
- Spec: added INV-UI9 to docs/spec/X6 (UTC storage, Asia/Jerusalem display).
Display-only; no layout/design/IA change → Claude Design gate N/A.
Invariants: G2 (single date formatter, no parallel ad-hoc formatting), INV-UI9.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regenerate src/lib/api/types.ts from the live production OpenAPI schema
after PR #358 (#202) added the read-only summarize_party_claims endpoints:
- GET /api/cases/{case_number}/research/party-claims-summary (read)
- GET /api/cases/{case_number}/research/party-claims-summary/download
- GET /api/cases/{case_number}/research/party-claims-summary/export-docx
The regen also picks up other endpoints that had drifted out of sync
(citation-verification, agents/reset, digest-radar, plans/fetch,
style-distance-history, scripts/{name}/run, canonical-halachot instances),
bringing the single generated client back in line with production.
Invariants: X6 (UI<->API contract kept in sync via the generated client),
G2 (single auto-generated client, no hand-edited parallel types).
tsc --noEmit passes clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
מנגנון-הייצור (claude_session→claude -p) לא העביר model/effort: BLOCK_CONFIG
הגדיר temp+model per-בלוק שהיו מטא-דאטה-מת (block_writer.py:447 העביר רק
prompt/timeout/tools). הייצור רץ על מודל ברירת-המחדל של ה-CLI עם פרומפטים-חופשיים
→ מקור אי-העקביות בטיוטת-הביניים.
תיקון (עקביות-מבנית, WS5):
- נעיצת model="claude-opus-4-8" (GENERATION_MODEL) לכל בלוקי-ה-AI + effort per-בלוק
שמועבר בפועל דרך claude_session.query → `claude -p --model --effort`.
מפת-effort: ה=medium · ו=medium · ז=high · ח=medium · ט=high · יא=high · י=xhigh.
- ניסוח-פתיחה קבוע לבלוק-ה (תמיד "לפנינו ערר…", סוף ל-OR "עניינה של החלטה זו").
- מבנה-פנימי קבוע לבלוק-ה; סדר-בלוקים בייצוא כבר קבוע (_INTERIM_BLOCK_ORDER).
- הסרת שדות temp/model המתים מ-BLOCK_CONFIG; "temp" deprecated (Opus 4.7/4.8 דוחים
temperature→400). decision_blocks.temperature נשמר=0 לתאימות-עמודה בלבד.
- timeout נגזר מ-effort (לא מפיצול sonnet/opus שכבר לא קיים).
- עדכון endpoint reference /api/settings/mcp/blocks + blocks-tab + McpBlock לחשוף effort.
- ספ: docs/block-schema.md §3 (effort מחליף temperature), docs/spec/06-export.md
(דטרמיניזם-מבני של טיוטת-הביניים).
G2 (מקור-אמת יחיד) — אין מסלול-ייצור מקביל: write_interim_draft מתזמר את אותו
write_and_store_block→write_block של ההחלטה המלאה.
claude_session-local-only — הייצור נשאר דרך גשר-ה-CLI, לא SDK/קונטיינר.
INV-EX1 — סדר-ייצוא דטרמיניסטי נשמר; G11 — עקרונות-הכתיבה ל-12 הבלוקים נשמרים.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
תיקוני-מבנה ללולאת-הלמידה (TaskMaster #157):
1. כרטיס ה-curator (/training טאב "אוצֵר") ספר decision_lessons WHERE source='curator'
— ערך שאף מסלול-קוד לא כתב → תמיד 0. עוצב-מחדש (דרך שער-העיצוב Claude Design, אושר)
להציג ביושר את שלושת ערוצי-ההזנה לכותב: דיסטילציה→appeal_type_rules (180, זורם),
פאנל→decision_lessons (81, ממתין), אוצֵר→source='curator'. get_curator_stats שוכתב.
2. drift ספ↔סוכן: 07-learning.md §1.1 + INV-LRN3 קבעו שהאוצֵר רושם ממצאים כ-decision_lesson
source='curator', אך הסוכן כתב comments בלבד — הממצאים האיכותיים אבדו. נוסף כלי-MCP
record_curator_findings + §A.5b ב-hermes-curator.md (read-only נשמר; הצעה מגודרת-שער).
3. get_recent_decision_lessons(limit=15) חתך בשקט — נוסף WARN על מה שנחתך (חוקה §6);
הפתרון האמיתי = סינתזת-לקחים (TaskMaster #158).
Invariants: מקיים INV-LRN1/G10 (שער-יו"ר), INV-LRN3 (לכידה מובנית), INV-IA2/IA5 (מקור-אמת
יחיד), G12 (leak-guard עובר), G2 (אין מסלול מקביל — אותם מאגרים). פער-מימוש פתוח מתועד:
§A לא רץ אוטומטית על mark-final (pipeline-wake exits) → נדחה לתכנון נפרד.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The two stage indicators in the compose page's "השלמה והעברה" rail
("הרץ למידת-קול" / "הרץ אימות-הלכות") were static placeholder text
translated from mockup 03 and never wired to data — they always read
"ממתין להעלאת הסופי", even after the final was uploaded and both
pipelines completed. (The real status was already shown correctly by
LearningStatusBadges on the case page.)
Wire them to useCaseLearningStatus (/api/cases/{n}/learning-status) — the
same source the drafts-panel badges use (G2). The trailing text is now
derived: "ממתין להעלאת הסופי" until the final is uploaded, then the live
state ("✓ הושלם · 12 לקחים הופקו · 12 הוצעו לאישור" / "✓ הושלם · חולצו 48
· 33 אושרו · 15 נדחו", or running/queued/failed).
Data-binding bugfix only — identical markup/classes (mockup-03 layout
preserved), so no visual redesign; within the design-gate bugfix exception
(chair-approved approach). tsc + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
qa_passed was set by the old agent pipeline but not included in the
trimmed 10-status CASE_STATUSES list or LEGACY_STATUS_LABELS, causing
the status badge and workflow timeline to render nothing ("לא ידוע").
Added qa_passed → "טיוטה" to LEGACY_STATUS_LABELS as a display-only
fallback so any case still carrying this value renders correctly until
migrated. Case 8174-12-24 status updated to drafted via PUT /api/cases.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a one-click 'reset agents' action for cases where writer/QA agents
are stuck in Paperclip's error state (triggered by recovery loop or
failed run). Addresses the root cause documented in
reference_paperclip_recovery_loops: reassigns open issues from agents
back to the chair user, and calls reset_agent_session for each error
agent to clear wedged runtime sessions.
Changes:
- paperclip_client.py: reset_case_agents() — reassigns stuck issues +
clears error status in Paperclip DB + calls reset_agent_session
- agent_platform_port.py: exports pc_reset_case_agents (G12 gate)
- app.py: POST /api/cases/{case_number}/agents/reset endpoint
- agents.ts: useResetCaseAgents mutation hook + AgentResetResult type
- agent-status-widget.tsx: 'אפס' button (shown only when error agents
exist) with Dialog confirmation + loading state + toast feedback
Invariants: G12 (Paperclip only via port), G2 (no parallel path —
uses existing reset_agent_session + pc_get_case_issues).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full code review of the case-detail page (14 components) surfaced these,
all fixed here:
Logic bugs
- case-edit-dialog: form.reset ran on the 5s useCase refetch while the dialog
was open, clobbering in-progress edits. Now resets only on open→true.
- status-changer: `selected` never synced to async/external `currentStatus`
(stale dropdown). Reworked to track currentStatus until an explicit pick;
resets to tracking after save.
- decision-blocks-panel: `block.content`/`word_count` accessed without null
guards (endpoint has no response model) → potential render crash. Coerced
with `?? ""` / `?? 0`. `STATUS_LABELS[status]` now falls back to the raw
status instead of rendering literal "undefined".
- document-type-editor: `await mutateAsync()` in async click handlers without
try/catch → unhandled promise rejection. Wrapped (errors still surface via
isError).
Resilience / hygiene
- page.tsx: a transient 5xx on the background poll flipped the WHOLE page to
the error card and discarded loaded data. Now gated on `!data`, plus a
"נסה שוב" retry.
- cases.ts useUpdateCase: invalidated casesKeys.all, which re-invalidated the
detail it had just optimistically patched. Scoped to the list prefix.
RTL correctness (logical properties)
- agent-status-widget `mr-auto`→`ms-auto`; agent-activity-feed `mr-auto`→
`me-auto`, icon `ml-1/ml-2`→`me-1/me-2`, required `*` `mr-1`→`ms-1`;
document-type-editor list `pr-4`→`ps-4`.
Minors
- drafts-panel: `<a href>`→`next/link` (operations + citation links) for SPA
nav. agent-activity-feed: issueMap memoized; comment Textarea aria-label.
upload-sheet: `unknown` status no longer shown as green success (neutral
icon + "רענן לאישור"). citations.ts: case_name typed `string | null`.
Design gate: visual-touching items (RTL gap side, retry button, neutral
upload icon) were chair-authorized via the reviewed-findings approval ("fix
all"); none alter an approved page layout — they are correctness fixes.
tsc clean; eslint clean (1 pre-existing form.watch warning, untouched line).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the Claude-Design-approved inline status banners for the
"חשב טיעונים" panel (mockup 25-legal-arguments-panel): the four endpoint
states (queued / exists / no_claims / skipped) now render as tone-coded
banners below the header instead of a transient toast. Toast is kept only
for hard transport errors.
Also fixes a pre-existing double-card bug found while reviewing the page:
the page's "arguments" tab already wraps the panel in <Card><CardContent>
(page.tsx:151-155), yet LegalArgumentsPanel rendered its OWN <Card> too —
unlike its sibling tab panels (DecisionBlocksPanel/DraftsPanel/
AgentActivityFeed) which render a plain <div>. The panel now matches that
convention (single card, no double border/padding), consistent with the
approved single-card mockup.
Design gate: card 25 approved by חיים. tsc + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The RunLogDialog rendered the live agent log inside a Radix ScrollArea.
Radix wraps Viewport children in an inner `display:table` div that
shrink-wraps to the widest unbreakable token — the long `toolu_…` IDs and
`/home/chaim/…` file paths in the raw JSON log. That table cell expands
instead of constraining width, so `whitespace-pre-wrap break-words` never
had a width to wrap against and the text spilled out past the dialog
borders.
Replace the ScrollArea with a plain width-constrained scrollable div and
switch break-words → break-all so the long unbreakable IDs/paths wrap too.
Pure presentational fix (no invariant surface).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The /aggregate-arguments endpoint ran an in-container BackgroundTask that
called claude_session (the local `claude` CLI) — which does not exist in the
FastAPI container. The button silently produced nothing, and on `force` it
destructively DELETEd existing arguments *before* the doomed LLM call.
Replace the inline task with the established delegation pattern used by
"חלץ עובדות שמאיות" (extract-appraiser-facts): a cheap in-container DB
pre-check (no_claims / exists), then a Paperclip wakeup of the company's
legal-analyst, which runs mcp__legal-ai__aggregate_claims_to_arguments
locally (where the CLI lives) and reports back. `force` now runs locally too,
so delete+recompute are atomic on the host — no more destructive failure.
Frontend: AggregateArgumentsResult becomes a discriminated union
(queued | no_claims | exists | skipped) and the toast is status-accurate
instead of the misleading fixed "refresh in a minute".
Invariants: G12 (Paperclip touch confined to paperclip_client behind the
agent_platform_port), G2 (replaces the broken path, no parallel capability),
engineering §6 (explicit statuses, no silent swallow).
UI change is logic/toast only (no visual-layout change) — within the
Claude-Design-gate bug-fix exemption. Richer inline status panels deferred
to the gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Frontend half of the citation-verification panel (backend in #323), as the agreed
third tab in /compose — matching the approved X17 mockup 24-citation-verification:
- lib/api/citation-verification.ts: hand-written types + useCitationVerification
query + useVerifyCitation mutation (POST verify, invalidates the view).
- components/compose/citation-verification-panel.tsx: per legal argument →
supporting corpus precedents with the cited_by authority chips (אומץ ×N /
אובחן ×N), the exact supporting_quote, ✓ מאמת / ✗ לא רלוונטי verify actions, a
per-citation chair-note field, and the per-issue 📡 radar (unlinked digests,
pointer-only). Summary strip + INV-AH/INV-DIG1 reminders.
- compose page: third tab "אימות פסיקה" alongside עורך הבלוקים / עמדות וטענות.
No api:types regen needed (endpoints return assembled dicts; types hand-written
per the lib/api convention). tsc + eslint clean.
Invariants: INV-AH (writer cites only verified), INV-DIG1 (digest never cited),
G2 (consumes the one backend view). Visual matches the gate-approved mockup.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hearing-date/updated/sync metadata dl dropped below the title (taking rows and
pushing the band down) when the appellants/respondents line was long, because the
outer row used flex-wrap. Fix:
- Drop flex-wrap on the title↔metadata row (and flex-1 on the title block) so the
metadata dl stays parallel to the H1; the title block shrinks instead.
- Clamp the parties line (and the subject fallback) to 2 lines with line-clamp-2 +
title tooltip, so a long party list no longer grows the band.
Chair-directed layout fix; existing components, no new design.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chair-directed navigation/layout fixes (existing components, no new design):
1. "פתח עורך החלטה" moved into the case-page band actions (right after "העלאת
מסמכים") so it's reachable from EVERY tab, not only סקירה. Removed the now-
redundant full-width CTA from the overview tab.
2. Prominent "→ חזרה לדף התיק" back-link added to the /compose header (the
breadcrumb link was too subtle).
3. Home cases table: rail trimmed 360→280px and the title cell made min-w-0 so the
table gets the width it needs and no longer shows a horizontal scrollbar.
No backend / API change. The larger NEW citation-verification panel stays gated
behind Claude Design (preview 24-citation-verification already pushed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The precedent-detail "ציטוטים מקושרים" panel rendered only MANUAL appeal-chain
links (case_law_relations), so it stayed empty even when decisions cite the
ruling — the automatic citation graph (precedent_internal_citations) was never
surfaced. e.g. עע"מ 317/10 (שפר) has 12 incoming citations in the DB, none shown.
Fills the existing panel from the citation graph (data/logic only, no new
visual design — gate-exempt per web-ui/AGENTS.md):
- citation_extractor.list_citations_to_case_law: return source precedent_level/
court/date too (additive; reuses the one canonical incoming query — G2).
- precedent_library.get_precedent: add incoming_citations[], shaped like the
RelatedCase row. No parallel resolution path.
- web-ui: render incoming citations in the same row style, read-only (no unlink),
de-duped against manual links; manual "קשר" linking preserved alongside.
Invariants: G2 (single citation-resolution path, reused), G1 (graph self-heals
via existing relink_orphan_citations on upload). No schema change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The /graph "משנה / עד שנה" dropdown was hardcoded to 1994–2026, so dated
precedents below 1994 were unreachable by the year filter. After backfilling
decision dates, the corpus now has precedents from 1982 (ע"א 725/81), 1988
(910/86) and 1990 (בג"ץ 1578/90).
Floor → 1980 (covers the oldest); ceiling → current year via getFullYear()
so the range never ages out. Pure UI-logic change — same year_from/year_to
params, no new data path (G2), no backend touch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The table fetched limit:200 while the header shows the true open count (~497,
via a separate COUNT). With accordion grouping this became visible: sections
summed to exactly 200 (e.g. 16 chair / 173 digest / 11 other) — and 90 of the
106 committee rows (the high-value "cited by Dafna" ones) were hidden past row
200. True buckets: chair 106 / digest 328 / other 63.
- web-ui: list limit 200 → 1000 so all open rows load; accordion section counts
(computed from the loaded set) now equal the header total.
- backend: list cap 500 → 2000 to allow it (response stays a few hundred KB).
Logic/data only — no visual change (bug-fix exception to the design gate).
tsc --noEmit clean; py_compile clean.
Follow-up: when the backlog (digests grow daily) approaches the cap, move to
server-side per-bucket counts + pagination/virtualization (design gate).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chair-approved (Claude Design card 09): split the flat table into collapsible
sections by where the gap surfaced, so the high-value "cited by Dafna" rulings
aren't buried among the 330 digest rows.
- Three <details> accordion sections within the active status tab:
· "צוטט ע״י דפנה" — rows with a resolved chair from the citation-graph bridge
(cited_by_chairs non-empty); open by default.
· "יומון" — discovery_source='digest'; collapsed.
· "אחר" — party-cited / manual remainder; collapsed.
- Row JSX extracted to renderRow() and reused across sections; shared
TableHeaderRow(); grouping via useMemo over the current result set.
- Empty sections are hidden; section header shows source chip + count.
No backend change — groups on existing cited_by_chairs / discovery_source.
tsc --noEmit clean.
Invariants: INV-IA* (task-oriented surface, single gate) — UI only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the chair-approved redesign of /missing-precedents (Claude Design
card 09). The "צוטט ע״י" and "תיק" columns were empty for ~98% of rows because
the corpus-decision reliance lives in precedent_internal_citations (keyed to
case_law), a different system than missing_precedents (keyed to cases).
Backend (G2 — read-time bridge, no stored duplication):
- list_missing_precedents: new cited_by_chairs / cited_by_decisions columns,
resolved from the citation graph by normalized docket number (same
normalization the relinker uses). For an open gap cited by committee
decisions, surfaces the chair(s) (e.g. דפנה תמיר) + the deciding case numbers.
Frontend (matches approved mockup):
- "צוטט ע״י" shows the chair chip (bridge) with priority over the generic
discovery-source chips; "תיק" shows the deciding decision number(s) + "+N".
- Status filter reduced to פתוח / נסגר (removed הועלה, לא-רלוונטי, הכל).
- "פסיקה" column collapses to a single line when case_name is empty — fixes the
duplicated-citation two-row rendering.
- Lifecycle note simplified to open → closed.
Verified: bridge SQL returns chair+decisions on live data; tsc --noEmit clean;
py_compile clean.
Follow-up (data, separate): LLM backfill of legal_topic (נושא); create
missing_precedents rows for the 28 graph-cited rulings not yet tracked.
Invariants: G2 (single source of truth — bridge derived at read, no duplicated
column) · INV-AH context (closing source gaps).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 3-judge panel deliberation was only surfaced in the pending queue; the
'rejected'/'approved' tabs hid WHY an item was auto-rejected/approved even
though the data is captured (reviewer='panel:... maj-not-entailed'). And those
tabs had no way to locate a specific item.
- useHalachotByStatus now requests include_panel_round=true and accepts a
server-side search term (same /api/halachot params the pending queue uses).
- HalachaRestoreCard renders the existing PanelDeliberation component when a
round exists — same card the chair already sees in pending.
- RestorePanel gains the same locate-bar (debounced, server-side search) +
a match-count indicator; the bar stays visible on empty/loading so a search
is always clearable.
Reuses already-approved UI (deliberation = Design card 18, locate-bar = card
19); composition onto a sibling surface, approved directly by chaim. tsc +
eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pending review queue split items into 'להכרעתך' (chair judgment) vs
'דורש תיקון-חילוץ' (needs re-extraction) by a too-blunt predicate: ANY
quality flag + no panel round → re-extraction. That misfiled fact-specific
'application' items (extraction is fine; the open question is whether the
rule generalizes) as extraction defects, contradicting the backend, which
treats only quote_unverified/truncated_quote/thin_restatement/near_duplicate
as defects and routes 'application' to chair judgment.
Now isExtractionFixItem checks the backend's DEFECT set, so 'application'
(and any other non-defect flag) lands in 'להכרעתך'. For 8508-03-24 this
moves 21 application items judgment→correct and leaves 6 thin_restatement
as the only re-extraction items.
Logic-only fix (predicate), no visual change — design-gate exempt per
web-ui/AGENTS.md. tsc + eslint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
- הסרת כרטיס "סקירת התיק" הדל (3 אלמנטים בלבד)
- כפתור "התחל תהליך" עלה לפס-הפעולות בראש הדף (ליד "העלאת מסמכים")
- כרטיס חדש AgentActivityPreview: 4 הissues האחרונים + קישור לטאב מלא
- "תוצאה צפויה" ירדה לשורת מטא-דאטה בראיל הסטטוס (מוצגת רק אם נקבעה)
- תיקון באג: expected_outcome לא נשמר — guard if expected_outcome: הוחלף ב-is not None
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
כרטיס-התכניות בדף /precedents הציג רק review_status=pending_review,
כך שתכניות מאושרות (התקינות, בשימוש-חוזר בבלוק ט) לא היו נגישות לעיון.
ה-backend כבר תמך ב-?review_status= ו-?q= — חסר רק חוט-UI.
- plans.ts: usePlansPending → usePlansAll (טעינת כל המרשם בקריאה אחת;
הפאנל מסנן client-side — מרשם קטן, מונה-מדויק לכל סגמנט, חיפוש מיידי)
- plans-review-panel: סרגל-מצב (ממתינים/מאושרות/כולן) עם מונים + תיבת-חיפוש
fuzzy (מספר/שם/ייעוד/aliases, מנורמל-bidi); הערת-קטיעה אם >1000 (בלי cap שקט)
- PlanCard מסתעף לפי review_status: מאושרת/נדחתה → תג-מצב + "החזר לתור"
(review→pending_review) במקום אשר/דחה; דדופ-candidates רק בתור
עבר דרך שער-עיצוב Claude Design (מוקאפ 22-plans-review מאושר ע"י חיים).
ללא שינוי-backend. מרחיב מרשם-V38 הקיים — לא מסלול מקביל.
Invariants: G2 (יכולת קיימת, endpoint קיים) · INV-IA2 (שער-יחיד /precedents)
· G10 נשמר (review_status שער-יו"ר).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
שני פערים שצפו מ-/precedents בחילוץ-ההלכות:
1. **practice_area לא סומן** — השדה הועבר ל-LLM כקונטקסט-קריאה-בלבד ולא חולץ
מעולם, כך שהעלאות שהשאירו אותו ריק נשארו ריקות והרדיו ב-/precedents הופיע
ללא בחירה. עכשיו נגזר ב-apply_to_record: עדיפות לגזירה דטרמיניסטית מקידומת
מספר-התיק (1xxx→rishuy, 8xxx→היטל, 9xxx→197 — מקור-אמת לדוקטי ועדת-ערר,
INV-AH rule-based), ובנפילה — סיווג-תוכן של ה-LLM (שדה practice_area חדש
בפרומפט, אנום-סגור) עבור פסקי-בית-משפט שהקידומת שלהם אינה מקודדת תחום.
ממלא רק כשריק (G1 — נרמול במקור, לא תיקון-בקריאה).
2. **שם-יו"ר לא חולץ** (למשל 1132-09-24) — המיזוג היה מגודר על
source_kind=='internal_committee' בלבד, ודילג בשקט על החלטות-ועדה שהועלו
במסלול הפסיקה החיצוני (external_upload + source_type=appeals_committee, כמו
החלטת ת"א מנבו) — היו"ר ישב בבלוק-החתימה אך לא חולץ. עכשיו מגודר על "האם זו
החלטת-ועדה" (source_type/level אפקטיביים), לעולם לא על פסק-בית-משפט. ה-CHECK
כופה non-empty רק ל-internal_committee, לכן כתיבה ל-external בטוחה.
חיזוק-פרומפט (לבקשת היו"ר): chair_name מציין מפורשות את בלוק-החתימה הדו-טורי
(מזכיר↔יו"ר — לקחת את צד-היו"ר) ומזהיר לא לחלץ יו"ר של פסקי-דין **מצוטטים**
בגוף ההחלטה.
UI (לוגיקה-בלבד, פטור משער-העיצוב): edit-sheet מסנכרן-מחדש מהרשומה הטרייה בכל
פתיחה (re-arm על סגירה) ו-usePrecedent עושה poll בזמן חילוץ — כך מילוי-רקע של
practice_area/chair_name מופיע בלי refresh מלא ("הכפתור לא נשאר מסומן").
בדיקות: test_metadata_extract_chair_practice_area.py (6 תרחישי-מיזוג, offline).
Invariants: G1 (נרמול-במקור), G2 (אותו extractor, לא מסלול מקביל),
INV-AH (גזירה דטרמיניסטית מועדפת, abstention כשאין ודאות).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ה-gitea_url מצביע על קובץ scripts/SCRIPTS.md, אבל הקישור הפר-סקריפט הוסיף
אחריו את שם-הסקריפט → …/scripts/SCRIPTS.md/<name> (404 ב-Gitea). מתקן:
מסיר את שם-הקובץ האחרון מהבסיס לפני הוספת שם-הסקריפט, כך שהקישור מצביע
על …/scripts/<name>.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SCRIPTS.md: הסקריפטים החד-פעמיים (25, לפי עמודת Scheduled) הוצאו מסעיף
"## סקריפטים פעילים" אל סעיף חדש "## חד פעמי" בתחתית (לפני .archive),
מקובצים באותם תתי-כותרות ### לפי תת-נושא. כל 79 השורות (54 חוזרות + 25
חד-פעמיות) נשמרו מילה-במילה. הערת-הקונבנציה עודכנה.
/scripts: ה-parser מזהה את סעיף "## חד פעמי" כסקשן נפרד; הרינדור דו-רמתי —
כותרת-סעיף "פעילים" + קבוצות, ואז כותרת-סעיף "חד פעמי" + קבוצות-משנה
מוזחות (ms-5), מקופלות כברירת-מחדל. סטטוס-השורה נגזר מהסעיף.
מאושר דרך שער-העיצוב (מוקאפ 16-scripts עודכן; חיים אישר מראש).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>