fix(agents): ניתוב הערת-יו"ר לא ל-issue סגור (cancelled) — תמיד ל-CEO החי
בעיה חוזרת: הקלדת הוראה בתיבת "כתוב הוראה לסוכנים..." נבלעה בשקט. השרת בחר את היעד ב-`active[-1]` תוך סינון `done` בלבד — לא `cancelled`. כשה-child האחרון בוטל (למשל 1027-04-26: "חישוב טיעונים" cancelled שנוצר אחרי ה-issue הראשי), ההערה נותבה אליו; Paperclip מדלג על wakeup ל-issue מבוטל → ההוראה לא הגיעה לאיש. תיקון (G1 — נרמול במקור, לא תיקון-בקריאה): - `pick_default_comment_target` חדש ב-paperclip_client — מעדיף את ה-issue הראשי החי של ה-CEO (top-level+open), נופל ל-open כלשהו, ואז top-level, ואף פעם לא מעדיף child-סגור. `get_case_issues` מחזיר כעת `parent_id`. - app.py מנתב דרכו; מחזיר `issue_status` כדי שה-UI יוכל לסמן יעד-סגור. - נחשף דרך ה-Port (G12) כ-`pc_pick_default_comment_target`. - 6 בדיקות ל-picker (כולל תרחיש 1027-04-26 המדויק). Invariants: G1 (נרמול-במקור), G2 (אין מסלול-ניתוב מקביל), G12 (מגע-Paperclip דרך ה-Port). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
78
web/tests/test_comment_target.py
Normal file
78
web/tests/test_comment_target.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Tests for pick_default_comment_target — chair-comment routing.
|
||||
|
||||
Regression guard for the recurring bug where a chair instruction typed into the
|
||||
"כתוב הוראה לסוכנים..." box was routed to a *cancelled* child issue, so
|
||||
Paperclip skipped the wakeup and the instruction was silently swallowed.
|
||||
|
||||
The picker must never default to a closed (done/cancelled) issue and must prefer
|
||||
the live top-level CEO issue. Pure function — no DB, no FastAPI.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
os.environ.setdefault("PAPERCLIP_DB_URL", "postgres://x:x@127.0.0.1:54329/paperclip")
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1])) # web/
|
||||
|
||||
pc = pytest.importorskip("paperclip_client", reason="web deps unavailable")
|
||||
|
||||
|
||||
def _iss(id_, status, parent_id=None):
|
||||
return {"id": id_, "status": status, "parent_id": parent_id, "identifier": id_}
|
||||
|
||||
|
||||
def test_prefers_open_top_level_over_newer_cancelled_child():
|
||||
"""The exact 1027-04-26 scenario: newest non-done issue is a cancelled child;
|
||||
routing must still land on the live top-level CEO issue."""
|
||||
issues = [
|
||||
_iss("main-old", "done"), # oldest top-level, closed
|
||||
_iss("main-live", "in_review"), # live CEO main issue
|
||||
_iss("child-cancelled", "cancelled", "main-live"), # newest non-done → the trap
|
||||
_iss("child-done", "done", "main-live"),
|
||||
]
|
||||
assert pc.pick_default_comment_target(issues)["id"] == "main-live"
|
||||
|
||||
|
||||
def test_never_returns_cancelled_when_an_open_issue_exists():
|
||||
issues = [
|
||||
_iss("a", "done"),
|
||||
_iss("b", "in_progress", "a"),
|
||||
_iss("c", "cancelled", "a"),
|
||||
]
|
||||
assert pc.pick_default_comment_target(issues)["status"] != "cancelled"
|
||||
|
||||
|
||||
def test_falls_back_to_open_child_when_no_open_top_level():
|
||||
issues = [
|
||||
_iss("main", "done"),
|
||||
_iss("child-live", "in_progress", "main"),
|
||||
]
|
||||
assert pc.pick_default_comment_target(issues)["id"] == "child-live"
|
||||
|
||||
|
||||
def test_prefers_top_level_when_everything_closed():
|
||||
"""No open issue anywhere — pick the newest top-level (better than a child)."""
|
||||
issues = [
|
||||
_iss("main-1", "done"),
|
||||
_iss("main-2", "cancelled"),
|
||||
_iss("child", "done", "main-2"),
|
||||
]
|
||||
assert pc.pick_default_comment_target(issues)["id"] == "main-2"
|
||||
|
||||
|
||||
def test_single_issue():
|
||||
issues = [_iss("only", "cancelled")]
|
||||
assert pc.pick_default_comment_target(issues)["id"] == "only"
|
||||
|
||||
|
||||
def test_picks_newest_open_top_level():
|
||||
issues = [
|
||||
_iss("main-1", "in_review"),
|
||||
_iss("main-2", "in_review"),
|
||||
]
|
||||
assert pc.pick_default_comment_target(issues)["id"] == "main-2"
|
||||
Reference in New Issue
Block a user