fix(notifications): one duplicate-tolerant role lookup for every singleton-role site

The duplicate-CEO-row fix landed on one call site but three identical
bare 'select(...).where(role == ...)' + scalar_one_or_none() lookups
remained in the orchestrator (auditor + two CEO), each still raising
MultipleResultsFound under the same condition. All five sites now route
through a shared get_agent_by_role helper (earliest-created row wins).
The five event-bus notification handlers also thread task_title so a
revived producer renders titles instead of #id8 (protocol signatures
updated to match the service), the pull_request CI trigger mirrors the
push trigger's paths so a fork PR touching only those paths still runs
CI, and a stale a2a comment about auditor/pr_reviewer lacking read_a2a
is corrected.
This commit is contained in:
Renn F
2026-07-22 03:30:50 +02:00
parent 161b36b563
commit 5143aab698
10 changed files with 196 additions and 43 deletions
+35 -3
View File
@@ -86,10 +86,18 @@ async def test_handle_task_blocked_calls_send_blocker() -> None:
notif.send_blocker_notification = AsyncMock()
set_event_context(notification_service=notif)
event = _make_event(
EventType.TASK_BLOCKED, task_id=str(uuid4()), team="backend", reason="x"
EventType.TASK_BLOCKED,
task_id=str(uuid4()),
team="backend",
reason="x",
task_title="Ship the widget",
)
await handle_task_status_change(event)
notif.send_blocker_notification.assert_called_once()
assert (
notif.send_blocker_notification.call_args.kwargs["task_title"]
== "Ship the widget"
)
@pytest.mark.asyncio
@@ -108,10 +116,17 @@ async def test_handle_task_awaiting_qa() -> None:
notif.send_qa_ready_notification = AsyncMock()
set_event_context(notification_service=notif)
event = _make_event(
EventType.TASK_AWAITING_QA, task_id=str(uuid4()), team="backend"
EventType.TASK_AWAITING_QA,
task_id=str(uuid4()),
team="backend",
task_title="Ship the widget",
)
await handle_task_status_change(event)
notif.send_qa_ready_notification.assert_called_once()
assert (
notif.send_qa_ready_notification.call_args.kwargs["task_title"]
== "Ship the widget"
)
@pytest.mark.asyncio
@@ -124,9 +139,14 @@ async def test_handle_task_qa_failed() -> None:
task_id=str(uuid4()),
assigned_to="be-dev-1",
qa_notes="please fix",
task_title="Ship the widget",
)
await handle_task_status_change(event)
notif.send_qa_failed_notification.assert_called_once()
assert (
notif.send_qa_failed_notification.call_args.kwargs["task_title"]
== "Ship the widget"
)
@pytest.mark.asyncio
@@ -145,10 +165,17 @@ async def test_handle_task_awaiting_docs() -> None:
notif.send_docs_ready_notification = AsyncMock()
set_event_context(notification_service=notif)
event = _make_event(
EventType.TASK_AWAITING_DOCS, task_id=str(uuid4()), team="backend"
EventType.TASK_AWAITING_DOCS,
task_id=str(uuid4()),
team="backend",
task_title="Ship the widget",
)
await handle_task_status_change(event)
notif.send_docs_ready_notification.assert_called_once()
assert (
notif.send_docs_ready_notification.call_args.kwargs["task_title"]
== "Ship the widget"
)
@pytest.mark.asyncio
@@ -173,9 +200,14 @@ async def test_handle_handoff_created_calls_notification() -> None:
task_id=str(uuid4()),
handoff_id=str(uuid4()),
team="backend",
task_title="Ship the widget",
)
await handle_handoff_created(event)
notif.send_handoff_notification.assert_called_once()
assert (
notif.send_handoff_notification.call_args.kwargs["task_title"]
== "Ship the widget"
)
@pytest.mark.asyncio