Skip to content

feat(cron): add failure destination support to failed cron jobs#31059

Merged
Takhoffman merged 14 commits intoopenclaw:mainfrom
kesor:fix/cron-failure-missing-features
Mar 2, 2026
Merged

feat(cron): add failure destination support to failed cron jobs#31059
Takhoffman merged 14 commits intoopenclaw:mainfrom
kesor:fix/cron-failure-missing-features

Conversation

@kesor
Copy link
Contributor

@kesor kesor commented Mar 1, 2026

Extends PR #24789 failure alerts with features from PR #29145:

  • Add webhook delivery mode for failure alerts (mode: 'webhook')
  • Add accountId support for multi-account channel configurations
  • Add bestEffort handling to skip alerts when job has bestEffort=true
  • Add separate failureDestination config (global + per-job in delivery)
  • Add duplicate prevention (prevents sending to same as primary delivery)
  • Add CLI flags: --failure-alert-mode, --failure-alert-account-id
  • Add UI fields for new options in web cron editor
  • Add global default failure destination via cron.failureDestination config

Summary

Describe the problem and fix in 2–5 bullets:

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • New global config: cron.failureDestination sets default failure channel/webhook for all jobs
  • New per-job config: delivery.failureDestination overrides global default per job
  • New CLI flags: --failure-alert-mode (announce/webhook), --failure-alert-account-id
  • New UI fields in cron editor: Alert mode dropdown, Alert account ID field
  • Failure alerts now respect bestEffort: true to skip non-critical job failures
  • Webhook delivery mode sends HTTP POST to configured URL on failure
  • Duplicate prevention skips sending failure to same destination as primary delivery

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No - uses existing cron.webhookToken for auth
  • New/changed network calls? (Yes/No) Yes - new webhook calls to user-configured URLs on failure
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: Added new webhook delivery using existing SSRF guard and webhookToken auth pattern from primary cron delivery.

Repro + Verification

Environment

  • OS: macOS/Linux
  • Runtime/container: Node 22+
  • Model/provider: N/A
  • Integration/channel (if any): Any messaging channel (Telegram, Slack, etc.)
  • Relevant config (redacted):
    cron:
      failureDestination:
        mode: webhook
        to: "https://hooks.example.com/failures"
    cronJobs:
      - name: critical-job
        delivery:
          channel: telegram
          to: "12345"
          failureDestination:
            mode: webhook
            to: "https://hooks.example.com/pagerduty"

Steps

  1. Configure cron.failureDestination globally
  2. Create a cron job without explicit failure destination
  3. Verify failure alerts go to global default
  4. Override with per-job delivery.failureDestination
  5. Verify override takes precedence
  6. Test bestEffort=true suppresses alerts

Expected

  • Jobs without override: Failure alerts to global default
  • Jobs with override: Failure alerts to per-job destination
  • bestEffort jobs: No failure alert on error

Actual

  • Working as expected in local testing

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: Build passes, unit tests pass (cron delivery tests, failure alert tests, CLI tests)
  • Edge cases checked: Webhook URL validation, duplicate prevention logic, bestEffort check, global config fallback
  • What you did NOT verify: End-to-end cron job with actual failure delivery to real webhook

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes - new fields are optional
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert commit or disable via config
  • Files/config to restore: N/A - config-based feature
  • Known bad symptoms reviewers should watch for: Failure alerts not being sent, duplicate alerts

Risks and Mitigations

  • Risk: Webhook delivery could expose internal services if URL is misconfigured
    • Mitigation: Uses existing SSRF guard (fetchWithSsrFGuard) same as primary cron webhook delivery
  • Risk: Users could accidentally create notification loops
    • Mitigation: Duplicate prevention checks if failure destination equals primary delivery destination

@openclaw-barnacle openclaw-barnacle bot added app: web-ui App: web-ui gateway Gateway runtime cli CLI command changes size: L labels Mar 1, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f7128b40d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 1, 2026

Greptile Summary

Adds comprehensive failure destination support to cron jobs, extending the existing threshold-based failure alerts with webhook delivery mode, multi-account support, and per-job configuration. The implementation correctly handles mode inheritance, duplicate prevention against primary delivery, and SSRF protection for webhooks.

Key changes:

  • Separate failureDestination config (global + per-job) sends notifications on every failure, distinct from threshold-based failureAlert
  • Webhook delivery mode with SSRF guard and auth token support
  • bestEffort flag properly skips alerts for non-critical jobs
  • Duplicate prevention prevents sending to same destination as primary delivery
  • CLI and UI support for new failure alert options (mode, accountId)

Found one issue with webhook payload consistency that should be addressed before merge.

Confidence Score: 3/5

  • Safe to merge after addressing the webhook payload inconsistency
  • The implementation is solid with proper SSRF protection, URL validation, and bestEffort handling. However, the webhook payload for failureDestination omits the formatted message field that's included in failureAlert webhooks, creating an inconsistency that could confuse webhook consumers. Once this is fixed, the PR introduces well-structured failure notification capabilities.
  • Fix the webhook payload in src/gateway/server-cron.ts (line 421-430) to include the formatted message

Last reviewed commit: cf2cff9

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

15 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

@kesor kesor marked this pull request as ready for review March 1, 2026 23:55
@kesor kesor force-pushed the fix/cron-failure-missing-features branch from 5edfee3 to d2d4e64 Compare March 1, 2026 23:55
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2d4e64dab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@openclaw-barnacle openclaw-barnacle bot added the app: macos App: macos label Mar 2, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23ed8cecff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 282b561199

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kesor kesor force-pushed the fix/cron-failure-missing-features branch from 62ef280 to f5cb41c Compare March 2, 2026 01:09
@openclaw-barnacle openclaw-barnacle bot removed the app: macos App: macos label Mar 2, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5cb41c10c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kesor kesor force-pushed the fix/cron-failure-missing-features branch from 3ba33c6 to 5d5b842 Compare March 2, 2026 03:08
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d5b8421fd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kesor kesor force-pushed the fix/cron-failure-missing-features branch from 5d5b842 to cf2cff9 Compare March 2, 2026 03:20
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf2cff96fa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

channel: Type.Optional(Type.Union([Type.Literal("last"), NonEmptyString])),
accountId: Type.Optional(NonEmptyString),
bestEffort: Type.Optional(Type.Boolean()),
failureDestination: Type.Optional(CronFailureDestinationSchema),

Choose a reason for hiding this comment

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

P2 Badge Restrict failureDestination to jobs that can persist delivery

The schema now allows delivery.failureDestination for any cron job, but main-session jobs still cannot retain non-webhook delivery: createJob will reject it via assertDeliverySupport, and applyJobPatch later clears job.delivery when sessionTarget === "main" and mode is not webhook (src/cron/service/jobs.ts). This means cron.add/cron.update requests for main jobs can pass schema validation yet have the per-job failure destination rejected or silently dropped, so the new override is not reliably usable for that job type.

Useful? React with 👍 / 👎.

Comment on lines +218 to +219
to: normalizeTo(jobConfig?.to) ?? normalizeTo(job.delivery?.to),
mode: jobConfig?.mode ?? globalConfig?.mode,

Choose a reason for hiding this comment

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

P2 Badge Stop reusing announce targets for webhook failure alerts

When failureAlert.mode is set to "webhook", resolveFailureAlert still falls back to job.delivery.to, which is typically a channel recipient for announce delivery rather than a webhook endpoint. In that case sendCronFailureAlert treats the chat target as a webhook URL and either skips the alert as invalid or posts to an unintended URL if it happens to parse as HTTP(S). Webhook mode should not inherit announce delivery targets; it should require an explicit webhook to source.

Useful? React with 👍 / 👎.

@kesor
Copy link
Contributor Author

kesor commented Mar 2, 2026

@Takhoffman you thrown #29145 out, throwing the baby with the bathwater. There are features in there that the other PR you merged did not include.

What's Implemented in the merged PR #24789:

  • cron.failureAlert global config + per-job failureAlert field
  • Configurable threshold (after) - alerts after N consecutive errors
  • Cooldown between alerts (cooldownMs)
  • CLI flags: --failure-alert, --failure-alert-after, --failure-alert-channel, --failure-alert-to, --failure-alert-cooldown
  • Web UI form support
  • Per-job disable with failureAlert: false

What's Implemented in this PR (taken from PR #29145)

Feature PR #24789 This PR
Separate failure destination delivery.failureDestination / cron.failureDestination
Webhook mode mode: "webhook"
bestEffort handling ✅ Respects bestEffort: true to suppress
Duplicate prevention ✅ Prevents sending to same as primary delivery
accountId support ✅ Multi-account channel configs
Global default cron.failureDestination config

Key Features Added

  1. Global default failure destination - Configure cron.failureDestination to set a default channel/webhook for all cron job failures
  2. Per-job failure destination - Override via delivery.failureDestination in each job
  3. Webhook mode - Send failure alerts via HTTP POST with SSRF protection
  4. bestEffort handling - Skip alerts when job has bestEffort: true
  5. Duplicate prevention - Prevents sending to same destination as primary delivery
  6. Account ID support - For multi-account channel setups
  7. Partial merge - Per-job config merges with global, only overrides explicit fields
  8. CLI flags - --failure-alert-mode, --failure-alert-account-id
  9. UI fields - Alert mode dropdown, Alert account ID field

Important Distinctions

  • failureAlert - Threshold-based alerts with after (consecutive errors) and cooldownMs. Sends after N consecutive failures.
  • failureDestination - Defined notification destinations for failures.

Example Config

# Global default - all cron jobs will send failures to this webhook
cron:
  failureDestination:
    mode: webhook
    to: "https://hooks.example.com/failures"

cronJobs:
  - name: critical-job
    delivery:
      channel: telegram
      to: "12345"
      # Override global - send to different webhook for this job
      failureDestination:
        mode: webhook
        to: "https://hooks.example.com/critical"
  
  - name: low-priority-job
    delivery:
      channel: telegram
      to: "12345"
      bestEffort: true
      # No failure alerts for bestEffort jobs

Implementation Details

  • Separate CronFailureDestinationSchema (without after/cooldownMs) vs CronFailureAlertSchema (with threshold fields)
  • Partial job configs properly merge with global defaults (unset fields inherit from global)
  • Explicit mode switching clears inherited to since URL semantics differ between announce/webhook
  • Fail-closed: webhook mode requires URL, missing URL logs warning and skips
  • Duplicate detection uses "announce" as default when delivery.mode is undefined

@Takhoffman Takhoffman added the close:not-planned PR close reason label Mar 2, 2026
@Takhoffman
Copy link
Contributor

Thanks for the detailed feature work.

We’re closing this as not planned for now. This adds broad new failure-destination capability and config surface, while current project direction is to prioritize stability and narrow bug-fix changes over feature expansion (see VISION.md).

Appreciate the contribution and the thorough implementation.

@Takhoffman Takhoffman closed this Mar 2, 2026
@Takhoffman Takhoffman reopened this Mar 2, 2026
@Takhoffman
Copy link
Contributor

Sorry my codex is being a bit ambitious lately with closing stuff. I'll try to take a look at this.

Comment on lines +421 to +430
const failureMessage = `Cron job "${job.name}" failed: ${evt.error ?? "unknown error"}`;
const failurePayload = {
jobId: job.id,
jobName: job.name,
status: evt.status,
error: evt.error,
runAtMs: evt.runAtMs,
durationMs: evt.durationMs,
nextRunAtMs: evt.nextRunAtMs,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

failureMessage created but not included in webhook payload, only sent via announce mode

failureMessage is formatted on line 421 but omitted from failurePayload sent to webhooks (line 453). Webhook consumers only receive raw evt.error field. For consistency with sendCronFailureAlert (which includes message in webhook payload at line 266), include the formatted message:

Suggested change
const failureMessage = `Cron job "${job.name}" failed: ${evt.error ?? "unknown error"}`;
const failurePayload = {
jobId: job.id,
jobName: job.name,
status: evt.status,
error: evt.error,
runAtMs: evt.runAtMs,
durationMs: evt.durationMs,
nextRunAtMs: evt.nextRunAtMs,
};
const failureMessage = `Cron job "${job.name}" failed: ${evt.error ?? "unknown error"}`;
const failurePayload = {
jobId: job.id,
jobName: job.name,
message: failureMessage,
status: evt.status,
error: evt.error,
runAtMs: evt.runAtMs,
durationMs: evt.durationMs,
nextRunAtMs: evt.nextRunAtMs,
};
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-cron.ts
Line: 421-430

Comment:
`failureMessage` created but not included in webhook payload, only sent via announce mode

`failureMessage` is formatted on line 421 but omitted from `failurePayload` sent to webhooks (line 453). Webhook consumers only receive raw `evt.error` field. For consistency with `sendCronFailureAlert` (which includes message in webhook payload at line 266), include the formatted message:

```suggestion
              const failureMessage = `Cron job "${job.name}" failed: ${evt.error ?? "unknown error"}`;
              const failurePayload = {
                jobId: job.id,
                jobName: job.name,
                message: failureMessage,
                status: evt.status,
                error: evt.error,
                runAtMs: evt.runAtMs,
                durationMs: evt.durationMs,
                nextRunAtMs: evt.nextRunAtMs,
              };
```

How can I resolve this? If you propose a fix, please make it concise.

kesor and others added 12 commits March 2, 2026 09:13
…tEffort handling

Extends PR openclaw#24789 failure alerts with features from PR openclaw#29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor
…ination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined
…ation

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.
…stination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).
- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel
When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.
… change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes
…ode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook
…s announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'
@Takhoffman Takhoffman force-pushed the fix/cron-failure-missing-features branch from 16ad648 to b3ad717 Compare March 2, 2026 15:15
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c484fda959

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +232 to +234
failureChannelNormalized === primaryChannelNormalized &&
failurePlan.to === primaryTo &&
failurePlan.accountId === primaryAccountId

Choose a reason for hiding this comment

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

P2 Badge Inherit accountId when checking duplicate failure destinations

Duplicate suppression currently requires failurePlan.accountId === primaryAccountId, so a job with primary delivery accountId set and a failure destination that omits accountId is treated as different even when both routes resolve to the same account (for example, single-account bindings or matching session lastAccountId). In that case the new failure-destination path still sends a second message to the same chat, which defeats the intended duplicate-prevention behavior for common multi-account setups.

Useful? React with 👍 / 👎.

Comment on lines +249 to +253
const resolvedTarget = await resolveDeliveryTarget(cfg, agentId, {
channel: target.channel as CronMessageChannel | undefined,
to: target.to,
accountId: target.accountId,
});

Choose a reason for hiding this comment

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

P2 Badge Resolve failure announce targets with the job session key

sendFailureNotificationAnnounce calls resolveDeliveryTarget without sessionKey, so implicit failure destinations (channel: "last" or missing to) resolve against the agent main session instead of the cron job’s configured session/thread. Jobs that run with a thread-specific sessionKey can therefore deliver failure alerts to the wrong conversation (or fail target resolution) even though primary cron delivery explicitly includes job.sessionKey.

Useful? React with 👍 / 👎.

@Takhoffman Takhoffman merged commit 4b4ea5d into openclaw:main Mar 2, 2026
24 of 26 checks passed
obviyus pushed a commit that referenced this pull request Mar 2, 2026
* feat(cron): add failure destination support with webhook mode and bestEffort handling

Extends PR #24789 failure alerts with features from PR #29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor

* fix(cron): merge failureAlert mode/accountId and preserve failureDestination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined

* fix(cron): validate webhook mode requires URL in resolveFailureDestination

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.

* fix(cron): fail closed on webhook mode without URL and make failureDestination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).

* fix(cron): use separate schema for failureDestination and fix type cast

- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel

* fix(cron): merge global failureDestination with partial job overrides

When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.

* fix(cron): avoid forcing announce mode and clear inherited to on mode change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes

* fix(cron): preserve explicit to on mode override and always include mode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook

* fix(cron): allow clearing accountId and treat undefined global mode as announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'

* Cron: harden failure destination routing and add regression coverage

* Cron: resolve failure destination review feedback

* Cron: drop unrelated timeout assertions from conflict resolution

* Cron: format cron CLI regression test

* Cron: align gateway cron test mock types

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
@kesor kesor deleted the fix/cron-failure-missing-features branch March 2, 2026 16:10
Linux2010 pushed a commit to Linux2010/openclaw that referenced this pull request Mar 2, 2026
…claw#31059)

* feat(cron): add failure destination support with webhook mode and bestEffort handling

Extends PR openclaw#24789 failure alerts with features from PR openclaw#29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor

* fix(cron): merge failureAlert mode/accountId and preserve failureDestination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined

* fix(cron): validate webhook mode requires URL in resolveFailureDestination

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.

* fix(cron): fail closed on webhook mode without URL and make failureDestination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).

* fix(cron): use separate schema for failureDestination and fix type cast

- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel

* fix(cron): merge global failureDestination with partial job overrides

When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.

* fix(cron): avoid forcing announce mode and clear inherited to on mode change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes

* fix(cron): preserve explicit to on mode override and always include mode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook

* fix(cron): allow clearing accountId and treat undefined global mode as announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'

* Cron: harden failure destination routing and add regression coverage

* Cron: resolve failure destination review feedback

* Cron: drop unrelated timeout assertions from conflict resolution

* Cron: format cron CLI regression test

* Cron: align gateway cron test mock types

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
execute008 pushed a commit to execute008/openclaw that referenced this pull request Mar 2, 2026
…claw#31059)

* feat(cron): add failure destination support with webhook mode and bestEffort handling

Extends PR openclaw#24789 failure alerts with features from PR openclaw#29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor

* fix(cron): merge failureAlert mode/accountId and preserve failureDestination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined

* fix(cron): validate webhook mode requires URL in resolveFailureDestination

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.

* fix(cron): fail closed on webhook mode without URL and make failureDestination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).

* fix(cron): use separate schema for failureDestination and fix type cast

- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel

* fix(cron): merge global failureDestination with partial job overrides

When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.

* fix(cron): avoid forcing announce mode and clear inherited to on mode change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes

* fix(cron): preserve explicit to on mode override and always include mode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook

* fix(cron): allow clearing accountId and treat undefined global mode as announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'

* Cron: harden failure destination routing and add regression coverage

* Cron: resolve failure destination review feedback

* Cron: drop unrelated timeout assertions from conflict resolution

* Cron: format cron CLI regression test

* Cron: align gateway cron test mock types

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
MillionthOdin16 pushed a commit to MillionthOdin16/openclaw that referenced this pull request Mar 3, 2026
…claw#31059)

* feat(cron): add failure destination support with webhook mode and bestEffort handling

Extends PR openclaw#24789 failure alerts with features from PR openclaw#29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor

* fix(cron): merge failureAlert mode/accountId and preserve failureDestination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined

* fix(cron): validate webhook mode requires URL in resolveFailureDestination

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.

* fix(cron): fail closed on webhook mode without URL and make failureDestination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).

* fix(cron): use separate schema for failureDestination and fix type cast

- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel

* fix(cron): merge global failureDestination with partial job overrides

When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.

* fix(cron): avoid forcing announce mode and clear inherited to on mode change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes

* fix(cron): preserve explicit to on mode override and always include mode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook

* fix(cron): allow clearing accountId and treat undefined global mode as announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'

* Cron: harden failure destination routing and add regression coverage

* Cron: resolve failure destination review feedback

* Cron: drop unrelated timeout assertions from conflict resolution

* Cron: format cron CLI regression test

* Cron: align gateway cron test mock types

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
MillionthOdin16 pushed a commit to MillionthOdin16/openclaw that referenced this pull request Mar 3, 2026
…claw#31059)

* feat(cron): add failure destination support with webhook mode and bestEffort handling

Extends PR openclaw#24789 failure alerts with features from PR openclaw#29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor

* fix(cron): merge failureAlert mode/accountId and preserve failureDestination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined

* fix(cron): validate webhook mode requires URL in resolveFailureDestination

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.

* fix(cron): fail closed on webhook mode without URL and make failureDestination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).

* fix(cron): use separate schema for failureDestination and fix type cast

- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel

* fix(cron): merge global failureDestination with partial job overrides

When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.

* fix(cron): avoid forcing announce mode and clear inherited to on mode change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes

* fix(cron): preserve explicit to on mode override and always include mode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook

* fix(cron): allow clearing accountId and treat undefined global mode as announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'

* Cron: harden failure destination routing and add regression coverage

* Cron: resolve failure destination review feedback

* Cron: drop unrelated timeout assertions from conflict resolution

* Cron: format cron CLI regression test

* Cron: align gateway cron test mock types

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
(cherry picked from commit 95bd539)
dawi369 pushed a commit to dawi369/davis that referenced this pull request Mar 3, 2026
…claw#31059)

* feat(cron): add failure destination support with webhook mode and bestEffort handling

Extends PR openclaw#24789 failure alerts with features from PR openclaw#29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor

* fix(cron): merge failureAlert mode/accountId and preserve failureDestination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined

* fix(cron): validate webhook mode requires URL in resolveFailureDestination

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.

* fix(cron): fail closed on webhook mode without URL and make failureDestination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).

* fix(cron): use separate schema for failureDestination and fix type cast

- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel

* fix(cron): merge global failureDestination with partial job overrides

When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.

* fix(cron): avoid forcing announce mode and clear inherited to on mode change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes

* fix(cron): preserve explicit to on mode override and always include mode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook

* fix(cron): allow clearing accountId and treat undefined global mode as announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'

* Cron: harden failure destination routing and add regression coverage

* Cron: resolve failure destination review feedback

* Cron: drop unrelated timeout assertions from conflict resolution

* Cron: format cron CLI regression test

* Cron: align gateway cron test mock types

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
OWALabuy pushed a commit to kcinzgg/openclaw that referenced this pull request Mar 4, 2026
…claw#31059)

* feat(cron): add failure destination support with webhook mode and bestEffort handling

Extends PR openclaw#24789 failure alerts with features from PR openclaw#29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor

* fix(cron): merge failureAlert mode/accountId and preserve failureDestination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined

* fix(cron): validate webhook mode requires URL in resolveFailureDestination

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.

* fix(cron): fail closed on webhook mode without URL and make failureDestination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).

* fix(cron): use separate schema for failureDestination and fix type cast

- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel

* fix(cron): merge global failureDestination with partial job overrides

When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.

* fix(cron): avoid forcing announce mode and clear inherited to on mode change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes

* fix(cron): preserve explicit to on mode override and always include mode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook

* fix(cron): allow clearing accountId and treat undefined global mode as announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'

* Cron: harden failure destination routing and add regression coverage

* Cron: resolve failure destination review feedback

* Cron: drop unrelated timeout assertions from conflict resolution

* Cron: format cron CLI regression test

* Cron: align gateway cron test mock types

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
…claw#31059)

* feat(cron): add failure destination support with webhook mode and bestEffort handling

Extends PR openclaw#24789 failure alerts with features from PR openclaw#29145:
- Add webhook delivery mode for failure alerts (mode: 'webhook')
- Add accountId support for multi-account channel configurations
- Add bestEffort handling to skip alerts when job has bestEffort=true
- Add separate failureDestination config (global + per-job in delivery)
- Add duplicate prevention (prevents sending to same as primary delivery)
- Add CLI flags: --failure-alert-mode, --failure-alert-account-id
- Add UI fields for new options in web cron editor

* fix(cron): merge failureAlert mode/accountId and preserve failureDestination on updates

- Fix mergeCronFailureAlert to merge mode and accountId fields
- Fix mergeCronDelivery to preserve failureDestination on updates
- Fix isSameDeliveryTarget to use 'announce' as default instead of 'none'
  to properly detect duplicates when delivery.mode is undefined

* fix(cron): validate webhook mode requires URL in resolveFailureDestination

When mode is 'webhook' but no 'to' URL is provided, return null
instead of creating an invalid plan that silently fails later.

* fix(cron): fail closed on webhook mode without URL and make failureDestination fields clearable

- sendCronFailureAlert: fail closed when mode is webhook but URL is missing
- mergeCronDelivery: use per-key presence checks so callers can clear
  nested failureDestination fields via cron.update

Note: protocol:check shows missing internalEvents in Swift models - this is
a pre-existing issue unrelated to these changes (upstream sync needed).

* fix(cron): use separate schema for failureDestination and fix type cast

- Create CronFailureDestinationSchema excluding after/cooldownMs fields
- Fix type cast in sendFailureNotificationAnnounce to use CronMessageChannel

* fix(cron): merge global failureDestination with partial job overrides

When job has partial failureDestination config, fall back to global
config for unset fields instead of treating it as a full override.

* fix(cron): avoid forcing announce mode and clear inherited to on mode change

- UI: only include mode in patch if explicitly set to non-default
- delivery.ts: clear inherited 'to' when job overrides mode, since URL
  semantics differ between announce and webhook modes

* fix(cron): preserve explicit to on mode override and always include mode in UI patches

- delivery.ts: preserve job-level explicit 'to' when overriding mode
- UI: always include mode in failureAlert patch so users can switch between announce/webhook

* fix(cron): allow clearing accountId and treat undefined global mode as announce

- UI: always include accountId in patch so users can clear it
- delivery.ts: treat undefined global mode as announce when comparing for clearing inherited 'to'

* Cron: harden failure destination routing and add regression coverage

* Cron: resolve failure destination review feedback

* Cron: drop unrelated timeout assertions from conflict resolution

* Cron: format cron CLI regression test

* Cron: align gateway cron test mock types

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui cli CLI command changes close:not-planned PR close reason gateway Gateway runtime size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants