fix(cron): null-safe deliver in cron list + re-resolve BSM secrets per run

Two live cron bugs, both surfaced by @banditburai in #35616 (whose larger
watchdog/supervisor work is already superseded by the CronScheduler provider
refactor on main):

- #32896: `cron list` crashed on a present-but-null `deliver` field —
  `job.get("deliver", ["local"])` returns None for an explicit null, which
  then hit `", ".join(None)`. Coalesce with `or ["local"]` (same pitfall
  the sibling `repeat` line already guards against).

- #33465: cron jobs 401'd on Bitwarden/BSM-backed secrets. The per-run env
  reload used a bare `load_dotenv(override=True)`, which re-applied only the
  .env placeholder — startup had already recorded this HERMES_HOME in
  env_loader._APPLIED_HOMES, so the external-secret re-pull no-oped. Route the
  reload through load_hermes_dotenv() and call reset_secret_source_cache()
  first to force the re-pull (Bitwarden's 300s value-cache keeps it off the
  network; override honours secrets.bitwarden.override_existing, mirroring
  startup).

Tests: null-deliver regression guard in test_cron.py; reset-before-reload
ordering guard in test_scheduler.py. Migrated 31 scheduler-reload test seams
from patching dotenv.load_dotenv to the new load_hermes_dotenv /
reset_secret_source_cache seam.
This commit is contained in:
teknium1 2026-07-01 00:39:00 -07:00 committed by Teknium
parent cf427ccf08
commit 836732f54f
5 changed files with 148 additions and 38 deletions

View file

@ -137,7 +137,11 @@ def cron_list(show_all: bool = False):
repeat_completed = repeat_info.get("completed", 0)
repeat_str = f"{repeat_completed}/{repeat_times}" if repeat_times else ""
deliver = job.get("deliver", ["local"])
# `deliver` may be present-but-null in the job record (same pitfall as
# `repeat` above), so coalesce to the default rather than relying on the
# dict-default, which only applies to a missing key. A null value would
# otherwise reach `", ".join(None)` and crash the whole listing (#32896).
deliver = job.get("deliver") or ["local"]
if isinstance(deliver, str):
deliver = [deliver]
deliver_str = ", ".join(deliver)