feat(case-ui): כרטיס "הפקת מסמכים" עם 3 כפתורי-הפק דטרמיניסטיים (#214)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 11s

מוסיף כרטיס "הפקת מסמכים" בראש טאב "טיוטות והערות" (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>
This commit is contained in:
2026-06-30 19:54:54 +00:00
parent c954dded66
commit 3ea5d3b536
5 changed files with 440 additions and 17 deletions

View File

@@ -79,6 +79,7 @@ from web.agent_platform_port import (
pc_wake_analyst_for_argument_aggregation,
rename_case_project,
pc_wake_ceo,
pc_wake_ceo_for_action,
pc_wake_ceo_for_feedback_fold,
pc_wake_curator_for_final,
pc_wake_for_precedent_extraction,
@@ -3090,6 +3091,51 @@ async def api_party_claims_summary(case_number: str):
return {"markdown": path.read_text(encoding="utf-8")}
# ── Deterministic document generation triggers (TaskMaster #214) ─────
# The "הפקת מסמכים" buttons in the case "טיוטות והערות" tab. Generation for the
# executive summary + interim draft runs HOST-SIDE (claude_session → claude -p,
# not in the container), so these endpoints fire a *structured-action* CEO wakeup
# (the CEO reads payload.action and routes to שלב H2 / שלב H — legal-ceo.md).
# Wakeup goes through the Paperclip API helper (pc_wake_ceo_for_action → the
# platform Port → POST /api/agents/{id}/wakeup), NEVER a direct DB insert.
# Polling for completion uses the existing read endpoints (research/party-claims-
# summary for the summary; the exports list for the טיוטת-ביניים-*.docx file).
async def _wake_ceo_action(case_number: str, action: str) -> dict:
"""Resolve the case's company + fire a structured-action CEO wakeup (#214)."""
case = await db.get_case_by_number(case_number)
if not case:
raise HTTPException(404, f"תיק {case_number} לא נמצא")
prefix = case_number[:1]
company_id = (
PAPERCLIP_COMPANIES["licensing"] if prefix == "1"
else PAPERCLIP_COMPANIES["betterment"] if prefix in ("8", "9")
else ""
)
try:
return await pc_wake_ceo_for_action(case_number, action, company_id=company_id)
except Exception as e:
logger.warning("CEO action %s wakeup failed for %s: %s", action, case_number, e)
return {"status": "error", "error": str(e)}
@app.post("/api/cases/{case_number}/generate/party-claims-summary", status_code=202)
async def api_generate_party_claims_summary(case_number: str):
"""Fire-and-accept: wake the CEO to generate the party-claims executive summary
(שלב H2 → summarize_party_claims). Runs host-side; poll
GET /api/cases/{n}/research/party-claims-summary for completion."""
return await _wake_ceo_action(case_number, "party_claims_summary")
@app.post("/api/cases/{case_number}/generate/interim-draft", status_code=202)
async def api_generate_interim_draft(case_number: str):
"""Fire-and-accept: wake the CEO to generate the partial "party-claims" draft
(שלב H → write_interim_draft + export_interim_draft). Runs host-side; poll the
exports list for the new טיוטת-ביניים-{case}-vN.docx."""
return await _wake_ceo_action(case_number, "interim_draft")
@app.get("/api/cases/{case_number}/research/party-claims-summary/download")
async def api_party_claims_summary_download(case_number: str):
"""Download the raw party-claims-summary.md file."""