From 3381dad0da8ede93d61358791e42e0fc2dd8ba9d Mon Sep 17 00:00:00 2001 From: iliya Date: Sun, 15 Mar 2026 13:12:02 +0200 Subject: [PATCH] feat: improve task assignment messaging and action protocol clarity - Revised task assignment messages to emphasize immediate action for newly assigned tasks, clarifying conditions for starting tasks and adding comments. - Updated instructions to ensure users communicate their status effectively when unable to start tasks, enhancing task management and accountability. - Enhanced test cases to validate the new messaging and ensure proper functionality in task handling scenarios. --- agent-teams-controller/src/internal/tasks.js | 14 ++++++++------ agent-teams-controller/test/controller.test.js | 10 +++++++++- mcp-server/test/tools.test.ts | 10 +++++++++- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/agent-teams-controller/src/internal/tasks.js b/agent-teams-controller/src/internal/tasks.js index d16041d0..c5d66eae 100644 --- a/agent-teams-controller/src/internal/tasks.js +++ b/agent-teams-controller/src/internal/tasks.js @@ -41,7 +41,7 @@ function buildAssignmentMessage(context, task, options = {}) { const lines = [ `New task assigned to you: ${taskLabel} "${task.subject}".`, ``, - `*If you are not currently working on another task, start this one now. If you are busy, start it as soon as your current task is finished.*`, + `*If you are idle and this task is ready to start, start it now. If you are busy, blocked, or still need more context, immediately add a short task comment with the reason and your best ETA or what you are waiting on, and keep this task in TODO until you actually begin.*`, ]; if (description) { @@ -57,9 +57,11 @@ function buildAssignmentMessage(context, task, options = {}) { wrapAgentBlock(`Use the board MCP tools to work this task correctly: 1. Check the latest full context before starting: task_get { teamName: "${context.teamName}", taskId: "${task.id}" } -2. If you are idle, start now; otherwise start as soon as your current task is done. When you actually begin work, mark it started: +2. If you are idle and the task is ready to start after checking dependencies and context, call task_start now: task_start { teamName: "${context.teamName}", taskId: "${task.id}" } -3. When the work is done, mark it completed: +3. If you are busy on another task, blocked, or still need more context, immediately add a task comment on this task with the reason and your best ETA or what you are waiting on, keep it pending/TODO, and do not call task_start until you truly begin: + task_add_comment { teamName: "${context.teamName}", taskId: "${task.id}", text: "", from: "" } +4. When the work is done, mark it completed: task_complete { teamName: "${context.teamName}", taskId: "${task.id}" }`) ); @@ -417,7 +419,7 @@ function buildMemberTaskProtocol(teamName) { - Use task_briefing as a compact queue view of your assigned tasks. - task_briefing may include full description/comments only for in_progress tasks; needsFix/pending/review/completed entries may be minimal on purpose. - Finish existing in_progress tasks first. - - If a newly assigned task must wait because you are still busy on another task, immediately add a short task comment on that waiting task with the reason and your best ETA. + - A newly assigned task must NOT remain silently pending/TODO. If you are idle and the task is ready to start, start it now. If it must wait because you are still busy on another task, blocked, or still need more context, immediately add a short task comment on that waiting task with the reason and your best ETA or what you are waiting on. - Keep any task you have not actually started in pending/TODO (use task_set_status pending if it was moved too early). - If you need more context for an in_progress task, you MAY call task_get, but it is not mandatory when task_briefing already gives enough detail. - Before starting a needsFix or pending task, call task_get for that specific task first. @@ -507,7 +509,7 @@ async function memberBriefing(context, memberName) { `Member briefing for ${requestedMemberName} on team "${context.teamName}" (${context.teamName}).`, `Role: ${role}.`, `CRITICAL: If a task gets a new comment and you are going to do additional implementation/fix/follow-up work on that same task, FIRST leave a short task comment saying what you are about to do, THEN move it to in_progress with task_start, THEN do the work, and when finished leave a short result comment and move it to done with task_complete. Never skip this comment -> reopen -> work -> comment -> done cycle.`, - `CRITICAL: If a newly assigned task must wait because you are already finishing another task, leave a short task comment on the waiting task immediately with the reason and your best ETA, keep it in pending/TODO, and only move it to in_progress with task_start when you truly begin.`, + `CRITICAL: A newly assigned task must NOT remain silently pending/TODO. If you are idle and the task is ready to start, start it now. If it must wait because you are already finishing another task, blocked, or still need more context, leave a short task comment on the waiting task immediately with the reason and your best ETA or what you are waiting on, keep it in pending/TODO, and only move it to in_progress with task_start when you truly begin.`, `Team lead: ${leadName}.`, buildMemberLanguageInstruction(config), `You must NOT start work, claim tasks, or improvise task/process protocol before reading and following this briefing.`, @@ -526,7 +528,7 @@ async function memberBriefing(context, memberName) { `Bootstrap flow:`, `1. Use this briefing as your durable rules source.`, `2. Use task_briefing as your compact queue view whenever you need to see assigned work.`, - `3. Before starting a pending or needs-fix task, call task_get for that specific task if you need the full context. If it must wait because another task is already active, add a short task comment with the reason + ETA and keep it pending/TODO until you actually begin.`, + `3. Before starting a pending or needs-fix task, call task_get for that specific task if you need the full context. A newly assigned task must not remain silently pending/TODO: if you are idle and the task is ready to start, start it now; if it must wait because another task is already active, because it is blocked, or because you still need more context, add a short task comment with the reason + ETA or what you are waiting on and keep it pending/TODO until you actually begin.`, `4. If this briefing was requested during reconnect, resume in_progress work first, then needs-fix tasks, then pending tasks.`, `5. If you cannot obtain the context you need, notify your team lead ("${leadName}") and wait instead of guessing.` ); diff --git a/agent-teams-controller/test/controller.test.js b/agent-teams-controller/test/controller.test.js index c3808430..e5804b46 100644 --- a/agent-teams-controller/test/controller.test.js +++ b/agent-teams-controller/test/controller.test.js @@ -340,11 +340,19 @@ describe('agent-teams-controller API', () => { expect(ownerInbox[0].summary).toContain(`#${pendingTask.displayId}`); expect(ownerInbox[0].text).toContain('task_get'); expect(ownerInbox[0].text).toContain('task_start'); + expect(ownerInbox[0].text).toContain('task_add_comment'); + expect(ownerInbox[0].text).toContain('If you are idle and this task is ready to start, start it now.'); expect(ownerInbox[0].text).toContain( - 'If you are not currently working on another task, start this one now.' + 'If you are busy, blocked, or still need more context, immediately add a short task comment' ); + expect(ownerInbox[0].text).toContain('Description:'); + expect(ownerInbox[0].text).toContain('Do this later'); + expect(ownerInbox[0].text).toContain('Instructions:'); + expect(ownerInbox[0].text).toContain('Check the migration plan first.'); expect(ownerInbox[0].leadSessionId).toBe('lead-session-1'); expect(ownerInbox[3].summary).toContain(`#${reassignedTask.displayId}`); + expect(ownerInbox[3].text).toContain('If you are idle and this task is ready to start, start it now.'); + expect(ownerInbox[3].text).toContain('task_add_comment'); const briefing = await controller.tasks.taskBriefing('bob'); expect(briefing).toContain('In progress:'); diff --git a/mcp-server/test/tools.test.ts b/mcp-server/test/tools.test.ts index 8d215dc8..a4a15149 100644 --- a/mcp-server/test/tools.test.ts +++ b/mcp-server/test/tools.test.ts @@ -613,8 +613,15 @@ describe('agent-teams-mcp tools', () => { expect(ownerInbox[0].summary).toContain(`#${queuedTask.displayId}`); expect(ownerInbox[0].text).toContain('task_get'); expect(ownerInbox[0].text).toContain('task_start'); + expect(ownerInbox[0].text).toContain('task_add_comment'); expect(ownerInbox[0].text).toContain('Read the plan before starting.'); + expect(ownerInbox[0].text).toContain('If you are idle and this task is ready to start, start it now.'); + expect(ownerInbox[0].text).toContain( + 'If you are busy, blocked, or still need more context, immediately add a short task comment' + ); expect(ownerInbox[3].summary).toContain(`#${unassignedTask.displayId}`); + expect(ownerInbox[3].text).toContain('If you are idle and this task is ready to start, start it now.'); + expect(ownerInbox[3].text).toContain('task_add_comment'); const briefing = (await getTool('task_briefing').execute({ claudeDir, @@ -643,8 +650,9 @@ describe('agent-teams-mcp tools', () => { 'You must NOT start work, claim tasks, or improvise task/process protocol' ); expect(memberBriefingText).toContain( - 'leave a short task comment on the waiting task immediately with the reason and your best ETA' + 'A newly assigned task must NOT remain silently pending/TODO. If you are idle and the task is ready to start, start it now.' ); + expect(memberBriefingText).toContain('reason and your best ETA or what you are waiting on'); expect(memberBriefingText).toContain('IMPORTANT: Communicate in English.'); expect(memberBriefingText).toContain('TURN ACTION MODE PROTOCOL (HIGHEST PRIORITY FOR EACH USER TURN):'); expect(memberBriefingText).toContain('Task briefing for alice:');