מנגנון-הייצור (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>
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>
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>
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>
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>
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>
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>
- הסרת כרטיס "סקירת התיק" הדל (3 אלמנטים בלבד)
- כפתור "התחל תהליך" עלה לפס-הפעולות בראש הדף (ליד "העלאת מסמכים")
- כרטיס חדש AgentActivityPreview: 4 הissues האחרונים + קישור לטאב מלא
- "תוצאה צפויה" ירדה לשורת מטא-דאטה בראיל הסטטוס (מוצגת רק אם נקבעה)
- תיקון באג: expected_outcome לא נשמר — guard if expected_outcome: הוחלף ב-is not None
Co-Authored-By: Claude Sonnet 4.6 <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>
יישור /compose למוקאפ 03-case-workspace (הכרעת חיים: המוקאפ נכון). ה-main
הופך לרצועת-2-טאבים:
- "עורך הבלוקים" (ברירת-מחדל) — שימוש-חוזר ב-DecisionBlocksPanel הקיים
(12 בלוקים, סטטוס, ספירת-מילים, עורך-inline על parchment, שמירה PUT).
- "עמדות וטענות" — עורך-העמדות/טענות הקיים (SubsectionCard/ChairEditor),
הועבר כמות-שהוא לטאב.
הגייטינג שונה: העמוד נטען לפי caseQuery (לא analysis), כך שעורך-הבלוקים
מוצג גם כשטרם בוצע ניתוח; ענפי-ה-analysis (pending/not-found/error/data)
עברו לתוך טאב-העמדות. band + rail (מסמכים/פסיקה/השלמה) ללא שינוי.
אין שינוי-backend — GET/PUT decision-blocks + useDecisionBlocks/useSaveBlock
כבר קיימים. מאושר דרך שער-העיצוב (מוקאפ 03 עודכן עם רצועת-הטאבים).
Invariant: G2 — שימוש-חוזר ב-DecisionBlocksPanel, ללא מסלול-עריכת-בלוקים מקביל.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
עיצוב-מחדש בכיוון "כרטיסים מרועננים" (מאושר ע"י חיים, Claude Design 01-approvals):
- פס-חומרה דק (3px) בקצה-התחלה של כל כרטיס (border-s לפי severity) במקום
תג-מספר קטן. הספירה הראשית (המספר הגדול) נשמרת.
- כרטיס תור-נקי מעומעם: רקע parchment, ללא צל, מספר ב-ink-muted, שבב "✓ תור נקי".
- מבנה 2×2 והלוגיקה (usePendingApprovals, samples, CTA) ללא שינוי.
מאושר דרך שער-העיצוב.
Invariant: INV-IA1 — מרכז-אישורים נשאר השער-האנושי-היחיד; שינוי ויזואלי בלבד.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two follow-ups to the usage-% feature:
1. The /api/oauth/usage endpoint 429s when polled often — /operations refreshes
every 5s and each refresh hit it (plus the supervisor + ad-hoc calls). Cache
the last good payload on the host bridge for 60s and serve it; only re-fetch
when stale, so Anthropic sees ~1 req/min regardless of dashboard polling. On
a fetch failure (e.g. a transient 429) serve the last good payload instead of
blanking the card. The 5-hour window moves slowly, so 60s stays fresh.
2. The third meter showed weekly-Opus, which is null on this account (the
per-model weekly cap that's actually populated is Sonnet). Switched the
display to seven_day_sonnet / "שבועי · Sonnet". (The supervisor keeps gating
on seven_day_opus — the halacha drain runs Opus, so the Opus cap is the
correct gate even when it's null/inactive.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surfaces the 5-hour / weekly / weekly-Opus utilization the Claude Code status
bar shows — the authoritative number, not a token estimate. Design approved via
the Claude Design gate (card 02c-operations-usage.html).
Three layers:
- court-fetch-service (host bridge): new GET /usage reads the OAuth token from
~/.claude/.credentials.json and proxies /api/oauth/usage with the required
claude-code User-Agent. Read-only, no auth (like /pm2). Host-only — the token
never enters the container.
- web/app.py: _ops_subscription_usage() proxies the bridge /usage; the
/api/operations snapshot gains a `subscription_usage` field (null when the
undocumented endpoint is unreachable).
- web-ui: SubscriptionUsagePanel renders three meters (label · % · bar · reset)
at the top of /operations; bar turns amber >75% / red >90%; hidden when usage
is null. Types added to operations.ts (hand-maintained snapshot type).
Also fixes a pre-existing react/no-unescaped-entities lint error in
learning-panel.tsx (escaped a `"` in Hebrew text — renders identically).
tsc --noEmit passes; lint error count 0. (Full next build is blocked only by the
manual-worktree node_modules symlink — the Docker build has real node_modules.)
Invariants: G2 (usage surfaced through the existing host bridge + /api/operations
snapshot — no parallel control path).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- legal-halacha-supervisor had no SERVICE_LABELS entry → the row rendered the raw
English pm2 name. Add a Hebrew label (the English id stays as the mono subtitle,
like every other row).
- legal-halacha-drain label said "×שעתיים" (every 2h) — wrong. The cron fires
hourly across the night band; the script trims to the 23:00–05:00 IL window.
Relabel to "חלון-לילה 23:00–05:00".
Content/i18n fix only — no visual/layout change (design-gate bug-fix exception).
Implements the chair-approved Claude Design mockup (02b-operations-burst): a single
gold "⚡ הפעל BURST" button on the legal-halacha-drain row that opens a deadline
dialog (datetime-local, default the upcoming Saturday 18:00, quick chips for Sat
18:00 / +5h / midnight). While a burst is active the row shows a gold pill with the
deadline + an "עצור BURST" stop button. Manual only.
- operations.ts: burst_until on OpsService + useDrainBurst hook (POST .../burst).
- page.tsx: BurstControl component, gated to legal-halacha-drain.
- types.ts: regenerated (npm run api:types) — the new burst route.
Active-state is derived from burst_until presence (the supervisor NULLs it at the
deadline, snapshot refetches every 5s) — no impure Date.now() in render.
Invariants: G1/G2 — single DB-backed control (drain_controls.burst_until), shared
with the host supervisor; no parallel path. UI passed the Claude Design gate.
הבעיה: בדף /missing-precedents לא ניתן היה לאתר פסיקה חסרה לפי מספר ההחלטה
החסרה עצמה (למשל 85074). השדה היחיד לחיפוש-תיק עשה get_case_by_number על
מספר ה-ערר שבו צוטטה הפסיקה — ולכן הקלדת מספר-הפסיקה החזירה רשימה ריקה,
למרות שהרשומה קיימת (ערר (ת"א 85074-04-25) ... status=open).
התיקון (הרחבת השדה הקיים, ללא עמוד/שדה חדש — בהנחיית חיים):
- db.list_missing_precedents: פרמטר q חדש — ILIKE על mp.citation +
mp.case_name + cited-in c.case_number (אינדקס-פרמטר יחיד, additive;
שאר הקוראים לא נוגעים).
- GET /api/missing-precedents: פרמטר q; case_id/case_number נשארים
מסננים-מדויקים לקוראים תכנותיים.
- web-ui: התווית "תיק (מספר ערר)" → "מספר תיק", placeholder
"85074 או 1017-03-26"; השדה שולח q (חיפוש חופשי) במקום case_number.
Debounce 350ms נשמר.
api:types לא חודש: ה-hook בונה את ה-querystring ידנית וה-response לא
השתנה; חידוש מול prod (שעוד לא נפרס) רק היה מושך drift לא-קשור.
בדיקות: tsc --noEmit נקי, eslint נקי על הקבצים שהשתנו, py_compile נקי.
Invariants: G2 (הרחבת היכולת הקיימת, לא מסלול-חיפוש מקביל), INV-IA1
(שער/דף יחיד לפסיקה-חסרה — בלי עמוד חדש), §6 (ללא בליעת-שגיאות).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
נוהל-יו"ר (2026-06-11): מבנה מספר-תיק = <סידורי>-<חודש>-<שנה>, ואורך הסידורי
מקודד את סוג-ההליך — 4 ספרות = ערר, 5 ספרות = בל"מ. הספרה הראשונה ממשיכה
לקבוע תחום בשני האורכים (1→רישוי, 8→היטל, 9→פיצויים). הכלל חד-כיווני:
5-ספרתי הוא תמיד בל"מ; 4-ספרתי אינו מחייב ערר (בל"מ-מורשת מזוהה מהנושא).
הבאג שדיווח עליו היו"ר: חיפוש פסיקה-חסרה לפי מספר-תיק החזיר 404 על כל ערך
שאינו תיק קיים — שבר את הטבלה תוך כדי הקלדה ועל מספרי 5-ספרות.
תיקונים:
- web/app.py: GET /api/missing-precedents — מסנן case_number שלא תאם תיק מחזיר
רשימה ריקה (200), לא 404. סמנטיקה תקינה ל-collection-filter.
- missing-precedents/page.tsx: debounce (350ms) על שדות-הסינון — קוורי אחד
אחרי שמפסיקים להקליד, לא אחד לכל הקשה.
- practice_area.py: regex סידורי \d{4}→\d{4,5}; case_serial_digits() +
is_blam_by_number() (5⇒בל"מ); derive_subtype_with_blam ו-derive_proceeding_type
מזהים בל"מ גם מ-5-ספרות (בנוסף לנושא). callers: cases.py, internal_decisions.py.
- proofreader.py: דפוסי חילוץ-שם-קובץ \d{3,4}→\d{3,5}.
- web-ui: practice-area.ts (מראָה ל-backend), schemas/case.ts (regex
serial-month-year, 4-or-5 ספרות, superRefine 5⇒בל"מ), placeholder בוויזרד.
- תיעוד: docs/spec/X1-identifiers.md §1א + legal-ai/CLAUDE.md.
Invariants: מקיים G1 (נרמול-במקור — ספרה ראשונה כמקור-אמת יחיד לתחום),
G2 (מסלול-סיווג יחיד, אין כפילות), INV-DM/X1 (מפתח קנוני + proceeding_type).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
יישום מלא של פריסת-הכרטיס מהמוקאפ המאושר 01-approvals (במקום ליטוש שטחי קודם):
מספר-גדול (text-3xl navy) במקום badge קטן · נקודת-חומרה + כותרת + שורת-ותק ·
שורות-מדגם עם המקור מיושר לקצה והפרדה עדינה · CTA gold. כל הנתונים/לוגיקה נשמרו.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- /home: כרטיס "מה ממתין להכרעתך" ב-aside (מצביע INV-IA1 ל-/approvals; usePendingApprovals,
ללא מונה-מתחרה) — תואם מוקאפ 04-home המאושר.
- web-ui/AGENTS.md: §"שער-עיצוב חובה — Claude Design קודם" — כל יצירת/שינוי עמוד-UI
עוברת קודם דרך פרויקט Claude Design "עוזר משפטי — IA Redesign (X17)"
(7a85b323-d880-4b6d-bac5-d4aa396fe93c) לאישור, ורק אז מוטמע (הנחיית חיים).
בדיקה: npx tsc --noEmit ✓.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
מונה "ממתין (בקלוג)" ב-/operations הציג 140 פריטים תקועים שהדריינר (Gemini, כל
15 דק') דיווח עליהם total_pending=0 — אי-התאמה בין שתי הגדרות-תור:
ה-UI סופר status='pending' (ברירת-מחדל של העמודה), בעוד הדריינר סורק רק
metadata_extraction_requested_at IS NOT NULL. שורות שקיבלו מטא במסלול אחר
(internal דטרמיניסטי, cited_only חסר-טקסט) נשארו על ברירת-המחדל 'pending' לנצח.
פילוח ה-140: 82 internal_committee (מטא דטרמיניסטי, מחוץ לצנרת-Gemini) ·
31 cited_only (אין טקסט לחלץ) · 27 external_upload (כבר מלאים).
תיקון-במקור (G1 — נרמול במקור, לא תיקון-בקריאה):
- db.create_internal_committee_decision: INSERT + ON CONFLICT קובעים
metadata_extraction_status='completed' ישירות → שורות פנימיות לא נכנסות
שוב למצב-הרפאים.
- scripts/reconcile_metadata_status.py: נרמול חד-פעמי/re-runnable של שורות
קיימות (internal/external מלא→completed · external חסר→requeue · cited_only→skipped).
הורץ: 82+27→completed, 31→skipped, pending=0.
- web-ui /operations: התווית "ממתין (בקלוג)" → "ממתין" (הסרת המילה הלועזית)
+ tooltip מדויק; הערת operations.ts מעודכנת.
Invariants: מקיים G1 (normalize-at-source) ו-INV-IA (מונה-אמת/מקור-אמת-יחיד).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
מגיש את scripts/SCRIPTS.md כדף ב-/scripts: שם · סוג · תפקיד · תזמון
לכל סקריפט בתיקיית scripts/. מקור-האמת היחיד נשאר SCRIPTS.md (G2 — אין
מסלול-תוכן מקביל); עריכה דרך git, לא מה-UI.
- web/app.py: GET /api/scripts/catalog קורא את הקובץ בזמן-ריצה (מחקה את
דפוס get_curator_prompt; HTTPException על כשל — אין בליעה שקטה §6)
- Dockerfile: COPY scripts/SCRIPTS.md (לא הועתק לקונטיינר עד כה)
- web-ui: דף /scripts (AppShell + רכיב Markdown הקיים) + מודול api + קישור ניווט
- SCRIPTS.md: תיעוד ingest_bulletins.py — היה הקובץ היחיד מ-73 שלא תועד
Invariants: G2 (מקור-אמת יחיד), G12 (אין מגע-Paperclip), X6 (UI↔API).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
הדף הציג את התורים באופן לא-אחיד (by_status גולמי), בלי הבחנה בין "ממתין"
(בקלוג: status=pending) ל"בתור" (התור הפעיל: requested_at IS NOT NULL), בלי
הצגת הפריט שרץ כרגע, ובלי שום שליטה בתהליכים.
מה נוסף:
1. כרטיסי-תור אחידים — בתור / ממתין(בקלוג) / בעיבוד / הושלם / נכשל + "רץ עכשיו"
(citation/case_number של הפריט בעיבוד) לכל drain (אחזור-פסיקה, מטא-דאטה,
הלכות, יומונים). שערי-אנוש (אישור-הלכות, פסיקה-חסרה) נשארים מוני-סטטוס.
2. פאנל ניהול-תהליכים בסגנון "שירותי Windows":
- דמון (court-fetch-service/xvfb/chat/reaper): הפעל-מחדש / עצור / הפעל.
- cron drain: "הרץ עכשיו" (pm2 restart) + מתג הפעל/כבה תזמון.
3. כל תגי-הסטטוס מתורגמים לעברית.
מנגנון:
- הפעל/כבה תזמון = דגל ב-DB (טבלה drain_controls). pm2 cron_restart מחיה תהליך
שעוצר ב-stop, לכן ה"כיבוי" האמין הוא דגל שכל drain בודק ב-startup (no-op מיידי
כשכבוי). הקונטיינר כותב/קורא ישירות מ-DB.
- הרץ-עכשיו + restart/stop/start = proxy ל-pm2 דרך endpoint חדש בגשר-המארח
(court_fetch_service /pm2/control), מאובטח Bearer + whitelist ל-legal-* בלבד.
- יומונים: drain_digests הועבר מ-crontab ל-pm2 (legal-digest-drain.config.cjs)
כדי שיופיע ויהיה שליט כמו כל drain. drain_halacha_queue.py הובא לבקרת-גרסאות.
Invariants: מקיים G2 (הרחבת /operations + הגשר הקיים, לא מסלול מקביל) ו-G1
(drain_controls = מקור-אמת יחיד לכיבוי, נורמליזציה במקור ולא תיקון-בקריאה).
אין בליעת שגיאות שקטה (הגשר מחזיר {ok,error}; המוטציות מציגות toast).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A single live page for all the background work that downloads/analyses, so the
chair can see what's running instead of guessing.
- court_fetch_service: GET /pm2 (unauthenticated, host-only) → trimmed pm2 jlist
for the legal-* services (status, restarts, mem, cron schedule).
- FastAPI GET /api/operations: aggregates the DB-backed pipelines (court_fetch
jobs, metadata + halacha extraction queues, halacha review gate,
missing_precedents, digests, recent court ingests) and proxies the host /pm2
over the docker bridge (graceful if the host service is down).
- web-ui /operations page (+ src/lib/api/operations.ts hook, nav entry under
admin): services grid (with Hebrew labels + schedules) + pipeline cards +
recent-fetch / recent-ingest lists. Auto-refreshes every 5s.
tsc --noEmit clean; pm2 status carries nothing sensitive and the bind
(10.0.1.1) is host/container-only, so /pm2 needs no secret.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final corpus-graph PR. Connects the graph to the chair's workflow and rounds
out the Obsidian-grade interactions.
Backend (web/graph_api.py): neighborhood depth cap 2 → 3 (still bounded by
NODE_CAP_MAX).
Frontend:
- URL deep-link: /graph?focus=cl:<id> is read on mount and written on focus
change (router.replace, scroll:false). GraphView wrapped in <Suspense> per
Next 16's useSearchParams requirement.
- "הצג בגרף" button on the precedent detail page → /graph?focus=cl:<id>.
- Depth slider (1–3) in the focused overlay → useNodeNeighborhood(id, depth).
- Export PNG: grabs the rendered <canvas> from the area ref → toDataURL →
download; failures surface a toast (UI4).
- Rich node panel: precedent nodes fetch headnote/summary via the existing
usePrecedent hook (Skeleton while pending, error surfaced — UI4).
- Edge-type legend (ציטוט / נושא-תחום / יומון) added under the node legend.
Deferred (noted for a later pass): expand-in-place merge, search→camera-center.
web-ui build + lint pass. Invariants: G2 (depth change is read-only), UI4
(PNG + detail errors surfaced, not swallowed). api:types post-deploy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Native, Obsidian-graph-view-like network of the precedent corpus, rendered
in web-ui from a read-only projection of the live DB. Replaces the idea of
exporting to an external Obsidian vault (which would be a parallel, drifting
copy of the corpus — the exact root cause G2 forbids).
The graph edges already existed in the data model; this only surfaces them:
nodes = precedents (case_law) + synthesized topic/practice-area hubs;
edges = cites (precedent_internal_citations) + same_chain (case_law_relations)
+ tagged/in_area (subject_tags / practice_area membership). Node size =
incoming-citation count (index-backed GROUP BY on idx_pic_target). Click a
node → local-graph neighborhood focus; panel deep-links to /precedents/[id].
Backend (read-only, SELECT only — G2):
- web/graph_api.py — Pydantic models (CorpusGraph/GraphNode/GraphEdge, so
OpenAPI emits real types — UI2) + SQL assembly over the shared db.get_pool().
- web/app.py — GET /api/graph/corpus, GET /api/graph/node/{id}/neighborhood,
both with explicit response_model. practice_area validated against the
closed enum (G5); both endpoints write nothing.
Frontend:
- react-force-graph-2d (canvas/d3-force), loaded via next/dynamic ssr:false.
- /graph page + nav entry; graph.ts TanStack hooks; filter panel (practice_area
/ source / min-citations / search / node-type toggles), node detail panel,
hover+selection neighborhood highlight. Explicit error handling (UI4).
Not a retrieval path (03-retrieval): returns graph topology, never ranked
search results. Halacha nodes + corroboration/equivalence edges are Phase 2,
already gated behind the node_types param (no contract change needed).
SQL validated read-only against the live DB (142 precedents, 85 resolved
citations, JSONB tag expansion, ANY(uuid[]) edge + BFS queries). web-ui lint
+ build pass; /graph in the route table.
Invariants: keeps G2 (single source of truth — live projection, no parallel
store), G5 (corpus separation filtered server-side), UI2 (response models),
UI4 (no swallowed UI errors).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the CSV-edit workflow with an in-app tagging page so the chair/Dafna
can label the extraction-quality gold-set by clicking, and see validator
precision/recall live.
Schema (V29): halacha_goldset — a stratified, human-tagged evaluation batch
(is_holding / correct_type / quote_complete, NULL until tagged).
db.py:
- goldset_create_sample (stratified round-robin over case×rule_type, idempotent),
- goldset_list (items + halacha content + the machine's own labels),
- goldset_tag (partial — one field at a time for keyboard tagging),
- goldset_score (ports the script's P/R/F1: each validator scored as a
not-a-holding detector against the human tags — the #81.8 input).
API: GET /api/goldset, POST /api/goldset/sample, GET /api/goldset/score,
PATCH /api/goldset/{id}.
web-ui:
- lib/api/goldset.ts (hooks),
- components/goldset/goldset-panel.tsx — card-per-item, keyboard-first
(J/K nav, H/N holding, C/X quote), progress bar, hide-tagged toggle, and a
collapsible live score table,
- app/goldset/page.tsx + nav link "מדגם-זהב" under ידע ולמידה.
Methodology guard kept explicit in UI + docstrings: tags are HUMAN ground truth,
no AI pre-fill (circular bias). Populated a 150-item stratified batch.
Verified: backend create/list/tag/score against the live DB; tsc --noEmit 0;
py_compile ok. (Local Turbopack build blocked by worktree symlink — CI builds clean.)
Invariants: G1 (eval set modeled at source in its own table); G2 (reuses the same
halacha_quality validators the extractor runs — no parallel scoring logic).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>