Merge pull request 'feat(learning): שער-אישור ל-decision_lessons — רק לקח מאושר זורם לכותב (INV-LRN1, #126)' (#202) from worktree-lesson-approval-gate into main
This commit was merged in pull request #202.
This commit is contained in:
10
web/app.py
10
web/app.py
@@ -1421,10 +1421,12 @@ class LessonPatch(BaseModel):
|
||||
lesson_text: str | None = None
|
||||
category: str | None = None
|
||||
applied_to_skill: bool | None = None
|
||||
review_status: str | None = None # proposed | approved | rejected (INV-LRN1 gate)
|
||||
|
||||
|
||||
_LESSON_CATEGORIES = {"style", "structure", "lexicon", "tabular", "general"}
|
||||
_LESSON_SOURCES = {"manual", "curator", "chair", "style_analyzer"}
|
||||
_LESSON_REVIEW_STATUSES = {"proposed", "approved", "rejected"}
|
||||
|
||||
|
||||
def _lesson_to_json(row: dict) -> dict:
|
||||
@@ -1435,6 +1437,8 @@ def _lesson_to_json(row: dict) -> dict:
|
||||
"category": row["category"],
|
||||
"source": row["source"],
|
||||
"applied_to_skill": bool(row["applied_to_skill"]),
|
||||
# review gate (INV-LRN1/G10): only 'approved' flows to the writer.
|
||||
"review_status": row.get("review_status", "proposed"),
|
||||
"created_by": row.get("created_by", ""),
|
||||
"created_at": row["created_at"].isoformat() if row.get("created_at") else "",
|
||||
"updated_at": row["updated_at"].isoformat() if row.get("updated_at") else "",
|
||||
@@ -1464,8 +1468,11 @@ async def add_corpus_lesson(corpus_id: str, body: LessonCreate):
|
||||
raise HTTPException(400, f"invalid category; allowed: {sorted(_LESSON_CATEGORIES)}")
|
||||
if body.source not in _LESSON_SOURCES:
|
||||
raise HTTPException(400, f"invalid source; allowed: {sorted(_LESSON_SOURCES)}")
|
||||
# A lesson the chair authors directly here is approved by construction — it skips
|
||||
# the proposal gate (which exists to vet machine-generated panel lessons).
|
||||
row = await db.add_decision_lesson(
|
||||
cid, lesson_text=text, category=body.category, source=body.source,
|
||||
review_status="approved",
|
||||
)
|
||||
if not row:
|
||||
raise HTTPException(500, "failed to insert lesson")
|
||||
@@ -1480,11 +1487,14 @@ async def patch_corpus_lesson(lesson_id: str, body: LessonPatch):
|
||||
raise HTTPException(400, "invalid lesson_id")
|
||||
if body.category is not None and body.category not in _LESSON_CATEGORIES:
|
||||
raise HTTPException(400, f"invalid category; allowed: {sorted(_LESSON_CATEGORIES)}")
|
||||
if body.review_status is not None and body.review_status not in _LESSON_REVIEW_STATUSES:
|
||||
raise HTTPException(400, f"invalid review_status; allowed: {sorted(_LESSON_REVIEW_STATUSES)}")
|
||||
result = await db.update_decision_lesson(
|
||||
lid,
|
||||
lesson_text=body.lesson_text,
|
||||
category=body.category,
|
||||
applied_to_skill=body.applied_to_skill,
|
||||
review_status=body.review_status,
|
||||
)
|
||||
if not result.get("updated"):
|
||||
if result.get("reason") == "not found":
|
||||
|
||||
Reference in New Issue
Block a user