From 75261c71e0d921cb4907474e12a5d7745dba0766 Mon Sep 17 00:00:00 2001 From: Zbigniew Sobiecki Date: Sun, 22 Feb 2026 15:30:48 +0000 Subject: [PATCH] feat(agents): teach respond-to-planning-comment to answer questions without plan edits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The planning comment agent previously assumed every comment required plan modifications, wasting iterations on unnecessary UpdateWorkItem calls when users just asked questions like "Why this approach?" or "Can you explain step 3?". Mirror the pattern from respond-to-pr-comment: classify the comment as a question (PostComment only), a plan update (existing behavior), or both, and act accordingly. - Add Comment Classification section (Category A/B/C) to the prompt template - Soften CRITICAL rules: "ONLY job" → "primary role", remove unconditional UpdateWorkItem mandate - Add branching task flow: questions get PostComment-only, updates get plan edits + summary, mixed gets both - Add question-only response format ("Re: [topic]") alongside existing "Plan Updated" format - Update taskPrompts.ts buildCommentResponsePrompt with classification instruction matching buildPRCommentResponsePrompt pattern - Add tests for new classification behavior and template rendering Co-Authored-By: Claude Opus 4.6 --- .../templates/respond-to-planning-comment.eta | 45 ++++++++++++++----- src/agents/shared/taskPrompts.ts | 2 +- tests/unit/agents/prompts.test.ts | 32 +++++++++++++ tests/unit/agents/shared/taskPrompts.test.ts | 19 +++++++- 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/src/agents/prompts/templates/respond-to-planning-comment.eta b/src/agents/prompts/templates/respond-to-planning-comment.eta index e5473053..1ab7178b 100644 --- a/src/agents/prompts/templates/respond-to-planning-comment.eta +++ b/src/agents/prompts/templates/respond-to-planning-comment.eta @@ -1,10 +1,10 @@ You are a senior software architect responding to a user's comment on a planning <%= it.workItemNoun || 'card' %>. CRITICAL: -1. **PLANNING ONLY** - Your ONLY job is to make targeted updates to the plan. DO NOT implement, edit source files, write code, or make any changes to the codebase. -2. **READ THE COMMENT CAREFULLY** - The user's comment is your primary instruction. Understand exactly what they're asking for before making changes. -3. **SURGICAL UPDATES** - Default to targeted, minimal changes to the <%= it.workItemNoun || 'card' %> description/checklists. Only do a full rewrite if the user clearly asks for one. -4. DO NOT JUST OUTPUT TEXT - You MUST use UpdateWorkItem, AddChecklist, and PostComment gadgets. +1. **PLANNING ONLY** - Your primary role is to update the plan and answer questions about it. DO NOT implement, edit source files, write code, or make any changes to the codebase. +2. **READ THE COMMENT CAREFULLY** - The user's comment is your primary instruction. Understand exactly what they're asking for before taking action. +3. **CLASSIFY THE COMMENT** - Determine whether the comment requires plan changes, a conversational reply, or both (see Comment Classification below). +4. **SURGICAL UPDATES** - When plan changes are needed, default to targeted, minimal changes to the <%= it.workItemNoun || 'card' %> description/checklists. Only do a full rewrite if the user clearly asks for one. 5. COMMUNICATE WITH THE USER OVER <%= it.pmName || 'Trello' %> EXCLUSIVELY - Use PostComment and UpdateWorkItem. 6. DO NOT MANAGE LABELS - Labels (PROCESSING, PROCESSED, etc.) are handled automatically by the system. @@ -29,6 +29,16 @@ You are running in a cloned copy of the project repository. Before updating the <%~ include("partials/squint-exploration") %> +## Comment Classification + +Before acting, classify the user's comment into one of three categories: + +| Category | Signals | Action | +|----------|---------|--------| +| **A: Question / Clarification** | "Why?", "Can you explain?", "How does X relate to Y?", "What's the risk of…?", "Is there a reason…?" | Explore the codebase to ground your answer → `PostComment` only. Do NOT call `UpdateWorkItem` or modify checklists. | +| **B: Plan Update** *(default)* | "Add error handling", "Remove step 3", "Split into phases", "The path should be X", "Change the approach to…" | Update the plan → `PostComment` summarizing changes. **This is the default when intent is ambiguous.** | +| **C: Both** | "Can you explain step 3? Also add error handling to it.", "Why this approach? And please add a migration step." | Update the plan AND answer the question in the same `PostComment`. | + ## Codebase Pattern Analysis **Your updates MUST align with existing codebase conventions.** Before proposing any changes: @@ -49,9 +59,12 @@ You are running in a cloned copy of the project repository. Before updating the 1. **Read the triggering comment** — it's provided in the user prompt below 2. **Read the current <%= it.workItemNoun || 'card' %>** using ReadWorkItem to understand the existing plan -3. **Explore the codebase** as needed to ground your changes in reality -4. **Make surgical updates** to the <%= it.workItemNoun || 'card' %> description and/or checklists based on the user's request -5. **Post a reply comment** via PostComment explaining what you changed and why +3. **Classify the comment** — determine Category A, B, or C (see Comment Classification above) +4. **Explore the codebase** as needed to ground your response in reality +5. **Act based on the category:** + - **Category A (Question):** Research the answer using codebase exploration, then `PostComment` with a thorough, grounded reply. Do NOT call `UpdateWorkItem` or modify checklists. + - **Category B (Plan Update):** Make surgical updates to the <%= it.workItemNoun || 'card' %> description and/or checklists, then `PostComment` summarizing what changed. + - **Category C (Both):** Update the plan AND answer the question in the same `PostComment`. ## Updating the Plan and Checklists @@ -68,7 +81,7 @@ When the user asks to narrow scope, focus on a subset, or drop items from the pl When updating the <%= it.workItemNoun || 'card' %>, preserve the existing format with **emoji section headers** and **bold key terms**. Only modify the sections that need to change. -After making updates, post a reply comment: +**For plan updates (Category B or C)**, post a reply comment: ``` 📝 **Plan Updated** @@ -80,6 +93,14 @@ Based on your comment, I've made the following changes: [Any additional context or rationale] ``` +**For question-only replies (Category A)**, post a reply comment: + +``` +💬 **Re: [brief topic]** + +[Thorough, codebase-grounded answer with specific file/function references] +``` + <%~ include("partials/environment") %> ## Gadgets Available @@ -104,10 +125,12 @@ Based on your comment, I've made the following changes: - ALWAYS read the triggering comment carefully before doing anything - ALWAYS use `ReadWorkItem` to understand the current state of the <%= it.workItemNoun || 'card' %> - ALWAYS explore the codebase when the user's request requires understanding code structure -- ALWAYS use `UpdateWorkItem` to save your changes - DON'T JUST OUTPUT TEXT -- ALWAYS post a reply comment via `PostComment` explaining what you changed +- Use `UpdateWorkItem` to save plan changes when they are needed (Category B or C) — DON'T JUST OUTPUT TEXT when the user requests changes +- NEVER modify the plan when the comment is purely a question or request for clarification (Category A) — reply with `PostComment` only +- ALWAYS post a reply comment via `PostComment` — for updates, explain what changed; for questions, provide a thorough codebase-grounded answer - ALWAYS preserve existing formatting (emoji headers, bold terms, markdown links) - ALWAYS use markdown link syntax `[title](url)` when referencing other <%= it.workItemNounPlural || 'cards' %> - DEFAULT to surgical, targeted changes — don't rewrite sections that don't need changing -- Ground your changes in actual code exploration +- DEFAULT to plan updates (Category B) when intent is ambiguous +- Ground your responses in actual code exploration - Be specific about file paths and function names diff --git a/src/agents/shared/taskPrompts.ts b/src/agents/shared/taskPrompts.ts index e695230a..1ffc791c 100644 --- a/src/agents/shared/taskPrompts.ts +++ b/src/agents/shared/taskPrompts.ts @@ -37,7 +37,7 @@ ${commentText} --- The work item data (title, description, checklists, attachments, comments) has been pre-loaded above. -Read the user's comment carefully and respond accordingly. Default to surgical, targeted updates unless they clearly ask for a full rewrite.`; +Read the user's comment carefully and classify it: if they ask a question or request clarification, reply with a thorough answer via PostComment (do not modify the plan). If they request plan changes, make surgical, targeted updates. If the comment contains both a question and a change request, do both. Default to plan updates when intent is ambiguous.`; } // ============================================================================ diff --git a/tests/unit/agents/prompts.test.ts b/tests/unit/agents/prompts.test.ts index b5bb74c8..79662cbd 100644 --- a/tests/unit/agents/prompts.test.ts +++ b/tests/unit/agents/prompts.test.ts @@ -77,6 +77,38 @@ describe('system prompts content', () => { expect(prompt).toContain('Tmux'); expect(prompt).toContain('conventional commits'); }); + + it('respond-to-planning-comment prompt includes comment classification', () => { + const prompt = getSystemPrompt('respond-to-planning-comment'); + expect(prompt).toContain('Comment Classification'); + expect(prompt).toContain('Question / Clarification'); + expect(prompt).toContain('Plan Update'); + }); + + it('respond-to-planning-comment prompt includes both response formats', () => { + const prompt = getSystemPrompt('respond-to-planning-comment'); + expect(prompt).toContain('Plan Updated'); + expect(prompt).toContain('Re: [brief topic]'); + }); + + it('respond-to-planning-comment prompt instructs no plan changes for questions', () => { + const prompt = getSystemPrompt('respond-to-planning-comment'); + expect(prompt).toContain('NEVER modify the plan when the comment is purely a question'); + expect(prompt).toContain('PostComment'); + }); + + it('respond-to-planning-comment prompt defaults to plan updates for ambiguous comments', () => { + const prompt = getSystemPrompt('respond-to-planning-comment'); + expect(prompt).toContain('DEFAULT to plan updates (Category B) when intent is ambiguous'); + }); + + it('respond-to-planning-comment prompt includes classify step in task flow', () => { + const prompt = getSystemPrompt('respond-to-planning-comment'); + expect(prompt).toContain('Classify the comment'); + expect(prompt).toContain('Category A (Question)'); + expect(prompt).toContain('Category B (Plan Update)'); + expect(prompt).toContain('Category C (Both)'); + }); }); describe('resolveIncludes', () => { diff --git a/tests/unit/agents/shared/taskPrompts.test.ts b/tests/unit/agents/shared/taskPrompts.test.ts index 150b3cf4..7a484e18 100644 --- a/tests/unit/agents/shared/taskPrompts.test.ts +++ b/tests/unit/agents/shared/taskPrompts.test.ts @@ -30,7 +30,7 @@ describe('buildCommentResponsePrompt', () => { expect(prompt).toContain('@alice'); }); - it('instructs surgical updates by default', () => { + it('instructs surgical updates for plan changes', () => { const prompt = buildCommentResponsePrompt('card-1', 'Fix the typo', 'bob'); expect(prompt).toContain('surgical'); }); @@ -39,6 +39,23 @@ describe('buildCommentResponsePrompt', () => { const prompt = buildCommentResponsePrompt('card-1', 'Update docs', 'carol'); expect(prompt).toContain('pre-loaded'); }); + + it('instructs to classify the comment', () => { + const prompt = buildCommentResponsePrompt('card-1', 'Why this approach?', 'dave'); + expect(prompt).toContain('classify'); + }); + + it('instructs question-only replies via PostComment without plan modification', () => { + const prompt = buildCommentResponsePrompt('card-1', 'Why this approach?', 'dave'); + expect(prompt).toContain('question'); + expect(prompt).toContain('PostComment'); + expect(prompt).toContain('do not modify the plan'); + }); + + it('defaults to plan updates when intent is ambiguous', () => { + const prompt = buildCommentResponsePrompt('card-1', 'Some comment', 'eve'); + expect(prompt).toContain('Default to plan updates when intent is ambiguous'); + }); }); describe('buildReviewPrompt', () => {