mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(dispatch): stop burning breaker strikes on dependency-held blocked tasks; collision notify rides the delegate transaction (#726)
* fix(dispatch): stop burning breaker strikes on dependency-held blocked tasks; collision notify rides the delegate transaction Two follow-ups from the 2026-07-29 blocked-task wave: - _dispatch_blocker_work now skips a blocked task whose dependencies are non-terminal BEFORE the respawn gate. spawn_agent's readiness gate was refusing these anyway, but each refused attempt burned a respawn- breaker strike and, once tripped at 4, a duplicate CEO escalation every tick (seen live on the eslint-audit task held behind a paused backend dependency). The dispatcher re-checks each tick and proceeds the moment the dependency goes terminal — same resume path as before, minus the noise. - send_collision_sequencing_notification gains the db_session threading its sibling senders already have, and TaskService passes its own session. delegate wires collision edges inside a transaction whose held-back child row is not yet committed; the notification INSERT ran on a separate auto-commit connection and died on the related_task_id FK (ForeignKeyViolationError seen live in cell_pm/delegate), silently losing the coordination alert. Riding the caller's transaction makes the row visible and commits both atomically. * test(notification): cast the fake session for the gate's tests-scope mypy --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -544,6 +544,25 @@ async def test_send_collision_sequencing_notification(
|
||||
assert any("sequenced behind" in r.subject for r in rows)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_collision_sequencing_notification_rides_caller_session(
|
||||
svc: NotificationService,
|
||||
) -> None:
|
||||
"""2026-07-29: delegate wires collision edges inside a transaction whose
|
||||
held-back task row is not yet committed — the notification INSERT must
|
||||
ride that same session, or its related_task_id FK fails on any other
|
||||
connection. No _patch_db_context here: opening a separate connection
|
||||
would hit the real engine and fail the test."""
|
||||
db = _FakeDb(agent_uuid=uuid4())
|
||||
await svc.send_collision_sequencing_notification(
|
||||
held_back_task_id="t2",
|
||||
blocking_task_id="t1",
|
||||
held_back_assignee="be-dev-1",
|
||||
db_session=cast("Any", db),
|
||||
)
|
||||
assert [r for r in db.added if r.related_task_id == "t2"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_unblock_notification(svc: NotificationService) -> None:
|
||||
aid = uuid4()
|
||||
|
||||
@@ -618,6 +618,23 @@ async def test_wire_sibling_collision_dag_notifies_only_for_new_edges() -> None:
|
||||
assert add_dep_mock.await_count == wiring_passes
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_collision_sequencing_notify_rides_task_service_session() -> None:
|
||||
"""2026-07-29: the held-back task row is uncommitted in this transaction —
|
||||
the notification must ride the SAME session or its related_task_id FK
|
||||
fails (ForeignKeyViolationError seen live in cell_pm/delegate)."""
|
||||
session = MagicMock(flush=AsyncMock())
|
||||
svc = TaskService(session)
|
||||
mock_ns = MagicMock()
|
||||
mock_ns.send_collision_sequencing_notification = AsyncMock()
|
||||
with patch(
|
||||
"roboco.services.notification.NotificationService", return_value=mock_ns
|
||||
):
|
||||
await svc._notify_collision_sequencing(uuid4(), uuid4(), None)
|
||||
kwargs = mock_ns.send_collision_sequencing_notification.await_args.kwargs
|
||||
assert kwargs["db_session"] is session
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mark_agent_idle_sets_status_idle() -> None:
|
||||
agent = MagicMock(id=uuid4(), status=AgentStatus.ACTIVE, current_task_id=uuid4())
|
||||
|
||||
Reference in New Issue
Block a user