feat(feedback): סימון "יושם" מפעיל CEO לקיפול הלקח לקובץ הנכון

סוגר את לולאת פידבק-יו"ר→ידע-סוכנים. עד כה resolve רק עדכן את ה-DB; עכשיו
לחיצה ב-/feedback מעירה את ה-CEO שמקפל את הלקח לקובץ לפי הקטגוריה.

- paperclip_client.py: wake_ceo_for_feedback_fold() — יוצר issue ב-Paperclip
  עם הלקח + rubric ניתוב (style→SKILL.md, wrong_structure→block-schema,
  אחר→lessons.md), מעיר CEO. משכפל את דפוס wake_for_precedent_extraction
- db.py: get_chair_feedback(id) — שליפת הערה בודדת עם case_number/appeal_type
- app.py: resolve endpoint מקבל fold (ברירת מחדל true); BackgroundTask
  fire-and-forget; guard — רק עם lesson_extracted. מחזיר fold_queued
- legal-ceo.md: dispatch ל-feedback_fold_ + סעיף "קיפול הערת יו"ר" עם rubric
- frontend: useResolveFeedback מקבל fold; /feedback שולח fold=true עם toast;
  drafts-panel שולח fold=false (bookkeeping per-case, בלי קיפול כפול)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 13:08:41 +00:00
parent dd0e754dad
commit 4174217179
7 changed files with 212 additions and 9 deletions

View File

@@ -64,6 +64,7 @@ from web.paperclip_client import (
restore_project as pc_restore_project,
wake_analyst_for_appraiser_facts as pc_wake_analyst_for_appraiser_facts,
wake_ceo_agent as pc_wake_ceo,
wake_ceo_for_feedback_fold as pc_wake_ceo_for_feedback_fold,
wake_curator_for_final as pc_wake_curator_for_final,
wake_for_precedent_extraction as pc_wake_for_precedent_extraction,
)
@@ -4972,13 +4973,53 @@ async def api_create_feedback_json(body: dict):
@app.patch("/api/feedback/{feedback_id}/resolve")
async def api_resolve_feedback(feedback_id: str, body: dict):
"""Mark feedback as resolved."""
async def api_resolve_feedback(
feedback_id: str,
body: dict,
background_tasks: BackgroundTasks,
):
"""Mark feedback as resolved. When ``fold`` is true (default) and the entry
has an extracted lesson, also wake the CEO to fold that lesson into the
right knowledge file (the feedback→agent-knowledge loop).
The fold is fire-and-forget (BackgroundTask) and best-effort — resolving
never fails because Paperclip is down. Pass ``fold=false`` for pure
bookkeeping resolves (e.g. from the per-case drafts panel) to avoid
spawning a CEO run per click."""
fid = UUID(feedback_id)
fold = body.get("fold", True)
fb = await db.get_chair_feedback(fid)
if not fb:
raise HTTPException(404, "הערה לא נמצאה")
await db.resolve_chair_feedback(
feedback_id=UUID(feedback_id),
feedback_id=fid,
applied_to=body.get("applied_to", []),
)
return {"status": "resolved"}
# Guard: only fold a real, lesson-bearing entry, and only when asked.
lesson = (fb.get("lesson_extracted") or "").strip()
fold_queued = False
if fold and lesson:
async def _fold():
try:
await pc_wake_ceo_for_feedback_fold(
feedback_id=str(fid),
feedback_text=fb.get("feedback_text") or "",
lesson_extracted=lesson,
category=fb.get("category") or "other",
block_id=fb.get("block_id") or "",
case_number=fb.get("case_number") or "",
practice_area=fb.get("case_appeal_type") or "",
)
except Exception:
logger.exception("feedback-fold wakeup failed (non-fatal) for %s", fid)
background_tasks.add_task(_fold)
fold_queued = True
return {"status": "resolved", "fold_queued": fold_queued}
@app.get("/api/chair-feedback/weekly-summary")