Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/issue-monster.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .github/workflows/issue-monster.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,8 @@ safeoutputs/assign_to_agent(issue_number=<issue_number>, agent="copilot")

Use the exact field name `issue_number` (underscore). Do **not** use `issue-number` (hyphen), which is invalid and will fail safe-output validation.

**Important**: Only call `assign_to_agent` for **issues**, never for pull requests. The pre-fetched list already contains only issues, so never pass a PR number here. If you are ever unsure whether a number refers to an issue or a PR, call `issue_read` with `method: get` and check: if the response includes a `pull_request` URL field, skip that item.

Do not use GitHub tools for this assignment. The `assign_to_agent` tool will handle the actual assignment.

The Copilot coding agent will:
Expand Down Expand Up @@ -641,6 +643,7 @@ Issue Monster runs frequently (every 30 minutes), so keeping each run lean is cr
- ✅ **Always report outcome**: If no issues are assigned, use the `noop` tool to explain why
- ✅ **Skip integrity-blocked issues**: If `issue_read` is blocked by integrity policy, skip that issue and continue — never call `missing_data` for integrity errors
- ❌ **Don't force batching**: If only 1-2 clearly separate issues exist, assign only those
- ❌ **Never assign pull requests**: `assign_to_agent` is for issues only — never pass a PR number

## skill: `issue-monster-report-formatting`
---
Expand Down
9 changes: 8 additions & 1 deletion actions/setup/js/assign_agent_helpers.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@ async function getIssueDetails(owner, repo, issueNumber, githubClient = github)
core.error("Could not get issue data");
return null;
}
// GitHub's issues API returns pull requests too; reject them here so callers
// never accidentally treat a PR as an assignable issue.
if (issue.pull_request) {
throw Object.assign(new Error(`#${issueNumber} is a pull request, not an issue — use pull_number instead of issue_number to assign to a pull request`), { isPullRequest: true });
}
const currentAssignees = (issue.assignees || []).map(assignee => ({
id: String(assignee.id),
login: assignee.login,
Expand All @@ -290,7 +295,9 @@ async function getIssueDetails(owner, repo, issueNumber, githubClient = github)
};
} catch (error) {
const errorMessage = getErrorMessage(error);
core.error(`Failed to get issue details: ${errorMessage}`);
if (!(/** @type {any} */ error.isPullRequest)) {
core.error(`Failed to get issue details: ${errorMessage}`);
}
throw error;
}
}
Expand Down
18 changes: 18 additions & 0 deletions actions/setup/js/assign_to_agent.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,24 @@ async function main(config = {}) {
} catch (error) {
let errorMessage = getErrorMessage(error);

// When the agent specified an issue_number that turns out to be a PR, skip
// silently without posting a comment — error comments on PRs are confusing.
if (/** @type {any} */ error.isPullRequest) {
core.warning(`Skipping assign_to_agent for #${number}: target is a pull request, not an issue.`);
_allResults.push({
issue_number: issueNumber,
pull_number: pullNumber,
agent: agentName,
owner: effectiveOwner,
repo: effectiveRepo,
pull_request_repo: effectivePullRequestRepoSlug,
success: false,
skipped: true,
error: errorMessage,
});
return { success: false, skipped: true, error: errorMessage };
}

const isAuthError = ["Bad credentials", "Not Authenticated", "Resource not accessible", "Insufficient permissions", "requires authentication"].some(msg => errorMessage.includes(msg));
const isAvailabilityError = errorMessage.includes("coding agent is not available for this repository");

Expand Down
28 changes: 28 additions & 0 deletions actions/setup/js/assign_to_agent.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,34 @@ describe("assign_to_agent", () => {
expect(mockGithub.rest.issues.createComment).not.toHaveBeenCalled();
});

it("should skip silently and not post a comment when issue_number resolves to a pull request", async () => {
setAgentOutput({
items: [{ type: "assign_to_agent", issue_number: 99, agent: "copilot" }],
errors: [],
});

// Simulate the issues.get API returning a PR (has pull_request field)
mockGithub.rest.issues.get.mockResolvedValueOnce({
data: {
id: 99999,
number: 99,
assignees: [],
html_url: "https://github.com/test-owner/test-repo/pull/99",
title: "Some PR",
body: "",
pull_request: { url: "https://api.github.com/repos/test-owner/test-repo/pulls/99" },
},
});

await eval(`(async () => { ${assignToAgentScript}; ${STANDALONE_RUNNER} })()`);

// Should warn about the PR but not post a comment on it
expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("pull request, not an issue"));
expect(mockGithub.rest.issues.createComment).not.toHaveBeenCalled();
// Should not call setFailed — skipping a PR target is not a workflow failure
expect(mockCore.setFailed).not.toHaveBeenCalled();
});

it("should post failure comment on single failed assignment", async () => {
setAgentOutput({
items: [{ type: "assign_to_agent", issue_number: 11, agent: "copilot" }],
Expand Down
Loading