Skip to content

cron: add command payload execution engine#62789

Closed
rocdesu wants to merge 11 commits into
openclaw:mainfrom
rocdesu:feat/cron-command-payload
Closed

cron: add command payload execution engine#62789
rocdesu wants to merge 11 commits into
openclaw:mainfrom
rocdesu:feat/cron-command-payload

Conversation

@rocdesu

@rocdesu rocdesu commented Apr 8, 2026

Copy link
Copy Markdown

Summary

This PR now contains the backend/runtime/server portion only for cron command payload execution.

  • add payload.kind="command" support to cron gateway protocol schema + normalization
  • execute isolated cron command jobs without shell wrapping
  • preserve timeout/abort behavior for command jobs, including waiting for timed-out children to terminate
  • support command delivery handling and command payload patch/merge semantics
  • add regression coverage for command execution, timeout/termination, delivery, normalization, and run-log filtering

Notes

  • CLI support was intentionally split out to keep this PR reviewable.
  • The stacked CLI follow-up is rocdesu/openclaw#1.

@rocdesu
rocdesu requested a review from a team as a code owner April 8, 2026 00:58
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime cli CLI command changes size: L labels Apr 8, 2026

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/cli/cron-cli/register.cron-edit.ts
Comment thread src/cron/normalize.ts
@greptile-apps

greptile-apps Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds payload.kind="command" support to cron add and cron edit CLI commands, wires up the runCommandJob dep in the gateway's cron service, and extends the gateway protocol schema and normalization layer to handle the new payload type.

Two P1 defects were found in the new edit path that need to be addressed before merging.

Confidence Score: 3/5

Not 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)

Vulnerabilities

  • Command execution uses spawn with shell: false and an explicit args array, so shell injection via args is not possible. The command string is passed directly as the executable path/name; users who can create cron jobs already have the same OS-level permissions, so this is within the expected trust boundary.
  • No secrets are logged or exposed in the new code paths.
  • No other security concerns identified.

Comments Outside Diff (1)

  1. src/cli/cron-cli/register.cron-edit.ts, line 200-246 (link)

    P1 Delivery-flag-only edit corrupts command payload kind

    When a user edits delivery settings on an existing command payload job using --announce (or --channel, --to, --account, --best-effort-deliver) without any --command flag, hasDeliveryModeFlag is true, which forces hasAgentTurnPatch = true. The else if (hasAgentTurnPatch) branch then sets patch.payload = { kind: "agentTurn" } (no message). On the server, mergeCronPayload sees a kind mismatch and calls buildPayloadFromPatch, which throws 'cron.update payload.kind="agentTurn" requires message'. The edit fails and the delivery config is never updated.

    Delivery flags should not be included in the hasAgentTurnPatch condition used to set the payload kind. The delivery update (line 273) already runs independently of the payload block; the payload block should only trigger when a payload-specific flag is present.

    const hasAgentTurnPatch =
      typeof opts.message === "string" ||
      Boolean(model) ||
      Boolean(thinking) ||
      typeof opts.lightContext === "boolean" ||
      typeof opts.tools === "string" ||
      opts.clearTools;
    

    The delivery fields (hasDeliveryModeFlag, hasDeliveryTarget, hasDeliveryAccount, hasBestEffort) can stay in the delivery block at line 273 without being part of the payload-kind discriminator.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/cli/cron-cli/register.cron-edit.ts
    Line: 200-246
    
    Comment:
    **Delivery-flag-only edit corrupts command payload kind**
    
    When a user edits delivery settings on an existing command payload job using `--announce` (or `--channel`, `--to`, `--account`, `--best-effort-deliver`) without any `--command` flag, `hasDeliveryModeFlag` is `true`, which forces `hasAgentTurnPatch = true`. The `else if (hasAgentTurnPatch)` branch then sets `patch.payload = { kind: "agentTurn" }` (no message). On the server, `mergeCronPayload` sees a kind mismatch and calls `buildPayloadFromPatch`, which throws `'cron.update payload.kind="agentTurn" requires message'`. The edit fails and the delivery config is never updated.
    
    Delivery flags should not be included in the `hasAgentTurnPatch` condition used to set the payload kind. The delivery update (line 273) already runs independently of the payload block; the payload block should only trigger when a payload-specific flag is present.
    
    ```
    const hasAgentTurnPatch =
      typeof opts.message === "string" ||
      Boolean(model) ||
      Boolean(thinking) ||
      typeof opts.lightContext === "boolean" ||
      typeof opts.tools === "string" ||
      opts.clearTools;
    ```
    
    The delivery fields (`hasDeliveryModeFlag`, `hasDeliveryTarget`, `hasDeliveryAccount`, `hasBestEffort`) can stay in the delivery block at line 273 without being part of the payload-kind discriminator.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/cli/cron-cli/register.cron-edit.ts
Line: 200-246

Comment:
**Delivery-flag-only edit corrupts command payload kind**

When a user edits delivery settings on an existing command payload job using `--announce` (or `--channel`, `--to`, `--account`, `--best-effort-deliver`) without any `--command` flag, `hasDeliveryModeFlag` is `true`, which forces `hasAgentTurnPatch = true`. The `else if (hasAgentTurnPatch)` branch then sets `patch.payload = { kind: "agentTurn" }` (no message). On the server, `mergeCronPayload` sees a kind mismatch and calls `buildPayloadFromPatch`, which throws `'cron.update payload.kind="agentTurn" requires message'`. The edit fails and the delivery config is never updated.

Delivery flags should not be included in the `hasAgentTurnPatch` condition used to set the payload kind. The delivery update (line 273) already runs independently of the payload block; the payload block should only trigger when a payload-specific flag is present.

```
const hasAgentTurnPatch =
  typeof opts.message === "string" ||
  Boolean(model) ||
  Boolean(thinking) ||
  typeof opts.lightContext === "boolean" ||
  typeof opts.tools === "string" ||
  opts.clearTools;
```

The delivery fields (`hasDeliveryModeFlag`, `hasDeliveryTarget`, `hasDeliveryAccount`, `hasBestEffort`) can stay in the delivery block at line 273 without being part of the payload-kind discriminator.

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

---

This is a comment left during a code review.
Path: src/gateway/protocol/schema/cron.ts
Line: 172-175

Comment:
**`--clear-args` is silently a no-op for command payload patch**

The CLI sends `payload.args = null` when `--clear-args` is passed. Three layers prevent this from working:

1. **Protocol schema** (here): `args` in `CronPayloadPatchSchema` is typed as `Type.Array(Type.String())``null` is not accepted, unlike `toolsAllow` in the agentTurn variant which explicitly uses `Type.Union([Type.Array(Type.String()), Type.Null()])`.
2. **Normalize layer** (`src/cron/normalize.ts:223-230`): `normalizeTrimmedStringArray(next.args)` is called without `{ allowNull: true }`, so `null` normalises to `undefined` and the key is deleted from the patch.
3. **Merge layer** (`src/cron/service/jobs.ts:672`): `if (Array.isArray(patch.args))` never fires for `null`, so existing args are silently preserved.

The fix needs to mirror the `toolsAllow` pattern used for agentTurn:

```suggestion
  cronCommandPayloadSchema({
    command: Type.Optional(NonEmptyString),
    args: Type.Optional(Type.Union([Type.Array(Type.String()), Type.Null()])),
  }),
```

Additionally, `src/cron/normalize.ts` should call `normalizeTrimmedStringArray(next.args, { allowNull: true })`, and `mergeCronPayload` in `src/cron/service/jobs.ts` should add `else if (patch.args === null) { delete next.args; }` after the `Array.isArray` check.

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

---

This is a comment left during a code review.
Path: src/cli/cron-cli/register.cron-add.ts
Line: 226-239

Comment:
**Error messages still say "agentTurn" after command payload support was added**

Both error messages mention `agentTurn` as the required payload kind, but the checks now apply equally to command payload jobs (any non-systemEvent isolated job). A user passing `--announce` with `--command` on a main session, or passing `--account` with a command job, will see a confusing error.

```suggestion
            throw new Error(
              "--announce/--no-deliver require an isolated/current/session non-systemEvent job.",
            );
```

And for line 238:
```
throw new Error("--account requires a non-main, non-systemEvent job with delivery.");
```

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

Reviews (1): Last reviewed commit: "cron: add command payload cli support" | Re-trigger Greptile

Comment thread src/gateway/protocol/schema/cron.ts
Comment thread src/cli/cron-cli/register.cron-add.ts

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/cli/cron-cli/register.cron-edit.ts
Comment thread src/cron/service/jobs.ts
@rocdesu
rocdesu force-pushed the feat/cron-command-payload branch from 514f415 to 1d82f02 Compare April 8, 2026 01:24

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/cron/service/timer.ts

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/cron/service/timer.ts
Comment thread src/gateway/server-cron.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/cron/types.ts

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/gateway/protocol/schema/cron.ts
Comment thread src/cli/cron-cli/register.cron-edit.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/gateway/server-cron.ts
Comment thread src/gateway/server-cron.ts

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/gateway/server-cron.ts Outdated
Comment on lines +498 to +499
const runSessionId = `cron-command:${job.id}`;
const agentSessionKey = `cron-command:${job.id}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

Comment on lines +530 to +534
return {
...result,
delivered: deliveryResult.delivered,
deliveryAttempted: deliveryResult.deliveryAttempted,
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

@rocdesu
rocdesu force-pushed the feat/cron-command-payload branch from 86b151f to 11cde19 Compare April 9, 2026 03:11

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/gateway/server-cron.ts Outdated
Comment on lines +516 to +517
runStartedAt: Date.now(),
runEndedAt: Date.now(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Comment thread src/cron/types.ts Outdated
Comment on lines +131 to +133
type CronCommandPayloadPatch = {
kind: "command";
} & Partial<CronCommandPayloadFields>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

@rocdesu
rocdesu force-pushed the feat/cron-command-payload branch from 11cde19 to dec0a9d Compare April 11, 2026 02:15
@rocdesu
rocdesu force-pushed the feat/cron-command-payload branch from dec0a9d to bdc521f Compare April 11, 2026 02:19
@openclaw-barnacle openclaw-barnacle Bot added the channel: discord Channel integration: discord label Apr 11, 2026
@rocdesu rocdesu changed the title cron: add CLI support for command payload jobs cron: add command payload execution engine Apr 11, 2026

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment on lines 64 to +68
const CronRunsStatusValueSchema = Type.Union([
Type.Literal("ok"),
Type.Literal("error"),
Type.Literal("skipped"),
Type.Literal("aborted"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Comment on lines +461 to +470
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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

@rocdesu

rocdesu commented Apr 11, 2026

Copy link
Copy Markdown
Author

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.

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/cron/normalize.ts
Comment on lines +343 to +344
if (command || hasCommandPayloadHint(next)) {
return { kind: "command", ...(command ? { command } : {}) } satisfies UnknownRecord;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment on lines +286 to +289
? setTimeout(() => {
timedOut = true;
child.kill("SIGTERM");
}, timeoutMs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

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

Copy link
Copy Markdown
Contributor

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: 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".

Comment thread src/cron/service/timer.ts
Comment on lines +1292 to +1294
if (abortSignal?.aborted && res.status !== "aborted") {
return { status: "error", error: timeoutErrorMessage() };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

@rocdesu

rocdesu commented Apr 11, 2026

Copy link
Copy Markdown
Author

Push update:

  • removed unrelated diff drift so this branch is now cron/gateway-only
  • current PR scope remains backend/runtime/server only; CLI follow-up lives in rocdesu/openclaw#1
  • locally verified:
    • node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts src/gateway/server.cron.test.ts src/gateway/protocol/cron-validators.test.ts
    • node scripts/test-projects.mjs src/cron/normalize.test.ts src/cron/run-log.test.ts

For reviewer context: earlier automated comments about cron add / cron edit CLI issues were against the pre-split branch state, not the current backend-only diff.

@openclaw-barnacle openclaw-barnacle Bot removed the channel: discord Channel integration: discord label Apr 11, 2026
@rocdesu

rocdesu commented Apr 11, 2026

Copy link
Copy Markdown
Author

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 cron add / cron edit CLI issues are stale relative to the current diff.

Locally re-verified on the current branch:

  • node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts src/gateway/server.cron.test.ts src/gateway/protocol/cron-validators.test.ts
  • node scripts/test-projects.mjs src/cron/normalize.test.ts src/cron/run-log.test.ts

Would appreciate a fresh review on the current narrowed scope.

@rocdesu

rocdesu commented Apr 11, 2026

Copy link
Copy Markdown
Author

@steipete @brunolorente @openclaw/secops quick nudge for a fresh look on the current narrowed diff when convenient. 🙏

@clawsweeper

clawsweeper Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

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 payload.kind="command" execution path, but current main still intentionally limits cron payloads to systemEvent and agentTurn. The requested deterministic command scheduling can be built outside core with the existing native plugin surface: plugins can register Gateway lifecycle hooks and CLI/command surfaces, observe/manage cron jobs, short-circuit cron-triggered agent turns before model execution via before_agent_reply, and run local commands through the public process runtime helper. The PR also expands a security-sensitive Gateway command-execution path and had substantial review feedback around delivery, timeout, and process-lifecycle correctness, so the conservative cleanup path is to move this capability to a ClawHub/plugin implementation rather than merge a new core payload kind.

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 before_agent_reply, execute via runCommandWithTimeout, and only propose a new plugin API if that implementation hits a concrete missing seam.

What I checked:

  • Current cron payload contract excludes command payloads: CronPayload and CronPayloadPatch are only systemEvent or agentTurn on current main; there is no command payload variant to preserve or close as implemented. (src/cron/types.ts:114, e476523082a6)
  • Current cron validation rejects command-style isolated payloads: Current main requires isolated/current/session cron jobs to use payload.kind="agentTurn", so the PR's core behavior is not already present. (src/cron/service/jobs.ts:172, e476523082a6)
  • Current execution path remains agent-turn only: The isolated cron executor returns skipped for any payload kind other than agentTurn, confirming command jobs are not implemented on main. (src/cron/service/timer.ts:1366, e476523082a6)
  • Gateway wire schema excludes command payloads: The cron protocol schema only accepts systemEvent and agentTurn payloads in current main; adding command would be a new core protocol surface, not cleanup of an existing one. (src/gateway/protocol/schema/cron.ts:133, e476523082a6)
  • Product direction favors plugins for optional capabilities: VISION.md says core should stay lean, optional capability should usually ship as plugins, and plugin promotion/discovery belongs in ClawHub. (VISION.md:54, e476523082a6)
  • Existing plugin APIs can host the capability: Plugin docs expose native plugins, runtime side effects, lifecycle hooks, CLI/command registration, before_agent_reply short-circuiting, cron lifecycle observation, and Gateway cron access; the runtime surface also exposes runCommandWithTimeout for command execution without adding a new core payload kind. Public docs: docs/plugins/hooks.md. (docs/plugins/hooks.md:81, e476523082a6)

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.

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 gateway Gateway runtime size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant