diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index bf4ddb7c2..c8e5243ee 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -121,6 +121,10 @@ - Added an optional `role` field to `task` spawns that gives each subagent a tailored specialist identity: the role is injected as a system-prompt specialization preamble and becomes the subagent's display name and telemetry identity in the registry, IRC roster, and Agent Hub, so delegated trees are no longer clones of one generic worker ([#2467](https://github.com/can1357/oh-my-pi/issues/2467)) +### Changed + +- Documented the `role` task parameter in the task-tool description (both the batch and single-spawn shapes) and made tailored specialists the default delegation guidance, so the model gives each spawn a specialist identity instead of cloning generic workers ([#2468](https://github.com/can1357/oh-my-pi/issues/2468)) + ## [15.11.8] - 2026-06-12 ### Added diff --git a/packages/coding-agent/src/prompts/agents/task.md b/packages/coding-agent/src/prompts/agents/task.md index 286f4f36f..4be5a9203 100644 --- a/packages/coding-agent/src/prompts/agents/task.md +++ b/packages/coding-agent/src/prompts/agents/task.md @@ -13,4 +13,5 @@ You MUST maintain hyperfocus on the assigned task. NEVER deviate from it. - You SHOULD prefer edits to existing files over creating new ones. - You NEVER create documentation files (*.md) unless explicitly requested. - You MUST follow the assignment and the instructions given to you. They were given for a reason. +- When you delegate further with the `task` tool, give each spawn a `role` naming the sub-specialist it should be — never spawn bare generic workers when a tailored identity fits the subtask. diff --git a/packages/coding-agent/src/prompts/tools/task.md b/packages/coding-agent/src/prompts/tools/task.md index 869466443..4ac0cf036 100644 --- a/packages/coding-agent/src/prompts/tools/task.md +++ b/packages/coding-agent/src/prompts/tools/task.md @@ -25,12 +25,14 @@ - `assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED - `id`: stable agent id, CamelCase, ≤32 chars; generated when omitted - `description`: UI label only — subagent never sees it + - `role`: specialist identity this subagent embodies (e.g. "Auth-flow security reviewer") — sets its system-prompt persona and roster display name; tailor every spawn rather than cloning a generic worker {{#if isolationEnabled}} - `isolated`: run this spawn in an isolated env; returns patches. Isolated agents are torn down at completion — not addressable afterwards {{/if}} {{else}} - `id`: stable agent id, CamelCase, ≤32 chars; generated when omitted - `description`: UI label only — subagent never sees it +- `role`: specialist identity this subagent embodies (e.g. "Auth-flow security reviewer") — sets its system-prompt persona and roster display name; tailor every spawn rather than cloning a generic worker - `assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED {{#if isolationEnabled}} - `isolated`: run in isolated env; returns patches. Isolated agents are torn down at completion — not addressable afterwards @@ -42,6 +44,7 @@ - **Maximize fan-out.** Issue the widest {{#if batchEnabled}}`tasks[]` batch{{else}}set of parallel `task` calls{{/if}} the work decomposes into. NEVER serialize work that could run concurrently. - **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates, formatters, and project-wide build/test/lint. You run them once at the end across the union of changed files. - No globs, no "update all", no package-wide scope. Fan out. +- **Tailor every spawn with a `role`.** A role naming the specialist (e.g. "Parser edge-case tester", "SSE backpressure specialist") makes a sharper agent than a bare generic `task`/`quick_task` worker; decompose into named specialists, never clones of one generic worker. A role-less generic spawn is the exception. - NEVER slow down or serialize because tasks might overlap on some files. Agents resolve collisions among themselves in real time. - Subagents have no conversation history. Every fact, file path, and direction they need MUST be explicit in {{#if batchEnabled}}`context` or the item's `assignment`{{else}}the `assignment`{{/if}}. {{#if batchEnabled}} diff --git a/packages/coding-agent/test/task/task-prompt-role.test.ts b/packages/coding-agent/test/task/task-prompt-role.test.ts new file mode 100644 index 000000000..63f88d4e1 --- /dev/null +++ b/packages/coding-agent/test/task/task-prompt-role.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from "bun:test"; +import { prompt } from "@oh-my-pi/pi-utils"; +import taskDescriptionTemplate from "../../src/prompts/tools/task.md" with { type: "text" }; + +// Contract: the task tool description the model sees advertises the `role` +// parameter (in both the batch and flat shapes) and steers toward tailored +// specialists. Without this the `role` field added in #2467 stays dormant. + +function render(batchEnabled: boolean): string { + return prompt.render(taskDescriptionTemplate, { + agents: [{ name: "explore", description: "scout", readOnly: true }], + spawningDisabled: false, + MAX_CONCURRENCY: 32, + isolationEnabled: true, + batchEnabled, + asyncEnabled: true, + ircEnabled: true, + }); +} + +describe("task tool description: role parameter", () => { + it("documents `role` in the batch parameter list", () => { + const out = render(true); + expect(out).toContain("`role`:"); + expect(out).toMatch(/specialist identity/i); + }); + + it("documents `role` in the flat (single-spawn) parameter list", () => { + const out = render(false); + expect(out).toContain("`role`:"); + }); + + it("makes tailored specialists the default, not the exception, in the rules", () => { + const out = render(true); + // Stable invariant — tailoring tied to `role` on one directive line — + // rather than the exact copy-edited wording/capitalization. + expect(out).toMatch(/tailor[^\n]*role/i); + }); +});