Skip to content

fix(cron): honest fallback/timeout/drift alerts + drift-guard alert-once - #85508

Merged
teknium1 merged 9 commits into
mainfrom
salv/85011-cron-alerts
Aug 13, 2026
Merged

fix(cron): honest fallback/timeout/drift alerts + drift-guard alert-once#85508
teknium1 merged 9 commits into
mainfrom
salv/85011-cron-alerts

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Cron failure alerts stop lying about fallback chains, timeouts, and drift — and drift alerts fire once per job instead of once per tick.

Salvage of #85011 (@victor-kyriazakos), which itself carries #81579 (@georgell-ceo); reconciled with #72056 (@sashmatash), the earliest submission of the drift-alert-delivery half. All contributor authorship preserved via cherry-pick.

Changes

Validation

Before After
No chain configured, provider fails "Fallback chain was exhausted or unavailable." "No fallback chain configured — add one with hermes fallback add, or…"
Inactivity watchdog trips "provider timeout. Fallback chain was exhausted…" "the job itself stalled — no tool/API activity…"
TERMINAL_CWD lock timeout "provider timeout. Fallback chain was exhausted…" "could not acquire the scheduler's working-directory lock…"
40 unpinned jobs after config change identical drift alert per job per tick one alert per job; re-arms when drift heals
Consumed finite one-shot drifts told to cronjob action=update a dead job told to create a new one-shot with explicit provider/model

Tests: 13 new across 4 files, asserting delivered content through run_one_job + _deliver_result. Full cron suite: 578 passed, 1 skipped.

Closes #85011, closes #81579, closes #72056.

Infographic

Honest cron alerts

Alexey (CTO) and others added 8 commits August 13, 2026 10:55
_summarize_cron_failure_for_delivery() unconditionally said 'Fallback
chain was exhausted or unavailable.' on every provider failure, even
when fallback_providers is empty (the default -- confirmed empty on
both the root and cto profile config.yaml). That phrasing implies a
fallback was attempted and failed, which sent the operator debugging
the wrong thing.

Add _fallback_chain_phrase(): reads the effective chain via
get_fallback_chain(load_config()) and returns 'No fallback chain
configured.' when it's empty, or the original wording when a chain
exists. Fails open to the original wording on any config read error.

The scheduler's own inactivity-watchdog mislabeling (idle-timeout
reported as provider timeout) was already fixed in a prior commit on
this branch; this closes the second half of t_29b8da55.

Data pull requested by the task (grep errors.log across profiles +
root for 'Provider has been unresponsive' + model=, 2026-07-21 to
2026-08-06): 9 stall events total, 5 on claude-sonnet-5, 4 on
claude-haiku-4-5, spread across 6 different cron jobs. No material
haiku-specific instability -- sonnet-5 stalls at least as often on the
cron path in this sample. Reporting per acceptance criteria; not
worth a routing change on this evidence.
…lert

A cron that dies on a provider timeout with no fallback chain configured
now tells the operator exactly how to fix it: `hermes fallback add` for a
personal chain, or the cron.model + cron.model_provider fleet defaults for
operator-managed fleets. The exhausted-chain branch stays terse — the chain
is intact there and no config command applies.

Field-reported: users hitting the empty-chain failure could not self-serve
from the alert text alone.
A fleet-wide inference config change previously produced one 'Skipped to
prevent unintended spend' alert per unpinned job per tick — 40 jobs meant
40 alerts every tick until each was re-pinned (Coatue field report,
2026-08-11). The #44585 guard now reuses the #73506 alert-once shape the
preflight path already established: a persisted drift_alerted bit on the
job record, a [drift_skip:silent] marker on repeat ticks that suppresses
delivery, and the bit clears on the next successful run so a future drift
re-alerts. Only the drift branch consults the bit — every other failure
keeps alerting per tick.

The alert text also now says it is sent once, so operators know the job
stays skipped silently until pinned or restored.
The generic failure summarizer caps unrecognized errors at 180 chars,
which cut the drift alert off mid-sentence before the pin command. The
drift branch now formats its own delivery from the guard's full message,
so the one alert the operator gets actually contains the fix.
…cific comments

- Widen the scheduler-internal timeout classification to the sibling
  TERMINAL_CWD lock-wait TimeoutError (#79768), which also matched the
  generic 'timed out' branch and was delivered as a provider timeout.
- Reconcile the drift-guard alert with #72056's lifecycle-aware
  remediation: finite one-shots are told to recreate the job, not to
  update a consumed one.
- Scrub environment-specific references from comments/docstrings.
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 14525ea — fix(docs): remove stray conflict marker in cron.md

⚠️ Warnings

OSV vulnerability scan · View job

5 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 4m45s vs 6m37s (-28.2%). 10 job(s) slower, 13 faster, 2 unchanged.

  • Python tests / Run tests slice 7/12: +44.0s
  • Python tests / Run tests slice 2/12: +44.0s
  • Python tests / Run tests slice 9/12: -42.0s
  • Python tests / Run tests slice 6/12: +42.0s
  • Python tests / Run tests slice 5/12: +41.0s

@alt-glitch alt-glitch added type/bug Something isn't working comp/cron Cron scheduler and job management P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Aug 13, 2026
@teknium1
teknium1 merged commit 70143de into main Aug 13, 2026
47 checks passed
@teknium1
teknium1 deleted the salv/85011-cron-alerts branch August 13, 2026 18:20
teknium1 added a commit that referenced this pull request Aug 13, 2026
…composed no_agent gate

The cherry-picked tests predate #85508's honest fallback-chain phrasing
and each other: assertions pinned the old 'exhausted or unavailable'
literal and #83188's no_agent fallback-note behavior, which #77648's
mode gate supersedes (no provider classification at all for no_agent
jobs). Assert the composed contract instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants