fix(cron): enforce a single scheduler owner - #66162
Conversation
Gate gateway and Desktop cron startup through one fail-closed owner policy and refresh configured external secrets before script-only jobs spawn.
Related: this implements a configuration-enforced single-owner policy for the desktop/gateway cron scheduler cluster (#43965, #44049, #52259). Its policy is broader than the existing live-gateway deferral approaches; maintainers should choose the ownership semantics. |
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Comment
Enforces a single scheduler owner for cron jobs to prevent duplicate job dispatch. Adds scheduler_owner config key and thread-safe secret refresh. 10 files, 27K diff. The pattern is sound and uses proper locking. Recommend human review for the full scope of the config/loader changes.
Reviewed by Hermes Agent
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the substantial concurrency and secret-handling work. One blocking cross-process regression needs resolution.
Problems
cron/scheduler.py:3876-3880now callson_jobs_changed()only throughreserved_active_scheduler_provider(). That registry is process-local (cron/scheduler_runtime.py:21-33), so a separatehermes cron/ tool process has no active entry and silently skips reconciliation. This conflicts with the external-provider contract incron/scheduler_provider.py:78-82, which requires create/update/remove/pause/resume to notify the provider.tests/cron/test_jobs_changed_notify.py:38-49mocks a local reservation, so it does not cover that cross-process owner case.
Suggested changes
- Route mutations to the active owner through a durable or authenticated cross-process mechanism, then add an integration test for an independent mutation process while an external owner is active.
Automated hermes-sweeper review.
| resolve_cron_scheduler().on_jobs_changed() | ||
| from cron.scheduler_runtime import reserved_active_scheduler_provider | ||
|
|
||
| with reserved_active_scheduler_provider() as reservation: |
There was a problem hiding this comment.
reserved_active_scheduler_provider() is process-local. A CLI/tool process that mutates jobs while another process owns an external scheduler will receive None here, so on_jobs_changed() never runs and the external registry is not reconciled. Please retain a cross-process notification path to the owner and cover that separate-process case.
|
Superseded by #70286, which is rebased on current main and contains only the scheduler-ownership/lifecycle fix. The older secret-refresh and cron-isolation work is intentionally not carried forward. |
Summary
HERMES_HOMEwith a full-lifetime kernel lease and dynamic gateway/Desktop handoffno_agentsubprocesses and capture an immutable sanitized child environment202Problem
Gateway and Desktop could both dispatch one shared cron registry. The file lock serialized individual ticks but allowed either process to win, so jobs alternated between processes with different managed-secret bootstrap contexts.
no_agentexecution also constructed subprocess environments before managed-secret refresh.Safety properties
dotenv → external sources → managed .envOP_SESSION_*credentials are stripped from childrenValidation
Exact reviewed head:
eda3bc24502cbdf6a106cd824607fb91cf0d8a53843 passedin the final affected upstream matrixgit diff --checkpassedNo secret values are included in this change.