From 892d563f019cd796dd2367d6319633e2c83cd724 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:30:26 +0000 Subject: [PATCH 01/10] Initial plan From 33db64b2545aa58fe1629cc3c89ac96fe83a4a6b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:02:10 +0000 Subject: [PATCH 02/10] feat(safe-outputs): add issue-intent metadata controls for close and assignment tools Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/assign_agent_helpers.cjs | 14 ++++- .../setup/js/assign_agent_helpers.test.cjs | 17 +++++ actions/setup/js/assign_to_agent.cjs | 21 ++++++- actions/setup/js/assign_to_user.cjs | 15 ++++- actions/setup/js/assign_to_user.test.cjs | 52 +++++++++++++++ actions/setup/js/close_issue.cjs | 29 +++++++-- actions/setup/js/close_issue.test.cjs | 63 +++++++++++++++++++ .../setup/js/generate_safe_outputs_tools.cjs | 24 ++++++- .../js/generate_safe_outputs_tools.test.cjs | 38 ++++++++++- actions/setup/js/safe_outputs_tools.json | 42 +++++++++++++ actions/setup/js/types/safe-outputs.d.ts | 4 +- pkg/workflow/js/safe_outputs_tools.json | 54 ++++++++++++++++ .../safe_output_validation_config_test.go | 12 ++-- pkg/workflow/safe_outputs_config_types.go | 1 + pkg/workflow/safe_outputs_handler_registry.go | 6 ++ pkg/workflow/safe_outputs_tools_generation.go | 20 ++++++ .../safe_outputs_tools_generation_test.go | 25 ++++++++ .../safe_outputs_validation_config.go | 11 +++- 18 files changed, 423 insertions(+), 25 deletions(-) diff --git a/actions/setup/js/assign_agent_helpers.cjs b/actions/setup/js/assign_agent_helpers.cjs index 96d231dae0c..9779aee4ff7 100644 --- a/actions/setup/js/assign_agent_helpers.cjs +++ b/actions/setup/js/assign_agent_helpers.cjs @@ -332,6 +332,8 @@ async function getPullRequestDetails(owner, repo, pullNumber, githubClient = git * @param {Object} [githubClient] - Authenticated GitHub client (defaults to global github) * @param {{owner: string, repo: string, type: "issue"|"pull", number: number}|null} [taskContext] - Source issue/PR context for REST path * @param {string|null} [pullRequestRepoSlug] - Optional pull request repository slug (owner/repo) for REST path + * @param {{rationale?: string, confidence?: "LOW"|"MEDIUM"|"HIGH", suggest?: boolean}} [intentMetadata] - Optional issue-intent metadata + * @param {boolean} [useIssueIntent] - Whether to include issue-intent metadata/headers * @returns {Promise} True if successful */ async function assignAgentToIssue( @@ -346,7 +348,9 @@ async function assignAgentToIssue( baseBranch = null, githubClient = github, taskContext = null, - pullRequestRepoSlug = null + pullRequestRepoSlug = null, + intentMetadata = {}, + useIssueIntent = true ) { // SECURITY: pullRequestRepoSlug specifies a cross-repo target repository slug. // Callers MUST validate the corresponding repository slug against allowedRepos using @@ -391,12 +395,16 @@ async function assignAgentToIssue( try { core.info(`Assigning via issues assignees REST API with login: ${agentLogin}`); - await githubClient.request("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", { + const assignParams = { owner: targetOwner, repo: targetRepo, issue_number: issueNumber, assignees: [agentLogin], - }); + }; + if (useIssueIntent && intentMetadata && Object.keys(intentMetadata).length > 0) { + Object.assign(assignParams, intentMetadata, { headers: { "GraphQL-Features": "update_issue_suggestions" } }); + } + await githubClient.request("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", assignParams); return true; } catch (error) { const errorMessage = getErrorMessage(error); diff --git a/actions/setup/js/assign_agent_helpers.test.cjs b/actions/setup/js/assign_agent_helpers.test.cjs index d66903bd11c..b81e23b6edb 100644 --- a/actions/setup/js/assign_agent_helpers.test.cjs +++ b/actions/setup/js/assign_agent_helpers.test.cjs @@ -383,6 +383,23 @@ describe("assign_agent_helpers.cjs", () => { expect(mockRequest).toHaveBeenCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", expect.objectContaining({ owner: "myorg", repo: "myrepo", issue_number: 42 })); }); + it("should include issue-intent metadata when enabled", async () => { + const mockRequest = vi.fn().mockResolvedValue({ status: 201 }); + const restClient = { request: mockRequest }; + + await assignAgentToIssue("id", "copilot-swe-agent[bot]", [], "copilot", null, null, null, null, null, restClient, taskContext, null, { rationale: "Agent owns the code path", confidence: "HIGH" }, true); + + expect(mockRequest).toHaveBeenCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", { + owner: "myorg", + repo: "myrepo", + issue_number: 42, + assignees: ["copilot-swe-agent[bot]"], + rationale: "Agent owns the code path", + confidence: "HIGH", + headers: { "GraphQL-Features": "update_issue_suggestions" }, + }); + }); + it("should return false and not call request when taskContext is missing", async () => { const mockRequest = vi.fn(); const restClient = { request: mockRequest }; diff --git a/actions/setup/js/assign_to_agent.cjs b/actions/setup/js/assign_to_agent.cjs index 2fc400af668..e9a9bb22f49 100644 --- a/actions/setup/js/assign_to_agent.cjs +++ b/actions/setup/js/assign_to_agent.cjs @@ -10,6 +10,7 @@ const { sleep } = require("./error_recovery.cjs"); const { parseAllowedRepos, validateRepo, resolveTargetRepoConfig, resolveAndValidateRepo } = require("./repo_helpers.cjs"); const { resolvePullRequestRepo } = require("./pr_helpers.cjs"); const { sanitizeContent } = require("./sanitize_content.cjs"); +const { normalizeIssueIntentMetadata } = require("./issue_intents.cjs"); /** * Module-level state — populated by main(), read by the exported getters below. @@ -64,6 +65,7 @@ async function main(config = {}) { const configuredBaseBranch = config["base-branch"] ? String(config["base-branch"]).trim() : null; const targetConfig = config.target ? String(config.target).trim() : "triggering"; const ignoreIfError = config["ignore-if-error"] === true || config["ignore-if-error"] === "true"; + const issueIntentEnabled = config.issue_intent !== false; const allowedAgents = config.allowed ? Array.isArray(config.allowed) ? config.allowed.map(a => String(a).trim()).filter(Boolean) @@ -87,6 +89,7 @@ async function main(config = {}) { if (configuredBaseBranch) core.info(`Configured base branch: ${configuredBaseBranch}`); core.info(`Target configuration: ${targetConfig}`); core.info(`Max count: ${maxCount}`); + core.info(`Issue intent enabled: ${issueIntentEnabled}`); if (ignoreIfError) core.info("Ignore-if-error mode enabled: Will not fail if agent assignment encounters auth or availability errors"); if (allowedAgents) core.info(`Allowed agents: ${allowedAgents.join(", ")}`); core.info(`Default target repo: ${defaultTargetRepo}`); @@ -179,6 +182,7 @@ async function main(config = {}) { } const agentName = message.agent ?? defaultAgent; + const intentMetadata = issueIntentEnabled ? normalizeIssueIntentMetadata(message) : {}; const model = defaultModel; const customAgent = defaultCustomAgent; const customInstructions = defaultCustomInstructions || null; @@ -387,7 +391,22 @@ async function main(config = {}) { if (customInstructions) core.info(`Using custom instructions: ${customInstructions.substring(0, 100)}${customInstructions.length > 100 ? "..." : ""}`); if (effectiveBaseBranch) core.info(`Using base branch: ${effectiveBaseBranch}`); - const success = await assignAgentToIssue(assignableId, agentLogin, currentAssignees, agentName, allowedAgents, model, customAgent, customInstructions, effectiveBaseBranch, githubClient, taskContext, effectivePullRequestRepoSlug); + const success = await assignAgentToIssue( + assignableId, + agentLogin, + currentAssignees, + agentName, + allowedAgents, + model, + customAgent, + customInstructions, + effectiveBaseBranch, + githubClient, + taskContext, + effectivePullRequestRepoSlug, + intentMetadata, + issueIntentEnabled + ); if (!success) throw new Error(`Failed to assign ${agentName} via REST`); core.info(`Successfully assigned ${agentName} coding agent to ${type} #${number}`); diff --git a/actions/setup/js/assign_to_user.cjs b/actions/setup/js/assign_to_user.cjs index 0ce67b04ae0..29a796f908d 100644 --- a/actions/setup/js/assign_to_user.cjs +++ b/actions/setup/js/assign_to_user.cjs @@ -13,6 +13,7 @@ const { logStagedPreviewInfo } = require("./staged_preview.cjs"); const { parseBoolTemplatable } = require("./templatable.cjs"); const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { createCountGatedHandler } = require("./handler_scaffold.cjs"); +const { normalizeIssueIntentMetadata } = require("./issue_intents.cjs"); /** @type {string} Safe output type handled by this module */ const HANDLER_TYPE = "assign_to_user"; @@ -29,6 +30,7 @@ const main = createCountGatedHandler({ const allowedAssignees = config.allowed ?? []; const blockedAssignees = config.blocked ?? []; const unassignFirst = parseBoolTemplatable(config.unassign_first, false); + const issueIntentEnabled = config.issue_intent !== false; const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config); const githubClient = await createAuthenticatedGitHubClient(config); const requiredLabels = Array.isArray(config.required_labels) ? config.required_labels : []; @@ -36,7 +38,7 @@ const main = createCountGatedHandler({ if (requiredLabels.length > 0) core.info(`Required labels (all): ${requiredLabels.join(", ")}`); if (requiredTitlePrefix) core.info(`Required title prefix: ${requiredTitlePrefix}`); - core.info(`Assign to user configuration: max=${maxCount}, unassign_first=${unassignFirst}`); + core.info(`Assign to user configuration: max=${maxCount}, unassign_first=${unassignFirst}, issue_intent=${issueIntentEnabled}`); if (allowedAssignees.length > 0) { core.info(`Allowed assignees: ${allowedAssignees.join(", ")}`); } @@ -68,6 +70,7 @@ const main = createCountGatedHandler({ core.info(`Target repository: ${itemRepo}`); const assignItem = message; + const intentMetadata = issueIntentEnabled ? normalizeIssueIntentMetadata(assignItem) : {}; // Determine issue number using shared helper const issueResult = resolveIssueNumber(assignItem); @@ -147,12 +150,18 @@ const main = createCountGatedHandler({ } // Add assignees to the issue - await githubClient.rest.issues.addAssignees({ + const addAssigneesParams = { owner: repoParts.owner, repo: repoParts.repo, issue_number: issueNumber, assignees: uniqueAssignees, - }); + }; + if (issueIntentEnabled && Object.keys(intentMetadata).length > 0) { + Object.assign(addAssigneesParams, intentMetadata, { + headers: { "GraphQL-Features": "update_issue_suggestions" }, + }); + } + await githubClient.rest.issues.addAssignees(addAssigneesParams); core.info(`Successfully assigned ${uniqueAssignees.length} user(s) to issue #${issueNumber} in ${itemRepo}`); diff --git a/actions/setup/js/assign_to_user.test.cjs b/actions/setup/js/assign_to_user.test.cjs index a78e81e8593..b3b4ecaba33 100644 --- a/actions/setup/js/assign_to_user.test.cjs +++ b/actions/setup/js/assign_to_user.test.cjs @@ -78,6 +78,58 @@ describe("assign_to_user (Handler Factory Architecture)", () => { }); }); + it("should include issue-intent metadata when enabled", async () => { + mockGithub.rest.issues.addAssignees.mockResolvedValue({}); + + const message = { + type: "assign_to_user", + assignees: ["user1"], + rationale: "Primary maintainer for impacted area", + confidence: "low", + }; + + const result = await handler(message, {}); + + expect(result.success).toBe(true); + expect(mockGithub.rest.issues.addAssignees).toHaveBeenCalledWith({ + owner: "test-owner", + repo: "test-repo", + issue_number: 123, + assignees: ["user1"], + rationale: "Primary maintainer for impacted area", + confidence: "LOW", + headers: { "GraphQL-Features": "update_issue_suggestions" }, + }); + }); + + it("should omit issue-intent metadata when disabled", async () => { + const { main } = require("./assign_to_user.cjs"); + const noIntentHandler = await main({ + max: 10, + allowed: ["user1", "user2", "user3"], + issue_intent: false, + }); + mockGithub.rest.issues.addAssignees.mockResolvedValue({}); + + const result = await noIntentHandler( + { + type: "assign_to_user", + assignees: ["user1"], + rationale: "Primary maintainer for impacted area", + confidence: "high", + }, + {} + ); + + expect(result.success).toBe(true); + expect(mockGithub.rest.issues.addAssignees).toHaveBeenCalledWith({ + owner: "test-owner", + repo: "test-repo", + issue_number: 123, + assignees: ["user1"], + }); + }); + it("should support singular assignee field", async () => { mockGithub.rest.issues.addAssignees.mockResolvedValue({}); diff --git a/actions/setup/js/close_issue.cjs b/actions/setup/js/close_issue.cjs index e1c018fa73e..035add799e2 100644 --- a/actions/setup/js/close_issue.cjs +++ b/actions/setup/js/close_issue.cjs @@ -11,6 +11,7 @@ const { ERR_NOT_FOUND } = require("./error_codes.cjs"); const { createCloseEntityHandler, ISSUE_CONFIG } = require("./close_entity_helpers.cjs"); const { loadTemporaryIdMapFromResolved, resolveRepoIssueTarget } = require("./temporary_id.cjs"); const { getErrorMessage } = require("./error_helpers.cjs"); +const { normalizeIssueIntentMetadata } = require("./issue_intents.cjs"); /** * Parse a `duplicate_of` value into { owner, repo, issueNumber }. @@ -157,14 +158,30 @@ async function addIssueComment(github, owner, repo, issueNumber, message) { * @param {string} [stateReason] - The reason for closing: "COMPLETED", "NOT_PLANNED", or "DUPLICATE" * @returns {Promise<{number: number, html_url: string, title: string, node_id: string}>} Issue details */ -async function closeIssue(github, owner, repo, issueNumber, stateReason) { - const { data: issue } = await github.rest.issues.update({ +async function closeIssue(github, owner, repo, issueNumber, stateReason, intentMetadata, useIssueIntent) { + const baseParams = { owner, repo, issue_number: issueNumber, state: "closed", state_reason: (stateReason || "COMPLETED").toLowerCase(), - }); + }; + + const hasIntentMetadata = Boolean(intentMetadata && Object.keys(intentMetadata).length > 0); + if (useIssueIntent && hasIntentMetadata) { + try { + const { data: issue } = await github.request("PATCH /repos/{owner}/{repo}/issues/{issue_number}", { + ...baseParams, + ...intentMetadata, + headers: { "GraphQL-Features": "update_issue_suggestions" }, + }); + return issue; + } catch (error) { + core.warning(`Issue-intent close path unavailable, falling back to legacy close path: ${getErrorMessage(error)}`); + } + } + + const { data: issue } = await github.rest.issues.update(baseParams); return issue; } @@ -176,12 +193,13 @@ async function closeIssue(github, owner, repo, issueNumber, stateReason) { */ async function main(config = {}) { const configStateReason = config.state_reason || "COMPLETED"; + const issueIntentEnabled = config.issue_intent !== false; const requiredLabels = config.required_labels || []; const requiredTitlePrefix = config.required_title_prefix || ""; const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config); const githubClient = await createAuthenticatedGitHubClient(config); - core.info(`Close issue configuration: max=${config.max || 10}, state_reason=${configStateReason}`); + core.info(`Close issue configuration: max=${config.max || 10}, state_reason=${configStateReason}, issue_intent=${issueIntentEnabled}`); if (requiredLabels.length > 0) { core.info(`Required labels: ${requiredLabels.join(", ")}`); } @@ -265,9 +283,10 @@ async function main(config = {}) { closeEntity(github, owner, repo, entityNumber, item) { // Support item-level state_reason override, falling back to config-level default const stateReason = item.state_reason || configStateReason; + const intentMetadata = issueIntentEnabled ? normalizeIssueIntentMetadata(item) : {}; core.info(`Closing issue #${entityNumber} with state_reason=${stateReason}`); - const closePromise = closeIssue(github, owner, repo, entityNumber, stateReason); + const closePromise = closeIssue(github, owner, repo, entityNumber, stateReason, intentMetadata, issueIntentEnabled); // When duplicate_of is provided and state_reason is DUPLICATE, create the native duplicate relationship const stateReasonUpper = stateReason.toUpperCase(); diff --git a/actions/setup/js/close_issue.test.cjs b/actions/setup/js/close_issue.test.cjs index ce8ec5b18d2..cf6c96db8a4 100644 --- a/actions/setup/js/close_issue.test.cjs +++ b/actions/setup/js/close_issue.test.cjs @@ -139,6 +139,69 @@ describe("close_issue", () => { expect(updateCalls[0].state).toBe("closed"); }); + it("should include issue-intent metadata on close when enabled", async () => { + const handler = await main({ max: 10 }); + const requestCalls = []; + mockGithub.request = async (route, params) => { + requestCalls.push({ route, params }); + return { + data: { + number: params.issue_number, + title: "Test Issue", + html_url: `https://github.com/${params.owner}/${params.repo}/issues/${params.issue_number}`, + }, + }; + }; + + const result = await handler( + { + issue_number: 456, + body: "Closing this issue", + rationale: "Duplicate confirmed", + confidence: "medium", + }, + {} + ); + + expect(result.success).toBe(true); + expect(requestCalls).toHaveLength(1); + expect(requestCalls[0].route).toBe("PATCH /repos/{owner}/{repo}/issues/{issue_number}"); + expect(requestCalls[0].params.rationale).toBe("Duplicate confirmed"); + expect(requestCalls[0].params.confidence).toBe("MEDIUM"); + expect(requestCalls[0].params.headers).toEqual({ "GraphQL-Features": "update_issue_suggestions" }); + }); + + it("should skip issue-intent metadata when explicitly disabled", async () => { + const handler = await main({ max: 10, issue_intent: false }); + const updateCalls = []; + mockGithub.rest.issues.update = async params => { + updateCalls.push(params); + return { + data: { + number: params.issue_number, + title: "Test Issue", + html_url: `https://github.com/${params.owner}/${params.repo}/issues/${params.issue_number}`, + }, + }; + }; + + const result = await handler( + { + issue_number: 456, + body: "Closing this issue", + rationale: "Duplicate confirmed", + confidence: "high", + }, + {} + ); + + expect(result.success).toBe(true); + expect(updateCalls).toHaveLength(1); + expect(updateCalls[0].rationale).toBeUndefined(); + expect(updateCalls[0].confidence).toBeUndefined(); + expect(updateCalls[0].headers).toBeUndefined(); + }); + it("should close an issue from context when issue_number not provided", async () => { const handler = await main({ max: 10 }); const updateCalls = []; diff --git a/actions/setup/js/generate_safe_outputs_tools.cjs b/actions/setup/js/generate_safe_outputs_tools.cjs index 159cd6d978b..c867f2b056a 100644 --- a/actions/setup/js/generate_safe_outputs_tools.cjs +++ b/actions/setup/js/generate_safe_outputs_tools.cjs @@ -38,6 +38,26 @@ const ADD_COMMENT_DISCUSSIONS_DISABLED_NOTE = "NOTE: Discussion comments are disabled for this workflow because discussions:write permission is not available. Set 'discussions: true' in the workflow's safe-outputs.add-comment configuration to enable discussion comments and request this permission."; const ADD_COMMENT_REPLY_SUPPORT_SENTENCE = "Supports reply_to_id for discussion threading."; const ADD_COMMENT_REPLY_SUPPORT_REGEX = /\s*Supports reply_to_id for discussion threading\./g; +const ISSUE_INTENT_SUFFIX = "INTENT: Include rationale (string, max 280 chars) and confidence (string, exactly one of: LOW, MEDIUM, HIGH) with each call."; +const ISSUE_INTENT_TOOL_NAMES = new Set(["set_issue_type", "set_issue_field", "add_labels", "close_issue", "assign_to_user", "assign_to_agent"]); + +/** + * Determine whether issue-intent guidance is enabled for a tool. + * Default is enabled; explicit issue_intent: false disables it. + * + * @param {string} toolName + * @param {unknown} toolConfig + * @returns {boolean} + */ +function isIssueIntentEnabledForTool(toolName, toolConfig) { + if (!ISSUE_INTENT_TOOL_NAMES.has(toolName)) { + return false; + } + if (toolConfig && typeof toolConfig === "object" && "issue_intent" in toolConfig) { + return toolConfig.issue_intent !== false; + } + return true; +} /** * Update add_comment description to match runtime-safe-output permissions. @@ -148,8 +168,8 @@ async function main() { if (descSuffix) { enhancedTool.description = (enhancedTool.description || "") + descSuffix; } - if (["set_issue_type", "set_issue_field", "add_labels"].includes(tool.name)) { - enhancedTool.description = `${enhancedTool.description || ""} INTENT: Include rationale (string, max 280 chars) and confidence (string, exactly one of: LOW, MEDIUM, HIGH) with each call.`.trim(); + if (isIssueIntentEnabledForTool(tool.name, config[tool.name])) { + enhancedTool.description = `${enhancedTool.description || ""} ${ISSUE_INTENT_SUFFIX}`.trim(); } if (tool.name === "add_comment") { diff --git a/actions/setup/js/generate_safe_outputs_tools.test.cjs b/actions/setup/js/generate_safe_outputs_tools.test.cjs index 38cad3e19d7..a4f23d78260 100644 --- a/actions/setup/js/generate_safe_outputs_tools.test.cjs +++ b/actions/setup/js/generate_safe_outputs_tools.test.cjs @@ -314,10 +314,24 @@ describe("generate_safe_outputs_tools", () => { { name: "set_issue_type", description: "Sets issue type.", inputSchema: { type: "object", properties: {} } }, { name: "set_issue_field", description: "Sets issue field.", inputSchema: { type: "object", properties: {} } }, { name: "add_labels", description: "Adds labels.", inputSchema: { type: "object", properties: {} } }, + { name: "close_issue", description: "Closes issue.", inputSchema: { type: "object", properties: {} } }, + { name: "assign_to_user", description: "Assigns users.", inputSchema: { type: "object", properties: {} } }, + { name: "assign_to_agent", description: "Assigns agent.", inputSchema: { type: "object", properties: {} } }, { name: "create_issue", description: "Creates a GitHub issue.", inputSchema: { type: "object", properties: {} } }, ]) ); - fs.writeFileSync(configPath, JSON.stringify({ set_issue_type: {}, set_issue_field: {}, add_labels: {}, create_issue: {} })); + fs.writeFileSync( + configPath, + JSON.stringify({ + set_issue_type: {}, + set_issue_field: {}, + add_labels: {}, + close_issue: {}, + assign_to_user: {}, + assign_to_agent: {}, + create_issue: {}, + }) + ); fs.writeFileSync(toolsMetaPath, JSON.stringify({ description_suffixes: {}, repo_params: {}, dynamic_tools: [] })); runScript(); @@ -327,6 +341,9 @@ describe("generate_safe_outputs_tools", () => { expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "set_issue_type").description).toContain(intentSuffix); expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "set_issue_field").description).toContain(intentSuffix); expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "add_labels").description).toContain(intentSuffix); + expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "close_issue").description).toContain(intentSuffix); + expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "assign_to_user").description).toContain(intentSuffix); + expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "assign_to_agent").description).toContain(intentSuffix); expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "create_issue").description).not.toContain(intentSuffix); }); @@ -350,4 +367,23 @@ describe("generate_safe_outputs_tools", () => { expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "set_issue_field").description).toContain(intentSuffix); expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "add_labels").description).toContain(intentSuffix); }); + + it("omits issue intent suffix when explicitly disabled per tool", () => { + fs.writeFileSync( + toolsSourcePath, + JSON.stringify([ + { name: "close_issue", description: "Closes issue.", inputSchema: { type: "object", properties: {} } }, + { name: "assign_to_user", description: "Assigns users.", inputSchema: { type: "object", properties: {} } }, + ]) + ); + fs.writeFileSync(configPath, JSON.stringify({ close_issue: { issue_intent: false }, assign_to_user: {} })); + fs.writeFileSync(toolsMetaPath, JSON.stringify({ description_suffixes: {}, repo_params: {}, dynamic_tools: [] })); + + runScript(); + + const result = JSON.parse(fs.readFileSync(outputPath, "utf8")); + const intentSuffix = "INTENT: Include rationale (string, max 280 chars) and confidence (string, exactly one of: LOW, MEDIUM, HIGH) with each call."; + expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "close_issue").description).not.toContain(intentSuffix); + expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "assign_to_user").description).toContain(intentSuffix); + }); }); diff --git a/actions/setup/js/safe_outputs_tools.json b/actions/setup/js/safe_outputs_tools.json index 9310999badf..3df1eb89a2f 100644 --- a/actions/setup/js/safe_outputs_tools.json +++ b/actions/setup/js/safe_outputs_tools.json @@ -217,6 +217,20 @@ "description": "Issue number to close. This is the numeric ID from the GitHub URL (e.g., 901 in github.com/owner/repo/issues/901). If omitted, closes the issue that triggered this workflow (requires an issue event trigger).", "x-synonyms": ["issueNumber"] }, + "rationale": { + "type": "string", + "maxLength": 280, + "description": "Optional rationale for closing the issue (max 280 characters)." + }, + "confidence": { + "type": "string", + "enum": ["LOW", "MEDIUM", "HIGH"], + "description": "Optional confidence level for closing the issue." + }, + "suggest": { + "type": "boolean", + "description": "When true, route through pending suggestion review." + }, "duplicate_of": { "type": ["number", "string"], "description": "Issue number or URL of the canonical original issue when closing as a duplicate. Accepts: a bare number (e.g. 123), a #-prefixed number (e.g. #123), an owner/repo#number reference (e.g. github/gh-aw#123), or a full GitHub issue URL. When provided together with state_reason: duplicate, creates a native GitHub duplicate relationship (marked_as_duplicate timeline event). Omit if not closing as a duplicate.", @@ -858,6 +872,20 @@ "type": "string", "description": "Agent identifier to assign. Defaults to 'copilot' (the Copilot coding agent) if not specified." }, + "rationale": { + "type": "string", + "maxLength": 280, + "description": "Optional rationale for the assignment (max 280 characters)." + }, + "confidence": { + "type": "string", + "enum": ["LOW", "MEDIUM", "HIGH"], + "description": "Optional confidence level for the assignment." + }, + "suggest": { + "type": "boolean", + "description": "When true, route through pending suggestion review." + }, "pull_request_repo": { "type": "string", "description": "Target repository where the pull request should be created, in 'owner/repo' format. If omitted, the PR will be created in the same repository as the issue. This allows issues and code to live in different repositories. The global pull-request-repo configuration (if set) is automatically allowed; additional repositories must be listed in allowed-pull-request-repos.", @@ -898,6 +926,20 @@ "type": "string", "description": "Single GitHub username to assign. Use 'assignees' array for multiple users." }, + "rationale": { + "type": "string", + "maxLength": 280, + "description": "Optional rationale for the assignment (max 280 characters)." + }, + "confidence": { + "type": "string", + "enum": ["LOW", "MEDIUM", "HIGH"], + "description": "Optional confidence level for the assignment." + }, + "suggest": { + "type": "boolean", + "description": "When true, route through pending suggestion review." + }, "secrecy": { "type": "string", "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\")." diff --git a/actions/setup/js/types/safe-outputs.d.ts b/actions/setup/js/types/safe-outputs.d.ts index ebae4edb52a..d98c46f2e20 100644 --- a/actions/setup/js/types/safe-outputs.d.ts +++ b/actions/setup/js/types/safe-outputs.d.ts @@ -91,7 +91,7 @@ interface CloseDiscussionItem extends BaseSafeOutputItem { /** * JSONL item for closing a GitHub issue */ -interface CloseIssueItem extends BaseSafeOutputItem { +interface CloseIssueItem extends BaseSafeOutputItem, IssueIntentMetadata { type: "close_issue"; /** Comment body to add when closing the issue */ body: string; @@ -368,7 +368,7 @@ interface SetIssueFieldItem extends BaseSafeOutputItem, IssueIntentMetadata { /** * JSONL item for assigning a GitHub Copilot coding agent to an issue or project item */ -interface AssignToAgentItem extends BaseSafeOutputItem { +interface AssignToAgentItem extends BaseSafeOutputItem, IssueIntentMetadata { type: "assign_to_agent"; /** Issue number to assign agent to */ issue_number: number | string; diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json index 009a8c0c202..324ccce62f9 100644 --- a/pkg/workflow/js/safe_outputs_tools.json +++ b/pkg/workflow/js/safe_outputs_tools.json @@ -268,6 +268,24 @@ "issueNumber" ] }, + "rationale": { + "type": "string", + "maxLength": 280, + "description": "Optional rationale for closing the issue (max 280 characters)." + }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "description": "Optional confidence level for closing the issue." + }, + "suggest": { + "type": "boolean", + "description": "When true, route through pending suggestion review." + }, "duplicate_of": { "type": [ "number", @@ -1100,6 +1118,24 @@ "type": "string", "description": "Agent identifier to assign. Defaults to 'copilot' (the Copilot coding agent) if not specified." }, + "rationale": { + "type": "string", + "maxLength": 280, + "description": "Optional rationale for the assignment (max 280 characters)." + }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "description": "Optional confidence level for the assignment." + }, + "suggest": { + "type": "boolean", + "description": "When true, route through pending suggestion review." + }, "pull_request_repo": { "type": "string", "description": "Target repository where the pull request should be created, in 'owner/repo' format. If omitted, the PR will be created in the same repository as the issue. This allows issues and code to live in different repositories. The global pull-request-repo configuration (if set) is automatically allowed; additional repositories must be listed in allowed-pull-request-repos.", @@ -1149,6 +1185,24 @@ "type": "string", "description": "Single GitHub username to assign. Use 'assignees' array for multiple users." }, + "rationale": { + "type": "string", + "maxLength": 280, + "description": "Optional rationale for the assignment (max 280 characters)." + }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "description": "Optional confidence level for the assignment." + }, + "suggest": { + "type": "boolean", + "description": "When true, route through pending suggestion review." + }, "secrecy": { "type": "string", "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\")." diff --git a/pkg/workflow/safe_output_validation_config_test.go b/pkg/workflow/safe_output_validation_config_test.go index efb1d203a84..6094759d75c 100644 --- a/pkg/workflow/safe_output_validation_config_test.go +++ b/pkg/workflow/safe_output_validation_config_test.go @@ -230,12 +230,10 @@ func TestFieldValidationMarshaling(t *testing.T) { } func TestIssueIntentRationaleMaxLength(t *testing.T) { - if got := ValidationConfig["set_issue_type"].Fields["rationale"].MaxLength; got != 280 { - t.Fatalf("set_issue_type rationale maxLength = %d, want 280", got) - } - - if got := ValidationConfig["set_issue_field"].Fields["rationale"].MaxLength; got != 280 { - t.Fatalf("set_issue_field rationale maxLength = %d, want 280", got) + for _, typeName := range []string{"set_issue_type", "set_issue_field", "close_issue", "assign_to_user", "assign_to_agent"} { + if got := ValidationConfig[typeName].Fields["rationale"].MaxLength; got != 280 { + t.Fatalf("%s rationale maxLength = %d, want 280", typeName, got) + } } } @@ -290,7 +288,7 @@ func TestUpdateIssueValidationConfig(t *testing.T) { } func TestIssueIntentValidationFields(t *testing.T) { - for _, typeName := range []string{"set_issue_type", "set_issue_field"} { + for _, typeName := range []string{"set_issue_type", "set_issue_field", "close_issue", "assign_to_user", "assign_to_agent"} { config, ok := ValidationConfig[typeName] if !ok { t.Fatalf("%s not found in ValidationConfig", typeName) diff --git a/pkg/workflow/safe_outputs_config_types.go b/pkg/workflow/safe_outputs_config_types.go index 911cc814089..d52b7544f7e 100644 --- a/pkg/workflow/safe_outputs_config_types.go +++ b/pkg/workflow/safe_outputs_config_types.go @@ -24,6 +24,7 @@ type BaseSafeOutputConfig struct { GitHubToken string `yaml:"github-token,omitempty"` // GitHub token for this specific output type GitHubApp *GitHubAppConfig `yaml:"github-app,omitempty"` // GitHub App credentials for minting a per-handler installation access token Staged *TemplatableBool `yaml:"staged,omitempty"` // Templatable preview-only mode for this specific output type + IssueIntent *bool `yaml:"issue-intent,omitempty"` // When false, disable issue-intent rationale/confidence guidance and schema requirements for this output type. NormalizeClosingKeywords *bool `yaml:"normalize-closing-keywords,omitempty"` // When true for this output type, strip backticks from recognized issue-closing keywords in body fields. // Samples carries deterministic replay samples for the hidden // `gh aw compile --use-samples` flag. Each entry is the JSON object diff --git a/pkg/workflow/safe_outputs_handler_registry.go b/pkg/workflow/safe_outputs_handler_registry.go index 70c732d89c9..a60c7e87904 100644 --- a/pkg/workflow/safe_outputs_handler_registry.go +++ b/pkg/workflow/safe_outputs_handler_registry.go @@ -110,6 +110,7 @@ var handlerRegistry = map[string]handlerBuilder{ AddStringSlice("allowed_repos", c.AllowedRepos). AddIfNotEmpty("state_reason", c.StateReason). AddBoolPtr("allow_body", c.AllowBody). + AddBoolPtr("issue_intent", c.IssueIntent). AddTemplatableBool("staged", templatableBoolPtrToStringPtr(c.Staged)). Build() }, @@ -138,6 +139,7 @@ var handlerRegistry = map[string]handlerBuilder{ AddTemplatableInt("max", c.Max). AddStringSlice("allowed", c.Allowed). AddStringSlice("blocked", c.Blocked). + AddBoolPtr("issue_intent", c.IssueIntent). AddIfNotEmpty("target", c.Target). AddIfNotEmpty("target-repo", c.TargetRepoSlug). AddStringSlice("allowed_repos", c.AllowedRepos). @@ -789,6 +791,7 @@ var handlerRegistry = map[string]handlerBuilder{ AddIfNotEmpty("custom-agent", c.DefaultCustomAgent). AddIfNotEmpty("custom-instructions", c.DefaultCustomInstructions). AddStringSlice("allowed", c.Allowed). + AddBoolPtr("issue_intent", c.IssueIntent). AddIfTrue("ignore-if-error", c.IgnoreIfError). AddIfNotEmpty("target", c.Target). AddIfNotEmpty("target-repo", c.TargetRepoSlug). @@ -913,6 +916,7 @@ var handlerRegistry = map[string]handlerBuilder{ AddStringSlice("allowed_repos", c.AllowedRepos). AddIfNotEmpty("github-token", c.GitHubToken). AddTemplatableBool("unassign_first", c.UnassignFirst). + AddBoolPtr("issue_intent", c.IssueIntent). AddTemplatableBool("staged", templatableBoolPtrToStringPtr(c.Staged)). Build() }, @@ -954,6 +958,7 @@ var handlerRegistry = map[string]handlerBuilder{ config := newHandlerConfigBuilder(). AddTemplatableInt("max", c.Max). AddStringSlice("allowed", c.Allowed). + AddBoolPtr("issue_intent", c.IssueIntent). AddIfNotEmpty("target", c.Target). AddIfNotEmpty("target-repo", c.TargetRepoSlug). AddStringSlice("allowed_repos", c.AllowedRepos). @@ -976,6 +981,7 @@ var handlerRegistry = map[string]handlerBuilder{ config := newHandlerConfigBuilder(). AddTemplatableInt("max", c.Max). AddStringSlice("allowed_fields", c.AllowedFields). + AddBoolPtr("issue_intent", c.IssueIntent). AddIfNotEmpty("target", c.Target).AddStringSlice("required_labels", c.RequiredLabels). AddIfNotEmpty("required_title_prefix", c.RequiredTitlePrefix).AddIfNotEmpty("target-repo", c.TargetRepoSlug). AddStringSlice("allowed_repos", c.AllowedRepos). diff --git a/pkg/workflow/safe_outputs_tools_generation.go b/pkg/workflow/safe_outputs_tools_generation.go index 25fd8c268b6..dbf60e84c01 100644 --- a/pkg/workflow/safe_outputs_tools_generation.go +++ b/pkg/workflow/safe_outputs_tools_generation.go @@ -254,9 +254,29 @@ func computeRequiredFieldAdditions(safeOutputs *SafeOutputsConfig) map[string][] if safeOutputs.CreatePullRequests != nil && safeOutputs.CreatePullRequests.RequireTemporaryID { additions["create_pull_request"] = []string{"temporary_id"} } + issueIntentRequiredFields := []string{"rationale", "confidence"} + if safeOutputs.SetIssueType != nil && issueIntentEnabled(safeOutputs.SetIssueType.IssueIntent) { + additions["set_issue_type"] = issueIntentRequiredFields + } + if safeOutputs.SetIssueField != nil && issueIntentEnabled(safeOutputs.SetIssueField.IssueIntent) { + additions["set_issue_field"] = issueIntentRequiredFields + } + if safeOutputs.CloseIssues != nil && issueIntentEnabled(safeOutputs.CloseIssues.IssueIntent) { + additions["close_issue"] = issueIntentRequiredFields + } + if safeOutputs.AssignToUser != nil && issueIntentEnabled(safeOutputs.AssignToUser.IssueIntent) { + additions["assign_to_user"] = issueIntentRequiredFields + } + if safeOutputs.AssignToAgent != nil && issueIntentEnabled(safeOutputs.AssignToAgent.IssueIntent) { + additions["assign_to_agent"] = issueIntentRequiredFields + } return additions } +func issueIntentEnabled(issueIntent *bool) bool { + return issueIntent == nil || *issueIntent +} + // generateToolsMetaJSON generates the content for tools_meta.json: a compact file // that captures the workflow-specific customisations (description constraints, // repo parameters, dynamic tools) without inlining the entire diff --git a/pkg/workflow/safe_outputs_tools_generation_test.go b/pkg/workflow/safe_outputs_tools_generation_test.go index 2b97615540b..e564fbbb1fb 100644 --- a/pkg/workflow/safe_outputs_tools_generation_test.go +++ b/pkg/workflow/safe_outputs_tools_generation_test.go @@ -411,3 +411,28 @@ func TestComputeRequiredFieldAdditionsDisabledByDefault(t *testing.T) { }) assert.Empty(t, additions) } + +func TestComputeRequiredFieldAdditionsIssueIntentDefaultEnabled(t *testing.T) { + additions := computeRequiredFieldAdditions(&SafeOutputsConfig{ + CloseIssues: &CloseIssuesConfig{}, + AssignToUser: &AssignToUserConfig{}, + AssignToAgent: &AssignToAgentConfig{}, + }) + + assert.Equal(t, []string{"rationale", "confidence"}, additions["close_issue"]) + assert.Equal(t, []string{"rationale", "confidence"}, additions["assign_to_user"]) + assert.Equal(t, []string{"rationale", "confidence"}, additions["assign_to_agent"]) +} + +func TestComputeRequiredFieldAdditionsIssueIntentOptOut(t *testing.T) { + disabled := false + additions := computeRequiredFieldAdditions(&SafeOutputsConfig{ + CloseIssues: &CloseIssuesConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &disabled}}, + AssignToUser: &AssignToUserConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &disabled}}, + AssignToAgent: &AssignToAgentConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &disabled}}, + }) + + assert.NotContains(t, additions, "close_issue") + assert.NotContains(t, additions, "assign_to_user") + assert.NotContains(t, additions, "assign_to_agent") +} diff --git a/pkg/workflow/safe_outputs_validation_config.go b/pkg/workflow/safe_outputs_validation_config.go index 835bd284836..76662a69d5e 100644 --- a/pkg/workflow/safe_outputs_validation_config.go +++ b/pkg/workflow/safe_outputs_validation_config.go @@ -166,6 +166,9 @@ var ValidationConfig = map[string]TypeValidationConfig{ "issue_number": {IssueNumberOrTemporaryID: true}, "pull_number": {OptionalPositiveInteger: true}, "agent": {Type: "string", Sanitize: true, MaxLength: 128}, + "rationale": {Type: "string", Sanitize: true, MaxLength: 280, StripOnError: true}, + "confidence": {Type: "string", Enum: []string{"LOW", "MEDIUM", "HIGH"}, StripOnError: true}, + "suggest": {Type: "boolean"}, "pull_request_repo": {Type: "string", MaxLength: 256}, // Optional: repository where the PR should be created "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, @@ -176,7 +179,10 @@ var ValidationConfig = map[string]TypeValidationConfig{ "issue_number": {IssueOrPRNumber: true}, "assignees": {Type: "[]string", Sanitize: true, MaxLength: 39}, // GitHub username max length is 39 "assignee": {Type: "string", Sanitize: true, MaxLength: 39}, // Single assignee alternative - "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" + "rationale": {Type: "string", Sanitize: true, MaxLength: 280, StripOnError: true}, + "confidence": {Type: "string", Enum: []string{"LOW", "MEDIUM", "HIGH"}, StripOnError: true}, + "suggest": {Type: "boolean"}, + "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, }, "update_issue": { @@ -285,6 +291,9 @@ var ValidationConfig = map[string]TypeValidationConfig{ Fields: map[string]FieldValidation{ "body": {Type: "string", Sanitize: true, MaxLength: MaxBodyLength}, "issue_number": {OptionalPositiveInteger: true}, + "rationale": {Type: "string", Sanitize: true, MaxLength: 280, StripOnError: true}, + "confidence": {Type: "string", Enum: []string{"LOW", "MEDIUM", "HIGH"}, StripOnError: true}, + "suggest": {Type: "boolean"}, "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, }, From 03a152bda82431b922f0397a61c7948740648b12 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:09:27 +0000 Subject: [PATCH 03/10] Plan: address issue-intent default behavior feedback Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../daily-assign-issue-to-user.lock.yml | 26 +++++++++- .../workflows/dependabot-go-checker.lock.yml | 26 +++++++++- .github/workflows/eslint-monster.lock.yml | 48 ++++++++++++++++++- .github/workflows/issue-monster.lock.yml | 26 +++++++++- .github/workflows/lint-monster.lock.yml | 48 ++++++++++++++++++- .../objective-impact-report.lock.yml | 26 +++++++++- .../semantic-function-refactor.lock.yml | 26 +++++++++- .../smoke-agent-public-approved.lock.yml | 26 +++++++++- .github/workflows/smoke-codex.lock.yml | 8 +++- .../smoke-copilot-aoai-apikey.lock.yml | 8 +++- .../smoke-copilot-aoai-entra.lock.yml | 8 +++- .github/workflows/smoke-copilot.lock.yml | 8 +++- .github/workflows/workflow-generator.lock.yml | 26 +++++++++- 13 files changed, 297 insertions(+), 13 deletions(-) diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml index 9a73b1c9504..0f3e1db3997 100644 --- a/.github/workflows/daily-assign-issue-to-user.lock.yml +++ b/.github/workflows/daily-assign-issue-to-user.lock.yml @@ -546,7 +546,13 @@ jobs: "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "assign_to_user": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -585,12 +591,30 @@ jobs: "sanitize": true, "maxLength": 39 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "issueOrPRNumber": true }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index 0a8e2d75f5b..63aa9de8d21 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -596,7 +596,13 @@ jobs: "create_issue": " CONSTRAINTS: Maximum 10 issue(s) can be created. Title will be prefixed with \"[deps]\". Labels [\"dependencies\" \"go\" \"cookie\"] will be automatically added." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "close_issue": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -608,12 +614,30 @@ jobs: "sanitize": true, "maxLength": 65000 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "optionalPositiveInteger": true }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/eslint-monster.lock.yml b/.github/workflows/eslint-monster.lock.yml index 011c97c79eb..9567a8849a2 100644 --- a/.github/workflows/eslint-monster.lock.yml +++ b/.github/workflows/eslint-monster.lock.yml @@ -562,7 +562,17 @@ jobs: "update_issue": " CONSTRAINTS: Maximum 10 issue(s) can be updated. The target issue title must start with \"[eslint-monster] \"." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "assign_to_agent": [ + "rationale", + "confidence" + ], + "close_issue": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -574,6 +584,15 @@ jobs: "sanitize": true, "maxLength": 128 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "issueNumberOrTemporaryId": true }, @@ -584,9 +603,18 @@ jobs: "type": "string", "maxLength": 256 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } }, "customValidation": "requiresOneOf:issue_number,pull_number" @@ -599,12 +627,30 @@ jobs: "sanitize": true, "maxLength": 65000 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "optionalPositiveInteger": true }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 571b9285e62..04698e1a38f 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -960,7 +960,13 @@ jobs: "assign_to_agent": " CONSTRAINTS: Maximum 3 issue(s) can be assigned to agent." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "assign_to_agent": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -994,6 +1000,15 @@ jobs: "sanitize": true, "maxLength": 128 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "issueNumberOrTemporaryId": true }, @@ -1004,9 +1019,18 @@ jobs: "type": "string", "maxLength": 256 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } }, "customValidation": "requiresOneOf:issue_number,pull_number" diff --git a/.github/workflows/lint-monster.lock.yml b/.github/workflows/lint-monster.lock.yml index 768748b68b4..390a615463b 100644 --- a/.github/workflows/lint-monster.lock.yml +++ b/.github/workflows/lint-monster.lock.yml @@ -557,7 +557,17 @@ jobs: "update_issue": " CONSTRAINTS: Maximum 10 issue(s) can be updated. The target issue title must start with \"[lint-monster] \"." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "assign_to_agent": [ + "rationale", + "confidence" + ], + "close_issue": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -569,6 +579,15 @@ jobs: "sanitize": true, "maxLength": 128 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "issueNumberOrTemporaryId": true }, @@ -579,9 +598,18 @@ jobs: "type": "string", "maxLength": 256 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } }, "customValidation": "requiresOneOf:issue_number,pull_number" @@ -594,12 +622,30 @@ jobs: "sanitize": true, "maxLength": 65000 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "optionalPositiveInteger": true }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/objective-impact-report.lock.yml b/.github/workflows/objective-impact-report.lock.yml index 1ee18e9d305..26ce2b21d1f 100644 --- a/.github/workflows/objective-impact-report.lock.yml +++ b/.github/workflows/objective-impact-report.lock.yml @@ -551,7 +551,13 @@ jobs: "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"Impact Efficiency Report - \"." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "close_issue": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -563,12 +569,30 @@ jobs: "sanitize": true, "maxLength": 65000 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "optionalPositiveInteger": true }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index d38dab90b8b..95fb058de7b 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -568,7 +568,13 @@ jobs: "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[refactor] \". Labels [\"refactoring\" \"code-quality\" \"automated-analysis\" \"cookie\"] will be automatically added." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "close_issue": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -580,12 +586,30 @@ jobs: "sanitize": true, "maxLength": 65000 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "optionalPositiveInteger": true }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/smoke-agent-public-approved.lock.yml b/.github/workflows/smoke-agent-public-approved.lock.yml index 4c8e2cc9f15..7ccd7e9ed40 100644 --- a/.github/workflows/smoke-agent-public-approved.lock.yml +++ b/.github/workflows/smoke-agent-public-approved.lock.yml @@ -629,7 +629,13 @@ jobs: "assign_to_agent": " CONSTRAINTS: Maximum 1 issue(s) can be assigned to agent." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "assign_to_agent": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -663,6 +669,15 @@ jobs: "sanitize": true, "maxLength": 128 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "issueNumberOrTemporaryId": true }, @@ -673,9 +688,18 @@ jobs: "type": "string", "maxLength": 256 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } }, "customValidation": "requiresOneOf:issue_number,pull_number" diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index a938f885269..ad70833ebb4 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -749,7 +749,13 @@ jobs: }, "name": "add_smoked_label" } - ] + ], + "required_field_additions": { + "set_issue_field": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml index 7c38e3e553d..e0e0c0bac6d 100644 --- a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml @@ -878,7 +878,13 @@ jobs: }, "name": "haiku_printer" } - ] + ], + "required_field_additions": { + "set_issue_type": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/smoke-copilot-aoai-entra.lock.yml b/.github/workflows/smoke-copilot-aoai-entra.lock.yml index 92e91eca05f..48cd54e84a2 100644 --- a/.github/workflows/smoke-copilot-aoai-entra.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-entra.lock.yml @@ -879,7 +879,13 @@ jobs: }, "name": "haiku_printer" } - ] + ], + "required_field_additions": { + "set_issue_type": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 96c9308457b..f40ca3ad7f3 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -894,7 +894,13 @@ jobs: }, "name": "haiku_printer" } - ] + ], + "required_field_additions": { + "set_issue_type": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index de80effe3e5..5932b2877db 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -600,7 +600,13 @@ jobs: "update_issue": " CONSTRAINTS: Maximum 1 issue(s) can be updated. Body updates are allowed." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [], + "required_field_additions": { + "assign_to_agent": [ + "rationale", + "confidence" + ] + } } GH_AW_VALIDATION_JSON: | { @@ -612,6 +618,15 @@ jobs: "sanitize": true, "maxLength": 128 }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "x-strip-on-error": true + }, "issue_number": { "issueNumberOrTemporaryId": true }, @@ -622,9 +637,18 @@ jobs: "type": "string", "maxLength": 256 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 280, + "x-strip-on-error": true + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } }, "customValidation": "requiresOneOf:issue_number,pull_number" From fda3b452d1a5fc5b97bcca36f51304697bc7bc12 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:24:34 +0000 Subject: [PATCH 04/10] fix(safe-outputs): make issue-intent requirements opt-in Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../setup/js/generate_safe_outputs_tools.cjs | 7 ++---- .../js/generate_safe_outputs_tools.test.cjs | 20 ++++++++-------- pkg/workflow/safe_outputs_config_types.go | 2 +- pkg/workflow/safe_outputs_tools_generation.go | 14 +++++------ .../safe_outputs_tools_generation_test.go | 24 +++++++++---------- 5 files changed, 32 insertions(+), 35 deletions(-) diff --git a/actions/setup/js/generate_safe_outputs_tools.cjs b/actions/setup/js/generate_safe_outputs_tools.cjs index c867f2b056a..f81b13af529 100644 --- a/actions/setup/js/generate_safe_outputs_tools.cjs +++ b/actions/setup/js/generate_safe_outputs_tools.cjs @@ -43,7 +43,7 @@ const ISSUE_INTENT_TOOL_NAMES = new Set(["set_issue_type", "set_issue_field", "a /** * Determine whether issue-intent guidance is enabled for a tool. - * Default is enabled; explicit issue_intent: false disables it. + * Default is disabled; explicit issue_intent: true enables it. * * @param {string} toolName * @param {unknown} toolConfig @@ -53,10 +53,7 @@ function isIssueIntentEnabledForTool(toolName, toolConfig) { if (!ISSUE_INTENT_TOOL_NAMES.has(toolName)) { return false; } - if (toolConfig && typeof toolConfig === "object" && "issue_intent" in toolConfig) { - return toolConfig.issue_intent !== false; - } - return true; + return !!(toolConfig && typeof toolConfig === "object" && "issue_intent" in toolConfig && toolConfig.issue_intent === true); } /** diff --git a/actions/setup/js/generate_safe_outputs_tools.test.cjs b/actions/setup/js/generate_safe_outputs_tools.test.cjs index a4f23d78260..95d8b17a38e 100644 --- a/actions/setup/js/generate_safe_outputs_tools.test.cjs +++ b/actions/setup/js/generate_safe_outputs_tools.test.cjs @@ -307,7 +307,7 @@ describe("generate_safe_outputs_tools", () => { expect(addCommentTool.description).not.toContain("Supports reply_to_id for discussion threading."); }); - it("adds issue intent suffix for issue tools without requiring a runtime feature", () => { + it("adds issue intent suffix for issue tools when explicitly enabled", () => { fs.writeFileSync( toolsSourcePath, JSON.stringify([ @@ -323,12 +323,12 @@ describe("generate_safe_outputs_tools", () => { fs.writeFileSync( configPath, JSON.stringify({ - set_issue_type: {}, - set_issue_field: {}, - add_labels: {}, - close_issue: {}, - assign_to_user: {}, - assign_to_agent: {}, + set_issue_type: { issue_intent: true }, + set_issue_field: { issue_intent: true }, + add_labels: { issue_intent: true }, + close_issue: { issue_intent: true }, + assign_to_user: { issue_intent: true }, + assign_to_agent: { issue_intent: true }, create_issue: {}, }) ); @@ -356,7 +356,7 @@ describe("generate_safe_outputs_tools", () => { { name: "add_labels", description: "Adds labels.", inputSchema: { type: "object", properties: {} } }, ]) ); - fs.writeFileSync(configPath, JSON.stringify({ set_issue_type: {}, set_issue_field: {}, add_labels: {} })); + fs.writeFileSync(configPath, JSON.stringify({ set_issue_type: { issue_intent: true }, set_issue_field: { issue_intent: true }, add_labels: { issue_intent: true } })); fs.writeFileSync(toolsMetaPath, JSON.stringify({ description_suffixes: {}, repo_params: {}, dynamic_tools: [] })); runScript({ GH_AW_RUNTIME_FEATURES: "other\nanother=true" }); @@ -368,7 +368,7 @@ describe("generate_safe_outputs_tools", () => { expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "add_labels").description).toContain(intentSuffix); }); - it("omits issue intent suffix when explicitly disabled per tool", () => { + it("omits issue intent suffix by default and when explicitly disabled per tool", () => { fs.writeFileSync( toolsSourcePath, JSON.stringify([ @@ -384,6 +384,6 @@ describe("generate_safe_outputs_tools", () => { const result = JSON.parse(fs.readFileSync(outputPath, "utf8")); const intentSuffix = "INTENT: Include rationale (string, max 280 chars) and confidence (string, exactly one of: LOW, MEDIUM, HIGH) with each call."; expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "close_issue").description).not.toContain(intentSuffix); - expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "assign_to_user").description).toContain(intentSuffix); + expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "assign_to_user").description).not.toContain(intentSuffix); }); }); diff --git a/pkg/workflow/safe_outputs_config_types.go b/pkg/workflow/safe_outputs_config_types.go index d52b7544f7e..412026f017d 100644 --- a/pkg/workflow/safe_outputs_config_types.go +++ b/pkg/workflow/safe_outputs_config_types.go @@ -24,7 +24,7 @@ type BaseSafeOutputConfig struct { GitHubToken string `yaml:"github-token,omitempty"` // GitHub token for this specific output type GitHubApp *GitHubAppConfig `yaml:"github-app,omitempty"` // GitHub App credentials for minting a per-handler installation access token Staged *TemplatableBool `yaml:"staged,omitempty"` // Templatable preview-only mode for this specific output type - IssueIntent *bool `yaml:"issue-intent,omitempty"` // When false, disable issue-intent rationale/confidence guidance and schema requirements for this output type. + IssueIntent *bool `yaml:"issue-intent,omitempty"` // When true, enable issue-intent rationale/confidence guidance and schema requirements for this output type. NormalizeClosingKeywords *bool `yaml:"normalize-closing-keywords,omitempty"` // When true for this output type, strip backticks from recognized issue-closing keywords in body fields. // Samples carries deterministic replay samples for the hidden // `gh aw compile --use-samples` flag. Each entry is the JSON object diff --git a/pkg/workflow/safe_outputs_tools_generation.go b/pkg/workflow/safe_outputs_tools_generation.go index dbf60e84c01..508fc6211ac 100644 --- a/pkg/workflow/safe_outputs_tools_generation.go +++ b/pkg/workflow/safe_outputs_tools_generation.go @@ -255,26 +255,26 @@ func computeRequiredFieldAdditions(safeOutputs *SafeOutputsConfig) map[string][] additions["create_pull_request"] = []string{"temporary_id"} } issueIntentRequiredFields := []string{"rationale", "confidence"} - if safeOutputs.SetIssueType != nil && issueIntentEnabled(safeOutputs.SetIssueType.IssueIntent) { + if safeOutputs.SetIssueType != nil && issueIntentRequired(safeOutputs.SetIssueType.IssueIntent) { additions["set_issue_type"] = issueIntentRequiredFields } - if safeOutputs.SetIssueField != nil && issueIntentEnabled(safeOutputs.SetIssueField.IssueIntent) { + if safeOutputs.SetIssueField != nil && issueIntentRequired(safeOutputs.SetIssueField.IssueIntent) { additions["set_issue_field"] = issueIntentRequiredFields } - if safeOutputs.CloseIssues != nil && issueIntentEnabled(safeOutputs.CloseIssues.IssueIntent) { + if safeOutputs.CloseIssues != nil && issueIntentRequired(safeOutputs.CloseIssues.IssueIntent) { additions["close_issue"] = issueIntentRequiredFields } - if safeOutputs.AssignToUser != nil && issueIntentEnabled(safeOutputs.AssignToUser.IssueIntent) { + if safeOutputs.AssignToUser != nil && issueIntentRequired(safeOutputs.AssignToUser.IssueIntent) { additions["assign_to_user"] = issueIntentRequiredFields } - if safeOutputs.AssignToAgent != nil && issueIntentEnabled(safeOutputs.AssignToAgent.IssueIntent) { + if safeOutputs.AssignToAgent != nil && issueIntentRequired(safeOutputs.AssignToAgent.IssueIntent) { additions["assign_to_agent"] = issueIntentRequiredFields } return additions } -func issueIntentEnabled(issueIntent *bool) bool { - return issueIntent == nil || *issueIntent +func issueIntentRequired(issueIntent *bool) bool { + return issueIntent != nil && *issueIntent } // generateToolsMetaJSON generates the content for tools_meta.json: a compact file diff --git a/pkg/workflow/safe_outputs_tools_generation_test.go b/pkg/workflow/safe_outputs_tools_generation_test.go index e564fbbb1fb..8aff34b8661 100644 --- a/pkg/workflow/safe_outputs_tools_generation_test.go +++ b/pkg/workflow/safe_outputs_tools_generation_test.go @@ -412,27 +412,27 @@ func TestComputeRequiredFieldAdditionsDisabledByDefault(t *testing.T) { assert.Empty(t, additions) } -func TestComputeRequiredFieldAdditionsIssueIntentDefaultEnabled(t *testing.T) { +func TestComputeRequiredFieldAdditionsIssueIntentDefaultDisabled(t *testing.T) { additions := computeRequiredFieldAdditions(&SafeOutputsConfig{ CloseIssues: &CloseIssuesConfig{}, AssignToUser: &AssignToUserConfig{}, AssignToAgent: &AssignToAgentConfig{}, }) - assert.Equal(t, []string{"rationale", "confidence"}, additions["close_issue"]) - assert.Equal(t, []string{"rationale", "confidence"}, additions["assign_to_user"]) - assert.Equal(t, []string{"rationale", "confidence"}, additions["assign_to_agent"]) + assert.NotContains(t, additions, "close_issue") + assert.NotContains(t, additions, "assign_to_user") + assert.NotContains(t, additions, "assign_to_agent") } -func TestComputeRequiredFieldAdditionsIssueIntentOptOut(t *testing.T) { - disabled := false +func TestComputeRequiredFieldAdditionsIssueIntentOptIn(t *testing.T) { + enabled := true additions := computeRequiredFieldAdditions(&SafeOutputsConfig{ - CloseIssues: &CloseIssuesConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &disabled}}, - AssignToUser: &AssignToUserConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &disabled}}, - AssignToAgent: &AssignToAgentConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &disabled}}, + CloseIssues: &CloseIssuesConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &enabled}}, + AssignToUser: &AssignToUserConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &enabled}}, + AssignToAgent: &AssignToAgentConfig{BaseSafeOutputConfig: BaseSafeOutputConfig{IssueIntent: &enabled}}, }) - assert.NotContains(t, additions, "close_issue") - assert.NotContains(t, additions, "assign_to_user") - assert.NotContains(t, additions, "assign_to_agent") + assert.Equal(t, []string{"rationale", "confidence"}, additions["close_issue"]) + assert.Equal(t, []string{"rationale", "confidence"}, additions["assign_to_user"]) + assert.Equal(t, []string{"rationale", "confidence"}, additions["assign_to_agent"]) } From 89813ab2f275320cedad19461312f6c35716210e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:36:31 +0000 Subject: [PATCH 05/10] Fix close_issue JSDoc param ordering for typecheck Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/close_issue.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/setup/js/close_issue.cjs b/actions/setup/js/close_issue.cjs index 035add799e2..dd01841fd0a 100644 --- a/actions/setup/js/close_issue.cjs +++ b/actions/setup/js/close_issue.cjs @@ -155,7 +155,7 @@ async function addIssueComment(github, owner, repo, issueNumber, message) { * @param {string} owner - Repository owner * @param {string} repo - Repository name * @param {number} issueNumber - Issue number - * @param {string} [stateReason] - The reason for closing: "COMPLETED", "NOT_PLANNED", or "DUPLICATE" + * @param {string} stateReason - The reason for closing: "COMPLETED", "NOT_PLANNED", or "DUPLICATE" * @returns {Promise<{number: number, html_url: string, title: string, node_id: string}>} Issue details */ async function closeIssue(github, owner, repo, issueNumber, stateReason, intentMetadata, useIssueIntent) { From 900f2e1292fa95177e7a3b8d8acbbe37f474d3c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:43:39 +0000 Subject: [PATCH 06/10] chore: start PR finisher triage plan Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .github/workflows/agentic-auto-upgrade.yml | 2 +- .github/workflows/avenger.lock.yml | 5 ++--- .../workflows/daily-assign-issue-to-user.lock.yml | 8 +------- .github/workflows/dependabot-go-checker.lock.yml | 8 +------- .github/workflows/eslint-monster.lock.yml | 12 +----------- .github/workflows/hourly-ci-cleaner.lock.yml | 5 ++--- .github/workflows/issue-monster.lock.yml | 8 +------- .github/workflows/lint-monster.lock.yml | 12 +----------- .github/workflows/objective-impact-report.lock.yml | 8 +------- .github/workflows/release.lock.yml | 6 +++--- .../workflows/semantic-function-refactor.lock.yml | 8 +------- .github/workflows/skillet.lock.yml | 5 ++--- .../workflows/smoke-agent-public-approved.lock.yml | 8 +------- .github/workflows/smoke-codex.lock.yml | 8 +------- .github/workflows/smoke-copilot-aoai-apikey.lock.yml | 8 +------- .github/workflows/smoke-copilot-aoai-entra.lock.yml | 8 +------- .github/workflows/smoke-copilot.lock.yml | 8 +------- .github/workflows/workflow-generator.lock.yml | 8 +------- 18 files changed, 23 insertions(+), 112 deletions(-) diff --git a/.github/workflows/agentic-auto-upgrade.yml b/.github/workflows/agentic-auto-upgrade.yml index 7035101ee35..e169cea32b6 100644 --- a/.github/workflows/agentic-auto-upgrade.yml +++ b/.github/workflows/agentic-auto-upgrade.yml @@ -34,7 +34,7 @@ name: Agentic Auto-Upgrade on: schedule: - - cron: "21 3 * * 5" # Weekly (auto-upgrade) + - cron: "11 4 * * 6" # Weekly (auto-upgrade) workflow_dispatch: permissions: diff --git a/.github/workflows/avenger.lock.yml b/.github/workflows/avenger.lock.yml index c1462431878..759bba75c28 100644 --- a/.github/workflows/avenger.lock.yml +++ b/.github/workflows/avenger.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"6807590fb97b42c0858f3b0e068f5f2b823e6d3a5a873b1a1652f5658361eb44","body_hash":"e5fd04fe008ba327d939d9aee395ffb802836e30fd1f3772ca36984bdd1478f4","strict":true,"agent_id":"claude","agent_model":"claude-haiku-4.5","engine_versions":{"claude":"2.1.210"}} -# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} +# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -44,7 +44,6 @@ # Custom actions used: # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 -# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1146,7 +1145,7 @@ jobs: GH_HOST="${GH_HOST#http://}" echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Check last CI workflow run status on main branch diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml index 0f3e1db3997..f4e9d32275f 100644 --- a/.github/workflows/daily-assign-issue-to-user.lock.yml +++ b/.github/workflows/daily-assign-issue-to-user.lock.yml @@ -546,13 +546,7 @@ jobs: "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "assign_to_user": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index 63aa9de8d21..18ce2b0f7a7 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -596,13 +596,7 @@ jobs: "create_issue": " CONSTRAINTS: Maximum 10 issue(s) can be created. Title will be prefixed with \"[deps]\". Labels [\"dependencies\" \"go\" \"cookie\"] will be automatically added." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "close_issue": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/eslint-monster.lock.yml b/.github/workflows/eslint-monster.lock.yml index 9567a8849a2..2421e25c7d9 100644 --- a/.github/workflows/eslint-monster.lock.yml +++ b/.github/workflows/eslint-monster.lock.yml @@ -562,17 +562,7 @@ jobs: "update_issue": " CONSTRAINTS: Maximum 10 issue(s) can be updated. The target issue title must start with \"[eslint-monster] \"." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "assign_to_agent": [ - "rationale", - "confidence" - ], - "close_issue": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml index ca0f005228e..430262a8018 100644 --- a/.github/workflows/hourly-ci-cleaner.lock.yml +++ b/.github/workflows/hourly-ci-cleaner.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8b86cc12904088cf1b7630e0faa28299f64081df184a028b6293cb4f8bfdd07d","body_hash":"8eb6f8fd1e8e3d63cfd268226d09333129d49634435ae9a3c88debb099521ed5","strict":true,"agent_id":"claude","engine_versions":{"claude":"2.1.210"}} -# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} +# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -45,7 +45,6 @@ # Custom actions used: # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 -# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1153,7 +1152,7 @@ jobs: GH_HOST="${GH_HOST#http://}" echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Check last CI workflow run status on main branch diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 04698e1a38f..5204e7d345e 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -960,13 +960,7 @@ jobs: "assign_to_agent": " CONSTRAINTS: Maximum 3 issue(s) can be assigned to agent." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "assign_to_agent": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/lint-monster.lock.yml b/.github/workflows/lint-monster.lock.yml index 390a615463b..7defa1a775d 100644 --- a/.github/workflows/lint-monster.lock.yml +++ b/.github/workflows/lint-monster.lock.yml @@ -557,17 +557,7 @@ jobs: "update_issue": " CONSTRAINTS: Maximum 10 issue(s) can be updated. The target issue title must start with \"[lint-monster] \"." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "assign_to_agent": [ - "rationale", - "confidence" - ], - "close_issue": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/objective-impact-report.lock.yml b/.github/workflows/objective-impact-report.lock.yml index 26ce2b21d1f..300b7df8836 100644 --- a/.github/workflows/objective-impact-report.lock.yml +++ b/.github/workflows/objective-impact-report.lock.yml @@ -551,13 +551,7 @@ jobs: "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"Impact Efficiency Report - \"." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "close_issue": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 5ffc07c471f..c3e4b45dede 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"2b85ad1e6c3e13e577935d3841133c8c2d72ba5ad9c46e4116d3d60d49ee875b","body_hash":"646353d7bb4e5523bc85349c2cce38188190095a303f83cc95961ff145a47043","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.70"}} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"anchore/sbom-action","sha":"e22c389904149dbc22b58101806040fa8d37a610","version":"v0.24.0"},{"repo":"docker/build-push-action","sha":"53b7df96c91f9c12dcc8a07bcb9ccacbed38856a","version":"v7.3.0"},{"repo":"docker/login-action","sha":"af1e73f918a031802d376d3c8bbc3fe56130a9b0","version":"v4.4.0"},{"repo":"docker/metadata-action","sha":"dc802804100637a589fabce1cb79ff13a1411302","version":"v6.2.0"},{"repo":"docker/setup-buildx-action","sha":"bb05f3f5519dd87d3ba754cc423b652a5edd6d2c","version":"v4.2.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"anchore/sbom-action","sha":"e22c389904149dbc22b58101806040fa8d37a610","version":"v0.24.0"},{"repo":"docker/build-push-action","sha":"53b7df96c91f9c12dcc8a07bcb9ccacbed38856a","version":"v7.3.0"},{"repo":"docker/login-action","sha":"af1e73f918a031802d376d3c8bbc3fe56130a9b0","version":"v4.4.0"},{"repo":"docker/metadata-action","sha":"dc802804100637a589fabce1cb79ff13a1411302","version":"v6.2.0"},{"repo":"docker/setup-buildx-action","sha":"bb05f3f5519dd87d3ba754cc423b652a5edd6d2c","version":"v4.2.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -47,7 +47,7 @@ # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) -# - actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # b7ad1dad31e06c5925ef5d2fc7ad053ef454303e +# - actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 # - anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 # - docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 @@ -1944,7 +1944,7 @@ jobs: env: RELEASE_TAG: ${{ needs.config.outputs.release_tag }} - name: Setup Go - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # b7ad1dad31e06c5925ef5d2fc7ad053ef454303e + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: cache: false go-version-file: go.mod diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index 95fb058de7b..b1b0ecbb947 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -568,13 +568,7 @@ jobs: "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[refactor] \". Labels [\"refactoring\" \"code-quality\" \"automated-analysis\" \"cookie\"] will be automatically added." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "close_issue": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/skillet.lock.yml b/.github/workflows/skillet.lock.yml index 2c83c89a672..d718a6999b3 100644 --- a/.github/workflows/skillet.lock.yml +++ b/.github/workflows/skillet.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"2b7791e73899163b45a0881db45f5747495a6a7dbd2dc4e17f3f31af4ecf6898","body_hash":"ff6f6b1fbf4788ce998b5ddca9ed8f907259189ec1db98a5cd9768e4e0ed2f50","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.70"}} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"3a2844b7e9c422d3c10d287c895573f7108da1b3"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"3a2844b7e9c422d3c10d287c895573f7108da1b3"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -45,7 +45,6 @@ # Custom actions used: # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 -# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 @@ -1728,7 +1727,7 @@ jobs: GH_AW_INFO_AWF_VERSION: "v0.27.35" GH_AW_INFO_ENGINE_ID: "copilot" - name: Checkout skills directory - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false sparse-checkout: | diff --git a/.github/workflows/smoke-agent-public-approved.lock.yml b/.github/workflows/smoke-agent-public-approved.lock.yml index 7ccd7e9ed40..fbaf98e3f04 100644 --- a/.github/workflows/smoke-agent-public-approved.lock.yml +++ b/.github/workflows/smoke-agent-public-approved.lock.yml @@ -629,13 +629,7 @@ jobs: "assign_to_agent": " CONSTRAINTS: Maximum 1 issue(s) can be assigned to agent." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "assign_to_agent": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 63abff2dd9d..25ea2f68fc5 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -749,13 +749,7 @@ jobs: }, "name": "add_smoked_label" } - ], - "required_field_additions": { - "set_issue_field": [ - "rationale", - "confidence" - ] - } + ] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml index 12a2a184229..0d51414fedb 100644 --- a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml @@ -878,13 +878,7 @@ jobs: }, "name": "haiku_printer" } - ], - "required_field_additions": { - "set_issue_type": [ - "rationale", - "confidence" - ] - } + ] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/smoke-copilot-aoai-entra.lock.yml b/.github/workflows/smoke-copilot-aoai-entra.lock.yml index 5f8d866206f..4808a276da8 100644 --- a/.github/workflows/smoke-copilot-aoai-entra.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-entra.lock.yml @@ -879,13 +879,7 @@ jobs: }, "name": "haiku_printer" } - ], - "required_field_additions": { - "set_issue_type": [ - "rationale", - "confidence" - ] - } + ] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index c025136d092..bc3378dbe8f 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -894,13 +894,7 @@ jobs: }, "name": "haiku_printer" } - ], - "required_field_additions": { - "set_issue_type": [ - "rationale", - "confidence" - ] - } + ] } GH_AW_VALIDATION_JSON: | { diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index 5932b2877db..e940b4bca43 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -600,13 +600,7 @@ jobs: "update_issue": " CONSTRAINTS: Maximum 1 issue(s) can be updated. Body updates are allowed." }, "repo_params": {}, - "dynamic_tools": [], - "required_field_additions": { - "assign_to_agent": [ - "rationale", - "confidence" - ] - } + "dynamic_tools": [] } GH_AW_VALIDATION_JSON: | { From e41a65c476f41badb437c64ed1e995b46803c8b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 17:31:05 +0000 Subject: [PATCH 07/10] fix: honor issue-intent opt-out at runtime for labels/type/field and schema coverage Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/add_labels.cjs | 3 +- actions/setup/js/add_labels.test.cjs | 30 +++++++++++-- actions/setup/js/set_issue_field.cjs | 9 ++-- actions/setup/js/set_issue_field.test.cjs | 31 ++++++++++++- actions/setup/js/set_issue_type.cjs | 44 ++++++++++++------- actions/setup/js/set_issue_type.test.cjs | 35 +++++++++++++-- pkg/parser/schema_safe_outputs_target_test.go | 14 ++++++ pkg/parser/schemas/main_workflow_schema.json | 24 ++++++++++ 8 files changed, 160 insertions(+), 30 deletions(-) diff --git a/actions/setup/js/add_labels.cjs b/actions/setup/js/add_labels.cjs index 9b49c0c1a04..fc08cb270ab 100644 --- a/actions/setup/js/add_labels.cjs +++ b/actions/setup/js/add_labels.cjs @@ -44,6 +44,7 @@ const main = createCountGatedHandler({ handlerType: HANDLER_TYPE, setup: async (config, maxCount, isStaged) => { const { allowed: allowedLabels = [], blocked: blockedPatterns = [] } = config; + const issueIntentEnabled = config.issue_intent === true; const requiredLabels = Array.isArray(config.required_labels) ? config.required_labels : []; const requiredTitlePrefix = config.required_title_prefix || ""; const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config); @@ -190,7 +191,7 @@ const main = createCountGatedHandler({ const labelsRequestPayload = uniqueLabels.map(name => { const labelSpec = requestedLabelSpecByLowerName.get(name.toLowerCase()) ?? { name }; const hasIntentMetadata = Boolean(labelSpec.rationale || labelSpec.confidence || labelSpec.suggest); - return hasIntentMetadata ? labelSpec : labelSpec.name; + return issueIntentEnabled && hasIntentMetadata ? labelSpec : labelSpec.name; }); core.info(`Adding ${uniqueLabels.length} labels to ${contextType} #${itemNumber} in ${itemRepo}: ${JSON.stringify(labelsRequestPayload)}`); diff --git a/actions/setup/js/add_labels.test.cjs b/actions/setup/js/add_labels.test.cjs index cff87925c90..e27ebf0406f 100644 --- a/actions/setup/js/add_labels.test.cjs +++ b/actions/setup/js/add_labels.test.cjs @@ -114,7 +114,7 @@ describe("add_labels", () => { }); it("should accept structured label entries and add normalized label names", async () => { - const handler = await main({ max: 10 }); + const handler = await main({ max: 10, issue_intent: true }); const addLabelsCalls = []; mockGithub.rest.issues.addLabels = async params => { @@ -137,7 +137,7 @@ describe("add_labels", () => { }); it("should send structured label metadata without requiring a runtime feature", async () => { - const handler = await main({ max: 10 }); + const handler = await main({ max: 10, issue_intent: true }); const addLabelsCalls = []; mockGithub.rest.issues.addLabels = async params => { @@ -159,7 +159,7 @@ describe("add_labels", () => { }); it("should normalize lowercase confidence in structured label metadata", async () => { - const handler = await main({ max: 10 }); + const handler = await main({ max: 10, issue_intent: true }); const addLabelsCalls = []; mockGithub.rest.issues.addLabels = async params => { @@ -513,7 +513,7 @@ describe("add_labels", () => { }); it("should prefer the metadata-bearing entry when a duplicate label name appears", async () => { - const handler = await main({ max: 10 }); + const handler = await main({ max: 10, issue_intent: true }); const addLabelsCalls = []; mockGithub.rest.issues.addLabels = async params => { @@ -537,6 +537,28 @@ describe("add_labels", () => { expect(addLabelsCalls[0].labels).toEqual([{ name: "bug", rationale: "Known crash path", confidence: "HIGH", suggest: true }]); }); + it("should strip structured intent metadata when issue_intent is disabled", async () => { + const handler = await main({ max: 10, issue_intent: false }); + const addLabelsCalls = []; + + mockGithub.rest.issues.addLabels = async params => { + addLabelsCalls.push(params); + return {}; + }; + + const result = await handler( + { + item_number: 456, + labels: [{ name: "bug", rationale: "Known crash path", confidence: "HIGH", suggest: true }], + }, + {} + ); + + expect(result.success).toBe(true); + expect(addLabelsCalls).toHaveLength(1); + expect(addLabelsCalls[0].labels).toEqual(["bug"]); + }); + it("should sanitize and trim label names", async () => { const handler = await main({ max: 10 }); const addLabelsCalls = []; diff --git a/actions/setup/js/set_issue_field.cjs b/actions/setup/js/set_issue_field.cjs index 27a329e4961..ca321dc4138 100644 --- a/actions/setup/js/set_issue_field.cjs +++ b/actions/setup/js/set_issue_field.cjs @@ -170,6 +170,7 @@ async function main(config = {}) { const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config); const githubClient = await createAuthenticatedGitHubClient(config); const isStaged = isStagedMode(config); + const issueIntentEnabled = config.issue_intent === true; core.info(`Set issue field configuration: max=${maxCount}`); const requiredLabels = Array.isArray(config.required_labels) ? config.required_labels : []; @@ -343,10 +344,12 @@ async function main(config = {}) { ...fieldUpdateResult.update, }; - Object.assign(fieldUpdate, normalizeIssueIntentMetadata(item)); - core.info("Using GraphQL-Features header for issue field mutation"); + if (issueIntentEnabled) { + Object.assign(fieldUpdate, normalizeIssueIntentMetadata(item)); + core.info("Using GraphQL-Features header for issue field mutation"); + } - await setIssueFieldValue(githubClient, issueNodeId, fieldUpdate, true); + await setIssueFieldValue(githubClient, issueNodeId, fieldUpdate, issueIntentEnabled); core.info(`Successfully set issue field ${JSON.stringify(fieldName || fieldNodeId)} to ${JSON.stringify(value)} on issue #${issueNumber}`); diff --git a/actions/setup/js/set_issue_field.test.cjs b/actions/setup/js/set_issue_field.test.cjs index 5b8619894cb..e1a667180a6 100644 --- a/actions/setup/js/set_issue_field.test.cjs +++ b/actions/setup/js/set_issue_field.test.cjs @@ -90,7 +90,7 @@ describe("set_issue_field (Handler Factory Architecture)", () => { }); const { main } = require("./set_issue_field.cjs"); - handler = await main({ max: 5 }); + handler = await main({ max: 5, issue_intent: true }); }); it("should return a function from main()", async () => { @@ -498,7 +498,7 @@ describe("set_issue_field (Handler Factory Architecture)", () => { it("should include issue intent metadata without requiring a runtime feature", async () => { const { main } = require("./set_issue_field.cjs"); - const featureHandler = await main({ max: 5 }); + const featureHandler = await main({ max: 5, issue_intent: true }); const result = await featureHandler( { @@ -530,4 +530,31 @@ describe("set_issue_field (Handler Factory Architecture)", () => { }) ); }); + + it("should omit intent metadata and GraphQL feature header when issue_intent is disabled", async () => { + const { main } = require("./set_issue_field.cjs"); + const handlerWithoutIntent = await main({ max: 5, issue_intent: false }); + + const result = await handlerWithoutIntent( + { + type: "set_issue_field", + issue_number: 42, + field_name: "Customer Impact", + value: "High", + rationale: "should be ignored", + confidence: "HIGH", + suggest: true, + }, + {} + ); + + expect(result.success).toBe(true); + const mutationCall = mockGraphql.mock.calls.find(([query]) => query.includes("setIssueFieldValue")); + expect(mutationCall).toBeTruthy(); + expect(mutationCall[1]).not.toHaveProperty("headers"); + expect(mutationCall[1].issueFields[0]).toEqual(expect.objectContaining({ fieldId: textFieldId, textValue: "High" })); + expect(mutationCall[1].issueFields[0]).not.toHaveProperty("rationale"); + expect(mutationCall[1].issueFields[0]).not.toHaveProperty("confidence"); + expect(mutationCall[1].issueFields[0]).not.toHaveProperty("suggest"); + }); }); diff --git a/actions/setup/js/set_issue_type.cjs b/actions/setup/js/set_issue_type.cjs index 0cafadfc7fa..574a25902a3 100644 --- a/actions/setup/js/set_issue_type.cjs +++ b/actions/setup/js/set_issue_type.cjs @@ -182,6 +182,7 @@ async function main(config = {}) { core.info(`Set issue type configuration: max=${maxCount}`); const requiredLabels = Array.isArray(config.required_labels) ? config.required_labels : []; const requiredTitlePrefix = config.required_title_prefix || ""; + const issueIntentEnabled = config.issue_intent === true; if (requiredLabels.length > 0) core.info(`Required labels (all): ${requiredLabels.join(", ")}`); if (requiredTitlePrefix) core.info(`Required title prefix: ${requiredTitlePrefix}`); if (allowedTypes.length > 0) { @@ -283,26 +284,35 @@ async function main(config = {}) { try { const { owner, repo } = repoParts; - const intentMetadata = normalizeIssueIntentMetadata(item); + const intentMetadata = issueIntentEnabled ? normalizeIssueIntentMetadata(item) : {}; if (!isClear) { - // GraphQL intent path: resolve the type's node ID from org issue types, then - // call setIssueTypeById with IssueTypeUpdateInput + the GraphQL-Features header. - core.info("Using GraphQL intent path with IssueTypeUpdateInput"); - core.info(`Fetching issue node ID for issue #${issueNumber}`); - const issueNodeId = await getIssueNodeId(githubClient, owner, repo, issueNumber); - core.info(`Fetching issue types for repo ${owner}/${repo}`); - const issueTypes = await fetchIssueTypesForRepo(githubClient, owner, repo); - core.info(`Found ${issueTypes.length} issue type(s) for repo ${owner}/${repo}`); - const typeNode = issueTypes.find(t => t.name.toLowerCase() === resolvedIssueTypeName.toLowerCase()); - if (!typeNode) { - const availableNames = issueTypes.map(t => t.name).join(", "); - const error = availableNames ? `Issue type ${JSON.stringify(resolvedIssueTypeName)} not found. Available types: ${availableNames}` : NO_ISSUE_TYPES_AVAILABLE_ERROR; - core.error(`Failed to set issue type on issue #${issueNumber}: ${error}`); - return { success: false, error }; + if (issueIntentEnabled) { + // GraphQL intent path: resolve the type's node ID from org issue types, then + // call setIssueTypeById with IssueTypeUpdateInput + the GraphQL-Features header. + core.info("Using GraphQL intent path with IssueTypeUpdateInput"); + core.info(`Fetching issue node ID for issue #${issueNumber}`); + const issueNodeId = await getIssueNodeId(githubClient, owner, repo, issueNumber); + core.info(`Fetching issue types for repo ${owner}/${repo}`); + const issueTypes = await fetchIssueTypesForRepo(githubClient, owner, repo); + core.info(`Found ${issueTypes.length} issue type(s) for repo ${owner}/${repo}`); + const typeNode = issueTypes.find(t => t.name.toLowerCase() === resolvedIssueTypeName.toLowerCase()); + if (!typeNode) { + const availableNames = issueTypes.map(t => t.name).join(", "); + const error = availableNames ? `Issue type ${JSON.stringify(resolvedIssueTypeName)} not found. Available types: ${availableNames}` : NO_ISSUE_TYPES_AVAILABLE_ERROR; + core.error(`Failed to set issue type on issue #${issueNumber}: ${error}`); + return { success: false, error }; + } + core.info(`Resolved issue type ${JSON.stringify(resolvedIssueTypeName)} to node ID ${typeNode.id}`); + await setIssueTypeById(githubClient, issueNodeId, typeNode.id, intentMetadata); + } else { + await githubClient.rest.issues.update({ + owner, + repo, + issue_number: issueNumber, + type: resolvedIssueTypeName, + }); } - core.info(`Resolved issue type ${JSON.stringify(resolvedIssueTypeName)} to node ID ${typeNode.id}`); - await setIssueTypeById(githubClient, issueNodeId, typeNode.id, intentMetadata); } else { // REST path: preserve the existing clear behavior. await githubClient.rest.issues.update({ diff --git a/actions/setup/js/set_issue_type.test.cjs b/actions/setup/js/set_issue_type.test.cjs index 616aa0371d3..a5a6ae834f2 100644 --- a/actions/setup/js/set_issue_type.test.cjs +++ b/actions/setup/js/set_issue_type.test.cjs @@ -77,7 +77,7 @@ describe("set_issue_type (Handler Factory Architecture)", () => { }); const { main } = require("./set_issue_type.cjs"); - handler = await main({ max: 5 }); + handler = await main({ max: 5, issue_intent: true }); }); it("should return a function from main()", async () => { @@ -157,6 +157,7 @@ describe("set_issue_type (Handler Factory Architecture)", () => { const handlerWithAllowed = await main({ max: 5, allowed: ["Bug", "Feature"], + issue_intent: true, }); const message = { @@ -174,6 +175,7 @@ describe("set_issue_type (Handler Factory Architecture)", () => { const handlerWithAllowed = await main({ max: 5, allowed: ["Bug", "Feature"], + issue_intent: true, }); const message = { @@ -192,6 +194,7 @@ describe("set_issue_type (Handler Factory Architecture)", () => { const handlerWithAllowed = await main({ max: 5, allowed: ["Bug", "Feature"], + issue_intent: true, }); const message = { @@ -330,6 +333,7 @@ describe("set_issue_type (Handler Factory Architecture)", () => { const handlerWithAllowed = await main({ max: 5, allowed: ["Bug", "Feature"], + issue_intent: true, }); const message = { @@ -373,7 +377,7 @@ describe("set_issue_type (Handler Factory Architecture)", () => { }); const { main } = require("./set_issue_type.cjs"); - const featureHandler = await main({ max: 5 }); + const featureHandler = await main({ max: 5, issue_intent: true }); const result = await featureHandler( { @@ -427,7 +431,7 @@ describe("set_issue_type (Handler Factory Architecture)", () => { }); const { main } = require("./set_issue_type.cjs"); - const featureHandler = await main({ max: 5 }); + const featureHandler = await main({ max: 5, issue_intent: true }); const result = await featureHandler( { @@ -468,4 +472,29 @@ describe("set_issue_type (Handler Factory Architecture)", () => { expect(metadata).not.toHaveProperty("rationale"); }); + + it("should use legacy REST issue type update when issue_intent is disabled", async () => { + const { main } = require("./set_issue_type.cjs"); + const handlerWithoutIntent = await main({ max: 5, issue_intent: false }); + + const result = await handlerWithoutIntent( + { + type: "set_issue_type", + issue_number: 99, + issue_type: "Bug", + rationale: "should be ignored", + confidence: "HIGH", + }, + {} + ); + + expect(result.success).toBe(true); + expect(mockGithub.rest.issues.update).toHaveBeenCalledWith({ + owner: "test-owner", + repo: "test-repo", + issue_number: 99, + type: "Bug", + }); + expect(mockGithub.graphql).not.toHaveBeenCalledWith(expect.stringContaining("updateIssue"), expect.anything()); + }); }); diff --git a/pkg/parser/schema_safe_outputs_target_test.go b/pkg/parser/schema_safe_outputs_target_test.go index 8527070cd88..4212e4f782b 100644 --- a/pkg/parser/schema_safe_outputs_target_test.go +++ b/pkg/parser/schema_safe_outputs_target_test.go @@ -304,6 +304,20 @@ func TestMainWorkflowSchema_SafeOutputsTargetProperties(t *testing.T) { }, }, }, + { + name: "issue-intent toggle accepted for close and assignment tools", + safeOutputs: map[string]any{ + "close-issue": map[string]any{ + "issue-intent": false, + }, + "assign-to-user": map[string]any{ + "issue-intent": false, + }, + "assign-to-agent": map[string]any{ + "issue-intent": false, + }, + }, + }, } for _, tt := range tests { diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 96b9a1f003a..1d7f6243811 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -4608,6 +4608,10 @@ "description": "When true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", "examples": [true, false] }, + "issue-intent": { + "type": "boolean", + "description": "Enable issue-intent metadata support (rationale/confidence/suggest) for this output type." + }, "samples": { "description": "Internal hidden feature. Optional list of declarative sample payloads that exercise this safe-output handler. Used by the hidden `gh aw compile --use-samples` flag to replace the agentic step with a deterministic replay through the safe-outputs MCP server. Each entry should conform to the corresponding MCP tool inputSchema; recognized sidecar keys (currently `patch` for create-pull-request and push-to-pull-request-branch) are stripped before schema validation and consumed by the replay driver.", "oneOf": [ @@ -5262,6 +5266,10 @@ "description": "When true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", "examples": [true, false] }, + "issue-intent": { + "type": "boolean", + "description": "Enable issue-intent metadata support (rationale/confidence/suggest) for this output type." + }, "samples": { "description": "Internal hidden feature. Optional list of declarative sample payloads that exercise this safe-output handler. Used by the hidden `gh aw compile --use-samples` flag to replace the agentic step with a deterministic replay through the safe-outputs MCP server. Each entry should conform to the corresponding MCP tool inputSchema; recognized sidecar keys (currently `patch` for create-pull-request and push-to-pull-request-branch) are stripped before schema validation and consumed by the replay driver.", "oneOf": [ @@ -5361,6 +5369,10 @@ "description": "When true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", "examples": [true, false] }, + "issue-intent": { + "type": "boolean", + "description": "Enable issue-intent metadata support (rationale/confidence/suggest) for this output type." + }, "samples": { "description": "Internal hidden feature. Optional list of declarative sample payloads that exercise this safe-output handler. Used by the hidden `gh aw compile --use-samples` flag to replace the agentic step with a deterministic replay through the safe-outputs MCP server. Each entry should conform to the corresponding MCP tool inputSchema; recognized sidecar keys (currently `patch` for create-pull-request and push-to-pull-request-branch) are stripped before schema validation and consumed by the replay driver.", "oneOf": [ @@ -6245,6 +6257,10 @@ "description": "When true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", "examples": [true, false] }, + "issue-intent": { + "type": "boolean", + "description": "Enable issue-intent metadata support (rationale/confidence/suggest) for this output type." + }, "samples": { "description": "Internal hidden feature. Optional list of declarative sample payloads that exercise this safe-output handler. Used by the hidden `gh aw compile --use-samples` flag to replace the agentic step with a deterministic replay through the safe-outputs MCP server. Each entry should conform to the corresponding MCP tool inputSchema; recognized sidecar keys (currently `patch` for create-pull-request and push-to-pull-request-branch) are stripped before schema validation and consumed by the replay driver.", "oneOf": [ @@ -8228,6 +8244,10 @@ "description": "When true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", "examples": [true, false] }, + "issue-intent": { + "type": "boolean", + "description": "Enable issue-intent metadata support (rationale/confidence/suggest) for this output type." + }, "samples": { "description": "Internal hidden feature. Optional list of declarative sample payloads that exercise this safe-output handler. Used by the hidden `gh aw compile --use-samples` flag to replace the agentic step with a deterministic replay through the safe-outputs MCP server. Each entry should conform to the corresponding MCP tool inputSchema; recognized sidecar keys (currently `patch` for create-pull-request and push-to-pull-request-branch) are stripped before schema validation and consumed by the replay driver.", "oneOf": [ @@ -8317,6 +8337,10 @@ "description": "When true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", "examples": [true, false] }, + "issue-intent": { + "type": "boolean", + "description": "Enable issue-intent metadata support (rationale/confidence/suggest) for this output type." + }, "samples": { "description": "Internal hidden feature. Optional list of declarative sample payloads that exercise this safe-output handler. Used by the hidden `gh aw compile --use-samples` flag to replace the agentic step with a deterministic replay through the safe-outputs MCP server. Each entry should conform to the corresponding MCP tool inputSchema; recognized sidecar keys (currently `patch` for create-pull-request and push-to-pull-request-branch) are stripped before schema validation and consumed by the replay driver.", "oneOf": [ From 739c45af963b89abcd62eec8eb02be7cd8d095c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 17:57:39 +0000 Subject: [PATCH 08/10] fix: remove intent feature header and enforce intent schema states Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/assign_agent_helpers.cjs | 2 +- .../setup/js/assign_agent_helpers.test.cjs | 1 - actions/setup/js/assign_to_user.cjs | 4 +- actions/setup/js/assign_to_user.test.cjs | 1 - actions/setup/js/close_issue.cjs | 1 - actions/setup/js/close_issue.test.cjs | 2 +- .../setup/js/generate_safe_outputs_tools.cjs | 40 ++++++++++ .../js/generate_safe_outputs_tools.test.cjs | 74 +++++++++++++++++++ actions/setup/js/set_issue_field.cjs | 27 +++---- actions/setup/js/set_issue_field.test.cjs | 7 +- actions/setup/js/set_issue_type.cjs | 3 +- actions/setup/js/set_issue_type.test.cjs | 4 +- 12 files changed, 131 insertions(+), 35 deletions(-) diff --git a/actions/setup/js/assign_agent_helpers.cjs b/actions/setup/js/assign_agent_helpers.cjs index 9779aee4ff7..d2bdc7bf30c 100644 --- a/actions/setup/js/assign_agent_helpers.cjs +++ b/actions/setup/js/assign_agent_helpers.cjs @@ -402,7 +402,7 @@ async function assignAgentToIssue( assignees: [agentLogin], }; if (useIssueIntent && intentMetadata && Object.keys(intentMetadata).length > 0) { - Object.assign(assignParams, intentMetadata, { headers: { "GraphQL-Features": "update_issue_suggestions" } }); + Object.assign(assignParams, intentMetadata); } await githubClient.request("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", assignParams); return true; diff --git a/actions/setup/js/assign_agent_helpers.test.cjs b/actions/setup/js/assign_agent_helpers.test.cjs index b81e23b6edb..9fb1d04ea12 100644 --- a/actions/setup/js/assign_agent_helpers.test.cjs +++ b/actions/setup/js/assign_agent_helpers.test.cjs @@ -396,7 +396,6 @@ describe("assign_agent_helpers.cjs", () => { assignees: ["copilot-swe-agent[bot]"], rationale: "Agent owns the code path", confidence: "HIGH", - headers: { "GraphQL-Features": "update_issue_suggestions" }, }); }); diff --git a/actions/setup/js/assign_to_user.cjs b/actions/setup/js/assign_to_user.cjs index 29a796f908d..718a5e38db5 100644 --- a/actions/setup/js/assign_to_user.cjs +++ b/actions/setup/js/assign_to_user.cjs @@ -157,9 +157,7 @@ const main = createCountGatedHandler({ assignees: uniqueAssignees, }; if (issueIntentEnabled && Object.keys(intentMetadata).length > 0) { - Object.assign(addAssigneesParams, intentMetadata, { - headers: { "GraphQL-Features": "update_issue_suggestions" }, - }); + Object.assign(addAssigneesParams, intentMetadata); } await githubClient.rest.issues.addAssignees(addAssigneesParams); diff --git a/actions/setup/js/assign_to_user.test.cjs b/actions/setup/js/assign_to_user.test.cjs index b3b4ecaba33..6bb28f84e82 100644 --- a/actions/setup/js/assign_to_user.test.cjs +++ b/actions/setup/js/assign_to_user.test.cjs @@ -98,7 +98,6 @@ describe("assign_to_user (Handler Factory Architecture)", () => { assignees: ["user1"], rationale: "Primary maintainer for impacted area", confidence: "LOW", - headers: { "GraphQL-Features": "update_issue_suggestions" }, }); }); diff --git a/actions/setup/js/close_issue.cjs b/actions/setup/js/close_issue.cjs index dd01841fd0a..89f874b942b 100644 --- a/actions/setup/js/close_issue.cjs +++ b/actions/setup/js/close_issue.cjs @@ -173,7 +173,6 @@ async function closeIssue(github, owner, repo, issueNumber, stateReason, intentM const { data: issue } = await github.request("PATCH /repos/{owner}/{repo}/issues/{issue_number}", { ...baseParams, ...intentMetadata, - headers: { "GraphQL-Features": "update_issue_suggestions" }, }); return issue; } catch (error) { diff --git a/actions/setup/js/close_issue.test.cjs b/actions/setup/js/close_issue.test.cjs index cf6c96db8a4..2205b2758d3 100644 --- a/actions/setup/js/close_issue.test.cjs +++ b/actions/setup/js/close_issue.test.cjs @@ -168,7 +168,7 @@ describe("close_issue", () => { expect(requestCalls[0].route).toBe("PATCH /repos/{owner}/{repo}/issues/{issue_number}"); expect(requestCalls[0].params.rationale).toBe("Duplicate confirmed"); expect(requestCalls[0].params.confidence).toBe("MEDIUM"); - expect(requestCalls[0].params.headers).toEqual({ "GraphQL-Features": "update_issue_suggestions" }); + expect(requestCalls[0].params.headers).toBeUndefined(); }); it("should skip issue-intent metadata when explicitly disabled", async () => { diff --git a/actions/setup/js/generate_safe_outputs_tools.cjs b/actions/setup/js/generate_safe_outputs_tools.cjs index f81b13af529..a2eb0c2dcbb 100644 --- a/actions/setup/js/generate_safe_outputs_tools.cjs +++ b/actions/setup/js/generate_safe_outputs_tools.cjs @@ -40,6 +40,7 @@ const ADD_COMMENT_REPLY_SUPPORT_SENTENCE = "Supports reply_to_id for discussion const ADD_COMMENT_REPLY_SUPPORT_REGEX = /\s*Supports reply_to_id for discussion threading\./g; const ISSUE_INTENT_SUFFIX = "INTENT: Include rationale (string, max 280 chars) and confidence (string, exactly one of: LOW, MEDIUM, HIGH) with each call."; const ISSUE_INTENT_TOOL_NAMES = new Set(["set_issue_type", "set_issue_field", "add_labels", "close_issue", "assign_to_user", "assign_to_agent"]); +const ISSUE_INTENT_SCHEMA_FIELDS = ["rationale", "confidence", "suggest"]; /** * Determine whether issue-intent guidance is enabled for a tool. @@ -56,6 +57,42 @@ function isIssueIntentEnabledForTool(toolName, toolConfig) { return !!(toolConfig && typeof toolConfig === "object" && "issue_intent" in toolConfig && toolConfig.issue_intent === true); } +/** + * Determine whether issue-intent schema fields should be omitted for a tool. + * Default is optional (present, not required); explicit issue_intent: false omits them. + * + * @param {string} toolName + * @param {unknown} toolConfig + * @returns {boolean} + */ +function isIssueIntentDisabledForTool(toolName, toolConfig) { + if (!ISSUE_INTENT_TOOL_NAMES.has(toolName)) { + return false; + } + return !!(toolConfig && typeof toolConfig === "object" && "issue_intent" in toolConfig && toolConfig.issue_intent === false); +} + +/** + * Remove issue-intent properties from a tool schema. + * @param {{inputSchema?: {properties?: Record, required?: string[]}}} tool + */ +function stripIssueIntentSchemaFields(tool) { + if (!tool.inputSchema || !tool.inputSchema.properties) { + return; + } + for (const field of ISSUE_INTENT_SCHEMA_FIELDS) { + if (field in tool.inputSchema.properties) { + delete tool.inputSchema.properties[field]; + } + } + if (Array.isArray(tool.inputSchema.required)) { + tool.inputSchema.required = tool.inputSchema.required.filter(f => !ISSUE_INTENT_SCHEMA_FIELDS.includes(f)); + if (tool.inputSchema.required.length === 0) { + delete tool.inputSchema.required; + } + } +} + /** * Update add_comment description to match runtime-safe-output permissions. * @param {string} description @@ -168,6 +205,9 @@ async function main() { if (isIssueIntentEnabledForTool(tool.name, config[tool.name])) { enhancedTool.description = `${enhancedTool.description || ""} ${ISSUE_INTENT_SUFFIX}`.trim(); } + if (isIssueIntentDisabledForTool(tool.name, config[tool.name])) { + stripIssueIntentSchemaFields(enhancedTool); + } if (tool.name === "add_comment") { enhancedTool.description = updateAddCommentDescription(enhancedTool.description, config.add_comment); diff --git a/actions/setup/js/generate_safe_outputs_tools.test.cjs b/actions/setup/js/generate_safe_outputs_tools.test.cjs index 95d8b17a38e..2a2ff46a06a 100644 --- a/actions/setup/js/generate_safe_outputs_tools.test.cjs +++ b/actions/setup/js/generate_safe_outputs_tools.test.cjs @@ -386,4 +386,78 @@ describe("generate_safe_outputs_tools", () => { expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "close_issue").description).not.toContain(intentSuffix); expect(result.find((/** @type {{name: string, description: string}} */ t) => t.name === "assign_to_user").description).not.toContain(intentSuffix); }); + + it("reflects required/optional/absent intent fields per tool configuration", () => { + fs.writeFileSync( + toolsSourcePath, + JSON.stringify([ + { + name: "close_issue", + description: "Closes issue.", + inputSchema: { + type: "object", + properties: { body: { type: "string" }, rationale: { type: "string" }, confidence: { type: "string" }, suggest: { type: "boolean" } }, + required: ["body"], + }, + }, + { + name: "assign_to_user", + description: "Assigns users.", + inputSchema: { + type: "object", + properties: { issue_number: { type: "number" }, rationale: { type: "string" }, confidence: { type: "string" }, suggest: { type: "boolean" } }, + required: ["issue_number"], + }, + }, + { + name: "assign_to_agent", + description: "Assigns agent.", + inputSchema: { + type: "object", + properties: { issue_number: { type: "number" }, rationale: { type: "string" }, confidence: { type: "string" }, suggest: { type: "boolean" } }, + required: ["issue_number"], + }, + }, + ]) + ); + fs.writeFileSync( + configPath, + JSON.stringify({ + close_issue: { issue_intent: true }, + assign_to_user: {}, + assign_to_agent: { issue_intent: false }, + }) + ); + fs.writeFileSync( + toolsMetaPath, + JSON.stringify({ + description_suffixes: {}, + repo_params: {}, + dynamic_tools: [], + required_field_additions: { close_issue: ["rationale", "confidence"] }, + }) + ); + + runScript(); + + const result = JSON.parse(fs.readFileSync(outputPath, "utf8")); + const closeIssue = result.find((/** @type {{name: string, inputSchema: {properties: Record, required: string[]}}} */ t) => t.name === "close_issue"); + const assignToUser = result.find((/** @type {{name: string, inputSchema: {properties: Record, required: string[]}}} */ t) => t.name === "assign_to_user"); + const assignToAgent = result.find((/** @type {{name: string, inputSchema: {properties: Record, required: string[]}}} */ t) => t.name === "assign_to_agent"); + + expect(closeIssue.inputSchema.properties).toHaveProperty("rationale"); + expect(closeIssue.inputSchema.properties).toHaveProperty("confidence"); + expect(closeIssue.inputSchema.required).toEqual(expect.arrayContaining(["rationale", "confidence"])); + + expect(assignToUser.inputSchema.properties).toHaveProperty("rationale"); + expect(assignToUser.inputSchema.properties).toHaveProperty("confidence"); + expect(assignToUser.inputSchema.required).not.toContain("rationale"); + expect(assignToUser.inputSchema.required).not.toContain("confidence"); + + expect(assignToAgent.inputSchema.properties).not.toHaveProperty("rationale"); + expect(assignToAgent.inputSchema.properties).not.toHaveProperty("confidence"); + expect(assignToAgent.inputSchema.properties).not.toHaveProperty("suggest"); + expect(assignToAgent.inputSchema.required).not.toContain("rationale"); + expect(assignToAgent.inputSchema.required).not.toContain("confidence"); + }); }); diff --git a/actions/setup/js/set_issue_field.cjs b/actions/setup/js/set_issue_field.cjs index ca321dc4138..6cb317ea94e 100644 --- a/actions/setup/js/set_issue_field.cjs +++ b/actions/setup/js/set_issue_field.cjs @@ -136,27 +136,21 @@ function buildFieldUpdatePayload(field, rawValue) { * @param {Object} githubClient - Authenticated GitHub client * @param {string} issueNodeId - GraphQL node ID of the issue * @param {{fieldId: string, singleSelectOptionId?: string, numberValue?: number, dateValue?: string, textValue?: string, rationale?: string, confidence?: "LOW"|"MEDIUM"|"HIGH", suggest?: boolean}} fieldUpdate - * @param {boolean} [useIntentHeader] - When true, includes the GraphQL-Features header to expose intent input types * @returns {Promise} */ -async function setIssueFieldValue(githubClient, issueNodeId, fieldUpdate, useIntentHeader) { - /** @type {Record} */ - const variables = { - issueId: issueNodeId, - issueFields: [fieldUpdate], - }; - if (useIntentHeader) { - variables.headers = { "GraphQL-Features": "update_issue_suggestions" }; - } +async function setIssueFieldValue(githubClient, issueNodeId, fieldUpdate) { await githubClient.graphql( `mutation($issueId: ID!, $issueFields: [IssueFieldCreateOrUpdateInput!]!) { - setIssueFieldValue(input: { issueId: $issueId, issueFields: $issueFields }) { - issue { - id - } + setIssueFieldValue(input: { issueId: $issueId, issueFields: $issueFields }) { + issue { + id } + } }`, - variables + { + issueId: issueNodeId, + issueFields: [fieldUpdate], + } ); } @@ -346,10 +340,9 @@ async function main(config = {}) { if (issueIntentEnabled) { Object.assign(fieldUpdate, normalizeIssueIntentMetadata(item)); - core.info("Using GraphQL-Features header for issue field mutation"); } - await setIssueFieldValue(githubClient, issueNodeId, fieldUpdate, issueIntentEnabled); + await setIssueFieldValue(githubClient, issueNodeId, fieldUpdate); core.info(`Successfully set issue field ${JSON.stringify(fieldName || fieldNodeId)} to ${JSON.stringify(value)} on issue #${issueNumber}`); diff --git a/actions/setup/js/set_issue_field.test.cjs b/actions/setup/js/set_issue_field.test.cjs index e1a667180a6..21f94f84535 100644 --- a/actions/setup/js/set_issue_field.test.cjs +++ b/actions/setup/js/set_issue_field.test.cjs @@ -475,7 +475,7 @@ describe("set_issue_field (Handler Factory Architecture)", () => { expect(mockCore.warning).not.toHaveBeenCalledWith(expect.stringContaining("No issue fields were discovered")); }); - it("should send the GraphQL-Features header even without optional intent metadata", async () => { + it("should set issue field without optional intent metadata", async () => { const result = await handler( { type: "set_issue_field", @@ -491,7 +491,6 @@ describe("set_issue_field (Handler Factory Architecture)", () => { expect.stringContaining("setIssueFieldValue"), expect.objectContaining({ issueFields: [expect.objectContaining({ fieldId: textFieldId, textValue: "High" })], - headers: { "GraphQL-Features": "update_issue_suggestions" }, }) ); }); @@ -526,12 +525,11 @@ describe("set_issue_field (Handler Factory Architecture)", () => { suggest: true, }), ], - headers: { "GraphQL-Features": "update_issue_suggestions" }, }) ); }); - it("should omit intent metadata and GraphQL feature header when issue_intent is disabled", async () => { + it("should omit intent metadata when issue_intent is disabled", async () => { const { main } = require("./set_issue_field.cjs"); const handlerWithoutIntent = await main({ max: 5, issue_intent: false }); @@ -551,7 +549,6 @@ describe("set_issue_field (Handler Factory Architecture)", () => { expect(result.success).toBe(true); const mutationCall = mockGraphql.mock.calls.find(([query]) => query.includes("setIssueFieldValue")); expect(mutationCall).toBeTruthy(); - expect(mutationCall[1]).not.toHaveProperty("headers"); expect(mutationCall[1].issueFields[0]).toEqual(expect.objectContaining({ fieldId: textFieldId, textValue: "High" })); expect(mutationCall[1].issueFields[0]).not.toHaveProperty("rationale"); expect(mutationCall[1].issueFields[0]).not.toHaveProperty("confidence"); diff --git a/actions/setup/js/set_issue_type.cjs b/actions/setup/js/set_issue_type.cjs index 574a25902a3..bd8a65421bf 100644 --- a/actions/setup/js/set_issue_type.cjs +++ b/actions/setup/js/set_issue_type.cjs @@ -78,7 +78,6 @@ async function setIssueTypeById(githubClient, issueNodeId, issueTypeId, intentMe { issueId: issueNodeId, issueType, - headers: { "GraphQL-Features": "update_issue_suggestions" }, } ); } @@ -289,7 +288,7 @@ async function main(config = {}) { if (!isClear) { if (issueIntentEnabled) { // GraphQL intent path: resolve the type's node ID from org issue types, then - // call setIssueTypeById with IssueTypeUpdateInput + the GraphQL-Features header. + // call setIssueTypeById with IssueTypeUpdateInput. core.info("Using GraphQL intent path with IssueTypeUpdateInput"); core.info(`Fetching issue node ID for issue #${issueNumber}`); const issueNodeId = await getIssueNodeId(githubClient, owner, repo, issueNumber); diff --git a/actions/setup/js/set_issue_type.test.cjs b/actions/setup/js/set_issue_type.test.cjs index a5a6ae834f2..41ceeec6689 100644 --- a/actions/setup/js/set_issue_type.test.cjs +++ b/actions/setup/js/set_issue_type.test.cjs @@ -106,7 +106,6 @@ describe("set_issue_type (Handler Factory Architecture)", () => { issueType: { issueTypeId: "IT_kwDO_bug", }, - headers: { "GraphQL-Features": "update_issue_suggestions" }, }) ); }); @@ -355,7 +354,7 @@ describe("set_issue_type (Handler Factory Architecture)", () => { ); }); - it("should use GraphQL intent path with IssueTypeUpdateInput and GraphQL-Features header", async () => { + it("should use GraphQL intent path with IssueTypeUpdateInput", async () => { const issueNodeId = "I_kwDO_testissue"; const issueTypeNodeId = "IT_kwDO_bug"; @@ -403,7 +402,6 @@ describe("set_issue_type (Handler Factory Architecture)", () => { confidence: "HIGH", suggest: true, }, - headers: { "GraphQL-Features": "update_issue_suggestions" }, }) ); }); From 326a94d5d6b284ba725b98769081dae8c3aa231c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 17:58:39 +0000 Subject: [PATCH 09/10] style: fix GraphQL mutation indentation in set_issue_field Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/set_issue_field.cjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/actions/setup/js/set_issue_field.cjs b/actions/setup/js/set_issue_field.cjs index 6cb317ea94e..1595ca83699 100644 --- a/actions/setup/js/set_issue_field.cjs +++ b/actions/setup/js/set_issue_field.cjs @@ -141,11 +141,11 @@ function buildFieldUpdatePayload(field, rawValue) { async function setIssueFieldValue(githubClient, issueNodeId, fieldUpdate) { await githubClient.graphql( `mutation($issueId: ID!, $issueFields: [IssueFieldCreateOrUpdateInput!]!) { - setIssueFieldValue(input: { issueId: $issueId, issueFields: $issueFields }) { - issue { - id + setIssueFieldValue(input: { issueId: $issueId, issueFields: $issueFields }) { + issue { + id + } } - } }`, { issueId: issueNodeId, From bf829dca4006c453fd27448628126356b66e36c1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 17:59:36 +0000 Subject: [PATCH 10/10] docs: clarify required-array normalization in schema helper Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/generate_safe_outputs_tools.cjs | 1 + 1 file changed, 1 insertion(+) diff --git a/actions/setup/js/generate_safe_outputs_tools.cjs b/actions/setup/js/generate_safe_outputs_tools.cjs index a2eb0c2dcbb..fd6d61f6ad2 100644 --- a/actions/setup/js/generate_safe_outputs_tools.cjs +++ b/actions/setup/js/generate_safe_outputs_tools.cjs @@ -74,6 +74,7 @@ function isIssueIntentDisabledForTool(toolName, toolConfig) { /** * Remove issue-intent properties from a tool schema. + * If this removes all required fields, the required array is omitted. * @param {{inputSchema?: {properties?: Record, required?: string[]}}} tool */ function stripIssueIntentSchemaFields(tool) {