Files
legal-ai/docs/spec/X6-ui-api-contract.md
Chaim 37c56ff22a docs(spec): cycle-2 — 8 application-surface domains (X6–X10) + ui-audit + GAP-24..62/FU-9..15
Extends the system spec beyond the core pipeline to the 8 surfaces outside it:
- X6 UI↔API contract + design rules (INV-UI1..6)
- X7 Paperclip client & connection params (INV-INT4..8)
- X8 field-population & extraction provenance (INV-FP1..5)
- X9 MCP tool contract — 71 tools (INV-TOOL1..6)
- X10 deploy/env/secrets (INV-ENV1..5)
- ui-audit.md — page-by-page UI audit (13 pages)
- 02-data-model: derived-entity invariants (INV-DM4..6)
- X4-agents: tool-grant map + INV-AG3
- gap-audit: GAP-24..62 → FU-9..15; cycle-1 (FU-1..8b) marked done
- constitution §7 + README index (X1..X10)

Planning/spec artifacts only — no application code. All engineering invariants
backed by ≥3 sources; every finding carries verified file:line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 16:21:27 +00:00

109 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# X6 — חוזה UI↔API וכללי-עיצוב הממשק (UI↔API Contract & Design Rules)
קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) והוא ה-deep-dive על **הממשק (web-ui) וחוזה
ה-API בינו לבקאנד** — שלא היה מכוסה בספ עד כה. הוא מגדיר: (א) חוזה-הקשר פרונט↔בק (OpenAPI כ-SSoT,
מודלי-תשובה, envelope, SSE, טיפול-שגיאות); (ב) **כללי-עיצוב הממשק** — מקור-אמת יחיד ל-enums/תוויות,
helpers משותפים, וחוזה-טופס לכל סוג-מסמך. הממצאים בפועל מתועדים ב-[ui-audit.md](ui-audit.md).
> **שני סוגי invariant כאן.** UI1UI5 הם **הנדסיים** (חוזה-API/קליינט כללי — ≥3 מקורות + סטטוס).
> UI6 (חוזה-טופס) הוא **פרויקטלי-תפעולי**, נגזר מ-[X8](X8-field-provenance.md), ומשרת
> [G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai).
---
## 1. ארכיטקטורה קיימת
- **web-ui** — Next.js 16 + TS + Tailwind v4 + shadcn + TanStack Query. 13 דפים (ראה [ui-audit.md](ui-audit.md)).
- **Proxy** — [next.config.ts](../../web-ui/next.config.ts): `/api/*``NEXT_PUBLIC_API_ORIGIN` (ברירת-מחדל `http://127.0.0.1:8000`); `/openapi.json` → schema של ה-FastAPI.
- **לקוח** — [client.ts](../../web-ui/src/lib/api/client.ts): `apiRequest<T>` + `ApiError` + `makeQueryClient`. 18 מודולי-API.
- **טיפוסים** — [types.ts](../../web-ui/src/lib/api/types.ts) (auto-gen `openapi-typescript`, 124 operations). `npm run api:types`.
- **SSE** — [sse.ts](../../web-ui/src/lib/sse.ts): `openSSE` (progress של העלאות/עיבוד).
- **בקאנד** — [web/app.py](../../web/app.py): 143 endpoints, מונוליטי, **~60% ללא Pydantic response model**.
---
## 2. Invariants של התחום
### INV-UI1: ה-OpenAPI schema הוא ה-SSoT לחוזה — טיפוסי-לקוח נגזרים, לא ידניים-סוטים
**כלל:** חוזה ה-API מוגדר **פעם אחת** ב-OpenAPI (שמופק מהבקאנד); טיפוסי-ה-frontend **נגזרים** ממנו
(`openapi-typescript`), ואינם מתוחזקים ידנית במקביל. אין "טיפוס-מראה" מקומי שמשכפל endpoint וסוטה ממנו.
מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) (מקור-אמת יחיד).
**מקורות:** OpenAPI Specification 3.1 (single contract / source of truth; JSON-Schema 2020-12)
(https://spec.openapis.org/oas/latest.html) · Pact — *consumer-driven contract testing*
(https://docs.pact.io/) · Speakeasy — *Pact vs OpenAPI* (provider-driven SSoT)
(https://www.speakeasy.com/blog/pact-vs-openapi) | סטטוס: verified
**אכיפה:** `npm run api:types` ב-CI; איסור טיפוסי-מראה ידניים. **כיום אין** — ה-frontend מתחזק טיפוסים ידניים.
**הפרה ידועה:** [cases.ts:1-9](../../web-ui/src/lib/api/cases.ts) מתעד מפורשות שה-`/api/cases` מחזיר `unknown`
ולכן מוחזק טיפוס `CaseDetail` ידני; `PracticeArea` מוגדר ב-3 מקומות עם ערכים שונים ([ui-audit.md](ui-audit.md), [gap-audit GAP-30/31](gap-audit.md)).
### INV-UI2: לכל endpoint נצרך — response model מפורש (חוזה-שלמות API)
**כלל:** כל endpoint שה-UI צורך נושא **response model מפורש** (Pydantic), כך ש-OpenAPI מפיק טיפוס אמיתי
(לא `unknown`/`object`). זהו פאֶט של [G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) (שלמות-חוזה לפני צריכה).
**מקורות:** OpenAPI 3.1 (schema objects) · Zalando *RESTful API Guidelines* (explicit schemas)
(https://opensource.zalando.com/restful-api-guidelines/) · FastAPI *Response Model* docs
(https://fastapi.tiangolo.com/tutorial/response-model/) | סטטוס: verified
**אכיפה:** linter/CI שמסמן endpoint נצרך ללא response_model. **כיום אין** — ~60% מהendpoints ללא מודל.
**הפרה ידועה:** רוב ה-endpoints ב-[app.py](../../web/app.py) מחזירים dict חופשי → `unknown` ב-types.ts ([gap-audit GAP-30](gap-audit.md)).
### INV-UI3: envelope-תשובה ושגיאה עקבי על-פני ה-API
**כלל:** כל ה-endpoints חולקים **מבנה-תשובה ומבנה-שגיאה אחיד** (לא string-לפעמים-JSON-לפעמים). שגיאות
לפי תבנית סטנדרטית (Problem Details). מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים).
**מקורות:** RFC 9457 — *Problem Details for HTTP APIs*
(https://www.rfc-editor.org/rfc/rfc9457) · Zalando *RESTful API Guidelines* (consistent responses) ·
Microsoft *REST API Guidelines* (error structure)
(https://github.com/microsoft/api-guidelines) | סטטוס: verified
**אכיפה:** envelope משותף ב-app.py + handler-שגיאות גלובלי. **כיום אין** — מעורב string/JSON/`{error}`/`{detail}`.
**הפרה ידועה:** [search.py](../../web/app.py) מחזיר `"לא נמצאו תוצאות."` או JSON; חלק מהכלים `{error:...}`, חלק raise ([gap-audit GAP-32](gap-audit.md), [X9 INV-TOOL1](X9-mcp-tool-contract.md)).
### INV-UI4: אין בליעת-שגיאה ב-UI
**כלל:** כל מצב-שגיאה (fetch/mutation) **מוצג או מטופל מפורשות** — error boundary ו/או טיפול ב-`error`
של `useQuery`/`useMutation`. אין כשל שקט שמשאיר את המשתמש בלי משוב. תואם כלל "אין בליעה שקטה"
([חוקה §6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)).
**מקורות:** React docs — *Error Boundaries*
(https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) ·
TanStack Query — *Error handling* (https://tanstack.com/query/latest/docs/framework/react/guides/query-functions#handling-and-throwing-errors) ·
Nielsen Norman Group — *Error-Message Guidelines* (https://www.nngroup.com/articles/error-message-guidelines/) | סטטוס: verified
**אכיפה:** error boundary ברמת-האפליקציה + רכיב-שגיאה משותף; code-review. **כיום חלקי** — חלק מהדפים אינם
מטפלים ב-`error`; כרטיסי-שגיאה משוכפלים ולא-עקביים.
**הפרה ידועה:** [ui-audit.md](ui-audit.md) — כרטיס-שגיאה משוכפל ×3, fallback של SSE שמסתיר כישלון כ-"completed" ([gap-audit GAP-32/33](gap-audit.md)).
### INV-UI5: חוזה-SSE/progress עם terminal states מוגדרים
**כלל:** ערוץ ה-progress (SSE) נושא **terminal states מפורשים** (completed/failed/timeout). אין הנחת-השלמה
שקטה על timeout; אי-התאמות-TTL (frontend↔backend) נמנעות. נקשר ל-freshness ([G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן)).
**מקורות:** WHATWG HTML — *Server-Sent Events / EventSource* (https://html.spec.whatwg.org/multipage/server-sent-events.html) ·
MDN — *Using server-sent events* (https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) ·
TanStack Query — *Important Defaults* (staleTime/refetch) (https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults) | סטטוס: verified
**אכיפה:** סכמת-אירוע SSE עם terminal state מפורש; יישור TTL. **כיום:** fallback של 10ש' מניח completed.
**הפרה ידועה:** [documents.ts:226-232](../../web-ui/src/lib/api/documents.ts) — timeout→`{status:"completed"}`; TTL 5ש' front מול 300ש' redis ([gap-audit GAP-33](gap-audit.md)).
### INV-UI6: חוזה-טופס מוצהר לכל סוג-מסמך + שיקוף מקור-המילוי
**כלל:** לכל סוג-מסמך (מסמך-תיק / פסיקה חיצונית / החלטה פנימית) יש **חוזה-טופס מוצהר** — אילו שדות,
חובה/רשות/אוטו/pending/editable — **נגזר מ-[X8](X8-field-provenance.md)**; וה-UI **משקף את מקור-המילוי**
(מסמן מה חולץ אוטומטית/ע"י-Opus מול מה שהיו"ר הזין), כדי שהיו"ר ידע מה לאמת. מופע של
[G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai) (שקיפות-מקור). **invariant פרויקטלי-תפעולי.**
**מקור-סמכות:** [X8-field-provenance.md](X8-field-provenance.md) (טבלת-ה-provenance); feedback היו"ר.
**אכיפה:** רכיב-טופס נגזר-X8 + אינדיקציית "מולא-ע"י-Opus"/"ממתין"/`searchable`. **כיום אין** — שדות-Opus
מוצגים כשדות-עריכה רגילים ללא סימון.
**הפרה ידועה:** [precedents/[id]/page.tsx](../../web-ui/src/app/precedents/%5Bid%5D/page.tsx) — `summary`/`headnote`/`key_quote` ללא חיווי-מקור; אין חיווי `searchable` ([gap-audit GAP-36](gap-audit.md)).
---
## 3. כללי-עיצוב (Design Rules) — נגזרים מה-invariants
- **SSoT ל-enums/תוויות/tones:** כל enum (CaseStatus, PracticeArea, AppealSubtype, DocType, outcome) +
תוויותיו + צבעיו מוגדרים **פעם אחת** ונצרכים מיבוא — לא משוכפלים בין דפים/רכיבים (מופע UI1/G2).
- **helpers משותפים:** פירמוט-תאריך, builder ל-FormData (העלאות), רכיב-שגיאה, query-config (intervals) —
משותפים, לא מועתקים.
- **חוזי-טופס:** ראה INV-UI6 ([X8](X8-field-provenance.md)).
הממצאים הקונקרטיים (כפילויות, הגדרות-שגויות, redundancy) ב-[ui-audit.md](ui-audit.md); התיקון — **FU-10**.
---
## 4. הפניות-אחיות
- [ui-audit.md](ui-audit.md) — audit דף-אחר-דף (13 דפים) בתבנית-ה-gap.
- [X8-field-provenance.md](X8-field-provenance.md) — מקור-מילוי-שדות (בסיס ל-INV-UI6).
- [X7-paperclip-client-params.md](X7-paperclip-client-params.md) — חוזה-ה-API שהפלאגין צורך.
- [X9-mcp-tool-contract.md](X9-mcp-tool-contract.md) — חוזה-envelope מקביל בכלי-ה-MCP.
- [00-constitution.md](00-constitution.md) — [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים), [G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש), [G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai), כלל "אין בליעה שקטה" ([§6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)).
- [web-ui/next.config.ts](../../web-ui/next.config.ts), [client.ts](../../web-ui/src/lib/api/client.ts), [types.ts](../../web-ui/src/lib/api/types.ts), [sse.ts](../../web-ui/src/lib/sse.ts).