diff --git a/docs/docker.md b/docs/docker.md index da5bb702..faa799fa 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -46,6 +46,13 @@ That's it for a real personal Docker install. Your existing `~/.hermes` directory is mounted, your `~/workspace` is browsable, and the WebUI auto-detects your UID/GID from the mounted volume. +The single-container setup runs the WebUI only. It can create cron jobs and run +them manually from the Tasks panel. In Docker, scheduled jobs require the Hermes gateway daemon +to tick while you are away. If System Settings shows `Gateway not configured`, +use `docker-compose.two-container.yml`, +`docker-compose.three-container.yml`, or run `hermes gateway` separately before +relying on offline scheduled runs. + For troubleshooting, reinstall, or onboarding reproduction trials, do not mount your real `~/.hermes` unless you intentionally want to test real state. Use an isolated Hermes home and follow diff --git a/static/index.html b/static/index.html index 29441d25..fe54a92c 100644 --- a/static/index.html +++ b/static/index.html @@ -205,6 +205,7 @@ +
${esc(body)}
+ `; +} + +async function loadCronGatewayNotice() { + const box = $('cronGatewayNotice'); + if (!box) return; + try { + const status = await api('/api/gateway/status'); + const html = _cronGatewayNoticeHtml(status); + if (html) { + box.innerHTML = html; + box.style.display = ''; + } else { + box.innerHTML = ''; + box.style.display = 'none'; + } + } catch (_) { + box.innerHTML = ''; + box.style.display = 'none'; + } +} + async function loadCrons(animate) { const box = $('cronList'); const refreshBtn = $('cronRefreshBtn'); + loadCronGatewayNotice(); if (animate && refreshBtn) { refreshBtn.style.opacity = '0.5'; refreshBtn.disabled = true; diff --git a/static/style.css b/static/style.css index 0f1354b9..80f1e96e 100644 --- a/static/style.css +++ b/static/style.css @@ -1109,6 +1109,7 @@ .panel-view.active{display:flex;} /* Cron panel */ .cron-list{flex:1;overflow-y:auto;padding:8px;} + .cron-gateway-notice{margin:8px 8px 0;} .cron-item{width:100%;min-width:0;box-sizing:border-box;border-radius:10px;border:1px solid var(--border);margin-bottom:6px;overflow:hidden;transition:border-color .15s,background .15s;background:rgba(255,255,255,.02);cursor:pointer;} .cron-item:hover{border-color:var(--border2);} .cron-header{display:flex;align-items:center;gap:8px;padding:10px 12px;cursor:pointer;} diff --git a/tests/test_issue2785_gateway_cron_guidance.py b/tests/test_issue2785_gateway_cron_guidance.py new file mode 100644 index 00000000..0fe30477 --- /dev/null +++ b/tests/test_issue2785_gateway_cron_guidance.py @@ -0,0 +1,37 @@ +"""Coverage for cron/gateway guidance in the Tasks panel and Docker docs.""" + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +INDEX_HTML = ROOT / "static" / "index.html" +PANELS_JS = ROOT / "static" / "panels.js" +DOCKER_DOC = ROOT / "docs" / "docker.md" + + +def test_tasks_panel_has_gateway_notice_container(): + html = INDEX_HTML.read_text(encoding="utf-8") + + assert 'id="cronGatewayNotice"' in html + assert "detail-alert" in html + + +def test_cron_panel_loads_gateway_status_for_scheduling_guidance(): + panels = PANELS_JS.read_text(encoding="utf-8") + + assert "function _cronGatewayNoticeHtml" in panels + assert "function loadCronGatewayNotice" in panels + assert "api('/api/gateway/status')" in panels + assert "Gateway not configured" in panels + assert "Gateway not running" in panels + assert "scheduled jobs require the Hermes gateway daemon" in panels + assert "loadCronGatewayNotice()" in panels + + +def test_docker_docs_explain_single_container_cron_gateway_boundary(): + docs = DOCKER_DOC.read_text(encoding="utf-8") + + assert "single-container setup runs the WebUI only" in docs + assert "scheduled jobs require the Hermes gateway daemon" in docs + assert "Gateway not configured" in docs + assert "docker-compose.two-container.yml" in docs