From 497e03df7903d1916f2a2a4e2a1f2a390b9c4f01 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 18 Apr 2026 20:59:17 +0000
Subject: [PATCH 1/3] Initial plan
From 97518500436a2bb3a12426495aecfde1b3847296 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 18 Apr 2026 21:14:06 +0000
Subject: [PATCH 2/3] optimize issue-monster token usage with narrower tools
and body-first triage
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/a3fcf924-20fa-4301-856e-a7a449f806f4
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
---
.github/workflows/issue-monster.lock.yml | 67 ++++++++++++++++-------
.github/workflows/issue-monster.md | 68 ++++++++++++------------
2 files changed, 84 insertions(+), 51 deletions(-)
diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml
index 52a01c4bc2a..6fdc5193c91 100644
--- a/.github/workflows/issue-monster.lock.yml
+++ b/.github/workflows/issue-monster.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"eac7305dc9f610adb3d3359f9367aca98730d48712f510c74e8523a3c70facf3","strict":true,"agent_id":"copilot","agent_model":"gpt-5.1-codex-mini"}
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"58910504a6bc1879eb9028720bdaa59bcc5fdcc50365fb48d90864d9b6996522","strict":true,"agent_id":"copilot","agent_model":"claude-haiku-4.5"}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_AGENT_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.24"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.24"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.24"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.24"},{"image":"ghcr.io/github/github-mcp-server:v1.0.0"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]}
# ___ _ _
# / _ \ | | (_)
@@ -145,6 +145,7 @@ name: "Issue Monster"
# core.setOutput('issue_count', 0);
# core.setOutput('issue_numbers', '');
# core.setOutput('issue_list', '');
+ # core.setOutput('issue_context', '');
# core.setOutput('has_issues', 'false');
# return;
# }
@@ -381,6 +382,7 @@ name: "Issue Monster"
# number: issue.number,
# title: issue.title,
# labels: issue.labels.map(l => l.name),
+ # body: issue.body,
# created_at: issue.created_at,
# score
# };
@@ -392,6 +394,15 @@ name: "Issue Monster"
# const labelStr = i.labels.length > 0 ? ` [${i.labels.join(', ')}]` : '';
# return `#${i.number}: ${i.title}${labelStr} (score: ${i.score.toFixed(1)})`;
# }).join('\n');
+ #
+ # // Pre-fetch compact body context for top candidates so the agent can
+ # // triage without extra reads in most runs.
+ # const issueContext = scoredIssues.slice(0, 8).map(i => {
+ # const body = (i.body || '').replace(/\s+/g, ' ').trim();
+ # const bodySnippet = body.length > 600 ? `${body.slice(0, 600)}…` : body;
+ # const labelStr = i.labels.length > 0 ? i.labels.join(', ') : 'none';
+ # return `#${i.number} | score=${i.score.toFixed(1)} | labels=${labelStr}\nTitle: ${i.title}\nBody: ${bodySnippet || '(no body)'}`;
+ # }).join('\n\n---\n\n');
#
# const issueNumbers = scoredIssues.map(i => i.number).join(',');
#
@@ -403,6 +414,7 @@ name: "Issue Monster"
# core.setOutput('issue_count', scoredIssues.length);
# core.setOutput('issue_numbers', issueNumbers);
# core.setOutput('issue_list', issueList);
+ # core.setOutput('issue_context', issueContext);
#
# if (scoredIssues.length === 0) {
# core.info('🍽️ No suitable candidate issues - the plate is empty!');
@@ -415,6 +427,7 @@ name: "Issue Monster"
# core.setOutput('issue_count', 0);
# core.setOutput('issue_numbers', '');
# core.setOutput('issue_list', '');
+ # core.setOutput('issue_context', '');
# core.setOutput('has_issues', 'false');
# }
workflow_dispatch:
@@ -468,7 +481,7 @@ jobs:
env:
GH_AW_INFO_ENGINE_ID: "copilot"
GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
- GH_AW_INFO_MODEL: "gpt-5.1-codex-mini"
+ GH_AW_INFO_MODEL: "claude-haiku-4.5"
GH_AW_INFO_VERSION: "1.0.21"
GH_AW_INFO_AGENT_VERSION: "1.0.21"
GH_AW_INFO_WORKFLOW_NAME: "Issue Monster"
@@ -537,6 +550,7 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT: ${{ needs.pre_activation.outputs.issue_context }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }}
@@ -544,20 +558,20 @@ jobs:
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_f8901b6202eb9fe6_EOF'
+ cat << 'GH_AW_PROMPT_e23ac80547a592d3_EOF'
- GH_AW_PROMPT_f8901b6202eb9fe6_EOF
+ GH_AW_PROMPT_e23ac80547a592d3_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_f8901b6202eb9fe6_EOF'
+ cat << 'GH_AW_PROMPT_e23ac80547a592d3_EOF'
Tools: add_comment(max:3), assign_to_agent(max:3), missing_tool, missing_data, noop
- GH_AW_PROMPT_f8901b6202eb9fe6_EOF
+ GH_AW_PROMPT_e23ac80547a592d3_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_f8901b6202eb9fe6_EOF'
+ cat << 'GH_AW_PROMPT_e23ac80547a592d3_EOF'
The following GitHub context information is available for this workflow:
{{#if __GH_AW_GITHUB_ACTOR__ }}
@@ -586,20 +600,21 @@ jobs:
{{/if}}
- GH_AW_PROMPT_f8901b6202eb9fe6_EOF
+ GH_AW_PROMPT_e23ac80547a592d3_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_f8901b6202eb9fe6_EOF'
+ cat << 'GH_AW_PROMPT_e23ac80547a592d3_EOF'
{{#runtime-import .github/workflows/shared/github-guard-policy.md}}
{{#runtime-import .github/workflows/shared/activation-app.md}}
{{#runtime-import .github/workflows/issue-monster.md}}
- GH_AW_PROMPT_f8901b6202eb9fe6_EOF
+ GH_AW_PROMPT_e23ac80547a592d3_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT: ${{ needs.pre_activation.outputs.issue_context }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }}
@@ -623,6 +638,7 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT: ${{ needs.pre_activation.outputs.issue_context }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }}
@@ -647,6 +663,7 @@ jobs:
GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT,
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT,
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST,
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS
@@ -782,9 +799,9 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c922b5e93a111615_EOF'
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_da8153b881a29d69_EOF'
{"add_comment":{"max":3,"target":"*"},"assign_to_agent":{"allowed":["copilot"],"max":3,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_c922b5e93a111615_EOF
+ GH_AW_SAFE_OUTPUTS_CONFIG_da8153b881a29d69_EOF
- name: Write Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
@@ -996,7 +1013,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_f578c422c31b5a81_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_7ae1d42a2b775ce8_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"github": {
@@ -1006,7 +1023,7 @@ jobs:
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
- "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ "GITHUB_TOOLSETS": "issues"
},
"guard-policies": {
"allow-only": {
@@ -1040,7 +1057,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_f578c422c31b5a81_EOF
+ GH_AW_MCP_CONFIG_7ae1d42a2b775ce8_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -1085,7 +1102,7 @@ jobs:
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- COPILOT_MODEL: gpt-5.1-codex-mini
+ COPILOT_MODEL: claude-haiku-4.5
GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
GH_AW_PHASE: agent
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
@@ -1529,7 +1546,7 @@ jobs:
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- COPILOT_MODEL: gpt-5.1-codex-mini
+ COPILOT_MODEL: claude-haiku-4.5
GH_AW_PHASE: detection
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_VERSION: dev
@@ -1576,6 +1593,7 @@ jobs:
outputs:
activated: ${{ steps.check_membership.outputs.is_team_member == 'true' && steps.check_skip_if_match.outputs.skip_check_ok == 'true' && steps.check_skip_if_no_match.outputs.skip_no_match_check_ok == 'true' && steps.check_skip_if_check_failing.outputs.skip_if_check_failing_ok == 'true' }}
has_issues: ${{ steps.search.outputs.has_issues }}
+ issue_context: ${{ steps.search.outputs.issue_context }}
issue_count: ${{ steps.search.outputs.issue_count }}
issue_list: ${{ steps.search.outputs.issue_list }}
issue_numbers: ${{ steps.search.outputs.issue_numbers }}
@@ -1721,6 +1739,7 @@ jobs:
core.setOutput('issue_count', 0);
core.setOutput('issue_numbers', '');
core.setOutput('issue_list', '');
+ core.setOutput('issue_context', '');
core.setOutput('has_issues', 'false');
return;
}
@@ -1957,6 +1976,7 @@ jobs:
number: issue.number,
title: issue.title,
labels: issue.labels.map(l => l.name),
+ body: issue.body,
created_at: issue.created_at,
score
};
@@ -1969,6 +1989,15 @@ jobs:
return `#${i.number}: ${i.title}${labelStr} (score: ${i.score.toFixed(1)})`;
}).join('\n');
+ // Pre-fetch compact body context for top candidates so the agent can
+ // triage without extra reads in most runs.
+ const issueContext = scoredIssues.slice(0, 8).map(i => {
+ const body = (i.body || '').replace(/\s+/g, ' ').trim();
+ const bodySnippet = body.length > 600 ? `${body.slice(0, 600)}…` : body;
+ const labelStr = i.labels.length > 0 ? i.labels.join(', ') : 'none';
+ return `#${i.number} | score=${i.score.toFixed(1)} | labels=${labelStr}\nTitle: ${i.title}\nBody: ${bodySnippet || '(no body)'}`;
+ }).join('\n\n---\n\n');
+
const issueNumbers = scoredIssues.map(i => i.number).join(',');
core.info(`Total candidate issues after filtering: ${scoredIssues.length}`);
@@ -1979,6 +2008,7 @@ jobs:
core.setOutput('issue_count', scoredIssues.length);
core.setOutput('issue_numbers', issueNumbers);
core.setOutput('issue_list', issueList);
+ core.setOutput('issue_context', issueContext);
if (scoredIssues.length === 0) {
core.info('🍽️ No suitable candidate issues - the plate is empty!');
@@ -1991,6 +2021,7 @@ jobs:
core.setOutput('issue_count', 0);
core.setOutput('issue_numbers', '');
core.setOutput('issue_list', '');
+ core.setOutput('issue_context', '');
core.setOutput('has_issues', 'false');
}
@@ -2013,7 +2044,7 @@ jobs:
GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
GH_AW_ENGINE_ID: "copilot"
- GH_AW_ENGINE_MODEL: "gpt-5.1-codex-mini"
+ GH_AW_ENGINE_MODEL: "claude-haiku-4.5"
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🍪 *Om nom nom by [{workflow_name}]({run_url})*{effective_tokens_suffix}{history_link}\",\"runStarted\":\"🍪 ISSUE! ISSUE! [{workflow_name}]({run_url}) hungry for issues on this {event_type}! Om nom nom...\",\"runSuccess\":\"🍪 YUMMY! [{workflow_name}]({run_url}) ate the issues! That was DELICIOUS! Me want MORE! 😋\",\"runFailure\":\"🍪 Aww... [{workflow_name}]({run_url}) {status}. No cookie for monster today... 😢\"}"
GH_AW_WORKFLOW_ID: "issue-monster"
GH_AW_WORKFLOW_NAME: "Issue Monster"
diff --git a/.github/workflows/issue-monster.md b/.github/workflows/issue-monster.md
index bd896a1f050..faf30dfcc70 100644
--- a/.github/workflows/issue-monster.md
+++ b/.github/workflows/issue-monster.md
@@ -94,6 +94,7 @@ on:
core.setOutput('issue_count', 0);
core.setOutput('issue_numbers', '');
core.setOutput('issue_list', '');
+ core.setOutput('issue_context', '');
core.setOutput('has_issues', 'false');
return;
}
@@ -330,6 +331,7 @@ on:
number: issue.number,
title: issue.title,
labels: issue.labels.map(l => l.name),
+ body: issue.body,
created_at: issue.created_at,
score
};
@@ -341,6 +343,15 @@ on:
const labelStr = i.labels.length > 0 ? ` [${i.labels.join(', ')}]` : '';
return `#${i.number}: ${i.title}${labelStr} (score: ${i.score.toFixed(1)})`;
}).join('\n');
+
+ // Pre-fetch compact body context for top candidates so the agent can
+ // triage without extra reads in most runs.
+ const issueContext = scoredIssues.slice(0, 8).map(i => {
+ const body = (i.body || '').replace(/\s+/g, ' ').trim();
+ const bodySnippet = body.length > 600 ? `${body.slice(0, 600)}…` : body;
+ const labelStr = i.labels.length > 0 ? i.labels.join(', ') : 'none';
+ return `#${i.number} | score=${i.score.toFixed(1)} | labels=${labelStr}\nTitle: ${i.title}\nBody: ${bodySnippet || '(no body)'}`;
+ }).join('\n\n---\n\n');
const issueNumbers = scoredIssues.map(i => i.number).join(',');
@@ -352,6 +363,7 @@ on:
core.setOutput('issue_count', scoredIssues.length);
core.setOutput('issue_numbers', issueNumbers);
core.setOutput('issue_list', issueList);
+ core.setOutput('issue_context', issueContext);
if (scoredIssues.length === 0) {
core.info('🍽️ No suitable candidate issues - the plate is empty!');
@@ -364,6 +376,7 @@ on:
core.setOutput('issue_count', 0);
core.setOutput('issue_numbers', '');
core.setOutput('issue_list', '');
+ core.setOutput('issue_context', '');
core.setOutput('has_issues', 'false');
}
@@ -375,7 +388,7 @@ permissions:
engine:
id: copilot
- model: gpt-5.1-codex-mini
+ model: claude-haiku-4.5
imports:
- shared/github-guard-policy.md
@@ -387,7 +400,7 @@ tools:
mount-as-clis: true
github:
min-integrity: approved
- toolsets: [default, pull_requests]
+ toolsets: [issues]
if: needs.pre_activation.outputs.has_issues == 'true'
@@ -397,6 +410,7 @@ jobs:
issue_count: ${{ steps.search.outputs.issue_count }}
issue_numbers: ${{ steps.search.outputs.issue_numbers }}
issue_list: ${{ steps.search.outputs.issue_list }}
+ issue_context: ${{ steps.search.outputs.issue_context }}
has_issues: ${{ steps.search.outputs.has_issues }}
safe-outputs:
@@ -473,7 +487,12 @@ Issues are scored and sorted by priority:
${{ needs.pre_activation.outputs.issue_list }}
```
-Work with this pre-fetched, filtered, and prioritized list of issues. Do not perform additional searches - the issue numbers are already identified above, sorted from highest to lowest priority.
+**Pre-fetched Body Context (top candidates):**
+```
+${{ needs.pre_activation.outputs.issue_context }}
+```
+
+Work with this pre-fetched, filtered, and prioritized list of issues. Do not perform additional searches - candidate issue numbers and body excerpts are already identified above.
### 1a. Handle Parent-Child Issue Relationships (for "task" or "plan" labeled issues)
@@ -494,17 +513,7 @@ For issues with the "task" or "plan" label, check if they are sub-issues linked
- Only after #101's PR is merged/closed, process #102
- This ensures orderly, sequential processing of related tasks
-### 2. Review the Pre-Filtered Issue List
-
-The pre-activation job has already performed comprehensive filtering, including:
-- Issues already assigned to Copilot
-- Issues with open PRs linked to them (from any author)
-- Issues with closed/merged PRs (treated as complete)
-- **For "task" or "plan" labeled sub-issues**: Check if any sibling sub-issue (same parent) has an open PR from Copilot
-
-The list you receive has already been filtered to exclude all of these cases, so you can focus on the actual assignment logic.
-
-### 3. Select Up to Three Issues to Work On
+### 2. Select Up to Three Issues to Work On
From the prioritized and filtered list (issues WITHOUT Copilot assignments or open PRs):
- **Select up to three appropriate issues** to assign
@@ -538,10 +547,13 @@ From the prioritized and filtered list (issues WITHOUT Copilot assignments or op
- Assign only the issues that are clearly separate in topic
- Do not force assignments just to reach the maximum
-### 4. Read and Understand Each Selected Issue
+### 3. Validate Selected Issues (Body-First)
For each selected issue (which has already been pre-filtered to ensure no open/closed PRs exist):
-- Read the full issue body and any comments
+- Use the pre-fetched body context first
+- If a body excerpt is ambiguous, call `issue_read` with `method: get` for that issue
+- Do **not** fetch comments by default
+- Only fetch comments (`issue_read` with `method: get_comments`) when a specific triage rule truly requires comment context
- Understand what fix is needed
- Identify the files that need to be modified
- Verify it doesn't overlap with the other selected issues
@@ -562,7 +574,7 @@ Some issues may be blocked by an integrity policy when you try to read them with
→ Call `noop` with: `"🛡️ All 3 candidates (#100, #102, #105) were integrity-filtered. No assignments made this run."`
-### 5. Assign Issues to Copilot Agent
+### 4. Assign Issues to Copilot Agent
For each selected issue, use the `assign_to_agent` tool from the `safeoutputs` MCP server to assign the Copilot coding agent:
@@ -578,7 +590,7 @@ The Copilot coding agent will:
3. Create a pull request with the fix
4. Follow the repository's AGENTS.md guidelines
-### 6. Add Comment to Each Assigned Issue
+### 5. Add Comment to Each Assigned Issue
For each issue you assign, use the `add_comment` tool from the `safeoutputs` MCP server to add a comment:
@@ -616,21 +628,11 @@ Issue Monster runs frequently (every 30 minutes), so keeping each run lean is cr
## Success Criteria
A successful run means:
-1. **Rate limiting check passed** - The search verified no recent PRs are rate-limited (or workflow skipped if rate limiting detected)
-2. You reviewed the pre-searched, filtered, and prioritized issue list
-3. The search already excluded issues with problematic labels (wontfix, question, discussion, etc.)
-4. The search already excluded issues with campaign labels (campaign:*) as these are managed by campaign orchestrators
-5. The search already excluded issues that already have assignees
-6. The search already excluded issues that have sub-issues (parent/organizing issues are not tasks)
-7. The search already excluded issues with closed or merged PRs (treated as complete)
-8. The search already excluded issues with open PRs from Copilot coding agent (already being worked on)
-9. Issues are sorted by priority score (good-first-issue, bug, security, etc. get higher scores)
-10. For "task" or "plan" issues: You checked for parent issues and sibling sub-issue PRs if necessary
-11. You selected up to three appropriate issues from the top of the priority list that are completely separate in topic
-12. You read and understood each issue
-13. You verified that the selected issues don't have overlapping concerns or file changes
-14. You assigned each issue to the Copilot coding agent using `assign_to_agent`
-15. You commented on each issue being assigned
+1. You used the pre-fetched prioritized list (and body context) without re-searching
+2. You selected up to three issues that are clearly separate in topic
+3. You used body-first validation and only fetched comments when strictly necessary
+4. You assigned each selected issue to Copilot using `assign_to_agent`
+5. You commented on each assigned issue (or called `noop` when no assignments were made)
## Error Handling
From a23ee19844e78c8a5f48152668f7f843d56d52be Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 18 Apr 2026 21:21:34 +0000
Subject: [PATCH 3/3] refine issue-monster body-context constants and
comment-read guidance
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/a3fcf924-20fa-4301-856e-a7a449f806f4
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
---
.github/workflows/issue-monster.lock.yml | 38 +++++++++++++-----------
.github/workflows/issue-monster.md | 8 +++--
2 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml
index 6fdc5193c91..bfe28e24d60 100644
--- a/.github/workflows/issue-monster.lock.yml
+++ b/.github/workflows/issue-monster.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"58910504a6bc1879eb9028720bdaa59bcc5fdcc50365fb48d90864d9b6996522","strict":true,"agent_id":"copilot","agent_model":"claude-haiku-4.5"}
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"d10076fe5d53fb3703246a470b8702dfe80f8d1d2f7f209456cb47e04446c174","strict":true,"agent_id":"copilot","agent_model":"claude-haiku-4.5"}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_AGENT_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.24"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.24"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.24"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.24"},{"image":"ghcr.io/github/github-mcp-server:v1.0.0"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]}
# ___ _ _
# / _ \ | | (_)
@@ -76,6 +76,8 @@ name: "Issue Monster"
# with:
# script: |
# const { owner, repo } = context.repo;
+ # const MAX_ISSUES_WITH_BODY_CONTEXT = 8;
+ # const BODY_SNIPPET_MAX_LENGTH = 600;
#
# try {
# // Check for recent rate-limited PRs to avoid scheduling more work during rate limiting
@@ -397,9 +399,9 @@ name: "Issue Monster"
#
# // Pre-fetch compact body context for top candidates so the agent can
# // triage without extra reads in most runs.
- # const issueContext = scoredIssues.slice(0, 8).map(i => {
+ # const issueContext = scoredIssues.slice(0, MAX_ISSUES_WITH_BODY_CONTEXT).map(i => {
# const body = (i.body || '').replace(/\s+/g, ' ').trim();
- # const bodySnippet = body.length > 600 ? `${body.slice(0, 600)}…` : body;
+ # const bodySnippet = body.length > BODY_SNIPPET_MAX_LENGTH ? `${body.slice(0, BODY_SNIPPET_MAX_LENGTH)}…` : body;
# const labelStr = i.labels.length > 0 ? i.labels.join(', ') : 'none';
# return `#${i.number} | score=${i.score.toFixed(1)} | labels=${labelStr}\nTitle: ${i.title}\nBody: ${bodySnippet || '(no body)'}`;
# }).join('\n\n---\n\n');
@@ -558,20 +560,20 @@ jobs:
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_e23ac80547a592d3_EOF'
+ cat << 'GH_AW_PROMPT_e301ff5ec303eb90_EOF'
- GH_AW_PROMPT_e23ac80547a592d3_EOF
+ GH_AW_PROMPT_e301ff5ec303eb90_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_e23ac80547a592d3_EOF'
+ cat << 'GH_AW_PROMPT_e301ff5ec303eb90_EOF'
Tools: add_comment(max:3), assign_to_agent(max:3), missing_tool, missing_data, noop
- GH_AW_PROMPT_e23ac80547a592d3_EOF
+ GH_AW_PROMPT_e301ff5ec303eb90_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_e23ac80547a592d3_EOF'
+ cat << 'GH_AW_PROMPT_e301ff5ec303eb90_EOF'
The following GitHub context information is available for this workflow:
{{#if __GH_AW_GITHUB_ACTOR__ }}
@@ -600,14 +602,14 @@ jobs:
{{/if}}
- GH_AW_PROMPT_e23ac80547a592d3_EOF
+ GH_AW_PROMPT_e301ff5ec303eb90_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_e23ac80547a592d3_EOF'
+ cat << 'GH_AW_PROMPT_e301ff5ec303eb90_EOF'
{{#runtime-import .github/workflows/shared/github-guard-policy.md}}
{{#runtime-import .github/workflows/shared/activation-app.md}}
{{#runtime-import .github/workflows/issue-monster.md}}
- GH_AW_PROMPT_e23ac80547a592d3_EOF
+ GH_AW_PROMPT_e301ff5ec303eb90_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
@@ -799,9 +801,9 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_da8153b881a29d69_EOF'
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_8c9d5b9222cb4f50_EOF'
{"add_comment":{"max":3,"target":"*"},"assign_to_agent":{"allowed":["copilot"],"max":3,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_da8153b881a29d69_EOF
+ GH_AW_SAFE_OUTPUTS_CONFIG_8c9d5b9222cb4f50_EOF
- name: Write Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
@@ -1013,7 +1015,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_7ae1d42a2b775ce8_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_214645539377f980_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"github": {
@@ -1057,7 +1059,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_7ae1d42a2b775ce8_EOF
+ GH_AW_MCP_CONFIG_214645539377f980_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -1670,6 +1672,8 @@ jobs:
with:
script: |
const { owner, repo } = context.repo;
+ const MAX_ISSUES_WITH_BODY_CONTEXT = 8;
+ const BODY_SNIPPET_MAX_LENGTH = 600;
try {
// Check for recent rate-limited PRs to avoid scheduling more work during rate limiting
@@ -1991,9 +1995,9 @@ jobs:
// Pre-fetch compact body context for top candidates so the agent can
// triage without extra reads in most runs.
- const issueContext = scoredIssues.slice(0, 8).map(i => {
+ const issueContext = scoredIssues.slice(0, MAX_ISSUES_WITH_BODY_CONTEXT).map(i => {
const body = (i.body || '').replace(/\s+/g, ' ').trim();
- const bodySnippet = body.length > 600 ? `${body.slice(0, 600)}…` : body;
+ const bodySnippet = body.length > BODY_SNIPPET_MAX_LENGTH ? `${body.slice(0, BODY_SNIPPET_MAX_LENGTH)}…` : body;
const labelStr = i.labels.length > 0 ? i.labels.join(', ') : 'none';
return `#${i.number} | score=${i.score.toFixed(1)} | labels=${labelStr}\nTitle: ${i.title}\nBody: ${bodySnippet || '(no body)'}`;
}).join('\n\n---\n\n');
diff --git a/.github/workflows/issue-monster.md b/.github/workflows/issue-monster.md
index faf30dfcc70..88f63cc4cfc 100644
--- a/.github/workflows/issue-monster.md
+++ b/.github/workflows/issue-monster.md
@@ -25,6 +25,8 @@ on:
with:
script: |
const { owner, repo } = context.repo;
+ const MAX_ISSUES_WITH_BODY_CONTEXT = 8;
+ const BODY_SNIPPET_MAX_LENGTH = 600;
try {
// Check for recent rate-limited PRs to avoid scheduling more work during rate limiting
@@ -346,9 +348,9 @@ on:
// Pre-fetch compact body context for top candidates so the agent can
// triage without extra reads in most runs.
- const issueContext = scoredIssues.slice(0, 8).map(i => {
+ const issueContext = scoredIssues.slice(0, MAX_ISSUES_WITH_BODY_CONTEXT).map(i => {
const body = (i.body || '').replace(/\s+/g, ' ').trim();
- const bodySnippet = body.length > 600 ? `${body.slice(0, 600)}…` : body;
+ const bodySnippet = body.length > BODY_SNIPPET_MAX_LENGTH ? `${body.slice(0, BODY_SNIPPET_MAX_LENGTH)}…` : body;
const labelStr = i.labels.length > 0 ? i.labels.join(', ') : 'none';
return `#${i.number} | score=${i.score.toFixed(1)} | labels=${labelStr}\nTitle: ${i.title}\nBody: ${bodySnippet || '(no body)'}`;
}).join('\n\n---\n\n');
@@ -553,7 +555,7 @@ For each selected issue (which has already been pre-filtered to ensure no open/c
- Use the pre-fetched body context first
- If a body excerpt is ambiguous, call `issue_read` with `method: get` for that issue
- Do **not** fetch comments by default
-- Only fetch comments (`issue_read` with `method: get_comments`) when a specific triage rule truly requires comment context
+- Only fetch comments (`issue_read` with `method: get_comments`) when a specific triage rule truly requires comment context (for example: to confirm whether maintainers already requested a specific implementation approach, or to capture additional repro steps posted after the original issue body)
- Understand what fix is needed
- Identify the files that need to be modified
- Verify it doesn't overlap with the other selected issues