Skip to content

Preserve safe-output partial success and preflight dispatch inputs - #53264

Closed
pelikhan with Copilot wants to merge 5 commits into
mainfrom
copilot/aw-failures-fix-safe-outputs-job
Closed

Preserve safe-output partial success and preflight dispatch inputs#53264
pelikhan with Copilot wants to merge 5 commits into
mainfrom
copilot/aw-failures-fix-safe-outputs-job

Conversation

CopilotAI commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

A single non-retryable safe-output failure currently marks the entire batch failed even after other outputs succeed. Workflow dispatches also reach GitHub’s API before detecting missing required inputs.

  • Partial-success batches

    • Keep per-item failures in summaries and warnings.
    • Fail safe_outputs only when no item in the batch succeeded.
  • Dispatch workflow preflight

    • Extract required workflow_dispatch inputs during compilation.
    • Pass required-input metadata to the runtime handler.
    • Reject missing inputs before createWorkflowDispatch.
constmissingInputs=requiredInputsForWorkflow.filter(input=>!Object.hasOwn(inputs,input));if(missingInputs.length>0){return{success: false,error: `Workflow "${workflowName}" requires input: '${input}'`};}
  • Regression coverage
    • Covers mixed successful/failed safe-output batches.
    • Covers required dispatch input rejection without an API call.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 13.7 AIC · ⌖ 5.77 AIC · ⊞ 8.8K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix safe_outputs job to avoid hard-failing entire batch on errorsPreserve safe-output partial success and preflight dispatch inputsAug 17, 2026
CopilotAI requested a review from pelikhanAugust 17, 2026 01:55
@pelikhan
pelikhan marked this pull request as ready for review August 17, 2026 01:59
CopilotAI balanced review requested due to automatic review settings August 17, 2026 01:59
@github-actions

github-actionsBot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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

No ADR enforcement needed: PR #53264 does not have the 'implementation' label and has only 58 new lines of code in business logic directories (threshold: 100).

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actionsBot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actionsBot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actionsBot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Lean already. Ship.

Generated by Ponytail Reviewer for #53264

@github-actions

github-actionsBot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-08-17T02:02:00Z
review_event: REQUEST_CHANGES
top_themes:
- mixed-result safe-output batches now exit green after fatal failures
files_reviewed:
- actions/setup/js/dispatch_workflow.cjs
- actions/setup/js/dispatch_workflow.test.cjs
- actions/setup/js/safe_output_handler_manager.cjs
- actions/setup/js/safe_output_handler_manager.test.cjs
- pkg/workflow/dispatch_workflow.go
- pkg/workflow/safe_outputs_config_generation_test.go
- pkg/workflow/safe_outputs_dispatch.go
- pkg/workflow/safe_outputs_handler_registry.go
comment_count: 1

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · gpt54 · 14.7 AIC · ⌖ 7.31 AIC · ⊞ 6.9K ·
Comment /review to run again

@github-actionsgithub-actionsBot 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.

Request changes

This patch changes runtime semantics in a risky way: mixed-result safe-output batches now report success even when a fatal output failed, which suppresses the only hard failure signal downstream jobs can reliably key off.

Blocking theme
  • fatal safe-output failures are downgraded to warnings whenever any other item succeeded, so workflow conclusions, alerts, and retry logic lose the failure signal

🔎 Code quality review by PR Code Quality Reviewer · gpt54 · 14.7 AIC · ⌖ 7.31 AIC · ⊞ 6.9K
Comment /review to run again

const failedItemLines = fatalFailures.map(r => ` - ${r.type}: ${r.error || "Unknown error"}`);
const failedItems = failedItemLines.join("\n");
failedOutputsMessage = `${failureCount} safe output(s) failed:\n${failedItems}`;
if (shouldFailSafeOutputs(fatalFailures, safeOutputsStatus)) {

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.

This partial-success change masks real handler failures from the job result. Once any safe output succeeds, failedOutputsMessage stays null, so the workflow exits green even when another output hard-failed; that breaks downstream failure handling and makes retries or alerts impossible for mixed-result batches.

💡 Why this is blocking

The previous behavior failed the job whenever a fatal handler failure existed. After this patch, a batch like create_check_run succeeds and push_to_pull_request_branch fails will only emit a warning and then return success at line 1825. Any workflow logic watching the step conclusion now loses the failure signal completely.

If partial success is the intended product behavior, the step still needs a machine-readable failure indicator beyond log text — for example, preserving core.setFailed(...) for fatal failures while introducing a separate non-blocking mode behind explicit config, or at minimum exporting a dedicated output that downstream jobs already consume before changing the step conclusion semantics.

@github-actionsgithub-actionsBot 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.

Review

The two features are well-scoped and correctly implemented.

Partial success preservation (shouldFailSafeOutputs): The condition fatalFailures.length > 0 && status.itemsSucceeded === 0 correctly gates job failure on a fully-failed batch, so a batch where at least one item succeeds degrades gracefully. Both branches are covered by tests.

Preflight dispatch input validation:required_inputs is populated at compile time via requiredWorkflowDispatchInputs (sorted for determinism) and threaded through to the CJS handler. The Object.hasOwn guard is the correct idiom for input presence. Error messages are clear and actionable.

No blocking issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 22 AIC · ⌖ 7.91 AIC · ⊞ 5.6K

@github-actionsgithub-actionsBot mentioned this pull request Aug 17, 2026

CopilotAI 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

Addresses #53263 by preserving successful safe outputs and validating workflow-dispatch inputs before API calls.

Changes:

  • Treat mixed-result safe-output batches as partial successes.
  • Compile and validate required dispatch inputs.
  • Add regression tests for both behaviors.
Show a summary per file
FileDescription
pkg/workflow/safe_outputs_handler_registry.goSerializes required-input metadata.
pkg/workflow/safe_outputs_dispatch.goExtracts required dispatch inputs.
pkg/workflow/safe_outputs_config_generation_test.goTests generated metadata.
pkg/workflow/dispatch_workflow.goStores required-input configuration.
actions/setup/js/safe_output_handler_manager.test.cjsTests partial-success handling.
actions/setup/js/safe_output_handler_manager.cjsAvoids failing mixed-result batches.
actions/setup/js/dispatch_workflow.test.cjsTests preflight rejection.
actions/setup/js/dispatch_workflow.cjsRejects missing inputs before dispatch.

Review details

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment on lines +111 to +113
if isRequired, _ := definitionMap["required"].(bool); isRequired {
required = append(required, name)
}
github-actions[bot]
github-actionsBot previously requested changes Aug 17, 2026

@github-actionsgithub-actionsBot 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.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — requesting changes on a few correctness and test-coverage gaps.

📋 Key Themes & Highlights

Issues

  • Silent degradation on parse error (safe_outputs_dispatch.go): when workflowDispatchInputs errors, continue skips required-input population but the workflow is already registered in WorkflowFiles, so it can be dispatched without preflight.
  • required as string "true" (requiredWorkflowDispatchInputs): GitHub Actions YAML permits required: "true" (string). The current type-assertion .(bool) silently misses these inputs.
  • Double-reporting warning (dispatch_workflow.cjs): core.warning(error) is called before returning { success: false, error }. Callers that surface the returned error field will produce duplicate log entries.
  • Test coverage gaps: shouldFailSafeOutputs has a single it with two expect calls (harder to read failure output); the plural missing-inputs code path in dispatch_workflow has no test.

Positive Highlights

  • ✅ Clean extraction of workflowHasAwContextInput into the more general workflowDispatchInputs — reduces duplication and makes the aw_context check a special case of a more general pattern.
  • sort.Strings(required) in requiredWorkflowDispatchInputs ensures deterministic YAML output across compilations.
  • shouldFailSafeOutputs is correctly exported and unit-tested; the partial-success logic is easy to reason about.
  • ✅ Preflight check correctly short-circuits before the API call and returns an actionable error message.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 49.1 AIC · ⌖ 9.19 AIC · ⊞ 7.7K
Comment /matt to run again

// We check the lock file first (compiled YAML), falling back to the markdown frontmatter.
if workflowHasAwContextInput(fileResult, workflowName) {
inputs, err := workflowDispatchInputs(fileResult)
if err != nil {

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.

[/diagnosing-bugs] Silent degradation on input-extraction error: continue on line ~68 skips RequiredInputs population but WorkflowFiles[workflowName] is already written (~line 64), so the workflow stays dispatchable at runtime without required-input preflight.

💡 Suggested fix

Unlike aw_context, required inputs are not optional — silently skipping them can allow dispatches that miss mandatory fields. Consider logging explicitly that preflight will be skipped, or removing the workflow entry from WorkflowFiles:

inputs, err:=workflowDispatchInputs(fileResult)
iferr!=nil {
safeOutputsConfigLog.Printf(
"Warning: error extracting inputs for %s — required-input preflight will be skipped: %v",
workflowName, err,
)
// fallthrough: dispatch is still allowed, but required-input check is disabled
}

@copilot please address this.

expect(fatalFailures).toEqual([{ type: "create_issue", success: false, error: "Validation failed" }]);
});

it("does not fail the job when a batch has successful outputs and failed items", () => {

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.

[/tdd] The shouldFailSafeOutputs test covers only two boundary cases and uses a single it block with two expect calls. This makes it hard to see which case fails and leaves the "all items failed" edge case (itemsSucceeded: 0, zero fatal failures) untested.

💡 Suggested improvement

Split into named cases and add the zero-failure edge:

it("does not fail the job when at least one item succeeded",()=>{expect(shouldFailSafeOutputs([{type: "update_pull_request",success: false}],{itemsSucceeded: 1})).toBe(false);});it("fails the job when every item failed",()=>{expect(shouldFailSafeOutputs([{type: "update_pull_request",success: false}],{itemsSucceeded: 0})).toBe(true);});it("does not fail the job when there are no fatal failures",()=>{expect(shouldFailSafeOutputs([],{itemsSucceeded: 0})).toBe(false);});

The third case confirms an empty fatalFailures array always returns false, matching the && short-circuit in the implementation.

@copilot please address this.

if (missingInputs.length > 0) {
const error = `Workflow "${workflowName}" requires input${missingInputs.length === 1 ? "" : "s"}: ${missingInputs.map(input => `'${input}'`).join(", ")}. Provide ${missingInputs.length === 1 ? "it" : "them"} and retry.`;
core.warning(error);
return { success: false, error };

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.

[/diagnosing-bugs] The error string in the PR description body ('${input}') references the loop variable input (singular), but the implementation correctly iterates missingInputs and formats them. However, the bug remains in the error message template in the PR description — not in the code itself — so no code change is needed. Flagging for accuracy of the PR description only.

Actually, looking more carefully at the implementation: the error message is correct. But there's a subtle issue: core.warning(error) is called and then{ success: false, error } is returned. The caller will also likely log or surface the error. This double-reporting (warning + returned error object) may cause duplicate messages in the Actions log.

💡 Suggestion

If the caller already surfaces the returned error field, remove the core.warning(error) call here to avoid duplicate messages:

consterror=`Workflow "${workflowName}" requires input${...}: ${...}.Provide${...} and retry.`;// Let the caller handle surfacing — don't double-warn herereturn{success: false, error };

Check how other early-return failure paths in this function handle logging vs. returning errors.

@copilot please address this.

expect(result).toEqual({
success: false,
error: `Workflow "test-workflow" requires input: 'message'. Provide it and retry.`,
});

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.

[/tdd] The dispatch test verifies the single-missing-input case but not multiple missing inputs. The implementation builds a plural-aware error string (requires inputs: 'a', 'b'), but there is no test that asserts the plural form or the comma-separated list.

💡 Suggested test case
it("should fail with all missing input names when multiple required inputs are absent",async()=>{consthandler=awaitmain({workflows: ["multi-workflow"],workflow_files: {"multi-workflow": ".lock.yml"},required_inputs: {"multi-workflow": ["env","message"]},});constresult=awaithandler({type: "dispatch_workflow",workflow_name: "multi-workflow",inputs: {}},{});expect(result.success).toBe(false);expect(result.error).toMatch(/requiresinputs:/);expect(result.error).toContain("'env'");expect(result.error).toContain("'message'");expect(github.rest.actions.createWorkflowDispatch).not.toHaveBeenCalled();});

@copilot please address this.

for name, definition := range inputs {
definitionMap, ok := definition.(map[string]any)
if !ok {
continue

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.

[/diagnosing-bugs]requiredWorkflowDispatchInputs silently skips any input whose definition is not a map[string]any. YAML inputs whose required key is set to the string "true" (instead of boolean true) will also be silently skipped — GitHub Actions YAML allows this form.

💡 Suggestion

Add a fallback to handle string "true" for robustness:

funcrequiredWorkflowDispatchInputs(inputsmap[string]any) []string {
varrequired []stringforname, definition:=rangeinputs {
definitionMap, ok:=definition.(map[string]any)
if!ok {
continue
}
switchv:=definitionMap["required"].(type) {
casebool:
ifv {
required=append(required, name)
}
casestring:
ifv=="true" {
required=append(required, name)
}
}
}
sort.Strings(required)
returnrequired
}

Add a test case with required: "true" to confirm this edge case.

@copilot please address this.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot this PR is ready for the next finishing pass.

Please address these items, newest first:

_Run:

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 16.2 AIC · ⌖ 5.85 AIC · ⊞ 8.8K ·
Comment /souschef to run again

CopilotAIand others added 3 commits August 17, 2026 03:49
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
…ix-safe-outputs-job
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

CopilotAI commented Aug 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot this PR is ready for the next finishing pass.

Please address these items, newest first:...

Addressed in 9e3cfe9 after refreshing from origin/main.

  • Required dispatch-input preflight now ignores required inputs that define defaults and handles required: "true".
  • Removed duplicate warning emission for missing dispatch inputs and added plural missing-input coverage.
  • Expanded safe-output failure helper coverage for partial/all-failed/no-fatal cases.
  • Confirmed the listed failed checks were cache-restore misses before project code ran.
  • Validation: targeted Go + JS tests passed; make agent-report-progress passed after preparing the expected temp prompt fixtures; automated code review found no comments. CodeQL timed out and instructed not to rerun.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot this PR is ready for the next finishing pass.

Please address these items, newest first:

  • Failed reviews to address:
    • PR Code Quality Reviewer requested changes.
  • Refresh the branch with the latest base changes.
  • Run the pr-finisher skill after the fixes and summarize the outcome.

Run: https://github.com/github/gh-aw/actions/runs/31996014734

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 13.7 AIC · ⌖ 5.77 AIC · ⊞ 8.8K ·
Comment /souschef to run again

Sign up for freeto 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.

[aw-failures] safe_outputs job hard-fails entire batch on one non-retryable error

4 participants

@gh-aw-bot@pelikhan