Skip to content

fix(scheduler): reload is unverifiable and unknown YAML sections vanish silently - #129

Open
mt-alarcon wants to merge 1 commit into
evolution-foundation:developfrom
mt-alarcon:fix/scheduler-silent-config-and-reload
Open

fix(scheduler): reload is unverifiable and unknown YAML sections vanish silently#129
mt-alarcon wants to merge 1 commit into
evolution-foundation:developfrom
mt-alarcon:fix/scheduler-silent-config-and-reload

Conversation

@mt-alarcon

@mt-alarcon mt-alarcon commented Jul 31, 2026

Copy link
Copy Markdown

Two independent bugs in scheduler.py, both the same shape: the scheduler knows something the operator needs and drops it silently.

1. SIGHUP reload is unverifiable

print(f"  {ts} [reload] SIGHUP received — clearing schedule and re-reading routines")
print(f"  {ts} [reload] {total} routines scheduled")

No flush=True. Since stdout is normally redirected to a log file, Python uses block buffering and these lines sit in the buffer indefinitely — in practice [reload] never appears in the log at all.

The consequence is worse than a missing line. Someone checking "did my config reload?" sees nothing and cannot distinguish a working reload from a broken one. On our install this produced a real false conclusion in both directions: one person could not prove the reload had worked, and a second person read a pre-reload routine run as proof it had failed. The reload had worked all along; the evidence only surfaced when the process shut down and flushed its buffer.

The routine-execution print already passes flush=True. Only these two did not.

2. Unknown YAML sections vanish without a word

The loader reads exactly daily, weekly, monthly. Any other top-level key — a typo, or a section someone assumed was supported — is dropped silently: the routines under it simply never run. No error, no warning, nothing in the log.

We found a section that had been sitting unread for weeks with routines under it. Nothing in the system could have told us; it was found by reading the loader source.

Now the loader warns and names the offending sections. It does not add new sections or change what is read — it only stops the silence.

Scope

Deliberately minimal: two bug fixes, no new features, no behaviour change beyond the two messages. Both are running on a live install.

Related but not included, since it would be a feature rather than a fix: the monthly loop is hardcoded to day 1 / hour 8 and ignores any per-routine scheduling key. Upstream never advertised such a key, so that is a separate discussion.

Also available as a follow-up if wanted: a boot-time check that every scheduled routine's script actually exists, shouting on startup instead of failing once per tick forever. It caught four separate incidents downstream where a routine had never run once. It is left out here because our implementation leans on downstream-only alerting infrastructure and would need adapting.

🤖 Generated with Claude Code

Summary by Sourcery

Clarify scheduler behavior when reloading and reading YAML configuration so operators receive explicit feedback instead of silent failures.

Bug Fixes:

  • Ensure SIGHUP-triggered reload messages are flushed to logs so reload events and resulting job counts are reliably visible.
  • Emit warnings for unknown top-level YAML sections so misconfigured or unsupported routine groups no longer disappear silently.

Both bugs share a shape: the scheduler knows something the operator needs and
drops it on the floor.

1. SIGHUP reload is unverifiable. The two `[reload]` prints lack flush=True.
   stdout is normally redirected to a log file, so block buffering holds the
   lines indefinitely — in practice `[reload]` never appears in the log at all.
   Anyone checking "did my config reload?" reads an empty result and cannot
   tell a working reload from a broken one. The routine-execution prints
   already flush; only these two did not.

2. Unknown YAML sections vanish without a word. The loader reads exactly
   daily/weekly/monthly. Anything else — a typo, or a section a user assumed
   was supported — is dropped silently: those routines simply never run, with
   no error, no warning, nothing in the log. Now it warns and names the
   sections.

Both were found on a live install: a config section had been sitting unread
for weeks with routines under it, and a reload could not be confirmed even
after it had in fact worked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adds explicit logging and validation around scheduler reloads and YAML configuration sections to make operator-facing behavior observable and debuggable, while keeping functional behavior unchanged except for new messages.

Sequence diagram for YAML loader handling unknown sections

sequenceDiagram
    participant Scheduler
    participant YAMLConfig

    Scheduler->>YAMLConfig: _load_routines_from_yaml(schedule, config_path, is_plugin)
    YAMLConfig-->>Scheduler: config
    Scheduler->>Scheduler: determine plugin_slug
    Scheduler->>Scheduler: compute _unknown = [k for k in config if k not in _known_sections]
    alt unknown_sections_present
        Scheduler->>Scheduler: print(WARN unread section(s) ..., flush=True)
    end
    Scheduler->>Scheduler: iterate config.get(daily), config.get(weekly), config.get(monthly)
Loading

Sequence diagram for scheduler SIGHUP reload logging

sequenceDiagram
    actor Operator
    participant OS
    participant Scheduler

    Operator->>OS: send SIGHUP to scheduler
    OS->>Scheduler: deliver SIGHUP
    Scheduler->>Scheduler: shutdown(sig, frame)
    Scheduler->>Scheduler: _reload_flag.is_set()
    alt reload_flag_set
        Scheduler->>Scheduler: print([reload] SIGHUP received ..., flush=True)
        Scheduler->>Scheduler: schedule.clear()
        Scheduler->>Scheduler: setup_schedule()
        Scheduler->>Scheduler: total = len(schedule.get_jobs())
        Scheduler->>Scheduler: print([reload] total routines scheduled, flush=True)
    end
    Scheduler->>Scheduler: schedule.run_pending()
Loading

File-Level Changes

Change Details Files
Warn when the YAML scheduler config contains unknown top-level sections so operators know routines under those sections will never run.
  • Introduce a fixed set of known sections (daily, weekly, monthly) in the YAML loader.
  • Compute the list of unknown top-level keys in the loaded config.
  • Emit a WARN log message naming the unread sections and explaining that routines under them will never run, with flush enabled.
scheduler.py
Make SIGHUP-driven scheduler reloads verifiable by ensuring the reload log messages are flushed immediately.
  • Update the log print for receiving SIGHUP to pass flush=True.
  • Update the log print reporting the number of routines scheduled after reload to pass flush=True.
scheduler.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant