Prerequisites
Install Method
Docker (docker compose up)
Operating System
Linux
Steps to Reproduce
- Docker Compose deployment, dev branch (2026-07-14), Ubuntu Server 26.04.
- Create a scheduled Prompt task (daily, any model — reproduced with qwen2.5:7b/14b and qwen3-coder:480b-cloud).
- Leave any Odysseus client open — a browser tab, or the PWA installed on a phone (which keeps heartbeating in the background).
- Let the task trigger (scheduled or via Run again).
- Observe the run in Tasks → Activity and in the task_runs table.
Expected Behaviour
The scheduled run executes to completion. If a foreground gate is desired, BACKGROUND_TASK_FOREGROUND_GATE=false should disable it entirely, and in any case a background run should be delayed/paused rather than aborted by ordinary UI polling.
Actual Behaviour
Every scheduled run is aborted within seconds of starting. task_runs rows show:
status=aborted, error="Stopped by user", result="Starting…" (or "Queued — waiting for Odysseus to be idle…")
started 2026-07-14 04:30:00, finished 2026-07-14 04:30:01 (1.5s)
The user did not stop anything — the run is killed by the app's own heartbeat/polling.
Cause (dev, 2026-07-14):
app.py:628 — the /api/activity/heartbeat route calls task_scheduler.stop_background_tasks_for_foreground(reason="browser heartbeat") unconditionally. There is no check of BACKGROUND_TASK_FOREGROUND_GATE (or any other flag) on this path.
app.py:199 — _InteractiveActivityMiddleware also calls it for tracked foreground requests, gated only by should_track_interactive_request(path, method).
src/interactive_gate.py:24 — BACKGROUND_TASK_FOREGROUND_GATE exists and is honoured by the waiting logic (wait_for_interactive_quiet), but nothing on the cancellation path consults it.
src/task_scheduler.py:947-951 — the run dies via asyncio.CancelledError; because foreground_cancel["hit"] is not set on this path, it is recorded with the misleading message "Stopped by user" rather than "Paused because Odysseus became active".
Net effect: with any client open (a phone PWA is enough, even in the background), scheduled tasks can never complete, and BACKGROUND_TASK_FOREGROUND_GATE=false does not help. This is easy to miss because the failure is silent and the error message blames the user.
Setting BACKGROUND_TASK_MAX_WAIT_SECONDS=120 and BACKGROUND_TASK_FOREGROUND_GATE=false (both verified live in the container via printenv) does not prevent the aborts.
Workaround: patching out the asyncio.create_task(_stop_background()) call in the /api/activity/heartbeat route makes scheduled tasks run to completion, even with the UI open. After that patch, an identical task completed successfully in ~30s and delivered its notification.
Note: BACKGROUND_TASK_* variables also have to be added to the environment: block of the odysseus service in docker-compose.yml before they reach the container at all — they are read by interactive_gate.py but not declared in compose. That is arguably a second (smaller) issue: setting them in .env alone silently does nothing.
Logs / Screenshots
2026-07-13 20:xx - src.task_scheduler - INFO - Stopped 2 background scheduler task(s): browser heartbeat
2026-07-13 20:xx - src.task_scheduler - INFO - Stopped 4 background scheduler task(s): foreground request GET /api/email/unread-state
2026-07-13 20:xx - src.task_scheduler - INFO - Task 'Briefing TEST' Stopped by user
task_runs rows (all aborted seconds after start, error="Stopped by user"):
('3db33ace...', 'aborted', '2026-07-14 17:17:21', '2026-07-14 17:17:34', 'Starting…', 'Stopped by user')
('e4b7ffb5...', 'aborted', '2026-07-14 04:30:00', '2026-07-14 04:30:01', 'Starting…', 'Stopped by user')
Model / Backend (if relevant)
Ollama (Docker) + qwen2.5:7b / qwen2.5:14b / qwen3-coder:480b-cloud — model-independent, scheduler-side
Are you willing to submit a fix?
Partially — I can help but need guidance
Additional Information
Happy to test a fix branch — Docker + Ollama + HA MCP setup available for repro.
Prerequisites
devbranch (the default branch you get on clone, where fixes land first) and the bug still reproduces there. Pleasegit pullthe latestdevbefore filing.Install Method
Docker (docker compose up)
Operating System
Linux
Steps to Reproduce
Expected Behaviour
The scheduled run executes to completion. If a foreground gate is desired,
BACKGROUND_TASK_FOREGROUND_GATE=falseshould disable it entirely, and in any case a background run should be delayed/paused rather than aborted by ordinary UI polling.Actual Behaviour
Every scheduled run is aborted within seconds of starting.
task_runsrows show:status=aborted, error="Stopped by user", result="Starting…" (or "Queued — waiting for Odysseus to be idle…")
started 2026-07-14 04:30:00, finished 2026-07-14 04:30:01 (1.5s)
The user did not stop anything — the run is killed by the app's own heartbeat/polling.
Cause (dev, 2026-07-14):
app.py:628— the/api/activity/heartbeatroute callstask_scheduler.stop_background_tasks_for_foreground(reason="browser heartbeat")unconditionally. There is no check ofBACKGROUND_TASK_FOREGROUND_GATE(or any other flag) on this path.app.py:199—_InteractiveActivityMiddlewarealso calls it for tracked foreground requests, gated only byshould_track_interactive_request(path, method).src/interactive_gate.py:24—BACKGROUND_TASK_FOREGROUND_GATEexists and is honoured by the waiting logic (wait_for_interactive_quiet), but nothing on the cancellation path consults it.src/task_scheduler.py:947-951— the run dies viaasyncio.CancelledError; becauseforeground_cancel["hit"]is not set on this path, it is recorded with the misleading message "Stopped by user" rather than "Paused because Odysseus became active".Net effect: with any client open (a phone PWA is enough, even in the background), scheduled tasks can never complete, and
BACKGROUND_TASK_FOREGROUND_GATE=falsedoes not help. This is easy to miss because the failure is silent and the error message blames the user.Setting
BACKGROUND_TASK_MAX_WAIT_SECONDS=120andBACKGROUND_TASK_FOREGROUND_GATE=false(both verified live in the container viaprintenv) does not prevent the aborts.Workaround: patching out the
asyncio.create_task(_stop_background())call in the/api/activity/heartbeatroute makes scheduled tasks run to completion, even with the UI open. After that patch, an identical task completed successfully in ~30s and delivered its notification.Note:
BACKGROUND_TASK_*variables also have to be added to theenvironment:block of the odysseus service indocker-compose.ymlbefore they reach the container at all — they are read byinteractive_gate.pybut not declared in compose. That is arguably a second (smaller) issue: setting them in.envalone silently does nothing.Logs / Screenshots
Model / Backend (if relevant)
Ollama (Docker) + qwen2.5:7b / qwen2.5:14b / qwen3-coder:480b-cloud — model-independent, scheduler-side
Are you willing to submit a fix?
Partially — I can help but need guidance
Additional Information
Happy to test a fix branch — Docker + Ollama + HA MCP setup available for repro.