From 05668faa5fd86bfa777d0a38b4b06f8a241a8405 Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 21 Jul 2026 20:41:14 +0000 Subject: [PATCH] fix(task): surfaced actionable shape error for batch calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flat single-spawn task wire schema carries arktype `"+": "delete"`, so a batch `{ context, tasks[] }` payload sent while `task.batch` is disabled has those keys stripped and is then rejected as `task must be a string (was missing)` in the agent loop. That preempts the tool's own actionable checks (validateShapeParams / validateSpawnParams), so the model only ever saw the misleading arktype error instead of "task.batch is disabled…". Mark TaskTool with lenientArgValidation so the agent loop forwards the raw args to execute() on any arktype failure, letting the tool's shape checks surface the real reason. Valid calls still normalize through arktype; the success path is unchanged. Mirrors the existing yield-tool pattern. Fixes #6039 --- packages/coding-agent/CHANGELOG.md | 4 ++++ packages/coding-agent/src/task/index.ts | 11 ++++++++++ .../coding-agent/test/task/task-batch.test.ts | 20 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 6f32dc885..35edf381b 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed the `task` tool rejecting a valid batch `{ context, tasks[] }` call with the misleading `task must be a string (was missing)` when `task.batch` was disabled. The flat single-spawn wire schema strips `tasks`/`context` (arktype `"+": "delete"`) and then fails on the now-missing `task` in the agent loop, preempting the tool's own actionable shape check. The tool now uses lenient argument validation so those raw args reach `execute()`, which explains the real cause (`task.batch is disabled…`) ([#6039](https://github.com/can1357/oh-my-pi/issues/6039)). + ## [17.0.5] - 2026-07-18 ### Added diff --git a/packages/coding-agent/src/task/index.ts b/packages/coding-agent/src/task/index.ts index 3ffbe3c57..b1a546310 100644 --- a/packages/coding-agent/src/task/index.ts +++ b/packages/coding-agent/src/task/index.ts @@ -520,6 +520,17 @@ export class TaskTool implements AgentTool { ); expect(text).toContain("Duplicate task name"); }); + + it("marks lenientArgValidation so execute() surfaces the actionable shape error", async () => { + // Regression (#6039): the flat single-spawn wire schema carries + // `"+": "delete"`, so a batch `{ context, tasks[] }` payload is stripped + // by arktype and rejected as `task must be a string (was missing)` in the + // agent loop — preempting the tool's own actionable message. The lenient + // flag makes the loop forward the raw args to execute() on that failure. + mockDiscovery(); + const tool = await TaskTool.create(createSession({ settings: { "task.batch": false } })); + expect(tool.lenientArgValidation).toBe(true); + + // The raw batch payload the loop would forward reaches execute() and + // yields the actionable reason, never arktype's misleading missing-`task`. + const text = await executeText( + { context: "Background.", tasks: [{ name: "Alpha", task: "Work." }] }, + { "task.batch": false }, + ); + expect(text).toContain("task.batch is disabled"); + expect(text).not.toContain("was missing"); + }); }); describe("task.batch spawning", () => {