Skip to content

fix(cron): recover flat params when LLM omits job wrapper#12124

Merged
tyler6204 merged 4 commits intomainfrom
fix/cron-flat-params-recovery
Feb 9, 2026
Merged

fix(cron): recover flat params when LLM omits job wrapper#12124
tyler6204 merged 4 commits intomainfrom
fix/cron-flat-params-recovery

Conversation

@tyler6204
Copy link
Member

@tyler6204 tyler6204 commented Feb 8, 2026

Problem

Closes #11310.

Non-frontier models (e.g. Grok) flatten job properties to the top level alongside action instead of nesting them inside the job parameter. The cron tool's add handler schema uses an opaque Type.Object({}, { additionalProperties: true }) for job, giving these models no structural hint — so they place name, schedule, payload, sessionTarget, etc. as siblings of action.

This causes either:

  • Error('job required') when params.job is undefined
  • Gateway validation failure (4 missing required properties) when params.job is {} and the actual fields are ignored at the top level

Fix

Added a flat-params recovery step at the top of the case 'add': handler in src/agents/tools/cron-tool.ts:

  1. When params.job is missing or an empty object, scan params for recognised job property names (name, schedule, payload, sessionTarget, enabled, message, text, model, thinking, etc.)
  2. If at least one meaningful signal field is found (schedule, payload, message, or text), construct a synthetic job object from those fields
  3. The synthetic job then flows into the existing normalizeCronJobCreate path which handles kind inference, defaults, and validation

If params.job already contains properties, the existing behavior is completely unchanged.

Tests

Added 5 test cases covering:

  • Flat params with no job wrapper → recovered successfully
  • Empty job: {} + flat params → recovered successfully
  • Message shorthand at top level → inferred as agentTurn payload
  • No meaningful fields present → still throws 'job required'
  • Non-empty job takes precedence over flat params

Greptile Overview

Greptile Summary

This PR updates the cron tool’s add handler to recover from “flat” LLM tool params where job fields (e.g. name, schedule, payload) are provided at the top level instead of under job. When params.job is missing or {}, it reconstructs a synthetic job from recognized keys (only if there’s a clear signal like schedule/payload/message/text) and then proceeds through the existing normalizeCronJobCreate path. The change is covered by 5 new tests validating recovery, shorthand message inference, the “no signal” error path, and that a non-empty nested job continues to take precedence.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk.
  • Changes are narrowly scoped to the cron tool add path, gated to only run when params.job is missing or empty, and further gated on strong intent signals (schedule/payload/message/text). Existing behavior for non-empty job is preserved, and new unit tests cover the main recovery and precedence cases. No broader code paths or schemas are modified.
  • No files require special attention

Context used:

  • Context from dashboard - CLAUDE.md (source)
  • Context from dashboard - AGENTS.md (source)

@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling maintainer Maintainer-authored PR labels Feb 8, 2026
@tyler6204 tyler6204 self-assigned this Feb 8, 2026
@tyler6204 tyler6204 force-pushed the fix/cron-flat-params-recovery branch from 5683066 to 2fc658d Compare February 9, 2026 06:57
Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params
Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.
@tyler6204 tyler6204 force-pushed the fix/cron-flat-params-recovery branch from 2fc658d to 03f3c74 Compare February 9, 2026 07:08
@tyler6204 tyler6204 merged commit 07375a6 into main Feb 9, 2026
24 checks passed
@tyler6204 tyler6204 deleted the fix/cron-flat-params-recovery branch February 9, 2026 07:10
@tyler6204
Copy link
Member Author

Merged via squash.

Thanks @tyler6204!

yeboster pushed a commit to yeboster/openclaw that referenced this pull request Feb 9, 2026
…2124)

* fix(cron): recover flat params when LLM omits job wrapper (openclaw#11310)

Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params

* fix(cron): floor nowMs to second boundary before croner lookback

Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.

* fix: add changelog entries for cron fixes (openclaw#12124) (thanks @tyler6204)

* test: stabilize warning filter emit assertion (openclaw#12124) (thanks @tyler6204)
NikolasP98 pushed a commit to NikolasP98/openclaw that referenced this pull request Feb 9, 2026
…2124)

* fix(cron): recover flat params when LLM omits job wrapper (openclaw#11310)

Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params

* fix(cron): floor nowMs to second boundary before croner lookback

Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.

* fix: add changelog entries for cron fixes (openclaw#12124) (thanks @tyler6204)

* test: stabilize warning filter emit assertion (openclaw#12124) (thanks @tyler6204)
NikolasP98 added a commit to NikolasP98/openclaw that referenced this pull request Feb 9, 2026
Integrated upstream improvements:
- CRITICAL: Fix bundled hooks broken since 2026.2.2 (openclaw#9295)
- Grok web search provider (xAI) with inline citations
- Telegram video note support with tests and docs
- QMD model cache sharing optimization (openclaw#12114)
- Context overflow false positive fix (openclaw#2078)
- Model failover 400 status handling (openclaw#1879)
- Dynamic config loading per-message (openclaw#11372)
- Gateway post-compaction amnesia fix (openclaw#12283)
- Skills watcher: ignore Python venvs and caches
- Telegram send recovery from stale thread IDs
- Cron job parameter recovery (openclaw#12124)
- Auto-reply weekday timestamps (openclaw#12438)
- Utility consolidation refactoring (PNG, JSON, errors)
- Cross-platform test normalization (openclaw#12212)
- macOS Nix defaults support (openclaw#12205)

Preserved DEV enhancements:
- Docker multi-stage build with enhanced tooling (gh, gog, obsidian-cli, uv, nano-pdf, mcporter, qmd)
- Comprehensive .env.example documentation (371 lines)
- Multi-environment docker-compose support (DEV/PRD)
- GOG/Tailscale integration
- Fork-sync and openclaw-docs skills
- UI config editor (Svelte)
- Fork workflow documentation

Merge strategy: Cherry-picked 22 upstream commits, preserved DEV Docker architecture.
Docker files unchanged: Dockerfile, docker-compose.yml, docker-setup.sh, .env.example

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Ethermious pushed a commit to Ethermious/openclaw that referenced this pull request Feb 9, 2026
…2124)

* fix(cron): recover flat params when LLM omits job wrapper (openclaw#11310)

Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params

* fix(cron): floor nowMs to second boundary before croner lookback

Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.

* fix: add changelog entries for cron fixes (openclaw#12124) (thanks @tyler6204)

* test: stabilize warning filter emit assertion (openclaw#12124) (thanks @tyler6204)
lucasmpramos pushed a commit to butley/openclaw that referenced this pull request Feb 10, 2026
…2124)

* fix(cron): recover flat params when LLM omits job wrapper (openclaw#11310)

Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params

* fix(cron): floor nowMs to second boundary before croner lookback

Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.

* fix: add changelog entries for cron fixes (openclaw#12124) (thanks @tyler6204)

* test: stabilize warning filter emit assertion (openclaw#12124) (thanks @tyler6204)
slathrop referenced this pull request in slathrop/openclaw-js Feb 11, 2026
- Add flat-params recovery in cron add handler for non-frontier models
- Floor nowMs to second boundary before croner lookback (off-by-one fix)
- Add tests for flat params recovery (5 cases)
- Add 6-field cron pattern tests for schedule second-floor fix
- Stabilize warning-filter emit assertion timing (spy emitWarning not stderr)
yeboster pushed a commit to yeboster/openclaw that referenced this pull request Feb 13, 2026
…2124)

* fix(cron): recover flat params when LLM omits job wrapper (openclaw#11310)

Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params

* fix(cron): floor nowMs to second boundary before croner lookback

Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.

* fix: add changelog entries for cron fixes (openclaw#12124) (thanks @tyler6204)

* test: stabilize warning filter emit assertion (openclaw#12124) (thanks @tyler6204)
skyhawk14 pushed a commit to skyhawk14/openclaw that referenced this pull request Feb 13, 2026
…2124)

* fix(cron): recover flat params when LLM omits job wrapper (openclaw#11310)

Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params

* fix(cron): floor nowMs to second boundary before croner lookback

Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.

* fix: add changelog entries for cron fixes (openclaw#12124) (thanks @tyler6204)

* test: stabilize warning filter emit assertion (openclaw#12124) (thanks @tyler6204)
mbelinky pushed a commit that referenced this pull request Feb 14, 2026
* fix(cron): recover flat params when LLM omits job wrapper (#11310)

Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params

* fix(cron): floor nowMs to second boundary before croner lookback

Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.

* fix: add changelog entries for cron fixes (#12124) (thanks @tyler6204)

* test: stabilize warning filter emit assertion (#12124) (thanks @tyler6204)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling maintainer Maintainer-authored PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cron.add tool \"invalid params\" despite valid schema JSON

1 participant

Comments