Skip to content

fix(cron): validate bot-chat deliver/failure_deliver on dashboard update - #102091

Open
nftpoetrist wants to merge 1 commit into
NousResearch:mainfrom
nftpoetrist:fix/cron-dashboard-bot-chat-deliver-validation
Open

nftpoetrist wants to merge 1 commit into
NousResearch:mainfrom
nftpoetrist:fix/cron-dashboard-bot-chat-deliver-validation

Conversation

@nftpoetrist

Copy link
Copy Markdown
Contributor

Summary

The CLI and the cronjob tool both run every create/update through tools.cronjob_tools._validate_bot_chat_deliver, which rejects a bot-chat[:<profile>] deliver/failure_deliver target whose named profile doesn't exist on this machine — at write time, not only when a run (or a failure) later needs to route through it and finds nothing there.

The dashboard's PUT /api/cron/jobs/{job_id} never went through that check for either field. It calls cron.jobs.update_job (via _mutate_cron_for_profile) directly after only text-normalizing the values in _normalize_dashboard_cron_updates.

Is this a new regression or pre-existing? Pre-existing, and it predates failure_deliver/NS-788. _validate_bot_chat_deliver was introduced in a2da0ab797 (the original bot-chat delivery feature) for the CLI/tool create+update paths only — the dashboard's update lane never called it back then either, so deliver has been unvalidated on the dashboard since bot-chat delivery shipped. failure_deliver simply inherited the same gap when NS-788 added it.

Today's same-day follow-up commit ("validate failure_deliver at preflight and dashboard update lanes", #101373) did not close this — I checked its diff to hermes_cli/web_server.py directly: it only added failure_deliver to the dashboard's text normalizer (empty clears the override instead of coalescing to a target, matching deliver's normalization). It did not add the bot-chat profile-existence check. And _preflight_check_delivery explicitly skips bot-chat targets for both deliver and failure_deliver ("Unknown-profile failures surface per run in last_delivery_error ... and are validated at create time") — deferring entirely to the create/update-time validator. So _validate_bot_chat_deliver was the only safety net for bot-chat targets, and the dashboard never called it.

Fix

Wire _validate_bot_chat_deliver into _update_cron_job_sync via a new _validate_dashboard_cron_bot_chat_deliver helper, called right after normalization and before the update is persisted (same point the CLI/tool path validates relative to its own update). Covers both deliver and failure_deliver.

Scope note: the dashboard's CREATE endpoint (CronJobCreate) does not expose failure_deliver at all, so only the update path needed a failure_deliver fix. CronJobCreate does expose deliver and has the identical pre-existing gap on create (also never calls _validate_bot_chat_deliver) — that's out of scope for this PR, which is focused on the update lane the same-day follow-up work targeted; happy to open a follow-up for create if wanted.

Empirical verification

Before the fix, calling update_cron_job with failure_deliver="bot-chat:definitely-not-a-real-profile" persisted the value with no exception raised anywhere in the path.

Added two regression tests in tests/hermes_cli/test_web_server_cron_profiles.py:

  • test_update_cron_job_rejects_unknown_bot_chat_failure_deliver
  • test_update_cron_job_rejects_unknown_bot_chat_deliver

Both assert: HTTPException 400 with the same "not found on this gateway's machine" message the CLI path raises, the scheduler provider is not notified, and the job's stored field is left unchanged.

Mutation-verified via patch-file diff/revert/reapply (not git stash — this worktree shares a .git with several sibling worktrees): reverted only hermes_cli/web_server.py, confirmed both new tests fail with DID NOT RAISE HTTPException, reapplied the patch, confirmed both pass.

Test plan

  • tests/hermes_cli/test_web_server_cron_profiles.py — 33 passed (full file, includes the 2 new tests)
  • tests/cron/test_cron_failure_deliver.py — 24 passed
  • tests/tools/test_cronjob_tools.py + tests/hermes_cli/test_cron_dashboard_off_loop.py + tests/hermes_cli/test_cron_fire_dashboard.py — 100 passed
  • Manually confirmed valid updates (deliver: local, failure_deliver: "" clear, bare bot-chat, bot-chat:<existing-profile>) still pass through unaffected

Competitor check

Searched gh pr list --search for "dashboard cron failure_deliver", "cron dashboard bot-chat validate", "_validate_bot_chat_deliver dashboard", "cron dashboard deliver validation", "bot-chat deliver dashboard", "cron dashboard bot-chat profile", "cron update_job bot-chat validate", "dashboard cron deliver bot-chat profile not found" (state=all). No open or merged PR addresses this specific gap; #101373 (merged today) is the closest hit but only did text normalization as shown above.

The CLI and the cronjob tool both run every create/update through
tools.cronjob_tools._validate_bot_chat_deliver, rejecting a bot-chat
deliver/failure_deliver target whose named profile does not exist on
this machine at write time rather than only discovering it later when
a run (or a failure) actually needs to route through it.

The dashboard's PUT /api/cron/jobs/{job_id} never went through that
check for either field — it calls cron.jobs.update_job (via
_mutate_cron_for_profile) directly after only text-normalizing the
values. This predates failure_deliver: deliver has been exposed on
the dashboard's update endpoint unvalidated since bot-chat delivery
itself shipped, and failure_deliver simply inherited the same gap
when NS-788 added it. Today's same-day follow-up commit
("validate failure_deliver at preflight and dashboard update lanes")
did not close this — it only added failure_deliver to the dashboard's
text normalizer (empty clears vs. coalesces), not the bot-chat
profile-existence check; _preflight_check_delivery explicitly skips
bot-chat targets for both lanes, deferring entirely to this create/
update-time validator, so it was the dashboard's only safety net and
the dashboard never called it.

Wire _validate_bot_chat_deliver into _update_cron_job_sync via a new
_validate_dashboard_cron_bot_chat_deliver helper, checked right after
normalization and before the update is persisted, covering both
deliver and failure_deliver. The dashboard's CREATE endpoint does not
expose failure_deliver at all (not in CronJobCreate), so only the
update path needed this.

Empirically confirmed the gap before fixing: calling update_cron_job
with failure_deliver="bot-chat:definitely-not-a-real-profile" stored
the value with no exception raised. Added two regression tests
(deliver and failure_deliver) asserting the dashboard rejects an
unresolvable bot-chat profile with a 400, does not notify the
provider, and leaves the job's stored field unchanged; both fail
without the fix (mutation-verified via patch-file diff/revert/
reapply) and pass with it. Ran the full cron dashboard test file
(33 passed), tests/cron/test_cron_failure_deliver.py (24 passed),
and tests/tools/test_cronjob_tools.py (100 passed) — no regressions.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cron Cron scheduler and job management comp/dashboard Web dashboard / control panel UI (dashboard/, landing) area/profiles Multi-profile isolation, HERMES_HOME scoping labels Sep 3, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference; please use your judgment.

PR #102091 — fix(cron): validate bot-chat deliver/failure_deliver on dashboard updates

  • hermes_cli/web_server.py:13041-13068 adds _validate_dashboard_cron_bot_chat_deliver() reusing tools.cronjob_tools._validate_bot_chat_deliver for both deliver and failure_deliver, raising 400 on an unresolvable bot-chat profile; wired into _update_cron_job_sync (hermes_cli/web_server.py:13438-13444) next to the existing context_from validation. Closes a pre-existing gap both fields had: CLI and cronjob tool fail loud up front, but dashboard PUTs only surfaced it per-run as last_delivery_error.
  • Only present fields are checked (if field not in updates: continue), so partial updates not touching delivery are unaffected.
  • Tests tests/hermes_cli/test_web_server_cron_profiles.py:1098-1183 pin 400s for unknown profiles in both fields.

Non-blocking:

  • The import is function-local (consistent with this module's heavy-lazy-import style); no cycle risk either way since tools.cronjob_tools doesn't import the web server.

Verdict: LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/cron Cron scheduler and job management comp/dashboard Web dashboard / control panel UI (dashboard/, landing) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants