Make tasks server status page use fresh heartbeat data and auto-refresh
Problem
Fix stale /status behavior so operators see current task-manager state without manual restarts or owner confusion. Ensure status page uses the active owner snapshot, triggers/reads a fresh heartbeat on page load, and supports lightweight auto-refresh or polling so counts and escalations stay current while viewing.
Context
The /status endpoint in tasks/tools/tasks_server.py reads a pre-written snapshot JSON file from disk (_snapshot_path(owner)) and renders it as HTML. The snapshot is only written when scripts/task-manager-heartbeat runs (typically on a cron/loop). This means:
- Stale data: If the heartbeat hasn't run recently,
/statusshows outdated counts, escalation signals, and task states. - No auto-refresh: The page is static HTML — operators must manually reload to see updates.
- Owner coupling: The server is initialized with a fixed
ownerparameter. If the heartbeat runs under a different owner slug, the status page reads the wrong snapshot file.
Key files:
- tasks/tools/tasks_server.py — FastAPI app with /status (HTML) and /status.json (JSON) endpoints.
- scripts/task_manager_lib.py — snapshot_payload(), collect_tasks(), classify_tasks() produce fresh data.
- scripts/task-manager-heartbeat — CLI that runs one heartbeat cycle and writes snapshot to disk.
Possible Solutions
A. Run heartbeat inline on each /status request
Call collect_tasks() + classify_tasks() + snapshot_payload() directly from the endpoint handler. Fresh every time, but blocks the HTTP response on task collection (which may call gh pr view and be slow).
B. Recommended: Subprocess heartbeat on /status + meta-refresh auto-reload
On /status load, spawn task-manager-heartbeat as a subprocess so the snapshot file is refreshed before rendering. Add a <meta http-equiv="refresh"> tag so the page auto-reloads every 30 seconds.
Trade-offs: subprocess adds ~1-2s latency on first load but keeps the server process simple and avoids importing the full heartbeat stack into the server. Meta-refresh is zero-JS and works everywhere.
C. Background thread heartbeat loop
Run a background thread in the FastAPI process that periodically calls snapshot_payload(). More complex, harder to test, couples server lifetime to heartbeat lifecycle.
Plan
Use approach B with two changes:
-
Fresh heartbeat on
/statusload: Add_refresh_heartbeat(owner)that runsscripts/task-manager-heartbeat --owner {owner} --format jsonas a subprocess with a short timeout. Call it from bothstatus_page()andstatus_json()before reading the snapshot file. -
Auto-refresh via meta-refresh: Add
<meta http-equiv="refresh" content="30">to the status page HTML shell so the browser reloads every 30 seconds.
Files to modify:
- tasks/tools/tasks_server.py — add _refresh_heartbeat() helper; call from endpoints; add meta-refresh to _render_docs_shell when rendering status.
- tests/core/test_tasks_server.py — test heartbeat subprocess invocation and meta-refresh presence.
Implementation Progress
Changes in tasks/tools/tasks_server.py
- Added
_refresh_heartbeat(owner)— runsscripts/task-manager-heartbeat --owner {owner} --format jsonas a subprocess with a 15s timeout. Usescontextlib.suppressso failures silently fall back to the existing (possibly stale) snapshot. - Both
/statusand/status.jsonnow call_refresh_heartbeat(owner)before reading the snapshot file. - Added
auto_refresh_secondsparameter to_render_docs_shell(). The/statusendpoint passesauto_refresh_seconds=30, which injects<meta http-equiv='refresh' content='30'>into the HTML head. Other pages are unaffected (default is 0).
Tests in tests/core/test_tasks_server.py
test_status_page_triggers_heartbeat_subprocess— verifies/statusspawns the heartbeat script with--owner.test_status_json_triggers_heartbeat_subprocess— verifies/status.jsonalso triggers refresh.test_status_page_contains_meta_refresh— asserts meta-refresh tag is present in status HTML.test_status_page_heartbeat_failure_still_renders— confirms graceful degradation when heartbeat times out.
QA Exploration
N/A — server-side change, no browser-specific UI beyond meta-refresh tag which is tested via response content assertion.
- [x] QA exploration completed (or N/A for non-UI tasks)
Review Feedback
- [ ] Review cleared