Skip to content

Config hooks and outbound webhooks fire for external-worker cron jobs (salvage #131831) - #132438

Merged
kshitijk4poor merged 2 commits into
NousResearch:mainfrom
kshitijk4poor:fix/p1-1003-cron-worker-hooks
Oct 3, 2026
Merged

kshitijk4poor merged 2 commits into
NousResearch:mainfrom
kshitijk4poor:fix/p1-1003-cron-worker-hooks

Conversation

@kshitijk4poor

Copy link
Copy Markdown

Cron jobs run by the restart-safe external worker now fire the profile's config hooks: (shell hooks and hooks.outbound webhooks), the same as jobs run in-process.

Fixes #131764

What happened

The external cron worker (cron/scheduler.py::_run_external_worker_payload, added in #101940) discovered plugins (since 411c0405b6) but never registered config hooks, so hooks: and hooks.outbound silently did nothing for worker-run jobs while plugin hooks still fired — which hid the gap.

Change

  • cron/scheduler.py::_run_external_worker_payload: after plugin discovery and inside the payload's home override and secret scope, call the gateway's own GatewayStartupMixin._register_config_hooks (the helper the gateway uses per profile at startup). Registration happens once per worker process; both registries are keyed per home and idempotent.
  • tests/cron/test_external_worker_config_hooks.py (new): a multiplexed payload registers the owning profile's shell hook and a secret_env outbound webhook under that profile's home key.

Credit

@liuhao1024 (#131831, draft) — commit 7c3dc30b57 cherry-picked with authorship kept (-x d1971c698a). Our follow-up 018d9130a1 reuses the gateway helper instead of a copied registration sequence and moves registration inside the secret scope: in the original ordering, an outbound webhook with secret_env raised UnscopedSecretError on a multiplexed gateway and was silently dropped.

@drafie found the same gap first in #119421 (shell hooks only).

Verification

Live probe — real _run_external_worker_payload, 2 payloads per worker on_session_start callbacks outbound callbacks hook fires
main (single-profile and multiplexed) 0 0 0
#131831 alone, multiplexed 1 0 2
this branch (single-profile and multiplexed) 1 1 2
profile without hooks: (neighbour), main and branch 0 0 —
  • New test: red on main (shell hook not registered) and red with only the contributor commit (outbound webhook missing); green on the branch.
  • 13 related test files (cron worker, outbound webhooks, shell hooks, gateway profile/hook scope, plugins, scheduler): 324 passed, 17 skipped.
  • Import cost of the lazy gateway.run_startup import in the one-shot worker: 2–3 ms (does not import gateway.run).

Independent review: correctness, tests/probe/mutation, reuse, quality and efficiency passes approved.

Maintainer notes

liuhao1024 and others added 2 commits October 4, 2026 00:52
… worker

The restart-safe external worker (python -m cron.scheduler
--external-worker-file) called discover_plugins() under the payload's
home override but never agent.shell_hooks.register_from_config() nor
agent.outbound_webhooks.register_from_config(), so hooks: blocks in the
owning profile's config.yaml silently stopped firing for cron sessions
once gateway fires were handed to the worker instead of running
in-process. Plugin hooks kept working, which made the gap invisible.

Register both right after plugin discovery + secret hydration, under the
same home override, with the gateway's never-raise shape (NousResearch#102504):
consent resolves non-interactively from the owning profile's allowlist /
HERMES_ACCEPT_HOOKS / hooks_auto_accept, and a broken hooks block logs a
warning instead of taking the cron execution down.

Fixes NousResearch#131764

(cherry picked from commit d1971c6)
…cret scope

The external worker now calls GatewayStartupMixin._register_config_hooks (the
helper gateway startup and each multiplexed profile already use) instead of a
copied load_config/register_from_config sequence, and calls it after
set_secret_scope rather than before. Registered outside the scope, a
hooks.outbound target with secret_env hits get_secret with no scope while
multiplexing, which raises UnscopedSecretError; the never-raise guard swallowed
it, so that profile's outbound webhooks were silently dropped again.

Both registries are idempotent per (home, event, ...) so a worker running
several payloads registers each hook once. The kept test gains a secret_env
target on a multiplexed payload to pin the ordering; the broken-config test is
dropped (register_from_config never raises on a malformed block, and the
helper's guard is already covered by the gateway path).

Refs NousResearch#131764
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) October 3, 2026 20:16
@kshitijk4poor
kshitijk4poor merged commit 8840751 into NousResearch:main Oct 3, 2026
47 checks passed
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cron Cron scheduler and job management area/config Config system, migrations, profiles labels Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/cron Cron scheduler and job management 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.

Cron external worker never registers config.yaml shell hooks — hooks silently dead for every scheduled job under a systemd gateway

3 participants