fix(backend): stop the async scanner counting awaited calls as blocking
Summary
scan_async_blockers classified every database.* import called inside an async def as a synchronous DB call, without checking whether it was awaited.
await get_async_redis_client() is the correct way to reach the shared async client. The scanner reported it as a blocking helper:
async_helpers_with_blocking: backend/routers/listen/registry.py:49
| proactive_message_dispatcher | get_async_redis_client:58
Why it needed fixing rather than working around
backend-async-blockers is a blocking pre-push gate, and this scanner has no allowlist, no inline waiver, and no way to record that a finding is wrong. So a correct change becomes unpushable, and the only routes past it are to restructure working code around the linter or to bypass the gate.
Found while routing the proactive dispatcher onto the publisher's shared Redis client — the dispatcher was subscribing to a different server than the publisher writes to, so no message ever arrived.
The change
Calls that are the direct operand of await are skipped. Keyed on AST node identity, not line number — one line can hold an awaited call and a synchronous one, and only the awaited half is safe.
The rule does not widen: an unawaited database.* call inside an async def is still reported, pinned by its own test.
Verification
python3 -m pytest backend/tests/unit/test_scan_async_blockers.py
-> 27 passed (25 pre-existing + 2 new)
Guard proven by removing it: with the awaited-call skip deleted, exactly one test fails — the awaited-accessor case. A test that has never failed is not evidence.
Product invariants affected
none
Failure class (fixes)
Failure-Class: none