Skip to content

Handle string-form create_issue.labels across safe-output validation and schema/tool definitions#38738

Open
Copilot wants to merge 3 commits into
mainfrom
copilot/aw-debug-workflow-failure
Open

Handle string-form create_issue.labels across safe-output validation and schema/tool definitions#38738
Copilot wants to merge 3 commits into
mainfrom
copilot/aw-debug-workflow-failure

Conversation

Copilot AI commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Daily Reliability Review runs were intermittently raising “No Safe Outputs Generated” even when the agent emitted a create_issue output. The failure path was a strict validator rejection when labels arrived as a comma-separated string instead of an array.

  • Root cause

    • safe_output_type_validator required create_issue.labels to be an array and rejected string input ("a,b"), causing the emitted safe output item to be dropped and the run to be reported as producing no safe outputs.
  • Change

    • Added a targeted compatibility normalization in array validation:
      • when validating labels
      • and the value is a string
      • split on commas, trim, and filter empties before normal array validation.
    • Updated create_issue tool schema/metadata to document and accept compatibility input:
      • labels now supports array | string in tool JSON/schema definitions
      • tool description now explicitly documents comma-separated string normalization.
    • Updated the safe outputs specification examples to reflect the same compatibility behavior.
  • Test coverage

    • Added a focused unit test asserting create_issue accepts labels: "reliability, telemetry" and normalizes to ["reliability", "telemetry"].
    • Ran targeted schema/validation tests to ensure schema consistency and tool JSON sync remain valid.
// safe_output_type_validator.cjs
if (fieldName === "labels" && typeof value === "string") {
  value = value
    .split(",")
    .map(item => item.trim())
    .filter(Boolean);
}

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Debug workflow failure in daily reliability review Handle string-form create_issue.labels in safe-output validation to prevent false “no safe outputs” Jun 11, 2026
Copilot AI requested a review from pelikhan June 11, 2026 23:52
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot Add this compatibility description to the specification and the tool description, and update the tool JSON to ensure it complies with the schema.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Add this compatibility description to the specification and the tool description, and update the tool JSON to ensure it complies with the schema.

Updated in cc6361c. I added the labels string-compatibility note to the safe outputs specification and create_issue tool description, and updated tool JSON/schema definitions to accept labels as array|string with normalization documented.

Copilot AI changed the title Handle string-form create_issue.labels in safe-output validation to prevent false “no safe outputs” Handle string-form create_issue.labels across safe-output validation and schema/tool definitions Jun 12, 2026
@pelikhan pelikhan marked this pull request as ready for review June 12, 2026 00:30
Copilot AI review requested due to automatic review settings June 12, 2026 00:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes intermittent “No Safe Outputs Generated” failures by making create_issue.labels tolerant to agents emitting labels as a comma-separated string, and keeping schema/tool docs aligned with that compatibility behavior.

Changes:

  • Allow create_issue.labels to be provided as either string[] or a comma-separated string in the JSON schemas/tool definitions.
  • Normalize comma-separated label strings into a trimmed string array during safe-output validation.
  • Add a unit test and update the safe-outputs specification examples to reflect the compatibility contract.
Show a summary per file
File Description
schemas/agent-output.json Expands create_issue.labels schema type to ["array","string"].
pkg/workflow/js/safe_outputs_tools.json Documents and allows labels as array | string for the create_issue tool schema.
pkg/workflow/js/safe_outputs_tools.json Documents and allows labels as array | string for the create_issue tool schema.
actions/setup/js/safe_outputs_tools.json Keeps runtime tool schema in sync with the compiler copy (adds array | string + docs).
docs/src/content/docs/specs/safe-outputs-specification.md Updates spec examples to show labels as ["array","string"] and documents compatibility.
actions/setup/js/safe_output_type_validator.test.cjs Adds a focused unit test for string-label normalization behavior.
actions/setup/js/safe_output_type_validator.cjs Adds normalization logic to split/trim/filter comma-separated label strings before array validation.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 6/6 changed files
  • Comments generated: 2

Comment thread schemas/agent-output.json
Comment on lines 79 to 83
"labels": {
"type": "array",
"type": ["array", "string"],
"description": "Optional labels to add to the issue",
"items": {
"type": "string"
Comment on lines +412 to +416
// Backward compatibility: agents sometimes provide comma-separated labels as a string.
// Normalize this into a string array before strict array validation.
if (fieldName === "labels" && typeof value === "string") {
value = value
.split(",")
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #38738 does not have the 'implementation' label (has_implementation_label=false) and has only 6 new lines of code in business logic directories (default_business_additions=6), well below the 100-line threshold. Neither Condition A nor Condition B is met.

@github-actions github-actions Bot mentioned this pull request Jun 12, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two correctness issues need fixing before merge

The backward-compat normalization for comma-separated labels is a sound idea, but two issues need to be addressed.

Blocking findings (2)

1. Schema–runtime mismatch for add_labels and create_pull_request (see inline comment on line 414)

The runtime normalises any labels field named "labels" regardless of tool type, so add_labels and create_pull_request silently accept strings too — but their tool schemas still declare type: "array". Either extend the schema update to cover those tools (preferred, since the behaviour is beneficial for all three), or restrict the normalisation to create_issue via an itemType guard.

2. Empty string bypasses required-field validation for add_labels (see inline comment on line 418)

labels: "" normalises to [], which passes Array.isArray and therefore satisfies the required: true constraint on add_labels.labels. The call then silently applies zero labels. Add a post-normalisation guard that returns an error when value.length === 0 && validation.required.

🔎 Code quality review by PR Code Quality Reviewer · 274.6 AIC · ⌖ 14.3 AIC · ⊞ 17.3K

if (validation.type === "array") {
// Backward compatibility: agents sometimes provide comma-separated labels as a string.
// Normalize this into a string array before strict array validation.
if (fieldName === "labels" && typeof value === "string") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normalization scope wider than schema update: this condition fires for every field named labels — including add_labels.labels (required) and create_pull_request.labels — but only create_issue's schema was updated to ["array", "string"]. The LLM still sees type: "array" for those tools yet the runtime silently converts string input.

💡 Details and fix options

Tools affected by the runtime normalisation but not by the schema update:

  • add_labels.labels — required field; safe_outputs_tools.json still declares type: "array"
  • create_pull_request.labels — optional field; same schema mismatch

The normalisation is arguably good behaviour for all three tools, but the inconsistency means:

  1. Agents that read the schema may be confused about which tools accept strings.
  2. A future engineer removing the create_issue branch might inadvertently break the hidden contract for the other tools.

Option A (prefer this): extend the schema update to add_labels and create_pull_request as well, and add tests for both.

Option B (narrower scope): restrict the normalisation to create_issue only by adding an itemType guard:

if (fieldName === "labels" && itemType === "create_issue" && typeof value === "string") {

value = value
.split(",")
.map(item => item.trim())
.filter(Boolean);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty-string input silently produces an empty array and passes required-field validation: labels: ""split(",")[""]filter(Boolean)[]. Array.isArray([]) is true, so add_labels.labels (which is required: true) will pass validation with an empty labels list.

💡 Impact and fix

An add_labels call with labels: "" passes the JS validator but then silently does nothing at the GitHub API level (zero labels to add). The operator sees no error but no label is applied.

The fix is to reject normalised-empty results when the field is required:

if (fieldName === "labels" && typeof value === "string") {
  value = value.split(",").map(item => item.trim()).filter(Boolean);
  // Guard: empty string must not bypass required validation
  if (value.length === 0 && validation.required) {
    return {
      isValid: false,
      error: `Line ${lineNum}: ${itemType} requires a non-empty '${fieldName}' field`,
    };
  }
}

Alternatively, add a minItems: 1 check to the general array validation path for any required array field.

expect(Array.isArray(result.normalizedItem.labels)).toBe(true);
});

it("should normalize comma-separated labels string to array", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage gap: only create_issue with a two-element comma string is tested, leaving undocumented (and unverified) the behaviour for the other two tools that share the same code path.

💡 Suggested additions

Add at least these cases:

// Verify the normalization is intentional for add_labels
it("should normalize comma-separated labels string for add_labels", async () => {
  const { validateItem } = await import("./safe_output_type_validator.cjs");
  const result = validateItem({ type: "add_labels", labels: "bug, enhancement" }, "add_labels", 1);
  expect(result.isValid).toBe(true);
  expect(result.normalizedItem.labels).toEqual(["bug", "enhancement"]);
});

// Guard against empty-string silent pass for a required array field
it("should reject empty string for required labels field", async () => {
  const { validateItem } = await import("./safe_output_type_validator.cjs");
  const result = validateItem({ type: "add_labels", labels: "" }, "add_labels", 1);
  expect(result.isValid).toBe(false); // fails today — empty array passes required check
});

The second test will fail with the current code, surfacing the empty-string issue noted on the normalization block.

@github-actions

Copy link
Copy Markdown
Contributor

``
@copilot review all comments and address unresolved review feedback.

Generated by 👨‍🍳 PR Sous Chef · 33.4 AIC · ⌖ 1.05 AIC · ⊞ 17.3K ·

@github-actions

Copy link
Copy Markdown
Contributor

``
@copilot refresh the branch and rerun checks, then summarize any remaining blockers.

Generated by 👨‍🍳 PR Sous Chef · 33.4 AIC · ⌖ 1.05 AIC · ⊞ 17.3K ·

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants