Skip to content

feat(runtime): the cron-management tool disables jobs instead of deleting them (#6159)#6165

Merged
houko merged 2 commits into
mainfrom
feat/cron-disable-not-delete
Jun 17, 2026
Merged

feat(runtime): the cron-management tool disables jobs instead of deleting them (#6159)#6165
houko merged 2 commits into
mainfrom
feat/cron-disable-not-delete

Conversation

@houko

@houko houko commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Design decision

The agent (bot) must not be able to silently throw away a configured scheduled job.
Before this change, the agent-facing cron tools cron_cancel and schedule_delete both called KernelHandle::cron_cancel, which hard-deletes the job from the scheduler and discards its schedule / action / delivery configuration.
A bot that "cancelled" a job left the operator with no record of what was set up and no way to recover it.

I repointed the two destructive tools to pause (disable) the job instead of deleting it, and added matching resume (enable) tools so the pause is reversible from the agent loop.
I chose to repoint rather than rename the existing tools: the names cron_cancel / schedule_delete are already in agent allow-lists, the tool_compat native-tool whitelist, the subagent deny-list, and persisted manifests, so renaming would be a wide backward-incompatible churn for no behavioural gain.
The tool descriptions now state honestly that they pause (not delete) and that permanent deletion is a human-only dashboard operation.

Hard deletion stays human-only: the dashboard DELETE /api/cron/jobs/{id} route is unchanged.
The agent has no tool that reaches KernelHandle::cron_cancel anymore.

What changed

New kernel-handle capability:

  • crates/librefang-kernel-handle/src/cron_control.rs — added CronControl::cron_set_enabled(job_id, enabled) (default impl returns Unavailable, matching the other methods).
  • crates/librefang-kernel/src/kernel/handles/cron_control.rs — implemented it against the existing CronScheduler::set_enabled (which clears error counters and recomputes next_run on re-enable) followed by persist(), mirroring the HTTP toggle_cron_job route's persist-after-toggle (PUT /api/cron/jobs/{id} swallows persist error while sibling DELETE/toggle log it #3515).

Agent tools repointed and added:

  • crates/librefang-runtime/src/tool_runner/cron.rstool_cron_cancel now calls cron_set_enabled(false); added tool_cron_enable (calls cron_set_enabled(true)).
    The shared require_owned_job_id helper keeps the existing per-agent ownership guard for both.
  • crates/librefang-runtime/src/tool_runner/schedule.rstool_schedule_delete now calls cron_set_enabled(false); added tool_schedule_resume.
    Shared require_owned_schedule_id helper preserves the ownership guard (arch: inconsistent error contracts — String / anyhow / LibreFangError / LlmError mixed #3576 second slice) for both.
  • crates/librefang-runtime/src/tool_runner/dispatch.rs — routed cron_enable and schedule_resume.
  • crates/librefang-runtime/src/tool_runner/mod.rs — imports for the new tool fns.
  • crates/librefang-runtime/src/tool_runner/definitions.rs — added cron_enable / schedule_resume ToolDefinitions; rewrote the cron_cancel / schedule_delete descriptions to say "Pause (disable) … config preserved … only a human can hard-delete via the dashboard".
    Definitions are appended in source order, so deterministic prompt ordering (Enforce deterministic ordering for LLM-bound registries to stabilize prompt cache #3298) is unaffected.

Supporting surfaces:

  • crates/librefang-runtime/src/tool_policy.rscron_enable / schedule_resume added to SUBAGENT_DENY_ALWAYS (consistent with the existing scheduling tools being top-level-only).
  • crates/librefang-types/src/tool_compat.rsschedule_resume added to the native-tool whitelist (the schedule_* family is listed there; the low-level cron_* family is not, so cron_enable is not added — matching cron_cancel).
  • docs/src/app/agent/tools/page.mdx, docs/src/app/zh/agent/tools/page.mdx, docs/src/app/architecture/page.mdx, docs/src/app/zh/architecture/page.mdx — documented the pause/resume semantics and updated the tool tables/counts.

Verification

Run in the repo's sanctioned Docker dev image (Dockerfile.rust-dev) with a per-worktree target volume, per CLAUDE.md "Verifying without a native toolchain".

  • cargo fmt --all — clean (no diff beyond the intended change).
  • cargo check --workspace --lib — clean.
  • cargo clippy -p librefang-kernel-handle -p librefang-types -p librefang-runtime --all-targets -- -D warnings — clean.
  • cargo clippy -p librefang-kernel --all-targets -- -D warnings — clean.

Tests (all green):

  • cargo test -p librefang-runtime --test tool_runner_forwarding_task_cron — 18 tests, including new behaviour tests:
    • test_cron_cancel_disables_and_does_not_hard_delete — asserts the tool calls cron_set_enabled(job, false) and never reaches KernelHandle::cron_cancel; user message says "paused/disabled".
    • test_schedule_delete_disables_and_does_not_hard_delete — same for the natural-language tool.
    • test_cron_enable_resumes_owned_job / test_schedule_resume_resumes_owned_job — assert cron_set_enabled(job, true).
    • test_cron_enable_unowned_job_renders_as_not_found — ownership guard on the new enable tool.
    • Updated unowned-job tests now also assert cron_set_enabled is never called for an unowned job.
  • cargo test -p librefang-runtime --lib -- tool_runner::cron::tests tool_runner::schedule::tests tool_policy::tests filter_tools_by_depth tool_runner::tests::workspace — 57 tests (new cron_enable_* / schedule_resume_without_kernel_* unit tests; test_builtin_tool_definitions updated for the two new tools).
  • cargo test -p librefang-kernel-handle --test defaults_returns — 7 tests (new cron_set_enabled default-Unavailable assertion in test_cron_defaults_return_errors).
  • cargo test -p librefang-types --lib tool_compat — 3 tests (whitelist update covered).
  • cargo test -p librefang-kernel --lib cron:: — 79 tests; cron::tests::test_set_enabled already proves the scheduler keeps the job (config preserved) after disable and resets on re-enable.

The Docker image runs Linux only; this change is platform-independent (no OS-specific paths), so the Linux run is sufficient.

Maintainer notes — delete-vs-disable design choice

I repointed cron_cancel / schedule_delete to disable rather than renaming them, to avoid a backward-incompatible churn across agent allow-lists, tool_compat, the subagent deny-list, and persisted manifests.
The trade-off is that the tool names still read "cancel" / "delete" while the behaviour is "pause"; I made the tool descriptions explicit about this and added the resume counterparts, so the LLM-visible contract is honest.
If you would prefer fully renamed tools (e.g. cron_pause / cron_resume) with deprecation aliases instead, I can follow up — but that touches a wider surface than this issue's scope, so I kept it to the behavioural fix.

Closes #6159

…ting them (#6159)

The agent-facing cron tools (`cron_cancel`, `schedule_delete`) used to hard-delete a scheduled job via `KernelHandle::cron_cancel`, which discards the schedule / action / delivery configuration entirely.
If a bot over-eagerly "cancelled" a configured job, the operator lost what was set up and had no way to recover it.

These tools now pause the job via a new `KernelHandle::cron_set_enabled(job_id, false)` rather than deleting it, so the job still exists with `enabled=false` and its configuration is preserved.
Hard deletion stays a human-only dashboard operation (`DELETE /api/cron/jobs/{id}`).

Pause is reversible from the agent loop: `cron_enable` (low-level) and `schedule_resume` (natural-language) re-enable a paused job.
The ownership guard that `cron_cancel` / `schedule_delete` already enforced is preserved and shared with the new tools, so an agent can only pause/resume jobs it owns.

The new `cron_set_enabled` kernel-handle method calls the existing `CronScheduler::set_enabled` (which clears error counters and recomputes `next_run` on re-enable) and persists, mirroring the HTTP `toggle_cron_job` route (#3515).
@github-actions github-actions Bot added size/L 250-999 lines changed area/docs Documentation and guides area/runtime Agent loop, LLM drivers, WASM sandbox area/kernel Core kernel (scheduling, RBAC, workflows) labels Jun 17, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 17, 2026

Copy link
Copy Markdown

@github-actions github-actions Bot added the ready-for-review PR is ready for maintainer review label Jun 17, 2026
All new doc-comments and MDX paragraphs added by this PR had sentences
wrapped at arbitrary column widths, violating the repo's prose-wrapping
rule (CLAUDE.md: one sentence = one line, break only at sentence
boundaries). Rewrap to sentence-per-line in the six touched files.
@houko
houko enabled auto-merge (squash) June 17, 2026 12:26
@houko
houko marked this pull request as draft June 17, 2026 12:32
auto-merge was automatically disabled June 17, 2026 12:32

Pull request was converted to draft

@houko
houko marked this pull request as ready for review June 17, 2026 12:37
@houko
houko merged commit f8a7943 into main Jun 17, 2026
35 checks passed
@houko
houko deleted the feat/cron-disable-not-delete branch June 17, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation and guides area/kernel Core kernel (scheduling, RBAC, workflows) area/runtime Agent loop, LLM drivers, WASM sandbox ready-for-review PR is ready for maintainer review size/L 250-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BOT 不可以随意删除任务, 可以改成暂停或者禁用

2 participants