Skip to content

Cron: auto-cleanup isolated :run: session entries#12570

Closed
gigi-trifle wants to merge 3 commits intoopenclaw:mainfrom
gigi-trifle:fix/cron-run-session-cleanup
Closed

Cron: auto-cleanup isolated :run: session entries#12570
gigi-trifle wants to merge 3 commits intoopenclaw:mainfrom
gigi-trifle:fix/cron-run-session-cleanup

Conversation

@gigi-trifle
Copy link
Copy Markdown

@gigi-trifle gigi-trifle commented Feb 9, 2026

Summary

  • Auto-delete :run: session entries from the session store after isolated cron job completion (default behavior via new cleanup field, defaulting to "delete").
  • Add a background sweeper (every 5 min) that removes stale :run: entries older than 10 minutes, catching any missed by crashes or race conditions.
  • Add cleanup?: "delete" | "keep" field to CronJob type, schemas, and job create/patch logic.

Fixes #11260.

Test plan

  • New test: cleanup called after isolated job with default cleanup ("delete")
  • New test: cleanup skipped when cleanup: "keep"
  • New test: cleanup skipped when sessionKey is not a :run: key
  • New test: cleanup failure does not crash job execution
  • New test: main jobs do not trigger cleanup
  • New test: isCronRunSessionKey regex matching
  • All 252 existing tests pass
  • pnpm lint clean (4 pre-existing errors in unrelated file)
  • pnpm build passes

🤖 Generated with Claude Code

Greptile Overview

Greptile Summary

This PR adds a cleanup?: "delete" | "keep" field to cron jobs (defaulting to delete) and invokes a best-effort cleanup hook after isolated job execution to remove per-run :run: session entries from the session store. It also introduces a background interval sweeper in src/gateway/server-cron.ts that periodically deletes stale :run: entries (older than 10 minutes) as a crash/race-condition backstop, and updates the gateway protocol cron schemas accordingly.

Confidence Score: 3/5

  • This PR is close to mergeable, but the session cleanup logic can delete the wrong entries and the sweeper won’t clean up non-default agent stores.
  • Core behavior is covered by new tests and the changes are localized, but (1) /:run:/ matching in the execution path can delete non-cron sessions, and (2) the sweeper currently only scans the default agent’s session store, missing stale :run: sessions for other agents targeted by cron jobs.
  • src/cron/service/timer.ts, src/gateway/server-cron.ts

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Context used:

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

@openclaw-barnacle openclaw-barnacle bot added app: web-ui App: web-ui gateway Gateway runtime labels Feb 9, 2026
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +16 to +19
/** True if the session key is a per-run cron key (contains `:run:`). */
function isCronRunKey(key: string): boolean {
return /:run:/.test(key);
}
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.

Overbroad :run: match

isCronRunKey currently treats any sessionKey containing ":run:" as a cron run key (/:run:/). That will incorrectly delete non-cron session entries if their key format happens to include :run: (e.g. other subsystems using run markers). Since cleanupCronRunSession does a hard delete store[sessionKey], this can remove unrelated sessions. Consider reusing the stricter isCronRunSessionKey logic from src/cron/service/sweeper.ts (or otherwise constrain the match to ...:cron:<jobId>:run:<uuid>).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cron/service/timer.ts
Line: 16:19

Comment:
**Overbroad `:run:` match**

`isCronRunKey` currently treats any `sessionKey` containing `":run:"` as a cron run key (`/:run:/`). That will incorrectly delete non-cron session entries if their key format happens to include `:run:` (e.g. other subsystems using run markers). Since `cleanupCronRunSession` does a hard `delete store[sessionKey]`, this can remove unrelated sessions. Consider reusing the stricter `isCronRunSessionKey` logic from `src/cron/service/sweeper.ts` (or otherwise constrain the match to `...:cron:<jobId>:run:<uuid>`).

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 33ef6bb — removed the local isCronRunKey and replaced both call sites with an import of isCronRunSessionKey from sweeper.ts, which uses the stricter /:cron:[^:]+:run:[^:]+$/ pattern.

Comment on lines +147 to +151
const { cfg, log } = params;
const agentId = resolveDefaultAgentId(cfg);
const sessStorePath = resolveStorePath(cfg.session?.store, { agentId });
const now = Date.now();
let removed = 0;
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.

Sweeper ignores per-job agentId

sweepStaleCronRunSessions always resolves the session store path using resolveDefaultAgentId(cfg) (sessStorePath = resolveStorePath(cfg.session?.store, { agentId })). But isolated cron runs can target non-default agents (job.agentId), and their :run: entries will be written to that agent’s session store. As written, the sweeper will never clean up stale :run: sessions for non-default agents.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-cron.ts
Line: 147:151

Comment:
**Sweeper ignores per-job agentId**

`sweepStaleCronRunSessions` always resolves the session store path using `resolveDefaultAgentId(cfg)` (`sessStorePath = resolveStorePath(cfg.session?.store, { agentId })`). But isolated cron runs can target non-default agents (`job.agentId`), and their `:run:` entries will be written to that agent’s session store. As written, the sweeper will never clean up stale `:run:` sessions for non-default agents.

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 33ef6bbsweepStaleCronRunSessions now iterates over all configured agents via listAgentIds(cfg) and sweeps each agent's session store independently.

…ores

- Replace overbroad isCronRunKey (/:run:/) in timer.ts with the stricter
  isCronRunSessionKey from sweeper.ts (/:cron:[^:]+:run:[^:]+$/)
- Update sweepStaleCronRunSessions to iterate over all configured agent
  session stores via listAgentIds(), not just the default agent

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@gumadeiras
Copy link
Copy Markdown
Member

Fix landed on main via e19a235.

Thanks!

@gumadeiras gumadeiras closed this Feb 10, 2026
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add automatic cleanup support for cron-created isolated sessions

2 participants