From d08ca84e9bc074c5f8a2eb5ecb875b56253b2363 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:48:05 +0000 Subject: [PATCH 01/10] Add issue intents runtime support Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/issue_intents.cjs | 112 +++++++++++++++ .../js/safe_output_type_validator.test.cjs | 48 ++++++- actions/setup/js/safe_outputs_tools.json | 120 +++++++++++----- actions/setup/js/set_issue_field.cjs | 7 +- actions/setup/js/set_issue_field.test.cjs | 40 ++++++ actions/setup/js/set_issue_type.cjs | 27 +++- actions/setup/js/set_issue_type.test.cjs | 37 +++++ actions/setup/js/types/safe-outputs.d.ts | 20 ++- actions/setup/js/update_issue.cjs | 67 +++++++-- actions/setup/js/update_issue.test.cjs | 103 ++++++++++++++ pkg/workflow/js/safe_outputs_tools.json | 134 +++++++++++++----- .../safe_output_validation_config_test.go | 59 ++++++-- .../safe_outputs_validation_config.go | 12 +- 13 files changed, 693 insertions(+), 93 deletions(-) create mode 100644 actions/setup/js/issue_intents.cjs diff --git a/actions/setup/js/issue_intents.cjs b/actions/setup/js/issue_intents.cjs new file mode 100644 index 00000000000..2092c2b537f --- /dev/null +++ b/actions/setup/js/issue_intents.cjs @@ -0,0 +1,112 @@ +// @ts-check +/// + +const { sanitizeLabelContent } = require("./sanitize_label_content.cjs"); +const { hasRuntimeFeature, parseRuntimeFeatures } = require("./runtime_features.cjs"); + +const ISSUE_INTENTS_FEATURE = "issue_intents"; +const ISSUE_INTENT_CONFIDENCE_VALUES = new Set(["LOW", "MEDIUM", "HIGH"]); + +function hasIssueIntentsRuntimeFeature() { + if (typeof global.hasRuntimeFeature === "function") { + return global.hasRuntimeFeature(ISSUE_INTENTS_FEATURE); + } + return hasRuntimeFeature(parseRuntimeFeatures(process.env.GH_AW_RUNTIME_FEATURES), ISSUE_INTENTS_FEATURE); +} + +function normalizeIssueIntentMetadata(source) { + if (!source || typeof source !== "object") { + return {}; + } + + /** @type {{ rationale?: string, confidence?: "LOW"|"MEDIUM"|"HIGH", suggest?: boolean }} */ + const metadata = {}; + + if (typeof source.rationale === "string") { + const rationale = source.rationale.trim(); + if (rationale) { + metadata.rationale = rationale; + } + } + + if (source.confidence !== undefined && source.confidence !== null && source.confidence !== "") { + const confidence = String(source.confidence).trim().toUpperCase(); + if (!ISSUE_INTENT_CONFIDENCE_VALUES.has(confidence)) { + throw new Error(`Invalid confidence ${JSON.stringify(source.confidence)}. Expected one of: LOW, MEDIUM, HIGH.`); + } + metadata.confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidence; + } + + if (source.suggest !== undefined) { + if (typeof source.suggest !== "boolean") { + throw new Error(`Invalid suggest ${JSON.stringify(source.suggest)}. Expected a boolean value.`); + } + if (source.suggest) { + metadata.suggest = true; + } + } + + return metadata; +} + +function normalizeIssueIntentLabelSpecs(labels) { + if (!Array.isArray(labels)) { + return []; + } + + return labels.map((label, index) => { + if (typeof label === "string") { + const name = sanitizeLabelContent(label); + if (!name) { + throw new Error(`Invalid labels[${index}] entry. Label names must be non-empty strings.`); + } + if (name.startsWith("-")) { + throw new Error(`Label removal is not permitted. Found line starting with '-': ${name}`); + } + return { name }; + } + + if (!label || typeof label !== "object" || typeof label.name !== "string") { + throw new Error(`Invalid labels[${index}] entry. Expected a string label name or an object with a string "name" field.`); + } + + const name = sanitizeLabelContent(label.name); + if (!name) { + throw new Error(`Invalid labels[${index}] entry. Label names must be non-empty strings.`); + } + if (name.startsWith("-")) { + throw new Error(`Label removal is not permitted. Found line starting with '-': ${name}`); + } + + return { + name, + ...normalizeIssueIntentMetadata(label), + }; + }); +} + +function getIssueIntentLabelNames(labelSpecs) { + return labelSpecs.map(label => label.name); +} + +function buildIssueIntentLabelUpdates(labelSpecs, labelIdByName) { + return labelSpecs.map(spec => { + const labelId = labelIdByName.get(spec.name.toLowerCase()); + if (!labelId) { + throw new Error(`Label ${JSON.stringify(spec.name)} not found. Ensure the label exists in the target repository.`); + } + + return { + labelId, + ...normalizeIssueIntentMetadata(spec), + }; + }); +} + +module.exports = { + buildIssueIntentLabelUpdates, + getIssueIntentLabelNames, + hasIssueIntentsRuntimeFeature, + normalizeIssueIntentLabelSpecs, + normalizeIssueIntentMetadata, +}; diff --git a/actions/setup/js/safe_output_type_validator.test.cjs b/actions/setup/js/safe_output_type_validator.test.cjs index 807866e966a..c2bbfe46ecd 100644 --- a/actions/setup/js/safe_output_type_validator.test.cjs +++ b/actions/setup/js/safe_output_type_validator.test.cjs @@ -38,11 +38,14 @@ const SAMPLE_VALIDATION_CONFIG = { }, update_issue: { defaultMax: 1, - customValidation: "requiresOneOf:status,title,body", + customValidation: "requiresOneOf:status,title,body,labels,assignees,milestone", fields: { status: { type: "string", enum: ["open", "closed"] }, title: { type: "string", sanitize: true, maxLength: 128 }, body: { type: "string", sanitize: true, maxLength: 65000 }, + labels: { type: "array" }, + assignees: { type: "array", itemType: "string", itemSanitize: true, itemMaxLength: 39 }, + milestone: { optionalPositiveInteger: true }, issue_number: { issueOrPRNumber: true }, }, }, @@ -94,6 +97,29 @@ const SAMPLE_VALIDATION_CONFIG = { repo: { type: "string", maxLength: 256 }, }, }, + set_issue_type: { + defaultMax: 5, + fields: { + issue_number: { issueOrPRNumber: true }, + issue_type: { required: true, type: "string", sanitize: true, maxLength: 128 }, + rationale: { type: "string", sanitize: true, maxLength: 1024 }, + confidence: { type: "string", enum: ["LOW", "MEDIUM", "HIGH"] }, + suggest: { type: "boolean" }, + }, + }, + set_issue_field: { + defaultMax: 5, + customValidation: "requiresOneOf:field_name,field_node_id", + fields: { + issue_number: { issueOrPRNumber: true }, + field_name: { type: "string", sanitize: true, maxLength: 128 }, + field_node_id: { type: "string", maxLength: 256 }, + value: { required: true, type: "string", sanitize: true, maxLength: 256 }, + rationale: { type: "string", sanitize: true, maxLength: 1024 }, + confidence: { type: "string", enum: ["LOW", "MEDIUM", "HIGH"] }, + suggest: { type: "boolean" }, + }, + }, link_sub_issue: { defaultMax: 5, customValidation: "parentAndSubDifferent", @@ -691,6 +717,14 @@ describe("safe_output_type_validator", () => { expect(result.isValid).toBe(true); }); + it("should pass when update_issue only includes labels", async () => { + const { validateItem } = await import("./safe_output_type_validator.cjs"); + + const result = validateItem({ type: "update_issue", labels: [{ name: "bug", confidence: "HIGH" }] }, "update_issue", 1); + + expect(result.isValid).toBe(true); + }); + it("should fail when none of the required fields are present", async () => { const { validateItem } = await import("./safe_output_type_validator.cjs"); @@ -822,6 +856,18 @@ describe("safe_output_type_validator", () => { expect(result.isValid).toBe(false); expect(result.error).toContain("must be 'open' or 'closed'"); }); + + it("should validate issue intent confidence enums", async () => { + const { validateItem } = await import("./safe_output_type_validator.cjs"); + + const typeResult = validateItem({ type: "set_issue_type", issue_type: "Bug", confidence: "high", suggest: true }, "set_issue_type", 1); + expect(typeResult.isValid).toBe(true); + expect(typeResult.normalizedItem.confidence).toBe("HIGH"); + + const fieldResult = validateItem({ type: "set_issue_field", field_name: "Priority", value: "P1", confidence: "medium", rationale: "Customer escalation" }, "set_issue_field", 1); + expect(fieldResult.isValid).toBe(true); + expect(fieldResult.normalizedItem.confidence).toBe("MEDIUM"); + }); }); describe("pattern validation", () => { diff --git a/actions/setup/js/safe_outputs_tools.json b/actions/setup/js/safe_outputs_tools.json index f24adc5010e..e26efb71594 100644 --- a/actions/setup/js/safe_outputs_tools.json +++ b/actions/setup/js/safe_outputs_tools.json @@ -1,19 +1,19 @@ [ { "name": "create_issue", - "description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema — required fields (title, body) are listed in this schema; if you are not ready to open the real issue, call `noop` instead. Creates a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. Compatibility: labels may be passed as either an array of strings or a comma-separated string; string input is split, trimmed, and normalized to an array.", + "description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema \u2014 required fields (title, body) are listed in this schema; if you are not ready to open the real issue, call `noop` instead. Creates a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. Compatibility: labels may be passed as either an array of strings or a comma-separated string; string input is split, trimmed, and normalized to an array.", "inputSchema": { "type": "object", "required": ["title", "body"], "properties": { "title": { "type": "string", - "description": "Concise issue title summarizing the bug, feature, or task. Must be the final intended title — not a placeholder or test value. The title appears as the main heading, so keep it brief and descriptive." + "description": "Concise issue title summarizing the bug, feature, or task. Must be the final intended title \u2014 not a placeholder or test value. The title appears as the main heading, so keep it brief and descriptive." }, "body": { "type": "string", "minLength": 20, - "description": "Detailed issue description in Markdown. Must be the final intended body — not a placeholder or test value. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate." + "description": "Detailed issue description in Markdown. Must be the final intended body \u2014 not a placeholder or test value. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate." }, "labels": { "type": ["array", "string"], @@ -43,12 +43,12 @@ }, "parent": { "type": ["number", "string"], - "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form (e.g., '#aw_Test123'); the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'." + "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form (e.g., '#aw_Test123'); the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'." }, "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Unique temporary identifier for this issue. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted and normalised to '#aw_abc1'. Use this same '#aw_ID' form in body text to cross-reference the issue; these references are replaced with the real issue number after creation.", + "description": "Unique temporary identifier for this issue. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted and normalised to '#aw_abc1'. Use this same '#aw_ID' form in body text to cross-reference the issue; these references are replaced with the real issue number after creation.", "x-synonyms": ["temporaryId"] }, "secrecy": { @@ -259,7 +259,7 @@ }, { "name": "add_comment", - "description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema — the required `body` field is listed in this schema; if you are not ready to post a real comment, call `noop` instead. Adds a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool does not require discussions:write permission. Set 'discussions: true' in the workflow's safe-outputs.add-comment configuration to enable discussion comments and request this permission.", + "description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema \u2014 the required `body` field is listed in this schema; if you are not ready to post a real comment, call `noop` instead. Adds a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool does not require discussions:write permission. Set 'discussions: true' in the workflow's safe-outputs.add-comment configuration to enable discussion comments and request this permission.", "inputSchema": { "type": "object", "required": ["body"], @@ -267,11 +267,11 @@ "body": { "type": "string", "maxLength": 65536, - "description": "The comment text in Markdown format. Must be the final intended comment — not a placeholder or test value. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated." + "description": "The comment text in Markdown format. Must be the final intended comment \u2014 not a placeholder or test value. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated." }, "item_number": { "type": ["number", "string"], - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error. Required when safe-outputs.add-comment.target is '*' (any item): calls without item_number (or pr_number/pr alias) are rejected. NOTE: this field is named item_number, NOT issue_number.", + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers \u2014 it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error. Required when safe-outputs.add-comment.target is '*' (any item): calls without item_number (or pr_number/pr alias) are rejected. NOTE: this field is named item_number, NOT issue_number.", "x-synonyms": ["issue_number", "itemNumber"] }, "pr_number": { @@ -286,12 +286,12 @@ "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Unique temporary identifier for this comment. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted and normalised to '#aw_abc1'. Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.", + "description": "Unique temporary identifier for this comment. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted and normalised to '#aw_abc1'. Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.", "x-synonyms": ["temporaryId"] }, "reply_to_id": { "type": "string", - "description": "Node ID of the discussion comment to reply to, enabling threaded discussion comments. When provided, the new comment is posted as a reply to the specified top-level discussion comment. If the given node ID belongs to a nested reply, the handler automatically resolves it to the top-level parent. Only applicable for discussion comments — ignored for issue and pull request comments.", + "description": "Node ID of the discussion comment to reply to, enabling threaded discussion comments. When provided, the new comment is posted as a reply to the specified top-level discussion comment. If the given node ID belongs to a nested reply, the handler automatically resolves it to the top-level parent. Only applicable for discussion comments \u2014 ignored for issue and pull request comments.", "x-synonyms": ["replyToId"] }, "comment_id": { @@ -363,7 +363,7 @@ "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Unique temporary identifier for this pull request. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_pr1', '#aw_fix_123'. The bare 'aw_pr1' form is also accepted and normalised to '#aw_pr1'. Use this same '#aw_ID' form in body text to cross-reference this PR; these references are replaced with the real pull request number after creation.", + "description": "Unique temporary identifier for this pull request. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_pr1', '#aw_fix_123'. The bare 'aw_pr1' form is also accepted and normalised to '#aw_pr1'. Use this same '#aw_ID' form in body text to cross-reference this PR; these references are replaced with the real pull request number after creation.", "x-synonyms": ["temporaryId"] }, "secrecy": { @@ -399,7 +399,7 @@ }, "pull_request_number": { "type": ["number", "string"], - "description": "Pull request number to add the review comment to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds the comment to the PR that triggered this workflow. Required when the workflow target is '*' (any PR) — omitting it will cause the comment to fail.", + "description": "Pull request number to add the review comment to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds the comment to the PR that triggered this workflow. Required when the workflow target is '*' (any PR) \u2014 omitting it will cause the comment to fail.", "x-synonyms": ["pullRequestNumber"] }, "start_line": { @@ -451,7 +451,7 @@ }, "pull_request_number": { "type": ["number", "string"], - "description": "Pull request number to submit the review on. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, submits the review on the PR that triggered this workflow. Required when the workflow target is '*' (any PR) — omitting it will cause the review to fail.", + "description": "Pull request number to submit the review on. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, submits the review on the PR that triggered this workflow. Required when the workflow target is '*' (any PR) \u2014 omitting it will cause the review to fail.", "x-synonyms": ["pullRequestNumber"] }, "repo": { @@ -589,12 +589,12 @@ "items": { "type": "string" }, - "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository. This field is required — omitting it will cause a validation error." + "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository. This field is required \u2014 omitting it will cause a validation error." }, "item_number": { "type": ["number", "string"], "pattern": "^(\\d+|#?aw_[A-Za-z0-9_]{3,12})$", - "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.", + "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required \u2014 omitting it will silently skip the label operation.", "x-synonyms": ["itemNumber"] }, "secrecy": { @@ -626,7 +626,7 @@ "item_number": { "type": ["number", "string"], "pattern": "^(\\d+|#?aw_[A-Za-z0-9_]{3,12})$", - "description": "Issue or PR number to remove labels from. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, removes labels from the item that triggered this workflow.", + "description": "Issue or PR number to remove labels from. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, removes labels from the item that triggered this workflow.", "x-synonyms": ["itemNumber"] }, "secrecy": { @@ -665,7 +665,7 @@ }, "pull_request_number": { "type": ["number", "string"], - "description": "Pull request number to add reviewers to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds reviewers to the PR that triggered this workflow. Only works for pull_request event triggers. For workflow_dispatch, schedule, or other triggers, pull_request_number is required — omitting it will silently skip the reviewer assignment.", + "description": "Pull request number to add reviewers to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds reviewers to the PR that triggered this workflow. Only works for pull_request event triggers. For workflow_dispatch, schedule, or other triggers, pull_request_number is required \u2014 omitting it will silently skip the reviewer assignment.", "x-synonyms": ["pullRequestNumber"] }, "secrecy": { @@ -689,7 +689,7 @@ "properties": { "issue_number": { "type": ["number", "string"], - "description": "Issue number to assign to the milestone. This is the numeric ID from the GitHub URL (e.g., 567 in github.com/owner/repo/issues/567). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'.", + "description": "Issue number to assign to the milestone. This is the numeric ID from the GitHub URL (e.g., 567 in github.com/owner/repo/issues/567). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'.", "x-synonyms": ["issueNumber"] }, "milestone_number": { @@ -699,7 +699,7 @@ }, "milestone_title": { "type": "string", - "description": "Milestone title to assign the issue to (e.g., \"v1.0\"). Used as an alternative to milestone_number — the handler looks up the milestone by title. Either milestone_number or milestone_title must be provided.", + "description": "Milestone title to assign the issue to (e.g., \"v1.0\"). Used as an alternative to milestone_number \u2014 the handler looks up the milestone by title. Either milestone_number or milestone_title must be provided.", "x-synonyms": ["milestoneTitle"] }, "secrecy": { @@ -722,7 +722,7 @@ "properties": { "issue_number": { "type": ["number", "string"], - "description": "Issue number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). Can also be a temporary_id from an issue created earlier in the same workflow run — use the '#aw_abc123' form (e.g., '#aw_Test123'); the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. The issue should contain clear, actionable requirements. Either issue_number or pull_number must be provided, but not both.", + "description": "Issue number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). Can also be a temporary_id from an issue created earlier in the same workflow run \u2014 use the '#aw_abc123' form (e.g., '#aw_Test123'); the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. The issue should contain clear, actionable requirements. Either issue_number or pull_number must be provided, but not both.", "x-synonyms": ["issueNumber"] }, "pull_number": { @@ -851,9 +851,37 @@ "labels": { "type": "array", "items": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Label name to apply." + }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": ["LOW", "MEDIUM", "HIGH"], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + } + }, + "additionalProperties": false + } + ] }, - "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Labels must exist in the repository." + "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Each entry can be either a label name string or an object with name plus optional rationale/confidence/suggest intent metadata. Labels must exist in the repository." }, "assignees": { "type": "array", @@ -988,7 +1016,7 @@ }, { "name": "push_to_pull_request_branch", - "description": "Push committed changes to a pull request's branch. APPEND-ONLY: this tool adds new commits on top of the existing PR branch — force-push is NOT supported and will be rejected. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. This is a write-once declaration for a real intended PR branch update, not a sandbox or probe: do not call it with probe branches, placeholder commit messages, or auth experiments. If you are not ready to push the real update, use noop or report_incomplete instead. Changes must be committed locally before calling this tool. The destination branch is always derived from the pull request's head ref — you do not specify it. IMPORTANT: do NOT use 'git merge' to update the branch against another branch — merge commits cannot be signed; the action will attempt to squash them into a single linear commit before pushing, but this rewrites history. Use 'git rebase' instead to avoid the rewrite.", + "description": "Push committed changes to a pull request's branch. APPEND-ONLY: this tool adds new commits on top of the existing PR branch \u2014 force-push is NOT supported and will be rejected. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. This is a write-once declaration for a real intended PR branch update, not a sandbox or probe: do not call it with probe branches, placeholder commit messages, or auth experiments. If you are not ready to push the real update, use noop or report_incomplete instead. Changes must be committed locally before calling this tool. The destination branch is always derived from the pull request's head ref \u2014 you do not specify it. IMPORTANT: do NOT use 'git merge' to update the branch against another branch \u2014 merge commits cannot be signed; the action will attempt to squash them into a single linear commit before pushing, but this rewrites history. Use 'git rebase' instead to avoid the rewrite.", "inputSchema": { "type": "object", "required": ["message"], @@ -1078,7 +1106,7 @@ "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Optional temporary identifier for this artifact upload. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_chart1', '#aw_img_out'. The bare 'aw_chart1' form is also accepted. Declare this ID here if you plan to embed the artifact URL in a subsequent message body using '#aw_ID' — for example '![chart](#aw_chart1)' in a create_discussion body. The safe-outputs processor replaces '#aw_ID' references with the actual artifact download URL after upload. When skip-archive is true the URL points directly to the file and is suitable for inline images.", + "description": "Optional temporary identifier for this artifact upload. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_chart1', '#aw_img_out'. The bare 'aw_chart1' form is also accepted. Declare this ID here if you plan to embed the artifact URL in a subsequent message body using '#aw_ID' \u2014 for example '![chart](#aw_chart1)' in a create_discussion body. The safe-outputs processor replaces '#aw_ID' references with the actual artifact download URL after upload. When skip-archive is true the URL points directly to the file and is suitable for inline images.", "x-synonyms": ["temporaryId"] }, "secrecy": { @@ -1111,7 +1139,7 @@ }, "body": { "type": "string", - "description": "Release body content in Markdown. Must be the final intended content — not a placeholder or test value. For 'replace', this becomes the entire release body. For 'append'/'prepend', this is added with a separator.", + "description": "Release body content in Markdown. Must be the final intended content \u2014 not a placeholder or test value. For 'replace', this becomes the entire release body. For 'append'/'prepend', this is added with a separator.", "minLength": 20 }, "secrecy": { @@ -1255,6 +1283,19 @@ "description": "Issue type name to set (e.g., \"Bug\", \"Feature\", \"Task\"). Use an empty string \"\" to clear the current issue type.", "x-synonyms": ["issueType"] }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": ["LOW", "MEDIUM", "HIGH"], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + }, "secrecy": { "type": "string", "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\")." @@ -1293,6 +1334,19 @@ "type": "string", "description": "Field value to set. For single-select fields, this must match an existing option name (e.g., \"P1\" or \"High\"). For date fields, use format: YYYY-MM-DD (for example, 2026-05-08)." }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": ["LOW", "MEDIUM", "HIGH"], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + }, "secrecy": { "type": "string", "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\")." @@ -1315,7 +1369,7 @@ "project": { "type": "string", "pattern": "^(https://github\\.com/(orgs|users)/[^/]+/projects/\\d+|#?aw_[A-Za-z0-9_]{3,12})$", - "description": "Full GitHub project URL (e.g., 'https://github.com/orgs/myorg/projects/42' or 'https://github.com/users/username/projects/5'), or a temporary project ID from a recent create_project call — use '#aw_abc1' (canonical) or bare 'aw_abc1' (also accepted). Project names or numbers alone are NOT accepted." + "description": "Full GitHub project URL (e.g., 'https://github.com/orgs/myorg/projects/42' or 'https://github.com/users/username/projects/5'), or a temporary project ID from a recent create_project call \u2014 use '#aw_abc1' (canonical) or bare 'aw_abc1' (also accepted). Project names or numbers alone are NOT accepted." }, "operation": { "type": "string", @@ -1330,7 +1384,7 @@ }, "content_number": { "type": ["number", "string"], - "description": "Issue or pull request number to add to the project. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123 for issue #123, or 456 in github.com/owner/repo/pull/456 for PR #456), or a temporary ID from a recent create_issue call — use '#aw_abc123' (canonical); bare 'aw_abc123' is also accepted. Required when content_type is 'issue' or 'pull_request'.", + "description": "Issue or pull request number to add to the project. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123 for issue #123, or 456 in github.com/owner/repo/pull/456 for PR #456), or a temporary ID from a recent create_issue call \u2014 use '#aw_abc123' (canonical); bare 'aw_abc123' is also accepted. Required when content_type is 'issue' or 'pull_request'.", "x-synonyms": ["contentNumber"] }, "target_repo": { @@ -1352,13 +1406,13 @@ "draft_issue_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Temporary ID of an existing draft issue to update — use '#aw_abc1' (canonical); bare 'aw_abc1' is also accepted. Use this to reference a draft created earlier with a matching temporary_id. When provided, draft_title is not required for updates.", + "description": "Temporary ID of an existing draft issue to update \u2014 use '#aw_abc1' (canonical); bare 'aw_abc1' is also accepted. Use this to reference a draft created earlier with a matching temporary_id. When provided, draft_title is not required for updates.", "x-synonyms": ["draftIssueId"] }, "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Unique temporary identifier for this draft issue. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted. Provide this when creating a new draft to enable future updates via draft_issue_id.", + "description": "Unique temporary identifier for this draft issue. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted. Provide this when creating a new draft to enable future updates via draft_issue_id.", "x-synonyms": ["temporaryId"] }, "fields": { @@ -1473,7 +1527,7 @@ }, { "name": "report_incomplete", - "description": "Signal that the task could not be completed due to an infrastructure or tool failure (e.g., MCP server crash, missing authentication, inaccessible repository). Use this when required tools or data are unavailable and the task cannot be meaningfully performed. This is distinct from noop (no action needed) — it indicates an active failure that prevented the task from running. The workflow framework will treat this as a failure signal even when the agent exits successfully.", + "description": "Signal that the task could not be completed due to an infrastructure or tool failure (e.g., MCP server crash, missing authentication, inaccessible repository). Use this when required tools or data are unavailable and the task cannot be meaningfully performed. This is distinct from noop (no action needed) \u2014 it indicates an active failure that prevented the task from running. The workflow framework will treat this as a failure signal even when the agent exits successfully.", "inputSchema": { "type": "object", "required": ["reason"], @@ -1514,13 +1568,13 @@ "item_url": { "type": "string", "pattern": "^(https://github\\\\.com/[^/]+/[^/]+/issues/(\\\\d+|#?aw_[A-Za-z0-9_]{3,12})|#?aw_[A-Za-z0-9_]{3,12})$", - "description": "Optional GitHub issue URL or temporary ID to add as the first item to the project. Accepts either a full URL (e.g., 'https://github.com/owner/repo/issues/123'), a URL with temporary ID (e.g., 'https://github.com/owner/repo/issues/#aw_abc1'), or a plain temporary ID — use '#aw_abc1' (canonical); bare 'aw_abc1' is also accepted.", + "description": "Optional GitHub issue URL or temporary ID to add as the first item to the project. Accepts either a full URL (e.g., 'https://github.com/owner/repo/issues/123'), a URL with temporary ID (e.g., 'https://github.com/owner/repo/issues/#aw_abc1'), or a plain temporary ID \u2014 use '#aw_abc1' (canonical); bare 'aw_abc1' is also accepted.", "x-synonyms": ["itemUrl"] }, "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Optional temporary identifier for this project. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted. If not provided, one will be auto-generated and returned in the response. Use this same '#aw_ID' form in add_project_item to reference this project.", + "description": "Optional temporary identifier for this project. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted. If not provided, one will be auto-generated and returned in the response. Use this same '#aw_ID' form in add_project_item to reference this project.", "x-synonyms": ["temporaryId"] }, "secrecy": { @@ -1660,7 +1714,7 @@ }, { "name": "create_check_run", - "description": "Create a GitHub Check Run to report agent analysis results on a commit or pull request. Check Runs appear in the PR checks UI and on commits with a pass/fail status. Use this to surface structured analysis results as a first-class GitHub check. The check run name is configured in the workflow frontmatter and is NOT accepted as a parameter — do not pass name. When `safe-outputs.create-check-run.target` is configured, pull request targeting follows standard PR target rules. With `target: \"*\"`, include `pull_request_number` (or `pr_number`/`pr`/`pull_number`) in each call.", + "description": "Create a GitHub Check Run to report agent analysis results on a commit or pull request. Check Runs appear in the PR checks UI and on commits with a pass/fail status. Use this to surface structured analysis results as a first-class GitHub check. The check run name is configured in the workflow frontmatter and is NOT accepted as a parameter \u2014 do not pass name. When `safe-outputs.create-check-run.target` is configured, pull request targeting follows standard PR target rules. With `target: \"*\"`, include `pull_request_number` (or `pr_number`/`pr`/`pull_number`) in each call.", "inputSchema": { "type": "object", "required": ["conclusion", "title", "summary"], diff --git a/actions/setup/js/set_issue_field.cjs b/actions/setup/js/set_issue_field.cjs index 37d59d85c6d..dd8d9796b05 100644 --- a/actions/setup/js/set_issue_field.cjs +++ b/actions/setup/js/set_issue_field.cjs @@ -12,6 +12,7 @@ const { isStagedMode, checkRequiredFilter } = require("./safe_output_helpers.cjs const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { parseAllowedIssueFields, validateAllowedIssueFieldName } = require("./allowed_issue_fields.cjs"); const { resolveSafeOutputIssueTarget } = require("./temporary_id.cjs"); +const { hasIssueIntentsRuntimeFeature, normalizeIssueIntentMetadata } = require("./issue_intents.cjs"); /** @type {string} Safe output type handled by this module */ const HANDLER_TYPE = "set_issue_field"; @@ -155,7 +156,7 @@ function buildFieldUpdatePayload(field, rawValue) { * Sets one issue field via GraphQL mutation. * @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}} fieldUpdate + * @param {{fieldId: string, singleSelectOptionId?: string, numberValue?: number, dateValue?: string, textValue?: string, rationale?: string, confidence?: "LOW"|"MEDIUM"|"HIGH", suggest?: boolean}} fieldUpdate * @returns {Promise} */ async function setIssueFieldValue(githubClient, issueNodeId, fieldUpdate) { @@ -348,6 +349,10 @@ async function main(config = {}) { ...fieldUpdateResult.update, }; + if (hasIssueIntentsRuntimeFeature()) { + Object.assign(fieldUpdate, normalizeIssueIntentMetadata(item)); + } + 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 bd149a119dc..3d4fe92e397 100644 --- a/actions/setup/js/set_issue_field.test.cjs +++ b/actions/setup/js/set_issue_field.test.cjs @@ -413,4 +413,44 @@ describe("set_issue_field (Handler Factory Architecture)", () => { expect(result.error).toContain("Selections can't be made directly on unions"); expect(mockCore.warning).not.toHaveBeenCalledWith(expect.stringContaining("No issue fields were discovered")); }); + + it("should include issue intent metadata when runtime feature is enabled", async () => { + process.env.GH_AW_RUNTIME_FEATURES = "issue_intents"; + + try { + const { main } = require("./set_issue_field.cjs"); + const featureHandler = await main({ max: 5 }); + + const result = await featureHandler( + { + type: "set_issue_field", + issue_number: 42, + field_name: "Customer Impact", + value: "High", + rationale: "Customer-reported with SLA breach risk", + confidence: "high", + suggest: true, + }, + {} + ); + + expect(result.success).toBe(true); + expect(mockGraphql).toHaveBeenCalledWith( + expect.stringContaining("setIssueFieldValue"), + expect.objectContaining({ + issueFields: [ + expect.objectContaining({ + fieldId: textFieldId, + textValue: "High", + rationale: "Customer-reported with SLA breach risk", + confidence: "HIGH", + suggest: true, + }), + ], + }) + ); + } finally { + delete process.env.GH_AW_RUNTIME_FEATURES; + } + }); }); diff --git a/actions/setup/js/set_issue_type.cjs b/actions/setup/js/set_issue_type.cjs index f3ec956c878..87638ec1101 100644 --- a/actions/setup/js/set_issue_type.cjs +++ b/actions/setup/js/set_issue_type.cjs @@ -11,6 +11,7 @@ const { logStagedPreviewInfo } = require("./staged_preview.cjs"); const { isStagedMode, checkRequiredFilter } = require("./safe_output_helpers.cjs"); const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { resolveSafeOutputIssueTarget } = require("./temporary_id.cjs"); +const { hasIssueIntentsRuntimeFeature, normalizeIssueIntentMetadata } = require("./issue_intents.cjs"); /** @type {string} Safe output type handled by this module */ const HANDLER_TYPE = "set_issue_type"; @@ -72,9 +73,30 @@ async function fetchIssueTypes(githubClient, owner, repo) { * @param {Object} githubClient - Authenticated GitHub client * @param {string} issueNodeId - GraphQL node ID of the issue * @param {string|null} typeId - GraphQL node ID of the issue type, or null to clear + * @param {{ rationale?: string, confidence?: "LOW"|"MEDIUM"|"HIGH", suggest?: boolean }} intentMetadata * @returns {Promise} */ -async function setIssueTypeById(githubClient, issueNodeId, typeId) { +async function setIssueTypeById(githubClient, issueNodeId, typeId, intentMetadata = {}) { + if (typeId !== null && hasIssueIntentsRuntimeFeature()) { + await githubClient.graphql( + `mutation($issueId: ID!, $issueType: IssueTypeUpdateInput) { + updateIssue(input: { id: $issueId, issueType: $issueType }) { + issue { + id + } + } + }`, + { + issueId: issueNodeId, + issueType: { + issueTypeId: typeId, + ...intentMetadata, + }, + } + ); + return; + } + await githubClient.graphql( `mutation($issueId: ID!, $typeId: ID) { updateIssue(input: { id: $issueId, issueTypeId: $typeId }) { @@ -204,6 +226,7 @@ async function main(config = {}) { try { const { owner, repo } = repoParts; + const intentMetadata = normalizeIssueIntentMetadata(item); // Get the issue's node ID for GraphQL const issueNodeId = await getIssueNodeId(githubClient, owner, repo, issueNumber); @@ -231,7 +254,7 @@ async function main(config = {}) { core.info(`Resolved issue type ${JSON.stringify(issueTypeName)} to node ID: ${typeId}`); } - await setIssueTypeById(githubClient, issueNodeId, typeId); + await setIssueTypeById(githubClient, issueNodeId, typeId, intentMetadata); const successMsg = isClear ? `Successfully cleared issue type on issue #${issueNumber}` : `Successfully set issue type to ${JSON.stringify(issueTypeName)} on issue #${issueNumber}`; core.info(successMsg); diff --git a/actions/setup/js/set_issue_type.test.cjs b/actions/setup/js/set_issue_type.test.cjs index ae07f9cfa20..1217ea5b8d5 100644 --- a/actions/setup/js/set_issue_type.test.cjs +++ b/actions/setup/js/set_issue_type.test.cjs @@ -308,4 +308,41 @@ describe("set_issue_type (Handler Factory Architecture)", () => { // Should still resolve to the Bug type expect(mockGraphql).toHaveBeenCalledWith(expect.stringContaining("updateIssue"), expect.objectContaining({ typeId: bugTypeId })); }); + + it("should use issueType intent metadata mutation when runtime feature is enabled", async () => { + process.env.GH_AW_RUNTIME_FEATURES = "issue_intents"; + + try { + const { main } = require("./set_issue_type.cjs"); + const featureHandler = await main({ max: 5 }); + + const result = await featureHandler( + { + type: "set_issue_type", + issue_number: 42, + issue_type: "Bug", + rationale: "Author explicitly requests a bug fix", + confidence: "high", + suggest: true, + }, + {} + ); + + expect(result.success).toBe(true); + expect(mockGraphql).toHaveBeenCalledWith( + expect.stringContaining("issueType: $issueType"), + expect.objectContaining({ + issueId: issueNodeId, + issueType: { + issueTypeId: bugTypeId, + rationale: "Author explicitly requests a bug fix", + confidence: "HIGH", + suggest: true, + }, + }) + ); + } finally { + delete process.env.GH_AW_RUNTIME_FEATURES; + } + }); }); diff --git a/actions/setup/js/types/safe-outputs.d.ts b/actions/setup/js/types/safe-outputs.d.ts index 0a53643f519..296670ba9d4 100644 --- a/actions/setup/js/types/safe-outputs.d.ts +++ b/actions/setup/js/types/safe-outputs.d.ts @@ -211,6 +211,20 @@ interface AddLabelsItem extends BaseSafeOutputItem { issue_number?: number; } +interface IssueIntentMetadata { + /** Optional rationale for the change */ + rationale?: string; + /** Optional confidence level for the change */ + confidence?: "LOW" | "MEDIUM" | "HIGH"; + /** When true, route through pending suggestion review */ + suggest?: boolean; +} + +interface IssueIntentLabel extends IssueIntentMetadata { + /** Label name to apply */ + name: string; +} + /** * JSONL item for removing labels from an issue or PR */ @@ -248,6 +262,8 @@ interface UpdateIssueItem extends BaseSafeOutputItem { body?: string; /** Optional issue number for target "*" */ issue_number?: number | string; + /** Optional labels to replace on the issue */ + labels?: Array; } /** @@ -318,7 +334,7 @@ interface AssignMilestoneItem extends BaseSafeOutputItem { /** * JSONL item for setting the type of a GitHub issue */ -interface SetIssueTypeItem extends BaseSafeOutputItem { +interface SetIssueTypeItem extends BaseSafeOutputItem, IssueIntentMetadata { type: "set_issue_type"; /** Issue type name to set (e.g., "Bug", "Feature"). Use empty string "" to clear the type. */ issue_type: string; @@ -329,7 +345,7 @@ interface SetIssueTypeItem extends BaseSafeOutputItem { /** * JSONL item for setting a custom issue field value */ -interface SetIssueFieldItem extends BaseSafeOutputItem { +interface SetIssueFieldItem extends BaseSafeOutputItem, IssueIntentMetadata { type: "set_issue_field"; /** Issue field name to set (e.g., "Priority", "Severity"). */ field_name?: string; diff --git a/actions/setup/js/update_issue.cjs b/actions/setup/js/update_issue.cjs index a101b367b2c..092fa8fc581 100644 --- a/actions/setup/js/update_issue.cjs +++ b/actions/setup/js/update_issue.cjs @@ -19,6 +19,8 @@ const { buildWorkflowRunUrl } = require("./workflow_metadata_helpers.cjs"); const { generateHistoryUrl } = require("./generate_history_link.cjs"); const { fetchIssueState, mergeIssueState } = require("./safe_output_execution_metadata.cjs"); const { MAX_LABELS, MAX_ASSIGNEES } = require("./constants.cjs"); +const { fetchAllRepoLabels } = require("./github_api_helpers.cjs"); +const { buildIssueIntentLabelUpdates, getIssueIntentLabelNames, hasIssueIntentsRuntimeFeature, normalizeIssueIntentLabelSpecs } = require("./issue_intents.cjs"); /** * Execute the issue update API call @@ -35,17 +37,30 @@ async function executeIssueUpdate(github, context, issueNumber, updateData) { let rawBody = updateData._rawBody; const includeFooter = updateData._includeFooter !== false; // Default to true const titlePrefix = updateData._titlePrefix || ""; + const labelsWereProvided = updateData.labels !== undefined; + const labelSpecs = labelsWereProvided ? normalizeIssueIntentLabelSpecs(updateData.labels) : undefined; + const useIssueIntentLabels = Boolean(labelSpecs) && hasIssueIntentsRuntimeFeature(); // Remove internal fields const { _operation, _rawBody, _includeFooter, _titlePrefix, _workflowRepo, ...apiData } = updateData; + if (labelSpecs) { + apiData.labels = getIssueIntentLabelNames(labelSpecs); + } + if (useIssueIntentLabels) { + delete apiData.labels; + } + + /** @type {any | null} */ + let currentIssue = null; // Fetch current issue if needed (title prefix validation or body update) - if (titlePrefix || rawBody !== undefined) { - const { data: currentIssue } = await github.rest.issues.get({ + if (titlePrefix || rawBody !== undefined || useIssueIntentLabels) { + const response = await github.rest.issues.get({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, }); + currentIssue = response.data; // Validate title prefix if specified if (titlePrefix) { @@ -102,12 +117,48 @@ async function executeIssueUpdate(github, context, issueNumber, updateData) { } } - const { data: issue } = await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issueNumber, - ...apiData, - }); + /** @type {any} */ + let issue = currentIssue; + if (Object.keys(apiData).length > 0) { + const response = await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + ...apiData, + }); + issue = response.data; + } + + if (useIssueIntentLabels && labelSpecs) { + const issueNodeId = issue?.node_id || currentIssue?.node_id; + if (!issueNodeId) { + throw new Error(`Failed to resolve GraphQL node ID for issue #${issueNumber}`); + } + + const repoLabels = await fetchAllRepoLabels(github, context.repo.owner, context.repo.repo); + const labelIdByName = new Map(repoLabels.map(label => [label.name.toLowerCase(), label.id])); + const labels = buildIssueIntentLabelUpdates(labelSpecs, labelIdByName); + const result = await github.graphql( + `mutation($issueId: ID!, $labels: [LabelUpdateInput!]!) { + updateIssue(input: { id: $issueId, labels: $labels }) { + issue { + id + labels(first: 100) { + nodes { + name + } + } + } + } + }`, + { issueId: issueNodeId, labels } + ); + + issue = { + ...(issue || currentIssue || {}), + labels: result?.updateIssue?.issue?.labels?.nodes || [], + }; + } return issue; } diff --git a/actions/setup/js/update_issue.test.cjs b/actions/setup/js/update_issue.test.cjs index a5562b17f37..52958e2de18 100644 --- a/actions/setup/js/update_issue.test.cjs +++ b/actions/setup/js/update_issue.test.cjs @@ -22,6 +22,7 @@ const mockGithub = { update: vi.fn(), }, }, + graphql: vi.fn(), }; const mockContext = { @@ -72,6 +73,14 @@ describe("update_issue.cjs - footer support", () => { html_url: "https://github.com/testowner/testrepo/issues/100", }, }); + mockGithub.graphql.mockResolvedValue({ + updateIssue: { + issue: { + id: "I_kwDO_testissue", + labels: { nodes: [] }, + }, + }, + }); }); describe("Footer addition", () => { @@ -919,4 +928,98 @@ describe("update_issue.cjs - cross-repo and operation integration", () => { expect(capturedBody).toContain(""); expect(capturedBody).toContain(""); }); + + it("should update labels via issue intents GraphQL when the runtime feature is enabled", async () => { + process.env.GH_AW_RUNTIME_FEATURES = "issue_intents"; + let capturedRestBody; + + mockGithub.rest.issues.get.mockResolvedValue({ + data: { + number: 100, + node_id: "I_kwDO_testissue", + title: "Test", + body: "Existing body", + html_url: "https://github.com/testowner/testrepo/issues/100", + }, + }); + mockGithub.rest.issues.update.mockImplementation(async ({ body, labels }) => { + capturedRestBody = { body, labels }; + return { + data: { + number: 100, + node_id: "I_kwDO_testissue", + title: "Test", + body, + html_url: "https://github.com/testowner/testrepo/issues/100", + }, + }; + }); + mockGithub.graphql.mockImplementation(async (query, variables) => { + if (query.includes("repository(owner")) { + return { + repository: { + labels: { + nodes: [ + { id: "LA_bug", name: "bug" }, + { id: "LA_perf", name: "perf" }, + ], + pageInfo: { hasNextPage: false, endCursor: null }, + }, + }, + }; + } + if (query.includes("updateIssue(input: { id: $issueId, labels: $labels })")) { + return { + updateIssue: { + issue: { + id: variables.issueId, + labels: { + nodes: [{ name: "bug" }, { name: "perf" }], + }, + }, + }, + }; + } + return {}; + }); + + try { + const { main } = await import("./update_issue.cjs"); + const handler = await main({ target: "*" }); + const result = await handler( + { + issue_number: 100, + labels: [ + { name: "bug", rationale: "Stack trace matches a known crash path", confidence: "high" }, + { name: "perf", rationale: "Slow query mentioned in repro steps", confidence: "medium", suggest: true }, + ], + }, + {} + ); + + expect(result.success).toBe(true); + expect(capturedRestBody).toBeUndefined(); + expect(mockGithub.graphql).toHaveBeenCalledWith( + expect.stringContaining("labels: $labels"), + expect.objectContaining({ + issueId: "I_kwDO_testissue", + labels: [ + { + labelId: "LA_bug", + rationale: "Stack trace matches a known crash path", + confidence: "HIGH", + }, + { + labelId: "LA_perf", + rationale: "Slow query mentioned in repro steps", + confidence: "MEDIUM", + suggest: true, + }, + ], + }) + ); + } finally { + delete process.env.GH_AW_RUNTIME_FEATURES; + } + }); }); diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json index 53b7fc01d8f..2271a1e2270 100644 --- a/pkg/workflow/js/safe_outputs_tools.json +++ b/pkg/workflow/js/safe_outputs_tools.json @@ -1,7 +1,7 @@ [ { "name": "create_issue", - "description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema — required fields (title, body) are listed in this schema; if you are not ready to open the real issue, call `noop` instead. Creates a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. Compatibility: labels may be passed as either an array of strings or a comma-separated string; string input is split, trimmed, and normalized to an array.", + "description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema \u2014 required fields (title, body) are listed in this schema; if you are not ready to open the real issue, call `noop` instead. Creates a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. Compatibility: labels may be passed as either an array of strings or a comma-separated string; string input is split, trimmed, and normalized to an array.", "inputSchema": { "type": "object", "required": [ @@ -11,12 +11,12 @@ "properties": { "title": { "type": "string", - "description": "Concise issue title summarizing the bug, feature, or task. Must be the final intended title — not a placeholder or test value. The title appears as the main heading, so keep it brief and descriptive." + "description": "Concise issue title summarizing the bug, feature, or task. Must be the final intended title \u2014 not a placeholder or test value. The title appears as the main heading, so keep it brief and descriptive." }, "body": { "type": "string", "minLength": 20, - "description": "Detailed issue description in Markdown. Must be the final intended body — not a placeholder or test value. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate." + "description": "Detailed issue description in Markdown. Must be the final intended body \u2014 not a placeholder or test value. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate." }, "labels": { "type": [ @@ -58,12 +58,12 @@ "number", "string" ], - "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form (e.g., '#aw_Test123'); the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'." + "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form (e.g., '#aw_Test123'); the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'." }, "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Unique temporary identifier for this issue. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted and normalised to '#aw_abc1'. Use this same '#aw_ID' form in body text to cross-reference the issue; these references are replaced with the real issue number after creation.", + "description": "Unique temporary identifier for this issue. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted and normalised to '#aw_abc1'. Use this same '#aw_ID' form in body text to cross-reference the issue; these references are replaced with the real issue number after creation.", "x-synonyms": [ "temporaryId" ] @@ -319,7 +319,7 @@ }, { "name": "add_comment", - "description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema — the required `body` field is listed in this schema; if you are not ready to post a real comment, call `noop` instead. Adds a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool does not require discussions:write permission. Set 'discussions: true' in the workflow's safe-outputs.add-comment configuration to enable discussion comments and request this permission.", + "description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema \u2014 the required `body` field is listed in this schema; if you are not ready to post a real comment, call `noop` instead. Adds a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool does not require discussions:write permission. Set 'discussions: true' in the workflow's safe-outputs.add-comment configuration to enable discussion comments and request this permission.", "inputSchema": { "type": "object", "required": [ @@ -329,14 +329,14 @@ "body": { "type": "string", "maxLength": 65536, - "description": "The comment text in Markdown format. Must be the final intended comment — not a placeholder or test value. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated." + "description": "The comment text in Markdown format. Must be the final intended comment \u2014 not a placeholder or test value. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated." }, "item_number": { "type": [ "number", "string" ], - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error. Required when safe-outputs.add-comment.target is '*' (any item): calls without item_number (or pr_number/pr alias) are rejected. NOTE: this field is named item_number, NOT issue_number.", + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers \u2014 it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error. Required when safe-outputs.add-comment.target is '*' (any item): calls without item_number (or pr_number/pr alias) are rejected. NOTE: this field is named item_number, NOT issue_number.", "x-synonyms": [ "issue_number", "itemNumber" @@ -362,14 +362,14 @@ "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Unique temporary identifier for this comment. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted and normalised to '#aw_abc1'. Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.", + "description": "Unique temporary identifier for this comment. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted and normalised to '#aw_abc1'. Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.", "x-synonyms": [ "temporaryId" ] }, "reply_to_id": { "type": "string", - "description": "Node ID of the discussion comment to reply to, enabling threaded discussion comments. When provided, the new comment is posted as a reply to the specified top-level discussion comment. If the given node ID belongs to a nested reply, the handler automatically resolves it to the top-level parent. Only applicable for discussion comments — ignored for issue and pull request comments.", + "description": "Node ID of the discussion comment to reply to, enabling threaded discussion comments. When provided, the new comment is posted as a reply to the specified top-level discussion comment. If the given node ID belongs to a nested reply, the handler automatically resolves it to the top-level parent. Only applicable for discussion comments \u2014 ignored for issue and pull request comments.", "x-synonyms": [ "replyToId" ] @@ -457,7 +457,7 @@ "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Unique temporary identifier for this pull request. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_pr1', '#aw_fix_123'. The bare 'aw_pr1' form is also accepted and normalised to '#aw_pr1'. Use this same '#aw_ID' form in body text to cross-reference this PR; these references are replaced with the real pull request number after creation.", + "description": "Unique temporary identifier for this pull request. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_pr1', '#aw_fix_123'. The bare 'aw_pr1' form is also accepted and normalised to '#aw_pr1'. Use this same '#aw_ID' form in body text to cross-reference this PR; these references are replaced with the real pull request number after creation.", "x-synonyms": [ "temporaryId" ] @@ -505,7 +505,7 @@ "number", "string" ], - "description": "Pull request number to add the review comment to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds the comment to the PR that triggered this workflow. Required when the workflow target is '*' (any PR) — omitting it will cause the comment to fail.", + "description": "Pull request number to add the review comment to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds the comment to the PR that triggered this workflow. Required when the workflow target is '*' (any PR) \u2014 omitting it will cause the comment to fail.", "x-synonyms": [ "pullRequestNumber" ] @@ -576,7 +576,7 @@ "number", "string" ], - "description": "Pull request number to submit the review on. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, submits the review on the PR that triggered this workflow. Required when the workflow target is '*' (any PR) — omitting it will cause the review to fail.", + "description": "Pull request number to submit the review on. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, submits the review on the PR that triggered this workflow. Required when the workflow target is '*' (any PR) \u2014 omitting it will cause the review to fail.", "x-synonyms": [ "pullRequestNumber" ] @@ -753,7 +753,7 @@ "items": { "type": "string" }, - "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository. This field is required — omitting it will cause a validation error." + "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository. This field is required \u2014 omitting it will cause a validation error." }, "item_number": { "type": [ @@ -761,7 +761,7 @@ "string" ], "pattern": "^(\\d+|#?aw_[A-Za-z0-9_]{3,12})$", - "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.", + "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required \u2014 omitting it will silently skip the label operation.", "x-synonyms": [ "itemNumber" ] @@ -800,7 +800,7 @@ "string" ], "pattern": "^(\\d+|#?aw_[A-Za-z0-9_]{3,12})$", - "description": "Issue or PR number to remove labels from. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, removes labels from the item that triggered this workflow.", + "description": "Issue or PR number to remove labels from. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. If omitted, removes labels from the item that triggered this workflow.", "x-synonyms": [ "itemNumber" ] @@ -848,7 +848,7 @@ "number", "string" ], - "description": "Pull request number to add reviewers to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds reviewers to the PR that triggered this workflow. Only works for pull_request event triggers. For workflow_dispatch, schedule, or other triggers, pull_request_number is required — omitting it will silently skip the reviewer assignment.", + "description": "Pull request number to add reviewers to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds reviewers to the PR that triggered this workflow. Only works for pull_request event triggers. For workflow_dispatch, schedule, or other triggers, pull_request_number is required \u2014 omitting it will silently skip the reviewer assignment.", "x-synonyms": [ "pullRequestNumber" ] @@ -879,7 +879,7 @@ "number", "string" ], - "description": "Issue number to assign to the milestone. This is the numeric ID from the GitHub URL (e.g., 567 in github.com/owner/repo/issues/567). Can also be a temporary_id from a previously created issue in the same workflow run — use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'.", + "description": "Issue number to assign to the milestone. This is the numeric ID from the GitHub URL (e.g., 567 in github.com/owner/repo/issues/567). Can also be a temporary_id from a previously created issue in the same workflow run \u2014 use the '#aw_abc123' form; the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'.", "x-synonyms": [ "issueNumber" ] @@ -896,7 +896,7 @@ }, "milestone_title": { "type": "string", - "description": "Milestone title to assign the issue to (e.g., \"v1.0\"). Used as an alternative to milestone_number — the handler looks up the milestone by title. Either milestone_number or milestone_title must be provided.", + "description": "Milestone title to assign the issue to (e.g., \"v1.0\"). Used as an alternative to milestone_number \u2014 the handler looks up the milestone by title. Either milestone_number or milestone_title must be provided.", "x-synonyms": [ "milestoneTitle" ] @@ -924,7 +924,7 @@ "number", "string" ], - "description": "Issue number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). Can also be a temporary_id from an issue created earlier in the same workflow run — use the '#aw_abc123' form (e.g., '#aw_Test123'); the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. The issue should contain clear, actionable requirements. Either issue_number or pull_number must be provided, but not both.", + "description": "Issue number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). Can also be a temporary_id from an issue created earlier in the same workflow run \u2014 use the '#aw_abc123' form (e.g., '#aw_Test123'); the bare 'aw_abc123' form is also accepted and normalised to '#aw_abc123'. The issue should contain clear, actionable requirements. Either issue_number or pull_number must be provided, but not both.", "x-synonyms": [ "issueNumber" ] @@ -1082,9 +1082,43 @@ "labels": { "type": "array", "items": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "Label name to apply." + }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + } + }, + "additionalProperties": false + } + ] }, - "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Labels must exist in the repository." + "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Each entry can be either a label name string or an object with name plus optional rationale/confidence/suggest intent metadata. Labels must exist in the repository." }, "assignees": { "type": "array", @@ -1265,7 +1299,7 @@ }, { "name": "push_to_pull_request_branch", - "description": "Push committed changes to a pull request's branch. APPEND-ONLY: this tool adds new commits on top of the existing PR branch — force-push is NOT supported and will be rejected. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. This is a write-once declaration for a real intended PR branch update, not a sandbox or probe: do not call it with probe branches, placeholder commit messages, or auth experiments. If you are not ready to push the real update, use noop or report_incomplete instead. Changes must be committed locally before calling this tool. The destination branch is always derived from the pull request's head ref — you do not specify it. IMPORTANT: do NOT use 'git merge' to update the branch against another branch — merge commits cannot be signed; the action will attempt to squash them into a single linear commit before pushing, but this rewrites history. Use 'git rebase' instead to avoid the rewrite.", + "description": "Push committed changes to a pull request's branch. APPEND-ONLY: this tool adds new commits on top of the existing PR branch \u2014 force-push is NOT supported and will be rejected. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. This is a write-once declaration for a real intended PR branch update, not a sandbox or probe: do not call it with probe branches, placeholder commit messages, or auth experiments. If you are not ready to push the real update, use noop or report_incomplete instead. Changes must be committed locally before calling this tool. The destination branch is always derived from the pull request's head ref \u2014 you do not specify it. IMPORTANT: do NOT use 'git merge' to update the branch against another branch \u2014 merge commits cannot be signed; the action will attempt to squash them into a single linear commit before pushing, but this rewrites history. Use 'git rebase' instead to avoid the rewrite.", "inputSchema": { "type": "object", "required": [ @@ -1368,7 +1402,7 @@ "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Optional temporary identifier for this artifact upload. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_chart1', '#aw_img_out'. The bare 'aw_chart1' form is also accepted. Declare this ID here if you plan to embed the artifact URL in a subsequent message body using '#aw_ID' — for example '![chart](#aw_chart1)' in a create_discussion body. The safe-outputs processor replaces '#aw_ID' references with the actual artifact download URL after upload. When skip-archive is true the URL points directly to the file and is suitable for inline images.", + "description": "Optional temporary identifier for this artifact upload. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_chart1', '#aw_img_out'. The bare 'aw_chart1' form is also accepted. Declare this ID here if you plan to embed the artifact URL in a subsequent message body using '#aw_ID' \u2014 for example '![chart](#aw_chart1)' in a create_discussion body. The safe-outputs processor replaces '#aw_ID' references with the actual artifact download URL after upload. When skip-archive is true the URL points directly to the file and is suitable for inline images.", "x-synonyms": [ "temporaryId" ] @@ -1411,7 +1445,7 @@ }, "body": { "type": "string", - "description": "Release body content in Markdown. Must be the final intended content — not a placeholder or test value. For 'replace', this becomes the entire release body. For 'append'/'prepend', this is added with a separator.", + "description": "Release body content in Markdown. Must be the final intended content \u2014 not a placeholder or test value. For 'replace', this becomes the entire release body. For 'append'/'prepend', this is added with a separator.", "minLength": 20 }, "secrecy": { @@ -1592,6 +1626,23 @@ "issueType" ] }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + }, "secrecy": { "type": "string", "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\")." @@ -1641,6 +1692,23 @@ "type": "string", "description": "Field value to set. For single-select fields, this must match an existing option name (e.g., \"P1\" or \"High\"). For date fields, use format: YYYY-MM-DD (for example, 2026-05-08)." }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + }, "secrecy": { "type": "string", "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\")." @@ -1665,7 +1733,7 @@ "project": { "type": "string", "pattern": "^(https://github\\.com/(orgs|users)/[^/]+/projects/\\d+|#?aw_[A-Za-z0-9_]{3,12})$", - "description": "Full GitHub project URL (e.g., 'https://github.com/orgs/myorg/projects/42' or 'https://github.com/users/username/projects/5'), or a temporary project ID from a recent create_project call — use '#aw_abc1' (canonical) or bare 'aw_abc1' (also accepted). Project names or numbers alone are NOT accepted." + "description": "Full GitHub project URL (e.g., 'https://github.com/orgs/myorg/projects/42' or 'https://github.com/users/username/projects/5'), or a temporary project ID from a recent create_project call \u2014 use '#aw_abc1' (canonical) or bare 'aw_abc1' (also accepted). Project names or numbers alone are NOT accepted." }, "operation": { "type": "string", @@ -1692,7 +1760,7 @@ "number", "string" ], - "description": "Issue or pull request number to add to the project. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123 for issue #123, or 456 in github.com/owner/repo/pull/456 for PR #456), or a temporary ID from a recent create_issue call — use '#aw_abc123' (canonical); bare 'aw_abc123' is also accepted. Required when content_type is 'issue' or 'pull_request'.", + "description": "Issue or pull request number to add to the project. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123 for issue #123, or 456 in github.com/owner/repo/pull/456 for PR #456), or a temporary ID from a recent create_issue call \u2014 use '#aw_abc123' (canonical); bare 'aw_abc123' is also accepted. Required when content_type is 'issue' or 'pull_request'.", "x-synonyms": [ "contentNumber" ] @@ -1722,7 +1790,7 @@ "draft_issue_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Temporary ID of an existing draft issue to update — use '#aw_abc1' (canonical); bare 'aw_abc1' is also accepted. Use this to reference a draft created earlier with a matching temporary_id. When provided, draft_title is not required for updates.", + "description": "Temporary ID of an existing draft issue to update \u2014 use '#aw_abc1' (canonical); bare 'aw_abc1' is also accepted. Use this to reference a draft created earlier with a matching temporary_id. When provided, draft_title is not required for updates.", "x-synonyms": [ "draftIssueId" ] @@ -1730,7 +1798,7 @@ "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Unique temporary identifier for this draft issue. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted. Provide this when creating a new draft to enable future updates via draft_issue_id.", + "description": "Unique temporary identifier for this draft issue. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted. Provide this when creating a new draft to enable future updates via draft_issue_id.", "x-synonyms": [ "temporaryId" ] @@ -1869,7 +1937,7 @@ }, { "name": "report_incomplete", - "description": "Signal that the task could not be completed due to an infrastructure or tool failure (e.g., MCP server crash, missing authentication, inaccessible repository). Use this when required tools or data are unavailable and the task cannot be meaningfully performed. This is distinct from noop (no action needed) — it indicates an active failure that prevented the task from running. The workflow framework will treat this as a failure signal even when the agent exits successfully.", + "description": "Signal that the task could not be completed due to an infrastructure or tool failure (e.g., MCP server crash, missing authentication, inaccessible repository). Use this when required tools or data are unavailable and the task cannot be meaningfully performed. This is distinct from noop (no action needed) \u2014 it indicates an active failure that prevented the task from running. The workflow framework will treat this as a failure signal even when the agent exits successfully.", "inputSchema": { "type": "object", "required": [ @@ -1917,7 +1985,7 @@ "item_url": { "type": "string", "pattern": "^(https://github\\\\.com/[^/]+/[^/]+/issues/(\\\\d+|#?aw_[A-Za-z0-9_]{3,12})|#?aw_[A-Za-z0-9_]{3,12})$", - "description": "Optional GitHub issue URL or temporary ID to add as the first item to the project. Accepts either a full URL (e.g., 'https://github.com/owner/repo/issues/123'), a URL with temporary ID (e.g., 'https://github.com/owner/repo/issues/#aw_abc1'), or a plain temporary ID — use '#aw_abc1' (canonical); bare 'aw_abc1' is also accepted.", + "description": "Optional GitHub issue URL or temporary ID to add as the first item to the project. Accepts either a full URL (e.g., 'https://github.com/owner/repo/issues/123'), a URL with temporary ID (e.g., 'https://github.com/owner/repo/issues/#aw_abc1'), or a plain temporary ID \u2014 use '#aw_abc1' (canonical); bare 'aw_abc1' is also accepted.", "x-synonyms": [ "itemUrl" ] @@ -1925,7 +1993,7 @@ "temporary_id": { "type": "string", "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$", - "description": "Optional temporary identifier for this project. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) — e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted. If not provided, one will be auto-generated and returned in the response. Use this same '#aw_ID' form in add_project_item to reference this project.", + "description": "Optional temporary identifier for this project. Canonical form: '#aw_' followed by 3 to 12 alphanumeric or underscore characters (A-Za-z0-9_) \u2014 e.g., '#aw_abc1', '#aw_pr_fix'. The bare 'aw_abc1' form is also accepted. If not provided, one will be auto-generated and returned in the response. Use this same '#aw_ID' form in add_project_item to reference this project.", "x-synonyms": [ "temporaryId" ] @@ -2102,7 +2170,7 @@ }, { "name": "create_check_run", - "description": "Create a GitHub Check Run to report agent analysis results on a commit or pull request. Check Runs appear in the PR checks UI and on commits with a pass/fail status. Use this to surface structured analysis results as a first-class GitHub check. The check run name is configured in the workflow frontmatter and is NOT accepted as a parameter — do not pass name. When `safe-outputs.create-check-run.target` is configured, pull request targeting follows standard PR target rules. With `target: \"*\"`, include `pull_request_number` (or `pr_number`/`pr`/`pull_number`) in each call.", + "description": "Create a GitHub Check Run to report agent analysis results on a commit or pull request. Check Runs appear in the PR checks UI and on commits with a pass/fail status. Use this to surface structured analysis results as a first-class GitHub check. The check run name is configured in the workflow frontmatter and is NOT accepted as a parameter \u2014 do not pass name. When `safe-outputs.create-check-run.target` is configured, pull request targeting follows standard PR target rules. With `target: \"*\"`, include `pull_request_number` (or `pr_number`/`pr`/`pull_number`) in each call.", "inputSchema": { "type": "object", "required": [ diff --git a/pkg/workflow/safe_output_validation_config_test.go b/pkg/workflow/safe_output_validation_config_test.go index 5dd9d65f310..68c53e05222 100644 --- a/pkg/workflow/safe_output_validation_config_test.go +++ b/pkg/workflow/safe_output_validation_config_test.go @@ -264,19 +264,58 @@ func TestUpdatePullRequestValidationConfig(t *testing.T) { } } +func TestUpdateIssueValidationConfig(t *testing.T) { + config, ok := ValidationConfig["update_issue"] + if !ok { + t.Fatal("update_issue not found in ValidationConfig") + } + + if config.CustomValidation != "requiresOneOf:status,title,body,labels,assignees,milestone" { + t.Errorf("update_issue customValidation = %q, want %q", config.CustomValidation, "requiresOneOf:status,title,body,labels,assignees,milestone") + } + + if _, ok := config.Fields["labels"]; !ok { + t.Error("update_issue Fields is missing the 'labels' field") + } +} + +func TestIssueIntentValidationFields(t *testing.T) { + for _, typeName := range []string{"set_issue_type", "set_issue_field"} { + config, ok := ValidationConfig[typeName] + if !ok { + t.Fatalf("%s not found in ValidationConfig", typeName) + } + + if _, ok := config.Fields["rationale"]; !ok { + t.Fatalf("%s Fields is missing 'rationale'", typeName) + } + if _, ok := config.Fields["confidence"]; !ok { + t.Fatalf("%s Fields is missing 'confidence'", typeName) + } + if _, ok := config.Fields["suggest"]; !ok { + t.Fatalf("%s Fields is missing 'suggest'", typeName) + } + + confidence := config.Fields["confidence"] + if len(confidence.Enum) != 3 || confidence.Enum[0] != "LOW" || confidence.Enum[1] != "MEDIUM" || confidence.Enum[2] != "HIGH" { + t.Fatalf("%s confidence enum = %v, want [LOW MEDIUM HIGH]", typeName, confidence.Enum) + } + } +} + func TestValidationConfigConsistency(t *testing.T) { // Verify that all types with customValidation have valid validation rules validCustomValidations := map[string]bool{ - "requiresOneOf:status,title,body": true, - "requiresOneOf:title,body": true, - "requiresOneOf:title,body,update_branch": true, - "requiresOneOf:title,body,labels": true, - "requiresOneOf:issue_number,pull_number": true, - "requiresOneOf:milestone_number,milestone_title": true, - "requiresOneOf:field_name,field_node_id": true, - "requiresOneOf:reviewers,team_reviewers": true, - "startLineLessOrEqualLine": true, - "parentAndSubDifferent": true, + "requiresOneOf:status,title,body,labels,assignees,milestone": true, + "requiresOneOf:title,body": true, + "requiresOneOf:title,body,update_branch": true, + "requiresOneOf:title,body,labels": true, + "requiresOneOf:issue_number,pull_number": true, + "requiresOneOf:milestone_number,milestone_title": true, + "requiresOneOf:field_name,field_node_id": true, + "requiresOneOf:reviewers,team_reviewers": true, + "startLineLessOrEqualLine": true, + "parentAndSubDifferent": true, } for typeName, config := range ValidationConfig { diff --git a/pkg/workflow/safe_outputs_validation_config.go b/pkg/workflow/safe_outputs_validation_config.go index c87a7099288..ebd21b05761 100644 --- a/pkg/workflow/safe_outputs_validation_config.go +++ b/pkg/workflow/safe_outputs_validation_config.go @@ -135,7 +135,10 @@ var ValidationConfig = map[string]TypeValidationConfig{ Fields: map[string]FieldValidation{ "issue_number": {IssueOrPRNumber: true}, "issue_type": {Required: true, Type: "string", Sanitize: true, MaxLength: 128}, // Empty string clears the type - "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" + "rationale": {Type: "string", Sanitize: true, MaxLength: 1024}, + "confidence": {Type: "string", Enum: []string{"LOW", "MEDIUM", "HIGH"}}, + "suggest": {Type: "boolean"}, + "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, }, "set_issue_field": { @@ -146,6 +149,9 @@ var ValidationConfig = map[string]TypeValidationConfig{ "field_name": {Type: "string", Sanitize: true, MaxLength: 128}, "field_node_id": {Type: "string", MaxLength: 256}, "value": {Required: true, Type: "string", Sanitize: true, MaxLength: 256}, + "rationale": {Type: "string", Sanitize: true, MaxLength: 1024}, + "confidence": {Type: "string", Enum: []string{"LOW", "MEDIUM", "HIGH"}}, + "suggest": {Type: "boolean"}, "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, }, @@ -171,13 +177,13 @@ var ValidationConfig = map[string]TypeValidationConfig{ }, "update_issue": { DefaultMax: 1, - CustomValidation: "requiresOneOf:status,title,body", + CustomValidation: "requiresOneOf:status,title,body,labels,assignees,milestone", Fields: map[string]FieldValidation{ "status": {Type: "string", Enum: []string{"open", "closed"}}, "title": {Type: "string", Sanitize: true, MaxLength: 128}, "body": {Type: "string", Sanitize: true, MaxLength: MaxBodyLength}, "operation": {Type: "string", Enum: []string{"replace", "append", "prepend", "replace-island"}}, - "labels": {Type: "array", ItemType: "string", ItemSanitize: true, ItemMaxLength: 128}, + "labels": {Type: "array"}, "assignees": {Type: "array", ItemType: "string", ItemSanitize: true, ItemMaxLength: MaxGitHubUsernameLength}, "milestone": {OptionalPositiveInteger: true}, "issue_number": {IssueOrPRNumber: true}, From 937cb815586e14688f891924cdc476b04d7616c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 19:15:50 +0000 Subject: [PATCH 02/10] Support issue-intent labels in add/remove safe outputs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/add_labels.cjs | 26 ++++- actions/setup/js/add_labels.test.cjs | 23 ++++ actions/setup/js/issue_intents.cjs | 9 +- actions/setup/js/remove_labels.cjs | 22 +++- actions/setup/js/remove_labels.test.cjs | 23 ++++ .../setup/js/safe_output_type_validator.cjs | 109 ++++++++++++++++++ .../js/safe_output_type_validator.test.cjs | 48 ++++++++ actions/setup/js/safe_outputs_tools.json | 64 +++++++++- actions/setup/js/types/safe-outputs.d.ts | 8 +- pkg/workflow/js/safe_outputs_tools.json | 76 +++++++++++- .../safe_output_validation_config_test.go | 17 +++ .../safe_outputs_validation_config.go | 4 +- 12 files changed, 407 insertions(+), 22 deletions(-) diff --git a/actions/setup/js/add_labels.cjs b/actions/setup/js/add_labels.cjs index 469ce2a56d7..998126b7784 100644 --- a/actions/setup/js/add_labels.cjs +++ b/actions/setup/js/add_labels.cjs @@ -13,7 +13,7 @@ * issue_number?: number|string, * pr_number?: number|string, * pull_number?: number|string, - * labels?: string[], + * labels?: Array, * repo?: string * }} AddLabelsMessage */ @@ -33,6 +33,7 @@ const { MAX_LABELS } = require("./constants.cjs"); const { createCountGatedHandler } = require("./handler_scaffold.cjs"); const { withRetry, RATE_LIMIT_RETRY_CONFIG } = require("./error_recovery.cjs"); const { resolveInvocationContext } = require("./invocation_context_helpers.cjs"); +const { getIssueIntentLabelNames, normalizeIssueIntentLabelSpecs } = require("./issue_intents.cjs"); /** * Main handler factory for add_labels @@ -91,6 +92,23 @@ const main = createCountGatedHandler({ const contextType = effectiveContext.eventPayload?.pull_request ? "pull request" : "issue"; const requestedLabels = message.labels ?? []; core.info(`Requested labels: ${JSON.stringify(requestedLabels)}`); + let requestedLabelNames; + try { + requestedLabelNames = requestedLabels.map((label, index) => { + if (typeof label === "string") { + return label; + } + const normalized = normalizeIssueIntentLabelSpecs([label]); + if (normalized.length !== 1) { + throw new Error(`Invalid labels[${index}] entry. Expected a single label specification.`); + } + return getIssueIntentLabelNames(normalized)[0]; + }); + } catch (error) { + const errorMessage = getErrorMessage(error); + core.warning(`Invalid add_labels payload: ${errorMessage}`); + return { success: false, error: errorMessage }; + } // Apply required-labels and required-title-prefix filters if (requiredLabels.length > 0 || requiredTitlePrefix) { @@ -113,7 +131,7 @@ const main = createCountGatedHandler({ } // If no labels provided, return a helpful message with allowed labels if configured - if (requestedLabels.length === 0) { + if (requestedLabelNames.length === 0) { const labelSource = allowedLabels.length > 0 ? `the allowed list: ${JSON.stringify(allowedLabels)}` : "the repository's available labels"; const error = `No labels provided. Please provide at least one label from ${labelSource}`; core.info(error); @@ -121,14 +139,14 @@ const main = createCountGatedHandler({ } // Enforce max limits on labels before validation - const limitResult = tryEnforceArrayLimit(requestedLabels, MAX_LABELS, "labels"); + const limitResult = tryEnforceArrayLimit(requestedLabelNames, MAX_LABELS, "labels"); if (!limitResult.success) { core.warning(`Label limit exceeded: ${limitResult.error}`); return { success: false, error: limitResult.error }; } // Use validation helper to sanitize and validate labels - const labelsResult = validateLabels(requestedLabels, allowedLabels, maxCount, blockedPatterns); + const labelsResult = validateLabels(requestedLabelNames, allowedLabels, maxCount, blockedPatterns); if (!labelsResult.valid) { // If no valid labels, log info and return gracefully diff --git a/actions/setup/js/add_labels.test.cjs b/actions/setup/js/add_labels.test.cjs index fbd0dceec57..7e94388c1a3 100644 --- a/actions/setup/js/add_labels.test.cjs +++ b/actions/setup/js/add_labels.test.cjs @@ -113,6 +113,29 @@ describe("add_labels", () => { expect(addLabelsCalls[0].labels).toEqual(["bug", "enhancement"]); }); + it("should accept structured label entries and add normalized label names", async () => { + const handler = await main({ max: 10 }); + 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(result.number).toBe(456); + expect(addLabelsCalls).toHaveLength(1); + expect(addLabelsCalls[0].labels).toEqual(["bug"]); + }); + it("should accept issue_number as an alias for item_number", async () => { const handler = await main({ max: 10 }); const addLabelsCalls = []; diff --git a/actions/setup/js/issue_intents.cjs b/actions/setup/js/issue_intents.cjs index 2092c2b537f..8782bdf505d 100644 --- a/actions/setup/js/issue_intents.cjs +++ b/actions/setup/js/issue_intents.cjs @@ -1,11 +1,13 @@ // @ts-check /// +const { sanitizeContent } = require("./sanitize_content.cjs"); const { sanitizeLabelContent } = require("./sanitize_label_content.cjs"); const { hasRuntimeFeature, parseRuntimeFeatures } = require("./runtime_features.cjs"); const ISSUE_INTENTS_FEATURE = "issue_intents"; const ISSUE_INTENT_CONFIDENCE_VALUES = new Set(["LOW", "MEDIUM", "HIGH"]); +const ISSUE_INTENT_RATIONALE_MAX_LENGTH = 1024; function hasIssueIntentsRuntimeFeature() { if (typeof global.hasRuntimeFeature === "function") { @@ -23,7 +25,7 @@ function normalizeIssueIntentMetadata(source) { const metadata = {}; if (typeof source.rationale === "string") { - const rationale = source.rationale.trim(); + const rationale = sanitizeContent(source.rationale, { maxLength: ISSUE_INTENT_RATIONALE_MAX_LENGTH }).trim(); if (rationale) { metadata.rationale = rationale; } @@ -50,9 +52,12 @@ function normalizeIssueIntentMetadata(source) { } function normalizeIssueIntentLabelSpecs(labels) { - if (!Array.isArray(labels)) { + if (labels === undefined) { return []; } + if (!Array.isArray(labels)) { + throw new Error(`Invalid labels ${JSON.stringify(labels)}. Expected an array of label names or label spec objects.`); + } return labels.map((label, index) => { if (typeof label === "string") { diff --git a/actions/setup/js/remove_labels.cjs b/actions/setup/js/remove_labels.cjs index 494f4cf16e1..3ddb1ee559b 100644 --- a/actions/setup/js/remove_labels.cjs +++ b/actions/setup/js/remove_labels.cjs @@ -16,6 +16,7 @@ const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { resolveSafeOutputIssueTarget } = require("./temporary_id.cjs"); const { createCountGatedHandler } = require("./handler_scaffold.cjs"); const { resolveInvocationContext } = require("./invocation_context_helpers.cjs"); +const { getIssueIntentLabelNames, normalizeIssueIntentLabelSpecs } = require("./issue_intents.cjs"); /** * Main handler factory for remove_labels @@ -82,6 +83,23 @@ const main = createCountGatedHandler({ const contextType = effectiveContext.eventPayload?.pull_request ? "pull request" : "issue"; const requestedLabels = message.labels ?? []; core.info(`Requested labels to remove: ${JSON.stringify(requestedLabels)}`); + let requestedLabelNames; + try { + requestedLabelNames = requestedLabels.map((label, index) => { + if (typeof label === "string") { + return label; + } + const normalized = normalizeIssueIntentLabelSpecs([label]); + if (normalized.length !== 1) { + throw new Error(`Invalid labels[${index}] entry. Expected a single label specification.`); + } + return getIssueIntentLabelNames(normalized)[0]; + }); + } catch (error) { + const errorMessage = getErrorMessage(error); + core.warning(`Invalid remove_labels payload: ${errorMessage}`); + return { success: false, error: errorMessage }; + } // Apply required-labels and required-title-prefix filters if (requiredLabels.length > 0 || requiredTitlePrefix) { @@ -104,7 +122,7 @@ const main = createCountGatedHandler({ } // If no labels provided, return a helpful message with allowed labels if configured - if (!requestedLabels || requestedLabels.length === 0) { + if (!requestedLabelNames || requestedLabelNames.length === 0) { let errorMessage = "No labels provided. Please provide at least one label from"; if (allowedLabels.length > 0) { errorMessage += ` the allowed list: ${JSON.stringify(allowedLabels)}`; @@ -119,7 +137,7 @@ const main = createCountGatedHandler({ } // Use validation helper to sanitize and validate labels - const labelsResult = validateLabels(requestedLabels, allowedLabels, maxCount, blockedPatterns); + const labelsResult = validateLabels(requestedLabelNames, allowedLabels, maxCount, blockedPatterns); if (!labelsResult.valid) { // If no valid labels, log info and return gracefully if (labelsResult.error?.includes("No valid labels")) { diff --git a/actions/setup/js/remove_labels.test.cjs b/actions/setup/js/remove_labels.test.cjs index e5e32bd0a17..a46965b905e 100644 --- a/actions/setup/js/remove_labels.test.cjs +++ b/actions/setup/js/remove_labels.test.cjs @@ -107,6 +107,29 @@ describe("remove_labels", () => { expect(removeLabelCalls[1].name).toBe("enhancement"); }); + it("should accept structured label entries and remove normalized label names", async () => { + const handler = await main({ max: 10 }); + const removeLabelCalls = []; + + mockGithub.rest.issues.removeLabel = async params => { + removeLabelCalls.push(params); + return {}; + }; + + const result = await handler( + { + item_number: 456, + labels: [{ name: "bug", rationale: "No longer needed", confidence: "medium" }], + }, + {} + ); + + expect(result.success).toBe(true); + expect(result.number).toBe(456); + expect(removeLabelCalls).toHaveLength(1); + expect(removeLabelCalls[0].name).toBe("bug"); + }); + it("should accept issue_number as an alias for item_number", async () => { const handler = await main({ max: 10 }); const removeLabelCalls = []; diff --git a/actions/setup/js/safe_output_type_validator.cjs b/actions/setup/js/safe_output_type_validator.cjs index 1b9d80aa10f..6a7d527cc37 100644 --- a/actions/setup/js/safe_output_type_validator.cjs +++ b/actions/setup/js/safe_output_type_validator.cjs @@ -38,6 +38,8 @@ const ISSUE_CLOSING_BOTH_BACKTICK_PATTERN = new RegExp(`\`(\\b(?:${ISSUE_CLOSING const ISSUE_CLOSING_KEYWORD_BACKTICK_PATTERN = new RegExp(`\`(\\b(?:${ISSUE_CLOSING_KEYWORDS})\\b)\`(\\s+)(${ISSUE_REFERENCE_PATTERN})`, "gi"); const ISSUE_CLOSING_REFERENCE_BACKTICK_PATTERN = new RegExp(`(\\b(?:${ISSUE_CLOSING_KEYWORDS})\\b)(\\s+)\`(${ISSUE_REFERENCE_PATTERN})\``, "gi"); const NORMALIZE_CLOSER_BODY_TYPES = new Set(["create_issue", "add_comment", "create_pull_request"]); +const ISSUE_INTENT_LABEL_TYPES = new Set(["add_labels", "remove_labels", "update_issue"]); +const ISSUE_INTENT_LABEL_CONFIDENCE_VALUES = new Set(["LOW", "MEDIUM", "HIGH"]); /** * Remove markdown backticks around recognized issue-closing keyword references. @@ -63,6 +65,109 @@ function normalizeIssueClosingKeywordBackticks(content) { return normalized.replace(ISSUE_CLOSING_REFERENCE_BACKTICK_PATTERN, "$1$2$3"); } +/** + * Validate and normalize issue-intent-aware label arrays. + * @param {any[]} value + * @param {number} lineNum + * @param {string} itemType + * @param {string} fieldName + * @param {ValidateOptions} [options] + * @returns {{isValid: boolean, normalizedValue?: any[], error?: string}} + */ +function validateIssueIntentLabels(value, lineNum, itemType, fieldName, options) { + const normalized = []; + for (let i = 0; i < value.length; i++) { + const label = value[i]; + if (typeof label === "string") { + const name = sanitizeContent(label, { + maxLength: 128, + allowedAliases: options?.allowedAliases || [], + maxBotMentions: options?.maxBotMentions, + }); + if (!name) { + return { isValid: false, error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}] must be a non-empty string` }; + } + normalized.push(name); + continue; + } + + if (!label || typeof label !== "object" || Array.isArray(label)) { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}] must be a string or an object with 'name'`, + }; + } + + const keys = Object.keys(label); + const invalidKeys = keys.filter(key => !["name", "rationale", "confidence", "suggest"].includes(key)); + if (invalidKeys.length > 0) { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}] contains unsupported fields: ${invalidKeys.join(", ")}`, + }; + } + if (typeof label.name !== "string") { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].name must be a string`, + }; + } + const name = sanitizeContent(label.name, { + maxLength: 128, + allowedAliases: options?.allowedAliases || [], + maxBotMentions: options?.maxBotMentions, + }); + if (!name) { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].name must be a non-empty string`, + }; + } + + /** @type {{ name: string, rationale?: string, confidence?: "LOW"|"MEDIUM"|"HIGH", suggest?: boolean }} */ + const normalizedLabel = { name }; + if (label.rationale !== undefined) { + if (typeof label.rationale !== "string") { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].rationale must be a string`, + }; + } + const rationale = sanitizeContent(unfenceMarkdown(label.rationale), { + maxLength: 1024, + allowedAliases: options?.allowedAliases || [], + maxBotMentions: options?.maxBotMentions, + }).trim(); + if (rationale) { + normalizedLabel.rationale = rationale; + } + } + if (label.confidence !== undefined && label.confidence !== null && label.confidence !== "") { + const confidence = String(label.confidence).trim().toUpperCase(); + if (!ISSUE_INTENT_LABEL_CONFIDENCE_VALUES.has(confidence)) { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].confidence must be one of: LOW, MEDIUM, HIGH`, + }; + } + normalizedLabel.confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidence; + } + if (label.suggest !== undefined) { + if (typeof label.suggest !== "boolean") { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].suggest must be a boolean`, + }; + } + if (label.suggest) { + normalizedLabel.suggest = true; + } + } + normalized.push(normalizedLabel); + } + return { isValid: true, normalizedValue: normalized }; +} + /** * @typedef {Object} FieldValidation * @property {boolean} [required] - Whether the field is required @@ -434,6 +539,10 @@ function validateField(value, fieldName, validation, itemType, lineNum, options) }; } + if (fieldName === "labels" && ISSUE_INTENT_LABEL_TYPES.has(itemType)) { + return validateIssueIntentLabels(value, lineNum, itemType, fieldName, options); + } + // Validate array items if (validation.itemType === "string") { const hasInvalidItem = value.some(item => typeof item !== "string"); diff --git a/actions/setup/js/safe_output_type_validator.test.cjs b/actions/setup/js/safe_output_type_validator.test.cjs index c2bbfe46ecd..3323a186e0f 100644 --- a/actions/setup/js/safe_output_type_validator.test.cjs +++ b/actions/setup/js/safe_output_type_validator.test.cjs @@ -36,6 +36,20 @@ const SAMPLE_VALIDATION_CONFIG = { labels: { type: "array", itemType: "string", itemSanitize: true, itemMaxLength: 128 }, }, }, + add_labels: { + defaultMax: 3, + fields: { + labels: { required: true, type: "array" }, + item_number: { issueNumberOrTemporaryId: true }, + }, + }, + remove_labels: { + defaultMax: 3, + fields: { + labels: { required: true, type: "array" }, + item_number: { issueNumberOrTemporaryId: true }, + }, + }, update_issue: { defaultMax: 1, customValidation: "requiresOneOf:status,title,body,labels,assignees,milestone", @@ -298,6 +312,40 @@ describe("safe_output_type_validator", () => { expect(result.error).toContain("body"); }); + it("should validate add_labels with structured label entries", async () => { + const { validateItem } = await import("./safe_output_type_validator.cjs"); + + const result = validateItem( + { + type: "add_labels", + item_number: 123, + labels: [{ name: "bug", rationale: "Known failure mode", confidence: "high", suggest: true }], + }, + "add_labels", + 1 + ); + + expect(result.isValid).toBe(true); + expect(result.normalizedItem.labels).toEqual([{ name: "bug", rationale: "Known failure mode", confidence: "HIGH", suggest: true }]); + }); + + it("should fail add_labels when structured label entry is invalid", async () => { + const { validateItem } = await import("./safe_output_type_validator.cjs"); + + const result = validateItem( + { + type: "add_labels", + item_number: 123, + labels: [{ rationale: "missing name" }], + }, + "add_labels", + 1 + ); + + expect(result.isValid).toBe(false); + expect(result.error).toContain("name"); + }); + it("should sanitize string fields", async () => { const { validateItem } = await import("./safe_output_type_validator.cjs"); diff --git a/actions/setup/js/safe_outputs_tools.json b/actions/setup/js/safe_outputs_tools.json index e26efb71594..22ee6d95630 100644 --- a/actions/setup/js/safe_outputs_tools.json +++ b/actions/setup/js/safe_outputs_tools.json @@ -587,9 +587,37 @@ "labels": { "type": "array", "items": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Label name to apply." + }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": ["LOW", "MEDIUM", "HIGH"], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + } + }, + "additionalProperties": false + } + ] }, - "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository. This field is required \u2014 omitting it will cause a validation error." + "description": "Labels to add (e.g., ['bug', 'priority-high']). Each entry can be either a label name string or an object with name plus optional rationale/confidence/suggest intent metadata. Labels must exist in the repository. This field is required \u2014 omitting it will cause a validation error." }, "item_number": { "type": ["number", "string"], @@ -619,9 +647,37 @@ "labels": { "type": "array", "items": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Label name to remove." + }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": ["LOW", "MEDIUM", "HIGH"], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + } + }, + "additionalProperties": false + } + ] }, - "description": "Label names to remove (e.g., ['smoke', 'needs-triage']). Non-existent labels are silently skipped." + "description": "Labels to remove (e.g., ['smoke', 'needs-triage']). Each entry can be either a label name string or an object with name plus optional rationale/confidence/suggest intent metadata. Non-existent labels are silently skipped." }, "item_number": { "type": ["number", "string"], diff --git a/actions/setup/js/types/safe-outputs.d.ts b/actions/setup/js/types/safe-outputs.d.ts index 296670ba9d4..6aba9c713e0 100644 --- a/actions/setup/js/types/safe-outputs.d.ts +++ b/actions/setup/js/types/safe-outputs.d.ts @@ -205,8 +205,8 @@ interface CreateCodeScanningAlertItem extends BaseSafeOutputItem { */ interface AddLabelsItem extends BaseSafeOutputItem { type: "add_labels"; - /** Array of label names to add */ - labels: string[]; + /** Array of label names or label specs to add */ + labels: Array; /** Target issue; otherwize resolved from current context */ issue_number?: number; } @@ -230,8 +230,8 @@ interface IssueIntentLabel extends IssueIntentMetadata { */ interface RemoveLabelsItem extends BaseSafeOutputItem { type: "remove_labels"; - /** Array of label names to remove */ - labels: string[]; + /** Array of label names or label specs to remove */ + labels: Array; /** Target issue; otherwise resolved from current context */ item_number?: number; } diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json index 2271a1e2270..ae792c14add 100644 --- a/pkg/workflow/js/safe_outputs_tools.json +++ b/pkg/workflow/js/safe_outputs_tools.json @@ -751,9 +751,43 @@ "labels": { "type": "array", "items": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "Label name to apply." + }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + } + }, + "additionalProperties": false + } + ] }, - "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository. This field is required \u2014 omitting it will cause a validation error." + "description": "Labels to add (e.g., ['bug', 'priority-high']). Each entry can be either a label name string or an object with name plus optional rationale/confidence/suggest intent metadata. Labels must exist in the repository. This field is required \u2014 omitting it will cause a validation error." }, "item_number": { "type": [ @@ -790,9 +824,43 @@ "labels": { "type": "array", "items": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "Label name to remove." + }, + "rationale": { + "type": "string", + "description": "Optional rationale for the change." + }, + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "description": "Optional confidence level for the change." + }, + "suggest": { + "type": "boolean", + "description": "When true, route the change through pending human review instead of applying it immediately." + } + }, + "additionalProperties": false + } + ] }, - "description": "Label names to remove (e.g., ['smoke', 'needs-triage']). Non-existent labels are silently skipped." + "description": "Labels to remove (e.g., ['smoke', 'needs-triage']). Each entry can be either a label name string or an object with name plus optional rationale/confidence/suggest intent metadata. Non-existent labels are silently skipped." }, "item_number": { "type": [ diff --git a/pkg/workflow/safe_output_validation_config_test.go b/pkg/workflow/safe_output_validation_config_test.go index 68c53e05222..f8a1b6349ba 100644 --- a/pkg/workflow/safe_output_validation_config_test.go +++ b/pkg/workflow/safe_output_validation_config_test.go @@ -303,6 +303,23 @@ func TestIssueIntentValidationFields(t *testing.T) { } } +func TestIssueIntentLabelValidationFields(t *testing.T) { + for _, typeName := range []string{"add_labels", "remove_labels", "update_issue"} { + config, ok := ValidationConfig[typeName] + if !ok { + t.Fatalf("%s not found in ValidationConfig", typeName) + } + + labels, ok := config.Fields["labels"] + if !ok { + t.Fatalf("%s Fields is missing 'labels'", typeName) + } + if labels.Type != "array" { + t.Fatalf("%s labels type = %q, want %q", typeName, labels.Type, "array") + } + } +} + func TestValidationConfigConsistency(t *testing.T) { // Verify that all types with customValidation have valid validation rules validCustomValidations := map[string]bool{ diff --git a/pkg/workflow/safe_outputs_validation_config.go b/pkg/workflow/safe_outputs_validation_config.go index ebd21b05761..b7e9d4dd02d 100644 --- a/pkg/workflow/safe_outputs_validation_config.go +++ b/pkg/workflow/safe_outputs_validation_config.go @@ -105,7 +105,7 @@ var ValidationConfig = map[string]TypeValidationConfig{ "add_labels": { DefaultMax: 5, Fields: map[string]FieldValidation{ - "labels": {Required: true, Type: "array", ItemType: "string", ItemSanitize: true, ItemMaxLength: 128}, + "labels": {Required: true, Type: "array"}, "item_number": {IssueNumberOrTemporaryID: true}, "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, @@ -393,7 +393,7 @@ var ValidationConfig = map[string]TypeValidationConfig{ "remove_labels": { DefaultMax: 5, Fields: map[string]FieldValidation{ - "labels": {Required: true, Type: "array", ItemType: "string", ItemSanitize: true, ItemMaxLength: 128}, + "labels": {Required: true, Type: "array"}, "item_number": {IssueNumberOrTemporaryID: true}, "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, From 3ea86493c26afcc039453456aad47b7562d95dc6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 19:24:59 +0000 Subject: [PATCH 03/10] Polish issue-intent label normalization for add/remove handlers Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/add_labels.cjs | 13 ++--------- actions/setup/js/issue_intents.cjs | 22 ++++++++++++++++++- actions/setup/js/remove_labels.cjs | 13 ++--------- .../setup/js/safe_output_type_validator.cjs | 18 ++++++++------- .../safe_outputs_validation_config.go | 4 ++-- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/actions/setup/js/add_labels.cjs b/actions/setup/js/add_labels.cjs index 998126b7784..d2c5b14c7af 100644 --- a/actions/setup/js/add_labels.cjs +++ b/actions/setup/js/add_labels.cjs @@ -33,7 +33,7 @@ const { MAX_LABELS } = require("./constants.cjs"); const { createCountGatedHandler } = require("./handler_scaffold.cjs"); const { withRetry, RATE_LIMIT_RETRY_CONFIG } = require("./error_recovery.cjs"); const { resolveInvocationContext } = require("./invocation_context_helpers.cjs"); -const { getIssueIntentLabelNames, normalizeIssueIntentLabelSpecs } = require("./issue_intents.cjs"); +const { normalizeIssueIntentLabelNames } = require("./issue_intents.cjs"); /** * Main handler factory for add_labels @@ -94,16 +94,7 @@ const main = createCountGatedHandler({ core.info(`Requested labels: ${JSON.stringify(requestedLabels)}`); let requestedLabelNames; try { - requestedLabelNames = requestedLabels.map((label, index) => { - if (typeof label === "string") { - return label; - } - const normalized = normalizeIssueIntentLabelSpecs([label]); - if (normalized.length !== 1) { - throw new Error(`Invalid labels[${index}] entry. Expected a single label specification.`); - } - return getIssueIntentLabelNames(normalized)[0]; - }); + requestedLabelNames = normalizeIssueIntentLabelNames(requestedLabels); } catch (error) { const errorMessage = getErrorMessage(error); core.warning(`Invalid add_labels payload: ${errorMessage}`); diff --git a/actions/setup/js/issue_intents.cjs b/actions/setup/js/issue_intents.cjs index 8782bdf505d..e004333f006 100644 --- a/actions/setup/js/issue_intents.cjs +++ b/actions/setup/js/issue_intents.cjs @@ -56,7 +56,8 @@ function normalizeIssueIntentLabelSpecs(labels) { return []; } if (!Array.isArray(labels)) { - throw new Error(`Invalid labels ${JSON.stringify(labels)}. Expected an array of label names or label spec objects.`); + const receivedType = labels === null ? "null" : typeof labels; + throw new Error(`Invalid labels. Expected an array of label names or label spec objects; received ${receivedType}.`); } return labels.map((label, index) => { @@ -90,6 +91,24 @@ function normalizeIssueIntentLabelSpecs(labels) { }); } +function normalizeIssueIntentLabelNames(labels) { + if (labels === undefined) { + return []; + } + if (!Array.isArray(labels)) { + const receivedType = labels === null ? "null" : typeof labels; + throw new Error(`Invalid labels. Expected an array of label names or label spec objects; received ${receivedType}.`); + } + + return labels.map((label, index) => { + if (typeof label === "string") { + return label; + } + const normalized = normalizeIssueIntentLabelSpecs([label]); + return normalized[0].name; + }); +} + function getIssueIntentLabelNames(labelSpecs) { return labelSpecs.map(label => label.name); } @@ -112,6 +131,7 @@ module.exports = { buildIssueIntentLabelUpdates, getIssueIntentLabelNames, hasIssueIntentsRuntimeFeature, + normalizeIssueIntentLabelNames, normalizeIssueIntentLabelSpecs, normalizeIssueIntentMetadata, }; diff --git a/actions/setup/js/remove_labels.cjs b/actions/setup/js/remove_labels.cjs index 3ddb1ee559b..a46444b150d 100644 --- a/actions/setup/js/remove_labels.cjs +++ b/actions/setup/js/remove_labels.cjs @@ -16,7 +16,7 @@ const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { resolveSafeOutputIssueTarget } = require("./temporary_id.cjs"); const { createCountGatedHandler } = require("./handler_scaffold.cjs"); const { resolveInvocationContext } = require("./invocation_context_helpers.cjs"); -const { getIssueIntentLabelNames, normalizeIssueIntentLabelSpecs } = require("./issue_intents.cjs"); +const { normalizeIssueIntentLabelNames } = require("./issue_intents.cjs"); /** * Main handler factory for remove_labels @@ -85,16 +85,7 @@ const main = createCountGatedHandler({ core.info(`Requested labels to remove: ${JSON.stringify(requestedLabels)}`); let requestedLabelNames; try { - requestedLabelNames = requestedLabels.map((label, index) => { - if (typeof label === "string") { - return label; - } - const normalized = normalizeIssueIntentLabelSpecs([label]); - if (normalized.length !== 1) { - throw new Error(`Invalid labels[${index}] entry. Expected a single label specification.`); - } - return getIssueIntentLabelNames(normalized)[0]; - }); + requestedLabelNames = normalizeIssueIntentLabelNames(requestedLabels); } catch (error) { const errorMessage = getErrorMessage(error); core.warning(`Invalid remove_labels payload: ${errorMessage}`); diff --git a/actions/setup/js/safe_output_type_validator.cjs b/actions/setup/js/safe_output_type_validator.cjs index 6a7d527cc37..3c7fd5ccd79 100644 --- a/actions/setup/js/safe_output_type_validator.cjs +++ b/actions/setup/js/safe_output_type_validator.cjs @@ -142,15 +142,17 @@ function validateIssueIntentLabels(value, lineNum, itemType, fieldName, options) normalizedLabel.rationale = rationale; } } - if (label.confidence !== undefined && label.confidence !== null && label.confidence !== "") { - const confidence = String(label.confidence).trim().toUpperCase(); - if (!ISSUE_INTENT_LABEL_CONFIDENCE_VALUES.has(confidence)) { - return { - isValid: false, - error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].confidence must be one of: LOW, MEDIUM, HIGH`, - }; + if (label.confidence !== undefined) { + if (label.confidence !== null && label.confidence !== "") { + const confidence = String(label.confidence).trim().toUpperCase(); + if (!ISSUE_INTENT_LABEL_CONFIDENCE_VALUES.has(confidence)) { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].confidence must be one of: LOW, MEDIUM, HIGH`, + }; + } + normalizedLabel.confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidence; } - normalizedLabel.confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidence; } if (label.suggest !== undefined) { if (typeof label.suggest !== "boolean") { diff --git a/pkg/workflow/safe_outputs_validation_config.go b/pkg/workflow/safe_outputs_validation_config.go index b7e9d4dd02d..143bb17aa71 100644 --- a/pkg/workflow/safe_outputs_validation_config.go +++ b/pkg/workflow/safe_outputs_validation_config.go @@ -105,7 +105,7 @@ var ValidationConfig = map[string]TypeValidationConfig{ "add_labels": { DefaultMax: 5, Fields: map[string]FieldValidation{ - "labels": {Required: true, Type: "array"}, + "labels": {Required: true, Type: "array"}, // Item-level validation/sanitization handled by JS issue-intent label normalization. "item_number": {IssueNumberOrTemporaryID: true}, "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, @@ -393,7 +393,7 @@ var ValidationConfig = map[string]TypeValidationConfig{ "remove_labels": { DefaultMax: 5, Fields: map[string]FieldValidation{ - "labels": {Required: true, Type: "array"}, + "labels": {Required: true, Type: "array"}, // Item-level validation/sanitization handled by JS issue-intent label normalization. "item_number": {IssueNumberOrTemporaryID: true}, "repo": {Type: "string", MaxLength: 256}, // Optional: target repository in format "owner/repo" }, From 7d1ee06b31955490af0f73c09e873cbe850fcab9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 20:08:48 +0000 Subject: [PATCH 04/10] chore: start PR finisher triage plan Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/ai-moderator.lock.yml | 5 +--- .github/workflows/approach-validator.lock.yml | 5 +--- .github/workflows/auto-triage-issues.lock.yml | 5 +--- .../aw-failure-investigator.lock.yml | 7 ++--- .github/workflows/bot-detection.lock.yml | 7 ++--- .github/workflows/ci-doctor.lock.yml | 7 ++--- .../workflows/code-scanning-fixer.lock.yml | 5 +--- .github/workflows/contribution-check.lock.yml | 5 +--- .../daily-experiment-report.lock.yml | 5 +--- .github/workflows/dependabot-repair.lock.yml | 7 ++--- .github/workflows/draft-pr-cleanup.lock.yml | 5 +--- .github/workflows/issue-triage-agent.lock.yml | 5 +--- .github/workflows/lint-monster.lock.yml | 7 ++--- .github/workflows/poem-bot.lock.yml | 12 +++------ .github/workflows/pr-triage-agent.lock.yml | 5 +--- .github/workflows/q.lock.yml | 5 +--- .github/workflows/scout.lock.yml | 5 +--- .github/workflows/smoke-antigravity.lock.yml | 5 +--- .github/workflows/smoke-ci.lock.yml | 17 +++--------- .github/workflows/smoke-claude.lock.yml | 5 +--- .github/workflows/smoke-codex.lock.yml | 26 +++++++++++++------ .../smoke-copilot-aoai-apikey.lock.yml | 26 +++++++++++++------ .../smoke-copilot-aoai-entra.lock.yml | 26 +++++++++++++------ .github/workflows/smoke-copilot-arm.lock.yml | 10 ++----- .github/workflows/smoke-copilot.lock.yml | 26 +++++++++++++------ .github/workflows/smoke-crush.lock.yml | 5 +--- .github/workflows/smoke-gemini.lock.yml | 5 +--- .github/workflows/smoke-opencode.lock.yml | 5 +--- .github/workflows/smoke-pi.lock.yml | 5 +--- .github/workflows/smoke-project.lock.yml | 10 ++----- .github/workflows/stale-pr-cleanup.lock.yml | 5 +--- .github/workflows/sub-issue-closer.lock.yml | 7 ++--- .github/workflows/workflow-generator.lock.yml | 7 ++--- .../workflow-health-manager.lock.yml | 7 ++--- 34 files changed, 117 insertions(+), 182 deletions(-) diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml index fe101d85ad2..7945f43a741 100644 --- a/.github/workflows/ai-moderator.lock.yml +++ b/.github/workflows/ai-moderator.lock.yml @@ -646,10 +646,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/approach-validator.lock.yml b/.github/workflows/approach-validator.lock.yml index 6f6023992af..95c81695827 100644 --- a/.github/workflows/approach-validator.lock.yml +++ b/.github/workflows/approach-validator.lock.yml @@ -693,10 +693,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml index 42bb56978be..30536e99d78 100644 --- a/.github/workflows/auto-triage-issues.lock.yml +++ b/.github/workflows/auto-triage-issues.lock.yml @@ -608,10 +608,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/aw-failure-investigator.lock.yml b/.github/workflows/aw-failure-investigator.lock.yml index 7a4256946a5..276e4ca3a3a 100644 --- a/.github/workflows/aw-failure-investigator.lock.yml +++ b/.github/workflows/aw-failure-investigator.lock.yml @@ -835,10 +835,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -869,7 +866,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml index de0d6131b02..3f5544b83e9 100644 --- a/.github/workflows/bot-detection.lock.yml +++ b/.github/workflows/bot-detection.lock.yml @@ -712,10 +712,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -746,7 +743,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 3f93f010370..4f5adce58d9 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -848,10 +848,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -882,7 +879,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index c1a57ed38da..5d4a7543ff6 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -606,10 +606,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 82ca2b64cb5..60a71a248e8 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -677,10 +677,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/daily-experiment-report.lock.yml b/.github/workflows/daily-experiment-report.lock.yml index 77bd01cf5de..c62302721d5 100644 --- a/.github/workflows/daily-experiment-report.lock.yml +++ b/.github/workflows/daily-experiment-report.lock.yml @@ -649,10 +649,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/dependabot-repair.lock.yml b/.github/workflows/dependabot-repair.lock.yml index 2726ef1c132..dee418b0f5e 100644 --- a/.github/workflows/dependabot-repair.lock.yml +++ b/.github/workflows/dependabot-repair.lock.yml @@ -735,10 +735,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -769,7 +766,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/draft-pr-cleanup.lock.yml b/.github/workflows/draft-pr-cleanup.lock.yml index f9a527004f8..dd4a6efe77c 100644 --- a/.github/workflows/draft-pr-cleanup.lock.yml +++ b/.github/workflows/draft-pr-cleanup.lock.yml @@ -588,10 +588,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml index 88bdda90d3e..8c55568014d 100644 --- a/.github/workflows/issue-triage-agent.lock.yml +++ b/.github/workflows/issue-triage-agent.lock.yml @@ -568,10 +568,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/lint-monster.lock.yml b/.github/workflows/lint-monster.lock.yml index d662cc5098e..b384051b16c 100644 --- a/.github/workflows/lint-monster.lock.yml +++ b/.github/workflows/lint-monster.lock.yml @@ -764,10 +764,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -798,7 +795,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index f385835e586..4f1f0974b80 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -701,10 +701,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1015,10 +1012,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -1049,7 +1043,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index ba26128d8ab..f0b2ef7b8b7 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -627,10 +627,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index b0d8f2bc7ec..33c4c7f11d3 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -758,10 +758,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index c34496ae81e..3ce5277f929 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -715,10 +715,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-antigravity.lock.yml b/.github/workflows/smoke-antigravity.lock.yml index 7166199bfff..6923a762f7b 100644 --- a/.github/workflows/smoke-antigravity.lock.yml +++ b/.github/workflows/smoke-antigravity.lock.yml @@ -718,10 +718,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-ci.lock.yml b/.github/workflows/smoke-ci.lock.yml index 274d11da2c7..9aa2bfbb703 100644 --- a/.github/workflows/smoke-ci.lock.yml +++ b/.github/workflows/smoke-ci.lock.yml @@ -677,10 +677,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -815,10 +812,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -860,10 +854,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -894,7 +885,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" }, "update_pull_request": { "defaultMax": 1, diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 43206ca57e0..46a67672d87 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -977,10 +977,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 92cf8b79dd2..13c8afe0885 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -776,10 +776,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -940,10 +937,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -970,6 +964,14 @@ jobs: "set_issue_field": { "defaultMax": 5, "fields": { + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, "field_name": { "type": "string", "sanitize": true, @@ -982,10 +984,18 @@ jobs: "issue_number": { "issueOrPRNumber": true }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 1024 + }, "repo": { "type": "string", "maxLength": 256 }, + "suggest": { + "type": "boolean" + }, "value": { "required": true, "type": "string", diff --git a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml index 85b580b16c7..d3f5153e6fb 100644 --- a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml @@ -894,10 +894,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1113,10 +1110,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1165,6 +1159,14 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, "issue_number": { "issueOrPRNumber": true }, @@ -1174,9 +1176,17 @@ jobs: "sanitize": true, "maxLength": 128 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 1024 + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/smoke-copilot-aoai-entra.lock.yml b/.github/workflows/smoke-copilot-aoai-entra.lock.yml index ca99355a3aa..afa56dc2fec 100644 --- a/.github/workflows/smoke-copilot-aoai-entra.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-entra.lock.yml @@ -895,10 +895,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1114,10 +1111,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1166,6 +1160,14 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, "issue_number": { "issueOrPRNumber": true }, @@ -1175,9 +1177,17 @@ jobs: "sanitize": true, "maxLength": 128 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 1024 + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml index 55e1426b592..d91f9aabb5c 100644 --- a/.github/workflows/smoke-copilot-arm.lock.yml +++ b/.github/workflows/smoke-copilot-arm.lock.yml @@ -827,10 +827,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1021,10 +1018,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 1ce4605a52c..da29d39a317 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -899,10 +899,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1118,10 +1115,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1170,6 +1164,14 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, "issue_number": { "issueOrPRNumber": true }, @@ -1179,9 +1181,17 @@ jobs: "sanitize": true, "maxLength": 128 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 1024 + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/smoke-crush.lock.yml b/.github/workflows/smoke-crush.lock.yml index 0868db8e677..ddfc57139b9 100644 --- a/.github/workflows/smoke-crush.lock.yml +++ b/.github/workflows/smoke-crush.lock.yml @@ -656,10 +656,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml index 1faeca269d5..fb7971fae31 100644 --- a/.github/workflows/smoke-gemini.lock.yml +++ b/.github/workflows/smoke-gemini.lock.yml @@ -722,10 +722,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index 9284f17e228..28d6a6f8323 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -660,10 +660,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-pi.lock.yml b/.github/workflows/smoke-pi.lock.yml index fe6c2b4edfd..22fba870718 100644 --- a/.github/workflows/smoke-pi.lock.yml +++ b/.github/workflows/smoke-pi.lock.yml @@ -690,10 +690,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml index eaa9f8cc137..29a90a3ffe0 100644 --- a/.github/workflows/smoke-project.lock.yml +++ b/.github/workflows/smoke-project.lock.yml @@ -700,10 +700,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -893,10 +890,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/stale-pr-cleanup.lock.yml b/.github/workflows/stale-pr-cleanup.lock.yml index e4834af998a..de691618b9a 100644 --- a/.github/workflows/stale-pr-cleanup.lock.yml +++ b/.github/workflows/stale-pr-cleanup.lock.yml @@ -587,10 +587,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml index 12a2b0dc023..46654bc065d 100644 --- a/.github/workflows/sub-issue-closer.lock.yml +++ b/.github/workflows/sub-issue-closer.lock.yml @@ -673,10 +673,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -707,7 +704,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index 3cbec36f5eb..9f7aa80b554 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -712,10 +712,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -746,7 +743,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml index 8862d67728c..43c7c5283ab 100644 --- a/.github/workflows/workflow-health-manager.lock.yml +++ b/.github/workflows/workflow-health-manager.lock.yml @@ -746,10 +746,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -780,7 +777,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 From fc638c3da28b1e2f7f23fa98554ba7cbed76b787 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 20:09:00 +0000 Subject: [PATCH 05/10] Revert "chore: start PR finisher triage plan" This reverts commit 7d1ee06b31955490af0f73c09e873cbe850fcab9. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/ai-moderator.lock.yml | 5 +++- .github/workflows/approach-validator.lock.yml | 5 +++- .github/workflows/auto-triage-issues.lock.yml | 5 +++- .../aw-failure-investigator.lock.yml | 7 +++-- .github/workflows/bot-detection.lock.yml | 7 +++-- .github/workflows/ci-doctor.lock.yml | 7 +++-- .../workflows/code-scanning-fixer.lock.yml | 5 +++- .github/workflows/contribution-check.lock.yml | 5 +++- .../daily-experiment-report.lock.yml | 5 +++- .github/workflows/dependabot-repair.lock.yml | 7 +++-- .github/workflows/draft-pr-cleanup.lock.yml | 5 +++- .github/workflows/issue-triage-agent.lock.yml | 5 +++- .github/workflows/lint-monster.lock.yml | 7 +++-- .github/workflows/poem-bot.lock.yml | 12 ++++++--- .github/workflows/pr-triage-agent.lock.yml | 5 +++- .github/workflows/q.lock.yml | 5 +++- .github/workflows/scout.lock.yml | 5 +++- .github/workflows/smoke-antigravity.lock.yml | 5 +++- .github/workflows/smoke-ci.lock.yml | 17 +++++++++--- .github/workflows/smoke-claude.lock.yml | 5 +++- .github/workflows/smoke-codex.lock.yml | 26 ++++++------------- .../smoke-copilot-aoai-apikey.lock.yml | 26 ++++++------------- .../smoke-copilot-aoai-entra.lock.yml | 26 ++++++------------- .github/workflows/smoke-copilot-arm.lock.yml | 10 +++++-- .github/workflows/smoke-copilot.lock.yml | 26 ++++++------------- .github/workflows/smoke-crush.lock.yml | 5 +++- .github/workflows/smoke-gemini.lock.yml | 5 +++- .github/workflows/smoke-opencode.lock.yml | 5 +++- .github/workflows/smoke-pi.lock.yml | 5 +++- .github/workflows/smoke-project.lock.yml | 10 +++++-- .github/workflows/stale-pr-cleanup.lock.yml | 5 +++- .github/workflows/sub-issue-closer.lock.yml | 7 +++-- .github/workflows/workflow-generator.lock.yml | 7 +++-- .../workflow-health-manager.lock.yml | 7 +++-- 34 files changed, 182 insertions(+), 117 deletions(-) diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml index 7945f43a741..fe101d85ad2 100644 --- a/.github/workflows/ai-moderator.lock.yml +++ b/.github/workflows/ai-moderator.lock.yml @@ -646,7 +646,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/approach-validator.lock.yml b/.github/workflows/approach-validator.lock.yml index 95c81695827..6f6023992af 100644 --- a/.github/workflows/approach-validator.lock.yml +++ b/.github/workflows/approach-validator.lock.yml @@ -693,7 +693,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml index 30536e99d78..42bb56978be 100644 --- a/.github/workflows/auto-triage-issues.lock.yml +++ b/.github/workflows/auto-triage-issues.lock.yml @@ -608,7 +608,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/aw-failure-investigator.lock.yml b/.github/workflows/aw-failure-investigator.lock.yml index 276e4ca3a3a..7a4256946a5 100644 --- a/.github/workflows/aw-failure-investigator.lock.yml +++ b/.github/workflows/aw-failure-investigator.lock.yml @@ -835,7 +835,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -866,7 +869,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml index 3f5544b83e9..de0d6131b02 100644 --- a/.github/workflows/bot-detection.lock.yml +++ b/.github/workflows/bot-detection.lock.yml @@ -712,7 +712,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -743,7 +746,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 4f5adce58d9..3f93f010370 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -848,7 +848,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -879,7 +882,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index 5d4a7543ff6..c1a57ed38da 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -606,7 +606,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 60a71a248e8..82ca2b64cb5 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -677,7 +677,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/daily-experiment-report.lock.yml b/.github/workflows/daily-experiment-report.lock.yml index c62302721d5..77bd01cf5de 100644 --- a/.github/workflows/daily-experiment-report.lock.yml +++ b/.github/workflows/daily-experiment-report.lock.yml @@ -649,7 +649,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/dependabot-repair.lock.yml b/.github/workflows/dependabot-repair.lock.yml index dee418b0f5e..2726ef1c132 100644 --- a/.github/workflows/dependabot-repair.lock.yml +++ b/.github/workflows/dependabot-repair.lock.yml @@ -735,7 +735,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -766,7 +769,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/draft-pr-cleanup.lock.yml b/.github/workflows/draft-pr-cleanup.lock.yml index dd4a6efe77c..f9a527004f8 100644 --- a/.github/workflows/draft-pr-cleanup.lock.yml +++ b/.github/workflows/draft-pr-cleanup.lock.yml @@ -588,7 +588,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml index 8c55568014d..88bdda90d3e 100644 --- a/.github/workflows/issue-triage-agent.lock.yml +++ b/.github/workflows/issue-triage-agent.lock.yml @@ -568,7 +568,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/lint-monster.lock.yml b/.github/workflows/lint-monster.lock.yml index b384051b16c..d662cc5098e 100644 --- a/.github/workflows/lint-monster.lock.yml +++ b/.github/workflows/lint-monster.lock.yml @@ -764,7 +764,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -795,7 +798,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 4f1f0974b80..f385835e586 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -701,7 +701,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -1012,7 +1015,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -1043,7 +1049,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index f0b2ef7b8b7..ba26128d8ab 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -627,7 +627,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 33c4c7f11d3..b0d8f2bc7ec 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -758,7 +758,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 3ce5277f929..c34496ae81e 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -715,7 +715,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-antigravity.lock.yml b/.github/workflows/smoke-antigravity.lock.yml index 6923a762f7b..7166199bfff 100644 --- a/.github/workflows/smoke-antigravity.lock.yml +++ b/.github/workflows/smoke-antigravity.lock.yml @@ -718,7 +718,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-ci.lock.yml b/.github/workflows/smoke-ci.lock.yml index 9aa2bfbb703..274d11da2c7 100644 --- a/.github/workflows/smoke-ci.lock.yml +++ b/.github/workflows/smoke-ci.lock.yml @@ -677,7 +677,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -812,7 +815,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -854,7 +860,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -885,7 +894,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" }, "update_pull_request": { "defaultMax": 1, diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 46a67672d87..43206ca57e0 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -977,7 +977,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 13c8afe0885..92cf8b79dd2 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -776,7 +776,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -937,7 +940,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -964,14 +970,6 @@ jobs: "set_issue_field": { "defaultMax": 5, "fields": { - "confidence": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ] - }, "field_name": { "type": "string", "sanitize": true, @@ -984,18 +982,10 @@ jobs: "issue_number": { "issueOrPRNumber": true }, - "rationale": { - "type": "string", - "sanitize": true, - "maxLength": 1024 - }, "repo": { "type": "string", "maxLength": 256 }, - "suggest": { - "type": "boolean" - }, "value": { "required": true, "type": "string", diff --git a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml index d3f5153e6fb..85b580b16c7 100644 --- a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml @@ -894,7 +894,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -1110,7 +1113,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -1159,14 +1165,6 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { - "confidence": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ] - }, "issue_number": { "issueOrPRNumber": true }, @@ -1176,17 +1174,9 @@ jobs: "sanitize": true, "maxLength": 128 }, - "rationale": { - "type": "string", - "sanitize": true, - "maxLength": 1024 - }, "repo": { "type": "string", "maxLength": 256 - }, - "suggest": { - "type": "boolean" } } }, diff --git a/.github/workflows/smoke-copilot-aoai-entra.lock.yml b/.github/workflows/smoke-copilot-aoai-entra.lock.yml index afa56dc2fec..ca99355a3aa 100644 --- a/.github/workflows/smoke-copilot-aoai-entra.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-entra.lock.yml @@ -895,7 +895,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -1111,7 +1114,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -1160,14 +1166,6 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { - "confidence": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ] - }, "issue_number": { "issueOrPRNumber": true }, @@ -1177,17 +1175,9 @@ jobs: "sanitize": true, "maxLength": 128 }, - "rationale": { - "type": "string", - "sanitize": true, - "maxLength": 1024 - }, "repo": { "type": "string", "maxLength": 256 - }, - "suggest": { - "type": "boolean" } } }, diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml index d91f9aabb5c..55e1426b592 100644 --- a/.github/workflows/smoke-copilot-arm.lock.yml +++ b/.github/workflows/smoke-copilot-arm.lock.yml @@ -827,7 +827,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -1018,7 +1021,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index da29d39a317..1ce4605a52c 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -899,7 +899,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -1115,7 +1118,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -1164,14 +1170,6 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { - "confidence": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ] - }, "issue_number": { "issueOrPRNumber": true }, @@ -1181,17 +1179,9 @@ jobs: "sanitize": true, "maxLength": 128 }, - "rationale": { - "type": "string", - "sanitize": true, - "maxLength": 1024 - }, "repo": { "type": "string", "maxLength": 256 - }, - "suggest": { - "type": "boolean" } } }, diff --git a/.github/workflows/smoke-crush.lock.yml b/.github/workflows/smoke-crush.lock.yml index ddfc57139b9..0868db8e677 100644 --- a/.github/workflows/smoke-crush.lock.yml +++ b/.github/workflows/smoke-crush.lock.yml @@ -656,7 +656,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml index fb7971fae31..1faeca269d5 100644 --- a/.github/workflows/smoke-gemini.lock.yml +++ b/.github/workflows/smoke-gemini.lock.yml @@ -722,7 +722,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index 28d6a6f8323..9284f17e228 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -660,7 +660,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-pi.lock.yml b/.github/workflows/smoke-pi.lock.yml index 22fba870718..fe6c2b4edfd 100644 --- a/.github/workflows/smoke-pi.lock.yml +++ b/.github/workflows/smoke-pi.lock.yml @@ -690,7 +690,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml index 29a90a3ffe0..eaa9f8cc137 100644 --- a/.github/workflows/smoke-project.lock.yml +++ b/.github/workflows/smoke-project.lock.yml @@ -700,7 +700,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", @@ -890,7 +893,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/stale-pr-cleanup.lock.yml b/.github/workflows/stale-pr-cleanup.lock.yml index de691618b9a..e4834af998a 100644 --- a/.github/workflows/stale-pr-cleanup.lock.yml +++ b/.github/workflows/stale-pr-cleanup.lock.yml @@ -587,7 +587,10 @@ jobs: }, "labels": { "required": true, - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "repo": { "type": "string", diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml index 46654bc065d..12a2b0dc023 100644 --- a/.github/workflows/sub-issue-closer.lock.yml +++ b/.github/workflows/sub-issue-closer.lock.yml @@ -673,7 +673,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -704,7 +707,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index 9f7aa80b554..3cbec36f5eb 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -712,7 +712,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -743,7 +746,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml index 43c7c5283ab..8862d67728c 100644 --- a/.github/workflows/workflow-health-manager.lock.yml +++ b/.github/workflows/workflow-health-manager.lock.yml @@ -746,7 +746,10 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array" + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 }, "milestone": { "optionalPositiveInteger": true @@ -777,7 +780,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" + "customValidation": "requiresOneOf:status,title,body" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 From 52295df3813e2dcecfb07b6411c601c5a48b3ad5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 20:21:50 +0000 Subject: [PATCH 06/10] Plan CI failure fix Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/ai-moderator.lock.yml | 5 +--- .github/workflows/approach-validator.lock.yml | 5 +--- .github/workflows/auto-triage-issues.lock.yml | 5 +--- .../aw-failure-investigator.lock.yml | 7 ++--- .github/workflows/bot-detection.lock.yml | 7 ++--- .github/workflows/ci-doctor.lock.yml | 7 ++--- .../workflows/code-scanning-fixer.lock.yml | 5 +--- .github/workflows/contribution-check.lock.yml | 5 +--- .../daily-experiment-report.lock.yml | 5 +--- .github/workflows/dependabot-repair.lock.yml | 7 ++--- .github/workflows/draft-pr-cleanup.lock.yml | 5 +--- .github/workflows/issue-triage-agent.lock.yml | 5 +--- .github/workflows/lint-monster.lock.yml | 7 ++--- .github/workflows/poem-bot.lock.yml | 12 +++------ .github/workflows/pr-triage-agent.lock.yml | 5 +--- .github/workflows/q.lock.yml | 5 +--- .github/workflows/scout.lock.yml | 5 +--- .github/workflows/smoke-antigravity.lock.yml | 5 +--- .github/workflows/smoke-ci.lock.yml | 17 +++--------- .github/workflows/smoke-claude.lock.yml | 5 +--- .github/workflows/smoke-codex.lock.yml | 26 +++++++++++++------ .../smoke-copilot-aoai-apikey.lock.yml | 26 +++++++++++++------ .../smoke-copilot-aoai-entra.lock.yml | 26 +++++++++++++------ .github/workflows/smoke-copilot-arm.lock.yml | 10 ++----- .github/workflows/smoke-copilot.lock.yml | 26 +++++++++++++------ .github/workflows/smoke-crush.lock.yml | 5 +--- .github/workflows/smoke-gemini.lock.yml | 5 +--- .github/workflows/smoke-opencode.lock.yml | 5 +--- .github/workflows/smoke-pi.lock.yml | 5 +--- .github/workflows/smoke-project.lock.yml | 10 ++----- .github/workflows/stale-pr-cleanup.lock.yml | 5 +--- .github/workflows/sub-issue-closer.lock.yml | 7 ++--- .github/workflows/workflow-generator.lock.yml | 7 ++--- .../workflow-health-manager.lock.yml | 7 ++--- 34 files changed, 117 insertions(+), 182 deletions(-) diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml index fe101d85ad2..7945f43a741 100644 --- a/.github/workflows/ai-moderator.lock.yml +++ b/.github/workflows/ai-moderator.lock.yml @@ -646,10 +646,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/approach-validator.lock.yml b/.github/workflows/approach-validator.lock.yml index 6f6023992af..95c81695827 100644 --- a/.github/workflows/approach-validator.lock.yml +++ b/.github/workflows/approach-validator.lock.yml @@ -693,10 +693,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml index 42bb56978be..30536e99d78 100644 --- a/.github/workflows/auto-triage-issues.lock.yml +++ b/.github/workflows/auto-triage-issues.lock.yml @@ -608,10 +608,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/aw-failure-investigator.lock.yml b/.github/workflows/aw-failure-investigator.lock.yml index 7a4256946a5..276e4ca3a3a 100644 --- a/.github/workflows/aw-failure-investigator.lock.yml +++ b/.github/workflows/aw-failure-investigator.lock.yml @@ -835,10 +835,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -869,7 +866,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml index de0d6131b02..3f5544b83e9 100644 --- a/.github/workflows/bot-detection.lock.yml +++ b/.github/workflows/bot-detection.lock.yml @@ -712,10 +712,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -746,7 +743,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 3f93f010370..4f5adce58d9 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -848,10 +848,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -882,7 +879,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index c1a57ed38da..5d4a7543ff6 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -606,10 +606,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 82ca2b64cb5..60a71a248e8 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -677,10 +677,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/daily-experiment-report.lock.yml b/.github/workflows/daily-experiment-report.lock.yml index 77bd01cf5de..c62302721d5 100644 --- a/.github/workflows/daily-experiment-report.lock.yml +++ b/.github/workflows/daily-experiment-report.lock.yml @@ -649,10 +649,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/dependabot-repair.lock.yml b/.github/workflows/dependabot-repair.lock.yml index 2726ef1c132..dee418b0f5e 100644 --- a/.github/workflows/dependabot-repair.lock.yml +++ b/.github/workflows/dependabot-repair.lock.yml @@ -735,10 +735,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -769,7 +766,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/draft-pr-cleanup.lock.yml b/.github/workflows/draft-pr-cleanup.lock.yml index f9a527004f8..dd4a6efe77c 100644 --- a/.github/workflows/draft-pr-cleanup.lock.yml +++ b/.github/workflows/draft-pr-cleanup.lock.yml @@ -588,10 +588,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml index 88bdda90d3e..8c55568014d 100644 --- a/.github/workflows/issue-triage-agent.lock.yml +++ b/.github/workflows/issue-triage-agent.lock.yml @@ -568,10 +568,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/lint-monster.lock.yml b/.github/workflows/lint-monster.lock.yml index d662cc5098e..b384051b16c 100644 --- a/.github/workflows/lint-monster.lock.yml +++ b/.github/workflows/lint-monster.lock.yml @@ -764,10 +764,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -798,7 +795,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index f385835e586..4f1f0974b80 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -701,10 +701,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1015,10 +1012,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -1049,7 +1043,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index ba26128d8ab..f0b2ef7b8b7 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -627,10 +627,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index b0d8f2bc7ec..33c4c7f11d3 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -758,10 +758,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index c34496ae81e..3ce5277f929 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -715,10 +715,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-antigravity.lock.yml b/.github/workflows/smoke-antigravity.lock.yml index 7166199bfff..6923a762f7b 100644 --- a/.github/workflows/smoke-antigravity.lock.yml +++ b/.github/workflows/smoke-antigravity.lock.yml @@ -718,10 +718,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-ci.lock.yml b/.github/workflows/smoke-ci.lock.yml index 274d11da2c7..9aa2bfbb703 100644 --- a/.github/workflows/smoke-ci.lock.yml +++ b/.github/workflows/smoke-ci.lock.yml @@ -677,10 +677,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -815,10 +812,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -860,10 +854,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -894,7 +885,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" }, "update_pull_request": { "defaultMax": 1, diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 43206ca57e0..46a67672d87 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -977,10 +977,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 92cf8b79dd2..13c8afe0885 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -776,10 +776,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -940,10 +937,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -970,6 +964,14 @@ jobs: "set_issue_field": { "defaultMax": 5, "fields": { + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, "field_name": { "type": "string", "sanitize": true, @@ -982,10 +984,18 @@ jobs: "issue_number": { "issueOrPRNumber": true }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 1024 + }, "repo": { "type": "string", "maxLength": 256 }, + "suggest": { + "type": "boolean" + }, "value": { "required": true, "type": "string", diff --git a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml index 85b580b16c7..d3f5153e6fb 100644 --- a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml @@ -894,10 +894,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1113,10 +1110,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1165,6 +1159,14 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, "issue_number": { "issueOrPRNumber": true }, @@ -1174,9 +1176,17 @@ jobs: "sanitize": true, "maxLength": 128 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 1024 + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/smoke-copilot-aoai-entra.lock.yml b/.github/workflows/smoke-copilot-aoai-entra.lock.yml index ca99355a3aa..afa56dc2fec 100644 --- a/.github/workflows/smoke-copilot-aoai-entra.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-entra.lock.yml @@ -895,10 +895,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1114,10 +1111,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1166,6 +1160,14 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, "issue_number": { "issueOrPRNumber": true }, @@ -1175,9 +1177,17 @@ jobs: "sanitize": true, "maxLength": 128 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 1024 + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml index 55e1426b592..d91f9aabb5c 100644 --- a/.github/workflows/smoke-copilot-arm.lock.yml +++ b/.github/workflows/smoke-copilot-arm.lock.yml @@ -827,10 +827,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1021,10 +1018,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 1ce4605a52c..da29d39a317 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -899,10 +899,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1118,10 +1115,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -1170,6 +1164,14 @@ jobs: "set_issue_type": { "defaultMax": 5, "fields": { + "confidence": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, "issue_number": { "issueOrPRNumber": true }, @@ -1179,9 +1181,17 @@ jobs: "sanitize": true, "maxLength": 128 }, + "rationale": { + "type": "string", + "sanitize": true, + "maxLength": 1024 + }, "repo": { "type": "string", "maxLength": 256 + }, + "suggest": { + "type": "boolean" } } }, diff --git a/.github/workflows/smoke-crush.lock.yml b/.github/workflows/smoke-crush.lock.yml index 0868db8e677..ddfc57139b9 100644 --- a/.github/workflows/smoke-crush.lock.yml +++ b/.github/workflows/smoke-crush.lock.yml @@ -656,10 +656,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml index 1faeca269d5..fb7971fae31 100644 --- a/.github/workflows/smoke-gemini.lock.yml +++ b/.github/workflows/smoke-gemini.lock.yml @@ -722,10 +722,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index 9284f17e228..28d6a6f8323 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -660,10 +660,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-pi.lock.yml b/.github/workflows/smoke-pi.lock.yml index fe6c2b4edfd..22fba870718 100644 --- a/.github/workflows/smoke-pi.lock.yml +++ b/.github/workflows/smoke-pi.lock.yml @@ -690,10 +690,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml index eaa9f8cc137..29a90a3ffe0 100644 --- a/.github/workflows/smoke-project.lock.yml +++ b/.github/workflows/smoke-project.lock.yml @@ -700,10 +700,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -893,10 +890,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/stale-pr-cleanup.lock.yml b/.github/workflows/stale-pr-cleanup.lock.yml index e4834af998a..de691618b9a 100644 --- a/.github/workflows/stale-pr-cleanup.lock.yml +++ b/.github/workflows/stale-pr-cleanup.lock.yml @@ -587,10 +587,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml index 12a2b0dc023..46654bc065d 100644 --- a/.github/workflows/sub-issue-closer.lock.yml +++ b/.github/workflows/sub-issue-closer.lock.yml @@ -673,10 +673,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -707,7 +704,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index 3cbec36f5eb..9f7aa80b554 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -712,10 +712,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -746,7 +743,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml index 8862d67728c..43c7c5283ab 100644 --- a/.github/workflows/workflow-health-manager.lock.yml +++ b/.github/workflows/workflow-health-manager.lock.yml @@ -746,10 +746,7 @@ jobs: "issueOrPRNumber": true }, "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "milestone": { "optionalPositiveInteger": true @@ -780,7 +777,7 @@ jobs: "maxLength": 128 } }, - "customValidation": "requiresOneOf:status,title,body" + "customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone" } } uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 From f26e2dbd4fa8fd0842f999bbfe20b8761a54a985 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 20:27:08 +0000 Subject: [PATCH 07/10] Fix JS confidence typing for issue intent label metadata Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/issue_intents.cjs | 7 ++++--- actions/setup/js/safe_output_type_validator.cjs | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/actions/setup/js/issue_intents.cjs b/actions/setup/js/issue_intents.cjs index e004333f006..f0cbc52bb21 100644 --- a/actions/setup/js/issue_intents.cjs +++ b/actions/setup/js/issue_intents.cjs @@ -32,11 +32,12 @@ function normalizeIssueIntentMetadata(source) { } if (source.confidence !== undefined && source.confidence !== null && source.confidence !== "") { - const confidence = String(source.confidence).trim().toUpperCase(); - if (!ISSUE_INTENT_CONFIDENCE_VALUES.has(confidence)) { + const confidenceRaw = String(source.confidence).trim().toUpperCase(); + if (!ISSUE_INTENT_CONFIDENCE_VALUES.has(confidenceRaw)) { throw new Error(`Invalid confidence ${JSON.stringify(source.confidence)}. Expected one of: LOW, MEDIUM, HIGH.`); } - metadata.confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidence; + const confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidenceRaw; + metadata.confidence = confidence; } if (source.suggest !== undefined) { diff --git a/actions/setup/js/safe_output_type_validator.cjs b/actions/setup/js/safe_output_type_validator.cjs index 3c7fd5ccd79..731a89d95b4 100644 --- a/actions/setup/js/safe_output_type_validator.cjs +++ b/actions/setup/js/safe_output_type_validator.cjs @@ -144,14 +144,15 @@ function validateIssueIntentLabels(value, lineNum, itemType, fieldName, options) } if (label.confidence !== undefined) { if (label.confidence !== null && label.confidence !== "") { - const confidence = String(label.confidence).trim().toUpperCase(); - if (!ISSUE_INTENT_LABEL_CONFIDENCE_VALUES.has(confidence)) { + const confidenceRaw = String(label.confidence).trim().toUpperCase(); + if (!ISSUE_INTENT_LABEL_CONFIDENCE_VALUES.has(confidenceRaw)) { return { isValid: false, error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].confidence must be one of: LOW, MEDIUM, HIGH`, }; } - normalizedLabel.confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidence; + const confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidenceRaw; + normalizedLabel.confidence = confidence; } } if (label.suggest !== undefined) { From d1121a14aa5084b82071c5e6481394e4c2f04474 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:06:38 +0000 Subject: [PATCH 08/10] Fix confidence narrowing in issue-intent JS helpers Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/issue_intents.cjs | 2 +- actions/setup/js/safe_output_type_validator.cjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/setup/js/issue_intents.cjs b/actions/setup/js/issue_intents.cjs index f0cbc52bb21..ad1a9b56f21 100644 --- a/actions/setup/js/issue_intents.cjs +++ b/actions/setup/js/issue_intents.cjs @@ -36,7 +36,7 @@ function normalizeIssueIntentMetadata(source) { if (!ISSUE_INTENT_CONFIDENCE_VALUES.has(confidenceRaw)) { throw new Error(`Invalid confidence ${JSON.stringify(source.confidence)}. Expected one of: LOW, MEDIUM, HIGH.`); } - const confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidenceRaw; + const confidence = confidenceRaw === "LOW" ? "LOW" : confidenceRaw === "MEDIUM" ? "MEDIUM" : "HIGH"; metadata.confidence = confidence; } diff --git a/actions/setup/js/safe_output_type_validator.cjs b/actions/setup/js/safe_output_type_validator.cjs index 731a89d95b4..8c34b4ab25e 100644 --- a/actions/setup/js/safe_output_type_validator.cjs +++ b/actions/setup/js/safe_output_type_validator.cjs @@ -151,7 +151,7 @@ function validateIssueIntentLabels(value, lineNum, itemType, fieldName, options) error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].confidence must be one of: LOW, MEDIUM, HIGH`, }; } - const confidence = /** @type {"LOW"|"MEDIUM"|"HIGH"} */ confidenceRaw; + const confidence = confidenceRaw === "LOW" ? "LOW" : confidenceRaw === "MEDIUM" ? "MEDIUM" : "HIGH"; normalizedLabel.confidence = confidence; } } From e96c6bc576084dc609f3adc0f582b588af1afe65 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:08:49 +0000 Subject: [PATCH 09/10] Harden confidence narrowing in issue-intent typecheck paths Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/issue_intents.cjs | 16 +++++++++++++++- .../setup/js/safe_output_type_validator.cjs | 19 ++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/actions/setup/js/issue_intents.cjs b/actions/setup/js/issue_intents.cjs index ad1a9b56f21..45ab9ff4d98 100644 --- a/actions/setup/js/issue_intents.cjs +++ b/actions/setup/js/issue_intents.cjs @@ -36,7 +36,21 @@ function normalizeIssueIntentMetadata(source) { if (!ISSUE_INTENT_CONFIDENCE_VALUES.has(confidenceRaw)) { throw new Error(`Invalid confidence ${JSON.stringify(source.confidence)}. Expected one of: LOW, MEDIUM, HIGH.`); } - const confidence = confidenceRaw === "LOW" ? "LOW" : confidenceRaw === "MEDIUM" ? "MEDIUM" : "HIGH"; + /** @type {"LOW"|"MEDIUM"|"HIGH"} */ + let confidence; + switch (confidenceRaw) { + case "LOW": + confidence = "LOW"; + break; + case "MEDIUM": + confidence = "MEDIUM"; + break; + case "HIGH": + confidence = "HIGH"; + break; + default: + throw new Error(`Invalid confidence ${JSON.stringify(source.confidence)}. Expected one of: LOW, MEDIUM, HIGH.`); + } metadata.confidence = confidence; } diff --git a/actions/setup/js/safe_output_type_validator.cjs b/actions/setup/js/safe_output_type_validator.cjs index 8c34b4ab25e..3c68d68b0c7 100644 --- a/actions/setup/js/safe_output_type_validator.cjs +++ b/actions/setup/js/safe_output_type_validator.cjs @@ -151,7 +151,24 @@ function validateIssueIntentLabels(value, lineNum, itemType, fieldName, options) error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].confidence must be one of: LOW, MEDIUM, HIGH`, }; } - const confidence = confidenceRaw === "LOW" ? "LOW" : confidenceRaw === "MEDIUM" ? "MEDIUM" : "HIGH"; + /** @type {"LOW"|"MEDIUM"|"HIGH"} */ + let confidence; + switch (confidenceRaw) { + case "LOW": + confidence = "LOW"; + break; + case "MEDIUM": + confidence = "MEDIUM"; + break; + case "HIGH": + confidence = "HIGH"; + break; + default: + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].confidence must be one of: LOW, MEDIUM, HIGH`, + }; + } normalizedLabel.confidence = confidence; } } From 3ca9696cee23f6188d3eadbc979a1f77925c4466 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:11:03 +0000 Subject: [PATCH 10/10] Remove redundant confidence prechecks in issue-intent paths Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/issue_intents.cjs | 4 ---- actions/setup/js/safe_output_type_validator.cjs | 7 ------- 2 files changed, 11 deletions(-) diff --git a/actions/setup/js/issue_intents.cjs b/actions/setup/js/issue_intents.cjs index 45ab9ff4d98..056d4572caa 100644 --- a/actions/setup/js/issue_intents.cjs +++ b/actions/setup/js/issue_intents.cjs @@ -6,7 +6,6 @@ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs"); const { hasRuntimeFeature, parseRuntimeFeatures } = require("./runtime_features.cjs"); const ISSUE_INTENTS_FEATURE = "issue_intents"; -const ISSUE_INTENT_CONFIDENCE_VALUES = new Set(["LOW", "MEDIUM", "HIGH"]); const ISSUE_INTENT_RATIONALE_MAX_LENGTH = 1024; function hasIssueIntentsRuntimeFeature() { @@ -33,9 +32,6 @@ function normalizeIssueIntentMetadata(source) { if (source.confidence !== undefined && source.confidence !== null && source.confidence !== "") { const confidenceRaw = String(source.confidence).trim().toUpperCase(); - if (!ISSUE_INTENT_CONFIDENCE_VALUES.has(confidenceRaw)) { - throw new Error(`Invalid confidence ${JSON.stringify(source.confidence)}. Expected one of: LOW, MEDIUM, HIGH.`); - } /** @type {"LOW"|"MEDIUM"|"HIGH"} */ let confidence; switch (confidenceRaw) { diff --git a/actions/setup/js/safe_output_type_validator.cjs b/actions/setup/js/safe_output_type_validator.cjs index 3c68d68b0c7..fc377c08d0d 100644 --- a/actions/setup/js/safe_output_type_validator.cjs +++ b/actions/setup/js/safe_output_type_validator.cjs @@ -39,7 +39,6 @@ const ISSUE_CLOSING_KEYWORD_BACKTICK_PATTERN = new RegExp(`\`(\\b(?:${ISSUE_CLOS const ISSUE_CLOSING_REFERENCE_BACKTICK_PATTERN = new RegExp(`(\\b(?:${ISSUE_CLOSING_KEYWORDS})\\b)(\\s+)\`(${ISSUE_REFERENCE_PATTERN})\``, "gi"); const NORMALIZE_CLOSER_BODY_TYPES = new Set(["create_issue", "add_comment", "create_pull_request"]); const ISSUE_INTENT_LABEL_TYPES = new Set(["add_labels", "remove_labels", "update_issue"]); -const ISSUE_INTENT_LABEL_CONFIDENCE_VALUES = new Set(["LOW", "MEDIUM", "HIGH"]); /** * Remove markdown backticks around recognized issue-closing keyword references. @@ -145,12 +144,6 @@ function validateIssueIntentLabels(value, lineNum, itemType, fieldName, options) if (label.confidence !== undefined) { if (label.confidence !== null && label.confidence !== "") { const confidenceRaw = String(label.confidence).trim().toUpperCase(); - if (!ISSUE_INTENT_LABEL_CONFIDENCE_VALUES.has(confidenceRaw)) { - return { - isValid: false, - error: `Line ${lineNum}: ${itemType} ${fieldName}[${i}].confidence must be one of: LOW, MEDIUM, HIGH`, - }; - } /** @type {"LOW"|"MEDIUM"|"HIGH"} */ let confidence; switch (confidenceRaw) {