Skip to content

fix(cron): bound restart catch-up to schedule activation#92398

Closed
amersheeny wants to merge 5 commits into
openclaw:mainfrom
amersheeny:fix/cron-startup-replay-updated-schedule
Closed

fix(cron): bound restart catch-up to schedule activation#92398
amersheeny wants to merge 5 commits into
openclaw:mainfrom
amersheeny:fix/cron-startup-replay-updated-schedule

Conversation

@amersheeny

@amersheeny amersheeny commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #91944.

Cron restart catch-up now replays only schedule slots that occurred after the schedule was activated. The scheduler records state.scheduleActivatedAtMs when a job is created or when its schedule/enabled state changes, and both startup catch-up and error-backoff replay use that boundary.

Existing cron rows are handled by openclaw doctor --fix: the cron preflight first imports legacy cron state, then transactionally stamps rows missing the activation field with the migration time. Runtime reads only the canonical field and fails closed when it is absent, so an upgrade cannot infer and send stale pre-upgrade outbound slots.

Metadata-only edits do not move the boundary. Public create/update inputs cannot inject scheduler-owned activation state.

Verification

  • 146 focused cron, doctor, protocol, and service tests passed locally.
  • Targeted oxfmt and oxlint passed.
  • Azure Linux Crabbox focused proof passed: lease cbx_4ce772051720, run run_6f4988261933, 120 tests.
  • Fresh Codex autoreview: no actionable findings, patch correct at 0.91 confidence.
  • Exact-head GitHub CI is running on 0e30df1f5a45d4a83d095e6cdf559db1ee05eda6.

Real behavior proof

Behavior addressed: An API-updated cron schedule must not replay a slot that predates the schedule activation after a gateway restart.

Real environment tested: The contributor ran a real macOS Gateway with a scratch SQLite cron store, created and edited the job through the real CLI/Gateway RPC path, injected the historical prior-schedule run, and restarted the Gateway. The final repaired head was additionally validated on Azure Linux Crabbox against the focused cron/doctor/SQLite suites.

Exact steps or command run after this patch: Create the cron job through openclaw cron add, change its expression through openclaw cron edit, set the prior-schedule lastRunAtMs in the scratch SQLite row, restart the Gateway, and inspect the job state and missed-job logs. For the final doctor-owned legacy path, remove scheduleActivatedAtMs, run the cron doctor preflight migration, and verify the canonical activation boundary is written before restart evaluation.

Evidence after fix:

real Gateway, activation field present:
  nextRunAtMs: 2026-07-11T14:18:00.000Z (unchanged after restart)
  lastRunAtMs: 2026-05-11T07:18:00.000Z (unchanged; job did not fire)
  new missed-job log lines: 0

final repaired head:
  Azure Linux lease: cbx_4ce772051720
  run: run_6f4988261933
  focused cron/doctor/SQLite tests: 120 passed
  doctor migration transactionally stamps only cron rows missing scheduleActivatedAtMs
  runtime does not infer a legacy activation boundary

Observed result after fix: The updated job remains on its future API-selected schedule after restart. Existing rows receive one conservative doctor-owned activation boundary, preventing stale pre-upgrade outbound replay.

What was not tested: A full version-to-version openclaw update install flow. The real Gateway restart covered the field-present runtime path; the final legacy migration replacement is covered by current-head SQLite integration tests and the Azure Linux run, so a maintainer proof override is applied for that portion.

Risk

This changes restart delivery behavior and persisted cron state. The conservative migration may skip a pre-upgrade missed slot, but it will not send an inferred stale outbound job. No config or environment surface is added.

Release-note context: cron jobs no longer fire slots that predate their current schedule activation after a gateway restart or upgrade. Reported by @suifatt7799-oss in #91944.

@openclaw-barnacle openclaw-barnacle Bot added size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 12, 2026
@clawsweeper

clawsweeper Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge. Reviewed June 30, 2026, 11:06 PM ET / 03:06 UTC.

Summary
The PR adds scheduler-owned cron schedule activation state, uses it to bound restart/backoff catch-up, backfills legacy cron rows in doctor preflight, updates protocol/types, and adds focused cron/doctor/protocol tests.

PR surface: Source +118, Tests +403. Total +521 across 14 files.

Reproducibility: yes. Current main is source-reproducible: cron.update can move nextRunAtMs without recording schedule activation, and restart catch-up still replays the current expression's previous slot solely because it is newer than lastRunAtMs.

Review metrics: 2 noteworthy metrics.

  • Persisted cron state surface: 1 scheduler-owned state field added, 1 doctor backfill added. This is the compatibility-sensitive surface that decides how existing cron rows behave during upgrade and restart catch-up.
  • Protocol cron state surface: 1 response field added, 0 public patch fields added. Consumers can observe the activation boundary, while callers are intentionally prevented from moving the scheduler-owned timestamp.

Stored data model
Persistent data-model change detected: migration/backfill/repair: src/commands/doctor-config-preflight.state-migration.test.ts, migration/backfill/repair: src/commands/doctor-config-preflight.ts, migration/backfill/repair: src/commands/doctor/cron/index.ts, migration/backfill/repair: src/commands/doctor/cron/schedule-activation-migration.test.ts, migration/backfill/repair: src/commands/doctor/cron/schedule-activation-migration.ts, serialized state: src/commands/doctor/cron/schedule-activation-migration.ts, and 3 more. Migration or upgrade compatibility proof is recorded; maintainers should verify it before merge.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

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

Rank-up moves:

  • Rebase onto current main and preserve schedulingInputsChanged for activation stamping.
  • Refresh focused cron restart-catchup, ops, doctor migration, and protocol schema validation after the rebase.

Risk before merge

  • [P1] The PR intentionally changes persisted cron runtime state and doctor upgrade behavior: legacy cron rows missing an activation boundary are stamped at migration time, which prevents stale replay but can skip a pre-upgrade missed slot.
  • [P1] The current PR head is conflicting with main; resolving the conflict by keeping the PR's scheduleChanged/enabledChanged condition would undo current-main idempotent re-save behavior and can suppress or move due runs.
  • [P1] The changed predicate controls scheduled outbound delivery on restart and error backoff, so an incorrect activation boundary can either skip a legitimate catch-up or send a stale job at the wrong time.

Maintainer options:

  1. Rebase with the current scheduling-input guard (recommended)
    Resolve the current conflict by placing the activation-boundary update under current main's schedulingInputsChanged path so idempotent schedule/enabled re-saves keep preserving due slots.
  2. Accept the conservative doctor boundary
    Maintainers can intentionally accept migration-time activation for legacy rows, knowing it favors skipping ambiguous pre-upgrade catch-up over sending stale outbound jobs.
  3. Pause if upgrade semantics are not accepted
    If maintainers do not want migration-time reactivation as the upgrade policy, pause this PR and choose a different persisted cron state model before merging.

Next step before merge

  • [P2] Needs maintainer-directed rebase and acceptance of persisted cron upgrade semantics; the remaining blocker is not a safe autonomous repair lane.

Security
Cleared: No concrete security or supply-chain regression was found; the diff changes cron scheduler state, protocol schema, and doctor migration code without new dependencies, secrets handling, or code-execution sources.

Review findings

  • [P2] Gate activation updates on real scheduling changes — src/cron/service/ops.ts:548-551
Review details

Best possible solution:

Rebase onto current main, stamp scheduleActivatedAtMs only when scheduling inputs actually change, keep the doctor/protocol ownership model, and have a maintainer explicitly accept the conservative upgrade boundary before landing.

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

Yes. Current main is source-reproducible: cron.update can move nextRunAtMs without recording schedule activation, and restart catch-up still replays the current expression's previous slot solely because it is newer than lastRunAtMs.

Is this the best way to solve the issue?

No, not as currently mergeable. The scheduler-owned activation boundary plus doctor backfill is the right fix shape, but the branch must be rebased to preserve current main's idempotent-update guard before maintainers accept the upgrade semantics.

Full review comments:

  • [P2] Gate activation updates on real scheduling changes — src/cron/service/ops.ts:548-551
    Current main now preserves due slots when the UI resubmits an unchanged schedule or enabled value. This PR still stamps scheduleActivatedAtMs whenever the patch merely includes schedule or enabled, so a rebase that keeps this condition would move the activation boundary on idempotent re-saves and can suppress legitimate catch-up. Use the current schedulingInputsChanged predicate for both recompute and activation stamping.
    Confidence: 0.86

Overall correctness: patch is incorrect
Overall confidence: 0.86

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • add status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Override: A maintainer applied proof: override for this PR.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦐 gold shrimp, so this older rating label is no longer current.
  • remove status: 👀 ready for maintainer look: Current PR status label is status: ⏳ waiting on author.

Label justifications:

  • P1: The PR targets a real user-facing cron restart regression that can send scheduled outbound work at the wrong time after a schedule update and upgrade restart.
  • merge-risk: 🚨 compatibility: The diff changes persisted cron state and doctor upgrade behavior for existing rows missing the new activation boundary.
  • merge-risk: 🚨 message-delivery: The changed restart/backoff predicate decides whether scheduled outbound cron jobs are sent, skipped, or deferred after restart.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Override: A maintainer applied proof: override for this PR.
Evidence reviewed

PR surface:

Source +118, Tests +403. Total +521 across 14 files.

View PR surface stats
Area Files Added Removed Net
Source 8 136 18 +118
Tests 6 407 4 +403
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 14 543 22 +521

What I checked:

  • Repository policy applied: Root AGENTS.md was read fully; its persisted-state, doctor-migration, compatibility-risk, and whole-surface PR review guidance apply because this PR changes cron runtime state and upgrade behavior. (AGENTS.md:1, fa3c9de45965)
  • Current main still has unbounded missed-slot replay: Current main still computes the previous cron slot and returns true solely when previousRunAtMs is newer than lastRunAtMs, without an activation-boundary check. (src/cron/service/timer.ts:1495, fa3c9de45965)
  • Current main added an idempotent-update invariant: Current main now preserves due slots on idempotent schedule/enabled re-saves by gating recompute on schedulingInputsChanged, which the PR must preserve when rebased. (src/cron/service/ops.ts:554, fa3c9de45965)
  • Latest release is still affected: v2026.6.11 still has the same previousRunAtMs > lastRunAtMs predicate and no scheduleActivatedAtMs field in CronJobState. (src/cron/service/timer.ts:1495, e085fa1a3ffd)
  • PR adds the canonical replay boundary: The PR head introduces hasReplayableMissedCronSlot and fails closed when scheduleActivatedAtMs is absent, so it addresses the central stale replay path rather than being obsolete. (src/cron/service/timer.ts:1498, 6a00c36e6667)
  • PR needs a rebase correction: The PR head stamps scheduleActivatedAtMs whenever a patch includes schedule/enabled, but current main narrowed that condition to actual scheduling input changes to preserve due slots on idempotent re-saves. (src/cron/service/ops.ts:548, 6a00c36e6667)

Likely related people:

  • vincentkoc: Blame/current-main history shows recent cron update semantics work, he is assigned on the PR, and he authored the PR commits that add the doctor migration and protect activation ownership. (role: recent area contributor and likely follow-up owner; confidence: high; commits: cb00045afca9, a5755cb08706, 6a00c36e6667; files: src/cron/service/ops.ts, src/commands/doctor/cron/schedule-activation-migration.ts, src/cron/service/jobs.ts)
  • Takhoffman: GitHub and local history show earlier commits establishing and narrowing cron restart catch-up replay semantics around the implicated timer path. (role: feature-history contributor; confidence: high; commits: 79d00ae39860, 28dc2e8a400c, cc5dad81bc70; files: src/cron/service/timer.ts, src/cron/service.restart-catchup.test.ts)
  • steipete: History shows adjacent missed-cron restart staggering, catch-up flow splitting, and cron persistence-churn work on the same scheduler surfaces. (role: adjacent area contributor; confidence: medium; commits: 96d17f3cb191, e86b38f09d85, 9267e694f7e7; files: src/cron/service/timer.ts, src/cron/service.restart-catchup.test.ts, src/cron/store.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.
Review history (1 earlier review cycle)
  • reviewed 2026-06-23T06:56:02.487Z sha 6a00c36 :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label Jun 12, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 12, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 12, 2026
@amersheeny

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

The previous review run failed to execute ("Review failed before ClawSweeper could summarize the requested change") and overwrote the prior completed verdict with an off-meta/needs-proof fallback. CI is green, the branch is mergeable/clean against latest main, and the Real behavior proof check passes — please re-run so the durable verdict reflects the actual patch.

@clawsweeper

clawsweeper Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@amersheeny

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

The prior re-review run (clawsweeper run 27493343782) has been stalled in in_progress for ~21 min with no progress since 08:32:34, so the durable verdict is still showing the earlier crashed-review fallback. CI is green, the branch is mergeable/clean, and the Real behavior proof check passes — please re-run so the verdict reflects the actual patch (fa84280).

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 14, 2026
@vincentkoc vincentkoc self-assigned this Jun 14, 2026
@amersheeny

Copy link
Copy Markdown
Contributor Author

Noting the competing implementation #92305 (@fsdwen) for this same issue, since the review flagged that maintainers should pick one cron state model before landing either. A factual comparison to help that decision:

Both PRs guard restart catch-up so a slot computed from a newly-updated cron expression isn't replayed as "missed." The difference is the state each relies on:

The decisive behavioral difference is coverage of the reported scenario, where the user updated the schedule and then upgraded:

Where #92305 is genuinely better: a metadata-only edit (rename/description) doesn't move scheduleUpdatedAtMs, so it won't suppress a legitimate best-effort catch-up; #92398's reuse of updatedAtMs does suppress that catch-up. That suppression is in the fail-safe direction (skips a late send rather than firing at the wrong time) and is documented inline, but getting full precision on metadata-only edits does require the dedicated timestamp #92305 adds.

So the trade-off: #92398 is leaner, adds no cron state, and fixes the as-reported upgrade case, at the cost of a documented fail-safe on metadata-only edits. #92305 is more precise on metadata-only edits, at the cost of a new persisted field (without a doctor migration) that doesn't cover already-updated jobs. Happy to align this PR with whichever state model maintainers prefer.

@amersheeny
amersheeny requested a review from a team as a code owner June 15, 2026 08:39
@amersheeny

Copy link
Copy Markdown
Contributor Author

Reconsidering my earlier comparison: I was over-weighting "no new field = leaner" and under-crediting precision — candidly, a slant toward my own approach. A single optional state field that makes the catch-up boundary exact is cheap and pays for itself; "additional field = expense" was the wrong frame.

So I've reworked this PR onto the dedicated-field design (state.scheduleActivatedAtMs, set only on schedule/enabled changes), which is the semantically correct model and the same approach as #92305. It removes the metadata-only-edit trade-off the previous updatedAtMs proxy carried (a later rename no longer masks a genuinely missed slot).

The one thing I kept from the updatedAtMs approach is a runtime fallback for jobs that predate the field: the reported misfire occurs at the upgrade restart, before any doctor migration could backfill the timestamp, so updatedAtMs is the only activation proxy available then. That fallback is what makes the reported scenario (schedule updated on the old build, then upgraded) actually fix for existing users — which the dedicated field alone does not cover.

Verified via the real gateway in both states (field present and field stripped to simulate a legacy job) — both suppress the pre-activation slot with zero missed-job log lines; details and output in the updated PR description. Net diff is test-heavy; prod change is +28/−7.

No disagreement with #92305's core idea — it's the right one, and credit to @fsdwen for it. The remaining difference is just the legacy fallback; if maintainers prefer to land #92305, adding the same ?? updatedAtMs fallback there would close its existing-jobs gap.

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 15, 2026
@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: L triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed size: M proof: supplied External PR includes structured after-fix real behavior proof. proof: sufficient ClawSweeper judged the real behavior proof convincing. labels Jun 16, 2026
@vincentkoc vincentkoc added the proof: override Maintainer override for the external PR real behavior proof gate. label Jun 16, 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 16, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Land-ready verification for 0e30df1f5a45d4a83d095e6cdf559db1ee05eda6:

  • Local focused proof: 146 cron, doctor, protocol, and service tests passed.
  • Targeted formatting and lint passed.
  • Azure Linux Crabbox proof: provider azure, lease cbx_4ce772051720, run run_6f4988261933; 120 focused cron/doctor/SQLite tests passed.
  • Real Gateway restart proof for the field-present schedule-edit path is copied into the PR body.
  • The final legacy-row path is doctor-owned and covered by current-head SQLite integration tests; proof: override records the explicit maintainer judgment and the untested full version-to-version install gap.
  • Fresh Codex autoreview reported no accepted/actionable findings, confidence 0.91.
  • Exact-head CI failures in check-prod-types, check-test-types, check-additional-boundaries-a, and the extension package boundary reproduce on main at https://github.com/openclaw/openclaw/actions/runs/27599570896. The matching root failures are the Codex bounded-turn declaration errors and three session-snapshot test failures; the PR's cron lanes are clean.

Best-fix verdict: scheduler-owned activation state plus a doctor migration is the canonical fix. Inferring activation from create/update/run timestamps would preserve ambiguous legacy behavior in the runtime path.

@amersheeny
amersheeny force-pushed the fix/cron-startup-replay-updated-schedule branch from 0e30df1 to 989cc7b Compare June 17, 2026 07:26
@amersheeny

Copy link
Copy Markdown
Contributor Author

@vincentkoc heads-up before you land this: I rebased it onto current main (it was ~420 commits behind) to clear the inherited CI red. The failing check-prod-types / check-test-types / check-additional-extension-package-boundary were the upstream extensions/codex/src/app-server/bounded-turn.ts TS2322 errors — since fixed on main by 6d22b8eb242 ("fix(ci): repair main type and lint checks").

New head is 989cc7b446, CI fully green (138 checks, 0 failures), including the Real behavior proof gate. The cron change itself is unchanged — the same 5 commits, replayed cleanly with no conflicts.

One thing to note: your proof: override was recorded against the prior SHA 0e30df1f5a, so it'll need re-applying (or a quick re-verify) on the new green commit. Sorry for moving the SHA under your land-ready pass — happy to do whatever's easiest on your side. Thanks again.

@amersheeny

Copy link
Copy Markdown
Contributor Author

@vincentkoc following up here since you'd already done the land-ready pass — all three of my open PRs are now green on current main: this one (#92398, new head 989cc7b446), plus #92399 and #93547.

The two siblings are stuck purely on ClawSweeper's Codex capacity, not on the changes: the re-review keeps failing at the "Wait for exact-review Codex capacity" step (latest run 27692800832 failed 2026-06-17 13:54 UTC after the 20-min wait). The diffs are byte-identical to the SHAs ClawSweeper already reviewed positively — #92399 ("ready for maintainer review / proof sufficient") and #93547 ("correct fix, supersedes #93315").

Two asks when you have a moment:

  1. For this PR — re-apply the proof: override on the new green SHA so your land-ready pass carries over (the rebase moved it off 0e30df1f5a).
  2. For fix(llm): collapse cumulative openai-responses message snapshots instead of concatenating [AI-assisted] #92399 / fix(acp): recover stale persistent sessions by structured resume-required code [AI-assisted] #93547 — could you land them or point me to the right reviewer? They're green and unchanged from the prior positive verdicts; a fresh bot pass is just blocked by capacity.

Happy to provide whatever helps. Thanks for the time on this batch.

amersheeny and others added 5 commits June 22, 2026 18:53
…on write

Startup catch-up classified a cron job as "missed" whenever the current
expression's previous occurrence was newer than lastRunAtMs, ignoring a
nextRunAtMs that a cron.update deliberately moved into the future. After
an API schedule change, the new expression's previous slot never existed
while that schedule was live, so gateway restarts (e.g. during openclaw
update) fired the job immediately after the startup deferral.

Bound the replay to slots that fell after updatedAtMs: writers that
recompute the schedule (update, run outcomes, interrupted-run marking)
bump it, so older slots were scheduled away on purpose. Applies to both
startup replay and error-backoff slot deferral, which shared the same
unbounded comparison.

Fixes openclaw#91944. Reported by @suifatt7799-oss.
…recisely

Replaces the updatedAtMs-only replay guard with a dedicated
state.scheduleActivatedAtMs, recorded whenever an update recomputes
nextRunAtMs from a schedule/enabled change (and at job creation). The
catch-up guard now skips slots that predate the schedule's activation
exactly, so a later metadata-only edit (rename/description) no longer
masks a genuinely missed slot — removing the fail-safe trade-off the
updatedAtMs proxy carried.

Jobs predating the field fall back to updatedAtMs at runtime: the
reported misfire happens at the upgrade restart, before any doctor
migration could backfill the field, and updatedAtMs is the only
activation proxy available then (it over-approximates activation, so the
fallback only ever over-suppresses in the safe direction). This keeps
the reported pre-upgrade-update scenario fixed for existing users while
giving post-upgrade updates exact precision.

Regression tests cover the field path, the legacy fallback, the
metadata-edit precision win, and that update() records the timestamp
only on schedule/enabled changes.
…e schema

The new scheduleActivatedAtMs cron state field was added to the runtime
type and persisted via state_json, but the closed CronJobStateSchema (and
its patch schema) reject unknown properties, so schema-validating
cron.get/list consumers could reject jobs carrying it. Declare the field
in both protocol schemas, matching the existing timestamp fields, and
assert its presence in the schema test.
@amersheeny
amersheeny force-pushed the fix/cron-startup-replay-updated-schedule branch from 989cc7b to 6a00c36 Compare June 23, 2026 06:48
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 6, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This assigned pull request has been automatically marked as stale after being open for 27 days.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jul 10, 2026
@openclaw-barnacle

Copy link
Copy Markdown

Closing due to inactivity.
If you believe this PR should be revived, post in #clawtributors on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

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 commands Command implementations gateway Gateway runtime 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: override Maintainer override for the external PR real behavior proof gate. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: L stale Marked as stale due to inactivity status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v2026.6.5: Cron doctor preflight overwrites API-updated schedules with stale legacy JSON data

3 participants