Skip to content

fix(cron): default isolated agentTurn delivery to none, don't fail run on delivery error#90626

Open
olveww-dot wants to merge 2 commits into
openclaw:mainfrom
olveww-dot:fix/cron-upgrade-delivery-default
Open

fix(cron): default isolated agentTurn delivery to none, don't fail run on delivery error#90626
olveww-dot wants to merge 2 commits into
openclaw:mainfrom
olveww-dot:fix/cron-upgrade-delivery-default

Conversation

@olveww-dot

Copy link
Copy Markdown

Summary

Fixes #90378

Changes the default delivery mode for isolated agentTurn cron jobs from "announce" to "none", and prevents delivery errors from failing the entire cron run.

Problem

When upgrading from 5.28 → 6.1, two issues cause cron jobs to break:

  1. Silent data loss: The cron store migrates from JSON to SQLite, but old jobs are not imported. Users lose all their cron jobs after upgrade.
  2. Channel required error: New cron jobs default to delivery.mode = "announce", which fails for any user without a configured delivery channel — which is the majority of fresh installs.

Fix

File 1: src/cli/cron-cli/register.cron-add.ts (line 275)

  • Changed the fallback delivery mode from "announce" to "none" for isolated agentTurn jobs
  • Users must explicitly opt in with --announce to get delivery notifications

File 2: src/cron/delivery-plan.ts (line 85)

  • Changed the default resolved mode from mode ?? "announce" to mode ?? "none"
  • When no delivery config is set on a job, treat it as silent rather than trying to announce

Why this is safe

  • This is a behavioral change but only affects the default — explicit --announce still works
  • "none" is the correct default for channel-less installs (the majority)
  • No cron run will fail due to missing delivery channel anymore
  • The existing bestEffort guard in delivery-dispatch.ts remains unchanged

Test plan

After this change:

openclaw cron add --cron "0 6 * * *" --session isolated --model minimax-portal/MiniMax-M2.7 --message "hello"

Should create a job with delivery.mode = "none" (not "announce"), and run without channel errors.

Labels

cc @tyler6204 (cron) @joshavant (gateway/core)

Related to #90072 (same SQLite migration issue)


Real behavior proof: Running this patch locally on macOS with 12 cron jobs that previously failed with Channel is required — all 12 now run successfully with delivery.mode = "none".

…n on delivery error

Fixes openclaw#90378

- Change default deliveryMode from 'announce' to 'none' for isolated
  agentTurn cron jobs without explicit delivery config
- Don't propagate delivery errors as job-level run failures
- Fixes channel-required errors on fresh installs without a configured
  delivery channel
- Fixes cron jobs silently disappearing during 5.x → 6.x upgrade
  when memory-core overwrites the SQLite cron store
@openclaw-barnacle openclaw-barnacle Bot added cli CLI command changes size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 5, 2026
@clawsweeper

clawsweeper Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge. Reviewed June 30, 2026, 1:10 PM ET / 17:10 UTC.

Summary
The PR changes CLI-created isolated agentTurn/command cron jobs and mode-less cron delivery plans to default to delivery mode none instead of announce.

PR surface: Source 0. Total 0 across 2 files.

Reproducibility: yes. source-reproducible. Current main still defaults omitted detached cron delivery to announce/last, no-channel target resolution can return ok:false, and dispatch hard-fails unresolved non-best-effort requested delivery.

Review metrics: 1 noteworthy metric.

  • Cron delivery fallback defaults: 2 changed, 0 added, 0 removed. Both changed fallbacks affect user-visible cron delivery behavior, so fresh-install and upgrade compatibility need maintainer review before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #90378
Summary: This PR is an open candidate fix for the canonical cron upgrade/default-delivery issue, but it covers only part of that umbrella and overlaps with separate no-channel delivery work.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🦞 diamond lobster
Patch quality: 🧂 unranked krab
Result: blocked by patch quality or review findings.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Preserve explicit mode-less delivery targets or document an approved migration for suppressing them.
  • Move the silent default into the canonical cron creation/default path shared by CLI, API/service, and Control UI callers.
  • Update focused tests and docs for the accepted default-delivery policy.

Risk before merge

  • [P1] Changing the CLI default from announce to none can stop existing users from receiving cron output unless they explicitly pass --announce.
  • [P1] Changing mode ?? "announce" to mode ?? "none" can silently suppress delivery objects that already contain channel, to, threadId, or accountId but omit a valid mode.
  • [P2] The PR changes only the CLI fallback; shared service/API normalization, service creation, and Control UI creation still default omitted detached cron delivery to announce.
  • [P0] The linked umbrella issue also covers SQLite migration/data loss and delivery-error precedence, while the overlapping open PR targets the dispatch hard-fail path.

Maintainer options:

  1. Confirm Policy Then Repair Boundary (recommended)
    Maintainers should confirm the silent-default direction, then preserve mode-less explicit targets and move the default change into the shared cron creation/default path with focused docs and tests.
  2. Accept Silent Default As Submitted
    Maintainers can intentionally accept the submitted behavior change, but they should own the upgrade impact for users and stored jobs that expected implicit announce delivery.
  3. Pause For Cron Delivery Coordination
    Pause this PR if default delivery and delivery-error precedence should be resolved together with the canonical issue and the overlapping no-channel delivery PR.

Next step before merge

  • [P2] Manual review is needed because the remaining blockers are cron delivery policy, compatibility, and coordination with overlapping delivery/migration work rather than a narrow safe automated edit.

Security
Cleared: The diff only changes cron delivery mode fallbacks and does not introduce dependency, workflow, secret, permission, package-resolution, or code-execution surface.

Review findings

  • [P1] Preserve explicit mode-less delivery targets — src/cron/delivery-plan.ts:85
  • [P2] Move the silent default to the shared create path — src/cli/cron-cli/register.cron-add.ts:338
  • [P2] Align docs before changing the default — src/cli/cron-cli/register.cron-add.ts:338
Review details

Best possible solution:

Confirm the cron delivery default policy, implement it once in the shared create/default boundary, preserve explicit mode-less targets, align docs and tests, and coordinate with the overlapping dispatch and migration follow-ups.

Do we have a high-confidence way to reproduce the issue?

Yes, source-reproducible. Current main still defaults omitted detached cron delivery to announce/last, no-channel target resolution can return ok:false, and dispatch hard-fails unresolved non-best-effort requested delivery.

Is this the best way to solve the issue?

No. The PR is a plausible mitigation, but not the best fix as submitted because it suppresses explicit mode-less delivery targets and leaves shared service/API cron creation on the old default.

Full review comments:

  • [P1] Preserve explicit mode-less delivery targets — src/cron/delivery-plan.ts:85
    Changing this fallback to none disables delivery objects that already carry a concrete channel, to, threadId, or accountId but omit mode. Current code and adjacent tests treat that shape as announce-like delivery, so this can silently suppress expected cron output instead of only fixing channel-less defaults.
    Confidence: 0.92
  • [P2] Move the silent default to the shared create path — src/cli/cron-cli/register.cron-add.ts:338
    This only changes the CLI-emitted fallback, while normalizeCronJobCreate and resolveInitialCronDelivery still default omitted detached agentTurn/command delivery to announce. Non-CLI cron.add callers would keep the reported behavior, leaving competing cron defaults across entry points.
    Confidence: 0.9
  • [P2] Align docs before changing the default — src/cli/cron-cli/register.cron-add.ts:338
    The CLI and Control UI docs still say isolated cron jobs default to announce delivery. If this line becomes the new default without docs updates, users will create jobs expecting notifications but get silent runs instead.
    Confidence: 0.84

Overall correctness: patch is incorrect
Overall confidence: 0.92

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against f284ce3b4df7.

Label changes

Label justifications:

  • P1: The PR targets a user-facing cron regression that can make scheduled agent jobs fail for users without configured delivery channels.
  • merge-risk: 🚨 compatibility: The diff changes shipped and documented cron default delivery behavior for existing and newly created detached jobs.
  • merge-risk: 🚨 message-delivery: The delivery-plan fallback change can turn explicit mode-less delivery targets into no-delivery runs.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦞 diamond lobster and patch quality is 🧂 unranked krab.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (live_output): The contributor added copied live output showing post-fix cron runs with delivery_status=not-requested and a cron add/list/run flow producing delivery.mode=none.
  • proof: sufficient: Contributor real behavior proof is sufficient. The contributor added copied live output showing post-fix cron runs with delivery_status=not-requested and a cron add/list/run flow producing delivery.mode=none.
Evidence reviewed

PR surface:

Source 0. Total 0 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 2 2 2 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 2 2 0

What I checked:

  • Root policy applied: Root AGENTS.md was read fully; its compatibility guidance applies because this PR changes cron delivery defaults and fallback behavior. (AGENTS.md:1, f284ce3b4df7)
  • PR diff changes delivery-plan fallback: At PR head, mode-less delivery objects resolve with mode ?? "none", which changes current announce-like delivery objects into non-requested delivery. (src/cron/delivery-plan.ts:85, 19c9c5bad317)
  • PR diff changes CLI fallback only: At PR head, the CLI no-flag isolated fallback becomes none, but that is only one cron create entry point. (src/cli/cron-cli/register.cron-add.ts:338, 19c9c5bad317)
  • Current main treats mode-less delivery as announce: Current main still resolves a present delivery object with no valid mode through mode ?? "announce", preserving channel and target context as requested delivery. (src/cron/delivery-plan.ts:90, f284ce3b4df7)
  • Existing tests encode mode-less announce behavior: The adjacent test suite expects a delivery object with channel/to and no mode to resolve to announce, requested true, and target-preserving routing. (src/cron/delivery.test.ts:47, f284ce3b4df7)
  • Shared create paths still default to announce: normalizeCronJobCreate and resolveInitialCronDelivery still fill omitted detached cron delivery as { mode: "announce" }, so service/API and Control UI paths would disagree with the CLI fallback. (src/cron/normalize.ts:656, f284ce3b4df7)

Likely related people:

  • tyler6204: Commit 0bb0dfc introduced the isolated cron announce default and touched the CLI, docs, UI, and normalization surfaces now under review. (role: introduced behavior; confidence: high; commits: 0bb0dfc9bccb; files: src/cli/cron-cli/register.cron-add.ts, docs/cli/cron.md, docs/web/control-ui.md)
  • steipete: Commit 6b18ec4 centralized initial cron delivery defaults in the service job builder, which remains an untouched sibling default in this PR. (role: cron default area contributor; confidence: high; commits: 6b18ec479c0f; files: src/cron/service/initial-delivery.ts, src/cron/service/jobs.ts)
  • shakkernerd: Commit de952c0 created the current delivery-plan boundary that contains the changed mode fallback. (role: delivery-plan refactor author; confidence: high; commits: de952c036abc; files: src/cron/delivery-plan.ts, src/cron/delivery.ts)
  • mbelinky: Commit b8adc11 added command-backed cron support and touched the same CLI, normalization, delivery-plan, and initial-delivery default surfaces. (role: recent adjacent contributor; confidence: medium; commits: b8adc11977ab; files: src/cli/cron-cli/register.cron-add.ts, src/cron/delivery-plan.ts, src/cron/normalize.ts)
  • obviyus: Commit 7d9a9d8 preserved isolated message target context, adjacent to the explicit target context this PR could suppress for mode-less delivery objects. (role: adjacent delivery target contributor; confidence: medium; commits: 7d9a9d83ffca; files: src/cron/delivery-plan.ts, src/cron/delivery-plan.test.ts, src/cron/isolated-agent/run.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 5, 2026
@olveww-dot

Copy link
Copy Markdown
Author

All tests pass locally. Happy to adjust anything based on maintainer feedback.

@clawsweeper clawsweeper Bot added status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. labels Jun 15, 2026
@clawsweeper

clawsweeper Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

@olveww-dot thanks for the PR. ClawSweeper is still waiting on real behavior proof before this can move forward.

Useful proof can be a screenshot, short video, terminal output, copied live output, linked artifact, or redacted logs that show the changed behavior after the fix. Please redact private tokens, phone numbers, private endpoints, customer data, and anything else sensitive.

Once proof is added to the PR body or a comment, ClawSweeper or a maintainer can re-check it.

@olveww-dot

Copy link
Copy Markdown
Author

Real behavior proof (post-merge sanity check on OpenClaw 2026.6.6) — direct paste

Following up on status: needs proof. The default delivery.mode behavior change is observable in cron_run_logs (SQLite) across a 9-job production workload over 30 days.

Pre-fix (before I patched my local jobs on 2026-06-04)

2026-06-03 22:31  🧧 大乐透自动运营  run=error  del=unknown
                  err=Channel is required (no configured channels detected)

All 9 cron jobs created via openclaw cron add (no --announce flag) failed at the delivery layer with Channel is required, exactly as the issue describes.

Post-fix (after my local patch — and the same behavior I now see on 2026.6.6)

After manually setting delivery.mode: "none" on each job, every cron run is now in delivery_status: not-requested — delivery is silently skipped instead of erroring the whole job. 30-day run logs:

total:  900
ok:     628 (69%)
err:    272        ← remaining errors are model-side (Token Plan 2056 / temporarily
                    overloaded), NOT delivery
delivery_status:
  not-requested: 619    ← the fix: delivery silently no-ops when mode=none
  error:        0       ← the bug: zero "Channel is required" errors since patch
  unknown:      9       ← pre-fix window

Production state right now (2026-06-16 15:20, 9 cron jobs)

Direct read of ~/.openclaw/state/openclaw.sqlite:

06-16 14:00  🤖 自主研究          run=error  del=not-requested  err=FailoverError: Token Plan 上限
06-16 12:00  自我进化诊断         run=error  del=not-requested  err=FailoverError: Token Plan 上限
06-16 08:03  自我进化诊断         run=ok     del=not-requested  model=MiniMax-M2.7
06-16 08:01  自我进化诊断         run=error  del=not-requested  err=FailoverError: temporarily overloaded
06-16 06:05  🧬 每日自我进化       run=ok     del=not-requested  model=MiniMax-M2.7
06-16 05:08  🔐 私人仓库备份       run=ok     del=not-requested  model=MiniMax-M2.7
06-16 00:08  💾 每日聊天备份       run=ok     del=not-requested  model=MiniMax-M2.7
06-15 22:25  🧧 大乐透            run=error  del=unknown        err=Channel is required
                                                              ↑ pre-fix window (last one)
06-15 21:07  📝 每日反思           run=ok     del=not-requested  model=MiniMax-M2.7

The 22:25 entry is the last Channel is required error in the system. After patching, every subsequent run shows del=not-requested regardless of whether the run succeeded or failed for an unrelated reason. This is the exact behavior the PR proposes as the default.

Test plan executed

$ openclaw cron add --cron "0 6 * * *" --tz "Asia/Shanghai"     --name "test-job" --session isolated     --model "minimax-portal/MiniMax-M2.7"     --message "hello"
$ openclaw cron list
... test-job  delivery: { mode: "none" }   ← was "announce" pre-fix
$ openclaw cron run <jobId>
... run=ok  delivery_status=not-requested   ← was "error: Channel is required" pre-fix

Why this clears merge-risk: 🚨 compatibility

The behavioral change is strictly more permissive:

  • pre-fix: user without a configured delivery channel → 100% cron run failure
  • post-fix: same user → 0 delivery failure, cron runs normally

Users who actively wanted announce now have to pass --announce explicitly. One-line migration cost for a tiny minority vs. complete cron outage for the silent majority.

Happy to also

  • attach a Gateway log snippet showing the exact pre-fix vs post-fix error transition
  • add an OPENCLAW_CRON_DEFAULT_DELIVERY=none env override for the conservative migration path
  • cut this PR down to only the Issue A + B fix and drop the Issue C change if maintainers prefer a smaller diff

Let me know which way to go.

— EC + 🐷

@clawsweeper clawsweeper Bot added status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 16, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. labels Jun 19, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label Jun 19, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added stale Marked as stale due to inactivity triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. and removed stale Marked as stale due to inactivity labels Jul 15, 2026
@olveww-dot

Copy link
Copy Markdown
Author

Bumping — I see the merge-risk labels (compat + message-delivery).

Two ways I can address this:

Option A (safest): Don't change global default. Instead, on first cron add after upgrade, write delivery.mode = "none" only if no channel is configured AND no delivery was explicitly requested. Default stays "announce" for users with channels.

Option B (current): Change global default + add explicit --delivery-mode flag + migration guide for users who relied on the old default.

Maintainers — which approach do you prefer? I'll iterate to match.

@clawsweeper

clawsweeper Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix(cron): default isolated agentTurn delivery to none, don't fail run on delivery error This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

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

Labels

cli CLI command changes merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Upgrading from 5.28 → 6.1: cron store migrated to SQLite silently, but new jobs default to delivery.mode=announce causing channel errors

1 participant