[e06dc4d9] fix: regenerate stale lifecycle artifacts + replace tautological assertion

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.
This commit is contained in:
Backend Developer 1
2026-08-01 21:57:28 +00:00
parent d61d179560
commit ca84df2635
3 changed files with 24 additions and 13 deletions
-1
View File
@@ -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 |
-11
View File
@@ -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": [
@@ -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)}"
)