Skip to content

fix(core): reject blank cron prompts - #5716

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
tt-a1i:fix/cron-create-non-empty-prompt
Jun 23, 2026
Merged

fix(core): reject blank cron prompts#5716
wenshao merged 1 commit into
QwenLM:mainfrom
tt-a1i:fix/cron-create-non-empty-prompt

Conversation

@tt-a1i

@tt-a1i tt-a1i commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

What this PR does

This PR makes cron_create reject empty and whitespace-only prompts before a scheduled job is created.

It also trims accepted prompts before handing them to the cron scheduler, so durable and session-only jobs store the prompt that will actually be enqueued later.

Why it's needed

cron_create schedules future agent input. A blank prompt creates a job that has no useful work to enqueue, and durable mode can persist that blank prompt to the scheduled tasks file.

Other prompt-driven tools already reject empty prompts. This change brings cron_create in line with that behavior and prevents meaningless future jobs.

Reviewer Test Plan

How to verify

Run the focused cron-create tests and confirm that blank prompts are rejected without creating scheduler jobs, while normal prompts still schedule successfully.

Evidence (Before & After)

Before: cron_create accepted prompt: "" or prompt: " " as long as the cron expression was valid, creating a scheduler job.

After: blank prompts fail parameter validation with Parameter "prompt" must be a non-empty string., and the scheduler remains empty. A prompt with surrounding whitespace is trimmed before scheduling.

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment (optional)

Local workspace tests on macOS with the repository npm workspace setup.

Commands run:

npm test --workspace=packages/core -- tools/cron-create.test.ts --coverage.enabled=false
npx prettier --check packages/core/src/tools/cron-create.ts packages/core/src/tools/cron-create.test.ts
npx eslint packages/core/src/tools/cron-create.ts packages/core/src/tools/cron-create.test.ts
npm run typecheck --workspace=packages/core --if-present
git diff --check

Sub-agent review: no blocking issues found.

Risk & Scope

  • Main risk or tradeoff: Existing callers that pass blank cron prompts now receive a validation error instead of creating a no-op scheduled job.
  • Not validated / out of scope: This PR does not change cron expression parsing or scheduler timing.
  • Breaking changes / migration notes: None.

Linked Issues

Fixes #5715

AI Assistance Disclosure

I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.

中文说明

What this PR does

这个 PR 让 cron_create 在创建 scheduled job 之前拒绝空字符串和纯空白 prompt。

同时,已接受的 prompt 会先 trim 再交给 cron scheduler,因此 durable 和 session-only job 存储的是之后真正会入队执行的 prompt。

Why it's needed

cron_create 会调度未来的 agent 输入。空白 prompt 会创建一个没有实际工作内容的 job,durable 模式还可能把这个空白 prompt 持久化到 scheduled tasks 文件。

其他 prompt-driven tools 已经会拒绝空 prompt。这个改动让 cron_create 和这些工具保持一致,避免创建无意义的未来任务。

Reviewer Test Plan

How to verify

运行 focused cron-create tests,确认空白 prompt 会被拒绝且不会创建 scheduler job,同时普通 prompt 仍然能正常创建计划任务。

Evidence (Before & After)

Before: 只要 cron 表达式有效,cron_create 会接受 prompt: ""prompt: " " 并创建 scheduler job。

After: 空白 prompt 会以 Parameter "prompt" must be a non-empty string. 参数校验失败,scheduler 保持为空。带前后空白的 prompt 会在 scheduling 前被 trim。

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment (optional)

在 macOS 上使用仓库 npm workspace 进行本地测试。

已运行命令:

npm test --workspace=packages/core -- tools/cron-create.test.ts --coverage.enabled=false
npx prettier --check packages/core/src/tools/cron-create.ts packages/core/src/tools/cron-create.test.ts
npx eslint packages/core/src/tools/cron-create.ts packages/core/src/tools/cron-create.test.ts
npm run typecheck --workspace=packages/core --if-present
git diff --check

子代理审查:未发现阻塞问题。

Risk & Scope

  • Main risk or tradeoff: 现有调用方如果传入空白 cron prompt,现在会收到校验错误,而不是创建一个 no-op scheduled job。
  • Not validated / out of scope: 本 PR 不改变 cron 表达式解析或 scheduler timing。
  • Breaking changes / migration notes: 无。

Linked Issues

Fixes #5715

AI Assistance Disclosure

I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.

@tt-a1i
tt-a1i marked this pull request as ready for review June 22, 2026 21:20
@wenshao

wenshao commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

On direction: this is a straightforward validation fix — cron_create accepting blank prompts is a real gap, and every other prompt-driven tool in the codebase (agent, exitPlanMode, skill, etc.) already rejects them. Aligned with the project and with the linked issue #5715. CHANGELOG has no direct reference but the roadmap/background-automation label on the issue confirms the area is in scope.

On approach: the diff is minimal and focused — exactly two files, one validation override and one .trim() in execute(). The validateToolParamValues override follows the exact same pattern used across 15+ other tools, and the error message matches the established convention. No scope creep, no drive-by changes. Looks right.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

方向:这是一个直接的校验修复——cron_create 接受空白 prompt 是一个真实的缺口,代码库中其他所有 prompt 驱动的工具(agentexitPlanModeskill 等)都已经会拒绝空 prompt。与项目和关联 issue #5715 对齐。CHANGELOG 没有直接引用,但 issue 上的 roadmap/background-automation 标签确认该领域在范围内。

方案:diff 最小且聚焦——恰好两个文件,一个 validation override 和 execute() 里的一个 .trim()validateToolParamValues override 遵循了 15+ 个其他工具使用的完全相同的模式,错误消息也匹配已有惯例。没有范围蔓延,没有顺手改动。看起来正确。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: override validateToolParamValues on CronCreateTool to reject blank/whitespace-only prompts (matching the pattern in agent.ts, exitPlanMode.ts, skill.ts), plus trim the prompt before passing it to the scheduler. Add tests for empty, whitespace-only, and trim behavior.

The PR's approach matches this exactly. The validateToolParamValues override follows the established convention, the error message is identical to other tools, and the .trim() in execute() ensures stored prompts are clean. No unused code, no over-abstraction, no wrong-package issues.

One minor observation: agent.ts also checks typeof params.prompt !== 'string' in its validation, while this PR doesn't. Not a blocker — the JSON schema already enforces type: 'string' on the prompt property, so the typeof check is purely defensive and unnecessary here.

Reuse check: the validation pattern is a standard per-tool override (not shared utility), which is the correct choice. .trim() is a built-in. Nothing to flag.

Verdict: clean, no blockers.

Testing

Ran the focused test suite against the PR branch:

$ cd packages/core && vitest run src/tools/cron-create.test.ts --coverage.enabled=false

 RUN  v3.2.4 /home/runner/work/qwen-code/qwen-code/packages/core

 ✓ src/tools/cron-create.test.ts (11 tests) 379ms
   ✓ CronCreateTool > rejects a cron that never matches a real date  317ms

 Test Files  1 passed (1)
      Tests  11 passed (11)
   Start at  22:11:50
   Duration  1.00s (transform 208ms, setup 24ms, collect 318ms, tests 379ms, environment 0ms, prepare 86ms)

All 11 tests pass, including the 3 new tests from this PR:

  • rejects blank prompt "" — empty prompt rejected, no job created
  • rejects blank prompt " " — whitespace-only prompt rejected, no job created
  • trims the prompt before scheduling the job — surrounding whitespace stripped, stored prompt is "check status" not " check status "

Dev build smoke test: CLI starts cleanly on the PR branch, no import or startup errors.

Note: full E2E tmux test with qwen -p "..." was not run because the validation fires at the tool-call level (the LLM generates tool parameters, not the user directly), making LLM-driven reproduction unreliable for this specific change. The unit tests are the authoritative evidence.

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Reflection

This is a textbook focused PR. Nine lines of validation, one .trim(), three well-targeted tests. Every line in the diff earns its place.

Going back to my independent proposal from Stage 2a — the PR does exactly what I would have done, using the same validateToolParamValues pattern that 15+ other tools in this codebase already use. The error message is consistent. The trim before scheduling is the right place to put it (in execute(), not in validation). No surprises.

The linked issue #5715 was already labeled type/bug and welcome-pr, confirming the maintainers wanted this fixed. The PR closes it cleanly.

The test results are unambiguous: 11/11 pass, the 3 new tests cover empty, whitespace-only, and trim scenarios. The dev build starts without issues.

I'd maintain this code happily. Nothing to regret, nothing to revisit. Approving.

中文说明

这是一个教科书级别的聚焦 PR。9 行校验代码,一个 .trim(),3 个精准测试。diff 中每一行都物有所值。

回到我在 Stage 2a 写的独立方案——PR 做的正是我会做的事,使用了代码库中 15+ 个其他工具已经在用的同一个 validateToolParamValues 模式。错误消息一致。在 execute() 中 trim 是正确的放置位置。没有意外。

关联 issue #5715 已经标记了 type/bugwelcome-pr,确认维护者希望修复这个问题。PR 干净地关闭了它。

测试结果明确:11/11 通过,3 个新测试覆盖了空字符串、纯空白和 trim 场景。开发构建启动正常。

我会很乐意维护这段代码。没有遗憾,没有需要重新审视的地方。批准合并。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@wenshao

wenshao commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

✅ Maintainer local verification — real qwen binary A/B

Verdict: PASS — safe to merge. Built both branches and drove the real CLI (not unit tests): I had qwen3.7-max emit the exact cron_create call from issue #5715 (prompt: " ") plus a whitespace-padded one, with durable: true so the persisted tasks file could be read back. The fix rejects blank prompts and trims accepted ones exactly as claimed; valid prompts are unaffected.

Method

One CLI binary; only core dist/ was swapped between this PR (5ada719) and its merge-base (2fd2104f) by rebuilding core — grep-confirmed in the built dist each side. Real TUI, qwen3.7-max, --approval-mode yolo, cron enabled, fresh project dir. The model passed the literal whitespace verbatim (visible in the tool-call args below), so the tool received exactly the input #5715 describes — a genuine end-to-end path, not a hand-built param.

A/B results

Case model-emitted cron_create BASE (pre-fix) This PR
Blank prompt (issue #5715) {cron:"*/5 * * * *", prompt:" ", durable:true} acceptedScheduled 8s5zpf3n … [durable], persisted to disk as prompt:" " (len 3) rejectedParameter "prompt" must be a non-empty string. — nothing scheduled, nothing persisted
Padded prompt (trim) {cron:"*/7 * * * *", prompt:" check status ", durable:true} persisted untrimmedprompt:" check status " (len 16) persisted trimmedprompt:"check status" (len 12)

~/.qwen/tmp/<hash>/scheduled_tasks.json after both turns:

BASE     →  [ {prompt:"   "  len 3},  {prompt:"  check status  " len 16} ]   # blank job persisted, padding kept
this PR   →  [ {prompt:"check status" len 12} ]                              # blank rejected, padding trimmed

Probe

🔍 The rejection fires in tool.build() (param validation), before execute() — confirmed because the FIXED blank-prompt turn left no durable file at all (no scheduler create, no disk write). The message names the offending parameter ("prompt"), and the agent surfaced it and stopped without retrying. So a blank prompt can never reach scheduler.create / createDurable.

Notes

  • Reproduces bug(core): cron_create accepts empty prompts #5715 verbatim, including the "durable mode persists the blank prompt" concern — visible as prompt:" " in scheduled_tasks.json on base, gone on this PR.
  • The whitespace prompt reached the tool unmodified through the live model→tool path, so this isn't only a unit-level guard — it's the real path that triggered the bug.
  • Behaviour and message shape match the sibling prompt-driven tools the issue references (loop_wakeup / agent / web_fetch).
🇨🇳 中文版

✅ 维护者本地验证 —— 真实 qwen 二进制 A/B

结论:通过 —— 可以合并。 构建了两个分支并驱动真实 CLI(不是单元测试):让 qwen3.7-max 发出 issue #5715 里那个原样的 cron_create 调用(prompt: " "),外加一个前后带空格的 prompt,都用 durable: true 以便回读持久化文件。修复确实拒绝了空白 prompt、并对接受的 prompt 做了 trim,与描述完全一致;合法 prompt 不受影响。

方法

同一个 CLI 二进制,只把 core 的 dist/ 在本 PR(5ada719)和 merge-base(2fd2104f)之间通过重新构建来回切 —— 每一侧都先在构建产物 dist 里 grep 确认。真实 TUI、qwen3.7-max--approval-mode yolo、cron 启用、全新项目目录。模型把空白字符原样传给了工具(见下方 tool-call 参数),所以工具收到的正是 #5715 描述的输入 —— 是真实的端到端路径,不是手搓的参数。

A/B 结果

用例 模型发出的 cron_create BASE(修复前) 本 PR
空白 prompt(issue #5715 {cron:"*/5 * * * *", prompt:" ", durable:true} 被接受Scheduled 8s5zpf3n … [durable],以 prompt:" "(长度 3)持久化到磁盘 被拒绝Parameter "prompt" must be a non-empty string. —— 没有创建任务、没有持久化
带空格 prompt(trim) {cron:"*/7 * * * *", prompt:" check status ", durable:true} 未 trim 持久化 → prompt:" check status "(长度 16) 已 trim 持久化 → prompt:"check status"(长度 12)

两轮之后的 ~/.qwen/tmp/<hash>/scheduled_tasks.json

BASE    →  [ {prompt:"   "  长度 3},  {prompt:"  check status  " 长度 16} ]   # 空白任务被持久化,空格保留
本 PR   →  [ {prompt:"check status" 长度 12} ]                               # 空白被拒,空格被 trim

探针

🔍 拒绝发生在 tool.build()(参数校验)里、在 execute() 之前 —— 证据是 FIXED 的空白 prompt 那一轮完全没有生成 durable 文件(没有创建任务、没有写盘)。报错点名了出错的参数("prompt"),agent 把错误如实抛出并停下、没有重试。所以空白 prompt 永远到不了 scheduler.create / createDurable

备注

  • 原样复现了 bug(core): cron_create accepts empty prompts #5715,包括「durable 模式会把空白 prompt 持久化」这点 —— 在 base 上能看到 scheduled_tasks.json 里的 prompt:" ",本 PR 上消失。
  • 空白 prompt 是通过真实的「模型→工具」链路原样到达工具的,所以这不只是单元级守卫,而是触发该 bug 的真实路径。
  • 行为与报错形态和 issue 引用的同类 prompt 驱动工具(loop_wakeup / agent / web_fetch)一致。

Local real-binary verification on macOS — worktree build + rebuild-only-core A/B, real TUI driven via tmux, durable scheduled_tasks.json read back.

@wenshao
wenshao merged commit f3f3bb0 into QwenLM:main Jun 23, 2026
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(core): cron_create accepts empty prompts

3 participants