fix(core): reject blank cron prompts - #5716
Conversation
|
@qwen-code /triage |
|
Thanks for the PR! Template looks good ✓ On direction: this is a straightforward validation fix — On approach: the diff is minimal and focused — exactly two files, one validation override and one Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 方向:这是一个直接的校验修复—— 方案:diff 最小且聚焦——恰好两个文件,一个 validation override 和 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewIndependent proposal: override The PR's approach matches this exactly. The One minor observation: Reuse check: the validation pattern is a standard per-tool override (not shared utility), which is the correct choice. Verdict: clean, no blockers. TestingRan the focused test suite against the PR branch: All 11 tests pass, including the 3 new tests from this PR:
Dev build smoke test: CLI starts cleanly on the PR branch, no import or startup errors. Note: full E2E tmux test with — Qwen Code · qwen3.7-max |
ReflectionThis is a textbook focused PR. Nine lines of validation, one Going back to my independent proposal from Stage 2a — the PR does exactly what I would have done, using the same The linked issue #5715 was already labeled 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 行校验代码,一个 回到我在 Stage 2a 写的独立方案——PR 做的正是我会做的事,使用了代码库中 15+ 个其他工具已经在用的同一个 关联 issue #5715 已经标记了 测试结果明确:11/11 通过,3 个新测试覆盖了空字符串、纯空白和 trim 场景。开发构建启动正常。 我会很乐意维护这段代码。没有遗憾,没有需要重新审视的地方。批准合并。 — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
✅ Maintainer local verification — real
|
| Case | model-emitted cron_create |
BASE (pre-fix) | This PR |
|---|---|---|---|
| Blank prompt (issue #5715) | {cron:"*/5 * * * *", prompt:" ", durable:true} |
✓ accepted → Scheduled 8s5zpf3n … [durable], persisted to disk as prompt:" " (len 3) |
✗ rejected → Parameter "prompt" must be a non-empty string. — nothing scheduled, nothing persisted |
| Padded prompt (trim) | {cron:"*/7 * * * *", prompt:" check status ", durable:true} |
persisted untrimmed → prompt:" check status " (len 16) |
persisted trimmed → prompt:"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:" "inscheduled_tasks.jsonon 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.
What this PR does
This PR makes
cron_createreject 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_createschedules 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_createin 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_createacceptedprompt: ""orprompt: " "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
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 --checkSub-agent review: no blocking issues found.
Risk & Scope
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
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
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.