Skip to content

feat(cron): split jobs.json into config and runtime state files#63105

Merged
gumadeiras merged 14 commits into
openclaw:mainfrom
Feelw00:feat/split-cron-store-state
Apr 20, 2026
Merged

feat(cron): split jobs.json into config and runtime state files#63105
gumadeiras merged 14 commits into
openclaw:mainfrom
Feelw00:feat/split-cron-store-state

Conversation

@Feelw00

@Feelw00 Feelw00 commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: jobs.json mixes config and runtime state in a single file. Every cron execution rewrites ~10 runtime fields, producing meaningless git diffs even when no configuration changed.
  • Why it matters: Users who track cron config in git get noise commits proportional to the number of scheduled jobs.
  • What changed: saveCronStore now writes config to jobs.json and runtime state to jobs-state.json. loadCronStore merges both files by job ID. Added configUpdatedAtMs to distinguish config modification time from execution time.
  • What did NOT change (scope boundary): persist(), ops.ts control flow, timer.ts execution logic, doctor-cron.ts migration — all unchanged. Split logic is fully encapsulated in store.ts.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

N/A — this is a new feature.

Regression Test Plan (if applicable)

N/A — new feature. Existing tests updated to validate split behavior.

User-visible / Behavior Changes

  • New file jobs-state.json created alongside jobs.json in the cron store directory
  • jobs.json no longer contains runtime state fields (updatedAtMs, state.*) — only state: {} stubs
  • New optional field configUpdatedAtMs on cron jobs, tracking when config was last modified (distinct from execution-time updatedAtMs)
  • Users can now git-track jobs.json and gitignore jobs-state.json without diff noise

Diagram (if applicable)

Before:
[cron runs] -> [jobs.json rewritten with config + runtime] -> [git diff noise]

After:
[cron runs] -> [jobs-state.json rewritten (runtime only)] -> [jobs.json untouched] -> [no git diff]
[config edit] -> [both files rewritten] -> [meaningful git diff]

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Linux
  • Runtime/container: Node 24
  • Model/provider: N/A
  • Integration/channel: N/A
  • Relevant config: default cron store path

Steps

  1. Create a cron job: openclaw cron add --name test --every 1m --message "ping"
  2. Wait for execution or run manually: openclaw cron run <id> --force
  3. Check jobs.json — should contain config only with state: {}
  4. Check jobs-state.json — should contain runtime state
  5. Run again — jobs.json should remain unchanged

Expected

  • jobs.json only changes on config edit (add/update/remove)
  • jobs-state.json updates on every execution

Actual

  • Verified locally: jobs.json stable after runtime-only changes

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

26 tests passing across store, service/store, service/ops, service/timer, doctor-cron. pnpm check and pnpm tsgo green.

Human Verification (required)

  • Verified scenarios: save/load round-trip, runtime-only change (no config file write), config change (both files written), backup on config change only, migration from old single-file format
  • Edge cases checked: empty store, missing state file, orphan state entries, jobs with missing state
  • What you did not verify: Windows EPERM fallback path (tested via mock only), multi-process concurrent access, production gateway integration

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes — old-format jobs.json is auto-migrated on first load
  • Config/env changes? No
  • Migration needed? No — automatic on first execution cycle
  • Downgrade safety: jobs.json retains state: {} stubs. All CronJobState fields are optional, so older versions read empty state without TypeError. Jobs resume with recalculated schedules.

Risks and Mitigations

  • Risk: configUpdatedAtMs backfilled from createdAtMs is inaccurate for jobs modified after creation
    • Mitigation: One-time migration cost. Original config modification timestamps were already lost (overwritten by execution timestamps). createdAtMs is the best available approximation.
  • Risk: Two-file write is not cross-file atomic
    • Mitigation: Each file is individually atomic (tmp + rename). jobs-state.json written first. Runtime state is always recoverable via recomputeNextRuns.

AI Disclosure

  • This PR is AI-assisted (Claude Code / Claude Opus 4.6)
  • Fully tested — pnpm build, pnpm check, pnpm tsgo, and 26 related tests all passing
  • I understand what the code does — the design was iteratively developed through analysis of store.ts, types.ts, ops.ts, timer.ts, and jobs.ts, with multiple rounds of review and self-critique before implementation
  • Codex review — no access to Codex; bot reviews (Greptile 5/5, Codex Connector) addressed and resolved
  • All bot review conversations resolved after addressing

🤖 Generated with Claude Code

@Feelw00
Feelw00 requested a review from a team as a code owner April 8, 2026 10:39

@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: 55e02618eb

ℹ️ 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/store.ts Outdated
@greptile-apps

greptile-apps Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR splits jobs.json into a config file (jobs.json) and a runtime-state file (jobs-state.json) so that cron executions no longer produce git noise when the job configuration is unchanged. The logic is cleanly encapsulated in store.ts: saveCronStore writes config vs. state to separate files guarded by their own cache keys, loadCronStore merges them on read, and backward migration from the single-file format is handled automatically on first save.

The three previously-flagged concerns (state-path derivation for non-.json paths, updatedAtMs undefined for manually-added jobs, and the double-chmod on the temp file) all appear resolved in the current code.

Confidence Score: 5/5

Safe to merge — logic is sound, all three prior P1/P2 concerns are resolved, and the one remaining issue is a minor mtime side-effect that does not affect correctness or git-diff stability.

All previously flagged issues (non-.json state path derivation, undefined updatedAtMs for manually-added jobs, redundant chmod) are addressed in the current code. The new concern (spurious jobs.json mtime touch on first persist in fresh-clone scenario) is P2 only — content is byte-for-byte identical so git shows no diff. Migration, round-trip, and runtime-only-churn paths are covered by tests.

src/cron/store.ts — the migrating + cache-miss interaction described in the inline comment.

Vulnerabilities

No security concerns identified. File permissions are correctly enforced (0o600 on store and state files, 0o700 on directory). No new network calls, secrets handling, or auth surface changes.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/cron/store.ts
Line: 218-248

Comment:
**`jobs.json` silently rewritten on first persist when state file is absent**

When `jobs.json` already exists in the new format (`state: {}`) but `jobs-state.json` does not yet exist, `loadCronStore` sets the config cache but leaves the state cache key unset (no state file → no `serializedStoreCache.set(`${storePath}:state`, ...)`). On the first `persist()` call (e.g., to repair a missing `nextRunAtMs` on startup):

1. `cachedState` is `undefined``stateChanged = true`
2. `!cachedState && state file absent``migrating = true`
3. `configChanged || migrating` is `true` even if `configChanged = false``jobs.json` is re-written with identical content

The file content is byte-for-byte identical so `git diff` stays clean, but the mtime changes. If a file-watcher or external tooling monitors `jobs.json` by mtime, it will trigger a false change on first startup in a fresh clone. The straightforward fix is to prime the state cache when there is no state file, so the first save only triggers a write when content actually differs:

```typescript
// After the "fresh clone or first run" path, prime the state cache to prevent
// a spurious config rewrite on the first persist.
const emptyStateJson = JSON.stringify(extractStateFile(store), null, 2);
serializedStoreCache.set(`${storePath}:state`, emptyStateJson);
```

With this in place, `stateChanged` will be `false` when nothing has changed and `migrating` will not be set unnecessarily.

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

Reviews (3): Last reviewed commit: "fix(cron): backfill updatedAtMs in fresh..." | Re-trigger Greptile

Comment thread src/cron/store.ts
Comment thread src/cron/store.ts
Comment thread src/cron/store.ts
@Feelw00

Feelw00 commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

@greptile review and provide confidence score

@greptile-apps

greptile-apps Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Tip:

Greploops — Automatically fix all review issues by running /greploops in Claude Code. It iterates: fix, push, re-review, repeat until 5/5 confidence.

Use the Greptile plugin for Claude Code to query reviews, search comments, and manage custom context directly from your terminal.

@Feelw00

Feelw00 commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

@greptile review and provide confidence score

@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: 0a8fbdae3d

ℹ️ 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/store.ts
@Daanvdplas

Copy link
Copy Markdown
Contributor

Thank you for this PR!

@Daanvdplas

Copy link
Copy Markdown
Contributor

@Feelw00 are you going to finish this PR? Otherwise I would like to finish it because I really need this feature

@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: dfe96d3bfa

ℹ️ 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/ops.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: 14d8978cbc

ℹ️ 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/store.ts
@Feelw00

Feelw00 commented Apr 15, 2026

Copy link
Copy Markdown
Contributor Author

@Daanvdplas
Hi! I really need this feature too! I've been trying to get feedback from maintainers on Discord but haven't heard back. If you have a way to get this merged or can connect with the maintainers, feel free to take it forward — happy to help however needed.

@Daanvdplas

Copy link
Copy Markdown
Contributor

@Feelw00 perhaps making the CI green would help

@Daanvdplas

Daanvdplas commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

He @gumadeiras you reviewed a PR of me 2 weeks ago, could you maybe review this PR?

@Feelw00 and I would be super excited for this to get merged. It would essentially allow to put cron definitions in github and have runtime state separated in another file not committed to git.

For me at least I have a team working on the same openclaw and thus a clean github repo is super useful.

Thanks in advance!

@Feelw00
Feelw00 force-pushed the feat/split-cron-store-state branch from 7bdec62 to b24451c Compare April 15, 2026 09:30

@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: b24451c7be

ℹ️ 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/store.ts Outdated
@Feelw00
Feelw00 force-pushed the feat/split-cron-store-state branch 2 times, most recently from c47e74b to 5ead5d4 Compare April 20, 2026 11:08
@Daanvdplas

Copy link
Copy Markdown
Contributor

I read somewhere that they were not allowing more features in for a while to focus on security. Changing the name to fix(cron) might help?

@Feelw00

Feelw00 commented Apr 20, 2026

Copy link
Copy Markdown
Contributor Author

@Daanvdplas
Thanks for the suggestion! Actually, a maintainer recently commented on Discord clarifying the current policy. I'll quote it here for context:

We can't GUARANTEE we will merge / review every PR. Even if it's XS and Greptile 5/5. Our focus right now is bugs, critical issues, reliability and stability.
We are frozen on feature work / changes and even past the freeze this week we will take a hard look at anything "new". If you want some fancy feature build and ship your own plugin.

So it's not specifically about security — new features have been intentionally frozen, and while the freeze lifts this week, there's no guarantee anything "new" will actually get merged since it'll be scrutinized closely.
My plan is to wait through this week and ping for feedback first. If there's no response, I'll consider your suggestion.

@Daanvdplas

Copy link
Copy Markdown
Contributor

Agree with your approach, I bet they will want this, it just increases flexibility and allows to manage openclaw instances better through github. So in my view it will increase security even.

@gumadeiras gumadeiras self-assigned this Apr 20, 2026
@openclaw-barnacle openclaw-barnacle Bot added the docs Improvements or additions to documentation label Apr 20, 2026
@gumadeiras
gumadeiras force-pushed the feat/split-cron-store-state branch 2 times, most recently from d1effd9 to acfbf76 Compare April 20, 2026 15:36

@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: acfbf76c81

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cron/store.ts Outdated
@gumadeiras
gumadeiras force-pushed the feat/split-cron-store-state branch 2 times, most recently from 7b0f5dc to 37c2b9b Compare April 20, 2026 15:51
@gumadeiras
gumadeiras force-pushed the feat/split-cron-store-state branch from 3939207 to 470bb25 Compare April 20, 2026 18:22
@gumadeiras
gumadeiras merged commit 4be6ff9 into openclaw:main Apr 20, 2026
6 checks passed
@gumadeiras

Copy link
Copy Markdown
Member

Merged via squash.

Thanks @Feelw00!

@Daanvdplas

Copy link
Copy Markdown
Contributor

Thanks @gumadeiras

@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: 470bb2561f

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cron/store.ts
loongfay pushed a commit to YuanbaoTeam/openclaw that referenced this pull request Apr 21, 2026
…claw#63105)

Merged via squash.

Prepared head SHA: 470bb25
Co-authored-by: Feelw00 <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…claw#63105)

Merged via squash.

Prepared head SHA: 470bb25
Co-authored-by: Feelw00 <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…claw#63105)

Merged via squash.

Prepared head SHA: 470bb25
Co-authored-by: Feelw00 <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…claw#63105)

Merged via squash.

Prepared head SHA: 470bb25
Co-authored-by: Feelw00 <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
…claw#63105)

Merged via squash.

Prepared head SHA: 470bb25
Co-authored-by: Feelw00 <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…claw#63105)

Merged via squash.

Prepared head SHA: 470bb25
Co-authored-by: Feelw00 <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…claw#63105)

Merged via squash.

Prepared head SHA: 470bb25
Co-authored-by: Feelw00 <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Split cron store into definitions + runtime state files

3 participants