fix: stop status polling from cancelling running scheduled tasks (#5789)

* fix: stop polling GET /api/tasks/runs/recent from cancelling running tasks

Two paths caused the scheduler to interrupt a running background task
when the frontend Activity view polled for status:

1. GET /api/tasks/runs/recent was not in _PASSIVE_EXACT_PATHS, so
   _InteractiveActivityMiddleware treated it as a foreground request
   and called stop_background_tasks_for_foreground, cancelling any
   in-flight scheduled task. Add it to _PASSIVE_EXACT_PATHS alongside
   the other read-only polling endpoints.

2. The /api/activity/heartbeat handler called
   stop_background_tasks_for_foreground unconditionally, ignoring
   BACKGROUND_TASK_FOREGROUND_GATE=false. Wrap the call in a
   _gate_enabled() guard so the env var fully disables heartbeat-
   triggered cancellations.

Fixes #5782

Signed-off-by: Christian Sidak <christian@sentineltech.eu>
Signed-off-by: Christian-Sidak <61099993+Christian-Sidak@users.noreply.github.com>

* fix(scheduler): respect foreground gate for heartbeat

---------

Signed-off-by: Christian Sidak <christian@sentineltech.eu>
Signed-off-by: Christian-Sidak <61099993+Christian-Sidak@users.noreply.github.com>
Co-authored-by: Alexandre Teixeira <alexandremagteixeira@gmail.com>
This commit is contained in:
Christian Sidak
2026-08-14 10:47:47 +01:00
committed by GitHub
co-authored by Alexandre Teixeira
parent a6bc86e331
commit b2789d04fb
3 changed files with 94 additions and 3 deletions
+14
View File
@@ -63,6 +63,7 @@ _PASSIVE_EXACT_PATHS = {
"/api/activity/heartbeat",
"/api/client-perf",
"/api/tasks/notifications",
"/api/tasks/runs/recent",
"/api/research/active",
"/api/email/urgency-state",
# UI idle poll sibling of urgency-state; must not pre-empt background tasks.
@@ -76,6 +77,19 @@ _PASSIVE_PREFIXES = (
)
async def maybe_stop_background_tasks_for_heartbeat(stop_background) -> bool:
"""Stop background work for browser activity only when the gate is enabled.
``stop_background`` is injected by the application boundary so this policy
remains independently testable without importing the full FastAPI app.
"""
if not _enabled():
return False
await stop_background(reason="browser heartbeat")
return True
def should_track_interactive_request(path: str, method: str = "GET") -> bool:
if not _enabled():
return False