cron: add command payload execution engine#62789
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 46950d7e32
ℹ️ 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".
Greptile SummaryThis PR adds Two P1 defects were found in the new edit path that need to be addressed before merging. Confidence Score: 3/5Not safe to merge as-is: two P1 defects in the edit path will cause silent data loss (--clear-args no-op) and runtime errors (delivery-flag-only edit corrupts payload kind). The add path and server-side execution are solid, but the edit path has two deterministic correctness defects introduced by this PR: --clear-args is broken end-to-end across schema/normalize/merge layers, and editing delivery settings on an existing command job via CLI flags that feed hasAgentTurnPatch will cause the server to reject the patch. src/cli/cron-cli/register.cron-edit.ts (delivery-flag / hasAgentTurnPatch logic), src/gateway/protocol/schema/cron.ts + src/cron/normalize.ts + src/cron/service/jobs.ts (clear-args null propagation chain)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 514f415dd5
ℹ️ 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".
514f415 to
1d82f02
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1d82f02635
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 93f19a87ef
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 861a320647
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3575acb5e8
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 83a4c1f32a
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 86b151f188
ℹ️ 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".
| const runSessionId = `cron-command:${job.id}`; | ||
| const agentSessionKey = `cron-command:${job.id}`; |
There was a problem hiding this comment.
Generate unique runSessionId for each command delivery
Using runSessionId = \cron-command:${job.id}`makes every run of the same job reuse the same direct-delivery idempotency key indispatchCronDelivery(which keys onrunSessionId), so after one successful send, later runs to the same target can be treated as already delivered and skipped. This causes recurring command jobs to silently stop emitting real notifications while still reporting delivered: true`.
Useful? React with 👍 / 👎.
| return { | ||
| ...result, | ||
| delivered: deliveryResult.delivered, | ||
| deliveryAttempted: deliveryResult.deliveryAttempted, | ||
| }; |
There was a problem hiding this comment.
Propagate dispatch delivery errors into command run outcome
dispatchCronDelivery can return an error result (for example when non-best-effort outbound send fails), but this return path only copies delivered/deliveryAttempted and preserves the original command execution status. In strict delivery mode, command runs can therefore be recorded as ok even though required delivery failed, violating cron’s non-best-effort delivery contract.
Useful? React with 👍 / 👎.
86b151f to
11cde19
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 11cde19954
ℹ️ 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".
| runStartedAt: Date.now(), | ||
| runEndedAt: Date.now(), |
There was a problem hiding this comment.
Pass real command run times to delivery dispatch
runStartedAt/runEndedAt are both stamped after runCronCommandJob returns, so delivery dispatch sees the command as starting at completion time. dispatchCronDelivery uses runStartedAt for stale-delivery gating (isStaleCronDelivery), so long-running command jobs (for example, >3h) can have announce delivery skipped as “stale” even when they actually started on schedule.
Useful? React with 👍 / 👎.
| type CronCommandPayloadPatch = { | ||
| kind: "command"; | ||
| } & Partial<CronCommandPayloadFields>; |
There was a problem hiding this comment.
Allow null args in command payload patch type
The runtime contract now supports clearing command args with args: null (CLI emits it and normalization/merge logic handles it), but CronCommandPayloadPatch still types args as string[] | undefined only. This makes typed callers of CronService.update unable to express the documented clear-args behavior without unsafe casts, and leaves the type surface out of sync with the protocol schema.
Useful? React with 👍 / 👎.
11cde19 to
dec0a9d
Compare
dec0a9d to
bdc521f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bdc521f8d1
ℹ️ 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".
| const CronRunsStatusValueSchema = Type.Union([ | ||
| Type.Literal("ok"), | ||
| Type.Literal("error"), | ||
| Type.Literal("skipped"), | ||
| Type.Literal("aborted"), |
There was a problem hiding this comment.
Raise cron.runs statuses maxItems for the new aborted state
This change adds "aborted" to the run-status enum, but CronRunsParamsSchema still constrains statuses to maxItems: 3, so a client selecting all advertised statuses (ok,error,skipped,aborted) is rejected at protocol validation. That creates a wire-contract mismatch introduced by this enum expansion and breaks UI/filter code that serializes all selected status chips.
Useful? React with 👍 / 👎.
| const primaryPlan = resolveCronDeliveryPlan(job); | ||
| const deliveryBestEffort = job.delivery?.bestEffort === true; | ||
| const summaryText = | ||
| typeof result.summary === "string" && result.summary.trim().length > 0 | ||
| ? result.summary.trim() | ||
| : typeof result.outputText === "string" && result.outputText.trim().length > 0 | ||
| ? result.outputText.trim() | ||
| : undefined; | ||
| if (!primaryPlan.requested || !summaryText) { | ||
| return result; |
There was a problem hiding this comment.
Preserve default announce behavior for API-created command jobs
The command execution path now relies on resolveCronDeliveryPlan(job) and returns immediately when requested is false, but resolveCronDeliveryPlan only auto-enables announce for isolated agentTurn jobs. Because cron.add allows omitting delivery, command jobs created through RPC (non-CLI clients) silently skip delivery unless they explicitly set it, unlike existing isolated agent-turn defaults.
Useful? React with 👍 / 👎.
|
Split update: I force-pushed this branch to the backend/runtime/server scope only, and opened the stacked CLI follow-up here: rocdesu#1\n\nThat should make the review boundary match the PR description again. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 260184adfc
ℹ️ 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".
| if (command || hasCommandPayloadHint(next)) { | ||
| return { kind: "command", ...(command ? { command } : {}) } satisfies UnknownRecord; |
There was a problem hiding this comment.
Carry command fields when inferring top-level command payloads
When top-level shorthand input is used (for example via cron-tool flat-parameter recovery), this branch infers payload.kind="command" but only copies command into payload. args and timeoutSeconds stay at the root and are not part of the inferred payload, so command jobs are created with missing command options and can fail gateway validation (validateCronAddParams) due extra top-level fields. This makes top-level command-style create/update inputs behave inconsistently with the existing top-level agentTurn shorthand.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 566c56ade4
ℹ️ 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".
| ? setTimeout(() => { | ||
| timedOut = true; | ||
| child.kill("SIGTERM"); | ||
| }, timeoutMs) |
There was a problem hiding this comment.
Escalate timed-out command processes beyond SIGTERM
When a command exceeds timeoutSeconds, this path only sends SIGTERM and never escalates or guarantees termination. If the child traps or ignores SIGTERM, the process can keep running in the background while cron records the run as finished after the timeout path resolves, which can leak processes and cause overlapping executions on later schedules. Fresh evidence: in this commit the timeout flow still settles the run immediately in the outer cron timeout wrapper, so this callback may not wait for actual child exit.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6bd708dd4d
ℹ️ 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".
| if (abortSignal?.aborted && res.status !== "aborted") { | ||
| return { status: "error", error: timeoutErrorMessage() }; | ||
| } |
There was a problem hiding this comment.
Convert timeout-triggered command aborts into error outcomes
When a command job hits the cron safety timeout without an explicit payload.timeoutSeconds, runCommandJob can return status: "aborted" after the abort signal, and this branch preserves that status instead of converting it to a timeout error. That breaks downstream state handling because applyJobResult only treats "error" as a failure path: one-shot at jobs are not disabled/retried correctly and recurring jobs skip error backoff/failure-alert behavior after timing out. In practice, a long-running command can repeatedly time out but be recorded as non-error aborted runs.
Useful? React with 👍 / 👎.
|
Push update:
For reviewer context: earlier automated comments about |
|
Reviewer nudge: this branch has been force-pushed and re-scoped to backend/runtime/server only for cron command payload execution. The CLI follow-up is split out to rocdesu#1, so earlier automated comments about Locally re-verified on the current branch:
Would appreciate a fresh review on the current narrowed scope. |
|
@steipete @brunolorente @openclaw/secops quick nudge for a fresh look on the current narrowed diff when convenient. 🙏 |
|
Thanks for the idea. I checked the current extension path, and this is a better fit for the ClawHub/community tide pool than OpenClaw core. This PR adds a first-class core cron Best possible solution: Close this core PR and move the feature to a ClawHub/native plugin: store plugin-owned command job config, use existing cron scheduling or a plugin-owned Gateway service, short-circuit cron-triggered agent turns before model execution with What I checked:
So I’m closing this as a scope-fit item for the plugin/community path rather than keeping it open as an OpenClaw core request. Codex review notes: model gpt-5.5, reasoning high; reviewed against e476523082a6. |
Summary
This PR now contains the backend/runtime/server portion only for cron command payload execution.
payload.kind="command"support to cron gateway protocol schema + normalizationNotes
rocdesu/openclaw#1.