From 48839e415885f25a019fa86957eb7ae7f01ba12f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:03:22 +0000 Subject: [PATCH 1/4] Initial plan From be0db9beee4362036c172b7b5dfa85e222e40b15 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:12:11 +0000 Subject: [PATCH 2/4] docs: add announcement-capable category recommendation for create-discussion Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../content/docs/reference/frontmatter-full.md | 3 +++ docs/src/content/docs/reference/safe-outputs.md | 16 ++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/src/content/docs/reference/frontmatter-full.md b/docs/src/content/docs/reference/frontmatter-full.md index fae631480f6..efcfea605da 100644 --- a/docs/src/content/docs/reference/frontmatter-full.md +++ b/docs/src/content/docs/reference/frontmatter-full.md @@ -2191,6 +2191,9 @@ safe-outputs: # available category. Matched first against category IDs, then against category # names, then against category slugs. Numeric values are automatically converted # to strings at runtime. + # + # Best Practice: Use announcement-capable categories (such as "announcements") + # for AI-generated content to ensure proper visibility and notification features. # (optional) category: null diff --git a/docs/src/content/docs/reference/safe-outputs.md b/docs/src/content/docs/reference/safe-outputs.md index 8f77c7eb1f3..d1a3b565f44 100644 --- a/docs/src/content/docs/reference/safe-outputs.md +++ b/docs/src/content/docs/reference/safe-outputs.md @@ -847,15 +847,19 @@ Creates discussions with optional `category` (slug, name, or ID; defaults to fir **Category Naming Standard**: Use lowercase, plural category names (e.g., `audits`, `general`, `reports`) for consistency and better searchability. GitHub Discussion category IDs (starting with `DIC_`) are also supported. +:::note[Announcement-Capable Categories] +When choosing a category automatically, use an **announcement-capable category** (such as the default "Announcements" category). Announcement categories provide enhanced visibility and notification features in GitHub Discussions. If the "Announcements" category is available in your repository, it's recommended to use it for AI-generated content to ensure proper visibility. +::: + ```yaml wrap safe-outputs: create-discussion: - title-prefix: "[ai] " # prefix for titles - category: "general" # category slug, name, or ID (use lowercase) - expires: 3 # auto-close after 3 days (or false to disable) - max: 3 # max discussions (default: 1) - target-repo: "owner/repo" # cross-repository - fallback-to-issue: true # fallback to issue creation on permission errors (default: true) + title-prefix: "[ai] " # prefix for titles + category: "announcements" # category slug, name, or ID (use lowercase, prefer announcement-capable) + expires: 3 # auto-close after 3 days (or false to disable) + max: 3 # max discussions (default: 1) + target-repo: "owner/repo" # cross-repository + fallback-to-issue: true # fallback to issue creation on permission errors (default: true) ``` #### Fallback to Issue Creation From da99dba20090ec22283b71b1fac6445001d0dd9a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:22:50 +0000 Subject: [PATCH 3/4] feat: prefer Announcements category by default in create_discussion When no category is specified or a non-existent category is requested, create_discussion now intelligently falls back to an "Announcements" category (if available) instead of simply using the first category. This change ensures AI-generated discussions are created in announcement-capable categories by default, providing enhanced visibility and notification features. Added tests to verify: - Fallback to Announcements when no category specified - Fallback to Announcements when non-existent category specified - Existing behavior preserved when Announcements not available Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/create_discussion.cjs | 15 +- ...discussion_category_normalization.test.cjs | 132 ++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/actions/setup/js/create_discussion.cjs b/actions/setup/js/create_discussion.cjs index 0d6daf14244..d34a5d68619 100644 --- a/actions/setup/js/create_discussion.cjs +++ b/actions/setup/js/create_discussion.cjs @@ -85,8 +85,21 @@ function resolveCategoryId(categoryConfig, itemCategory, categories) { } } - // Fall back to first category if available + // Fall back to announcement-capable category if available, otherwise first category if (categories.length > 0) { + // Try to find an "Announcements" category (case-insensitive) + const announcementCategory = categories.find(cat => cat.name.toLowerCase() === "announcements" || cat.slug.toLowerCase() === "announcements"); + + if (announcementCategory) { + return { + id: announcementCategory.id, + matchType: "fallback-announcement", + name: announcementCategory.name, + requestedCategory: categoryToMatch, + }; + } + + // Otherwise use first category return { id: categories[0].id, matchType: "fallback", diff --git a/actions/setup/js/create_discussion_category_normalization.test.cjs b/actions/setup/js/create_discussion_category_normalization.test.cjs index ed5e0abf307..b4d4010a865 100644 --- a/actions/setup/js/create_discussion_category_normalization.test.cjs +++ b/actions/setup/js/create_discussion_category_normalization.test.cjs @@ -271,4 +271,136 @@ describe("create_discussion category normalization", () => { expect(createMutationCall).toBeDefined(); expect(createMutationCall[1].categoryId).toBe("DIC_kwDOGFsHUM4BsUn1"); // General (first) }); + + it("should prefer Announcements category when no category specified", async () => { + // Mock categories with Announcements available + mockGithub.graphql = vi.fn().mockImplementation((query, variables) => { + if (query.includes("discussionCategories")) { + return Promise.resolve({ + repository: { + id: "R_test123", + discussionCategories: { + nodes: [ + { + id: "DIC_kwDOGFsHUM4BsUn1", + name: "General", + slug: "general", + description: "General discussions", + }, + { + id: "DIC_kwDOGFsHUM4BsUn4", + name: "Announcements", + slug: "announcements", + description: "Announcements", + }, + { + id: "DIC_kwDOGFsHUM4BsUn2", + name: "Audits", + slug: "audits", + description: "Audit reports", + }, + ], + }, + }, + }); + } + if (query.includes("createDiscussion")) { + return Promise.resolve({ + createDiscussion: { + discussion: { + id: "D_test456", + number: 42, + title: variables.title, + url: "https://github.com/test-owner/test-repo/discussions/42", + }, + }, + }); + } + return Promise.reject(new Error("Unknown GraphQL query")); + }); + + const handler = await createDiscussionMain({ + max: 5, + // No category specified + }); + + const result = await handler( + { + title: "Test Discussion", + body: "This is a test discussion.", + }, + {} + ); + + expect(result.success).toBe(true); + expect(result.number).toBe(42); + + // Verify Announcements category was used (not General which is first) + const createMutationCall = mockGithub.graphql.mock.calls.find(call => call[0].includes("createDiscussion")); + expect(createMutationCall).toBeDefined(); + expect(createMutationCall[1].categoryId).toBe("DIC_kwDOGFsHUM4BsUn4"); // Announcements + }); + + it("should prefer Announcements category when non-existent category specified", async () => { + // Mock categories with Announcements available + mockGithub.graphql = vi.fn().mockImplementation((query, variables) => { + if (query.includes("discussionCategories")) { + return Promise.resolve({ + repository: { + id: "R_test123", + discussionCategories: { + nodes: [ + { + id: "DIC_kwDOGFsHUM4BsUn1", + name: "General", + slug: "general", + description: "General discussions", + }, + { + id: "DIC_kwDOGFsHUM4BsUn4", + name: "Announcements", + slug: "announcements", + description: "Announcements", + }, + ], + }, + }, + }); + } + if (query.includes("createDiscussion")) { + return Promise.resolve({ + createDiscussion: { + discussion: { + id: "D_test456", + number: 42, + title: variables.title, + url: "https://github.com/test-owner/test-repo/discussions/42", + }, + }, + }); + } + return Promise.reject(new Error("Unknown GraphQL query")); + }); + + const handler = await createDiscussionMain({ + max: 5, + category: "NonExistentCategory", + }); + + const result = await handler( + { + title: "Test Discussion", + body: "This is a test discussion.", + }, + {} + ); + + expect(result.success).toBe(true); + expect(result.number).toBe(42); + + // Verify Announcements category was used (not General which is first) + const createMutationCall = mockGithub.graphql.mock.calls.find(call => call[0].includes("createDiscussion")); + expect(createMutationCall).toBeDefined(); + expect(createMutationCall[1].categoryId).toBe("DIC_kwDOGFsHUM4BsUn4"); // Announcements + }); }); From ec45bbe407181a4c6cd1b3ad4c258357666ea9b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:39:16 +0000 Subject: [PATCH 4/4] feat: add announcement category hint to discussion fallback message When create-discussion fails and falls back to creating an issue, the fallback message now includes a tip suggesting to use announcement-capable categories like "Announcements". This helps users understand that discussion creation may fail if the specified category is not announcement-capable, guiding them toward the correct configuration. Updated test to verify the new message content. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/create_discussion.cjs | 2 +- actions/setup/js/create_discussion_fallback.test.cjs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/actions/setup/js/create_discussion.cjs b/actions/setup/js/create_discussion.cjs index d34a5d68619..c66173d0ab7 100644 --- a/actions/setup/js/create_discussion.cjs +++ b/actions/setup/js/create_discussion.cjs @@ -140,7 +140,7 @@ function isPermissionsError(errorMessage) { async function handleFallbackToIssue(createIssueHandler, item, qualifiedItemRepo, resolvedTemporaryIds, contextMessage) { try { // Prepare issue message with a note about the fallback - const fallbackNote = `\n\n---\n\n> **Note:** This was intended to be a discussion, but discussions could not be created due to permissions issues. This issue was created as a fallback.\n`; + const fallbackNote = `\n\n---\n\n> **Note:** This was intended to be a discussion, but discussions could not be created due to permissions issues. This issue was created as a fallback.\n>\n> **Tip:** Discussion creation may fail if the specified category is not announcement-capable. Consider using the "Announcements" category or another announcement-capable category in your workflow configuration.\n`; const issueMessage = { ...item, body: (item.body || "") + fallbackNote, diff --git a/actions/setup/js/create_discussion_fallback.test.cjs b/actions/setup/js/create_discussion_fallback.test.cjs index f7793ab79c1..38c0a456a9b 100644 --- a/actions/setup/js/create_discussion_fallback.test.cjs +++ b/actions/setup/js/create_discussion_fallback.test.cjs @@ -141,6 +141,12 @@ describe("create_discussion fallback with close_older_discussions", () => { }) ); + // Verify the fallback note includes the announcement category tip + const createCallArgs = mockGithub.rest.issues.create.mock.calls[0][0]; + expect(createCallArgs.body).toContain("announcement-capable"); + expect(createCallArgs.body).toContain("Announcements"); + expect(createCallArgs.body).toContain("category"); + // Verify search for older issues was performed expect(mockGithub.rest.search.issuesAndPullRequests).toHaveBeenCalledWith({ q: 'repo:test-owner/test-repo is:issue is:open "gh-aw-workflow-id: test-workflow" in:body',