From ca84df2635a3744c8268afeacc943805a411e61b Mon Sep 17 00:00:00 2001 From: Backend Developer 1 Date: Sat, 1 Aug 2026 21:57:28 +0000 Subject: [PATCH] [e06dc4d9] fix: regenerate stale lifecycle artifacts + replace tautological assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CI 'Python quality gate' (make quality) failed on PR #780's head because foundation-check detected lifecycle-artifact drift: the committed panel/lib/lifecycle.json and docs/rag/lifecycle/status-transitions.md still carried an `awaiting_pm_review -> claimed` claim transition that the current lifecycle spec no longer emits. make gate (format/lint/mypy/xenon) does not run foundation-check, so the reviewer could not reproduce locally. Fix: ran `make lifecycle` to regenerate the artifacts from the spec and commit the diff — no hand-written spec change, just stale generated output brought current. Second finding: tests/integration/test_notification_reescalation_dedup_exemption.py:164 had a tautological `assert target.id is not None` (target.id was uuid4() at construction, so it could never fail). Replaced with a real DB query that fetches all BLOCKER_ESCALATION rows for the sender/task, filters to those addressed to target.id, and asserts exactly 2 re-escalation rows exist — proving both _re_escalate_recipient calls delivered to the right recipient. Verified: make gate green (format/lint/mypy/xenon); quality-fast suite shows 7779 passed (same count as pre-change), the only failure is the pre-existing local-only cloud_auth test (needs localhost:5432 which CI provides); no regression in the notification dedup tests. --- docs/rag/lifecycle/status-transitions.md | 1 - panel/lib/lifecycle.json | 11 -------- ...tification_reescalation_dedup_exemption.py | 25 ++++++++++++++++++- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/docs/rag/lifecycle/status-transitions.md b/docs/rag/lifecycle/status-transitions.md index 083614ab..d7f39fdf 100644 --- a/docs/rag/lifecycle/status-transitions.md +++ b/docs/rag/lifecycle/status-transitions.md @@ -11,7 +11,6 @@ | awaiting_documentation | claimed | claim | documenter | | awaiting_pm_review | awaiting_ceo_approval | escalate_to_ceo | head_marketing, main_pm, product_owner | | awaiting_pm_review | cancelled | cancel | cell_pm, ceo, main_pm | -| awaiting_pm_review | claimed | claim | cell_pm, main_pm | | awaiting_pm_review | completed | complete | cell_pm, main_pm | | awaiting_pm_review | needs_revision | request_changes | cell_pm, main_pm | | awaiting_pr_review | awaiting_pm_review | pr_pass | pr_reviewer | diff --git a/panel/lib/lifecycle.json b/panel/lib/lifecycle.json index 8d0aa4c3..c673a92e 100644 --- a/panel/lib/lifecycle.json +++ b/panel/lib/lifecycle.json @@ -2,7 +2,6 @@ "claim_rules": { "auditor": [], "cell_pm": [ - "awaiting_pm_review", "needs_revision", "pending" ], @@ -17,7 +16,6 @@ ], "head_marketing": [], "main_pm": [ - "awaiting_pm_review", "needs_revision", "pending" ], @@ -531,15 +529,6 @@ "source": "awaiting_pm_review", "target": "cancelled" }, - { - "action": "claim", - "roles": [ - "cell_pm", - "main_pm" - ], - "source": "awaiting_pm_review", - "target": "claimed" - }, { "action": "complete", "roles": [ diff --git a/tests/integration/test_notification_reescalation_dedup_exemption.py b/tests/integration/test_notification_reescalation_dedup_exemption.py index 9f413245..b380723b 100644 --- a/tests/integration/test_notification_reescalation_dedup_exemption.py +++ b/tests/integration/test_notification_reescalation_dedup_exemption.py @@ -161,4 +161,27 @@ async def test_two_sequential_due_reescalations_both_deliver( ) expected_total_rows = 3 # the seeded original + both re-escalation attempts assert count == expected_total_rows - assert cast("UUID", target.id) is not None # target row exists + resolvable + # Both re-escalation rows must be addressed to target.id (the escalation + # target), proving the ladder re-fired to the right recipient — not a + # tautology on target.id which was uuid4() at construction. + all_rows = ( + ( + await db_session.execute( + select(NotificationTable).where( + NotificationTable.from_agent == cast("UUID", sender.id), + NotificationTable.type == NotificationType.BLOCKER_ESCALATION, + NotificationTable.related_task_id == task_id, + ) + ) + ) + .scalars() + .all() + ) + re_escalation_rows = [ + r for r in all_rows if cast("UUID", target.id) in (r.to_agents or []) + ] + expected_re_escalation_rows = 2 # one per _re_escalate_recipient call + assert len(re_escalation_rows) == expected_re_escalation_rows, ( + f"expected {expected_re_escalation_rows} re-escalation rows addressed " + f"to target ({target.slug}), got {len(re_escalation_rows)}" + )