Skip to content

Keep assign_to_agent failures from failing safe_outputs - #36112

Merged
pelikhan merged 4 commits into
mainfrom
copilot/aw-failures-fix-safe-outputs-job
May 31, 2026
Merged

pelikhan merged 4 commits into
mainfrom
copilot/aw-failures-fix-safe-outputs-job

Conversation

Copilot AI commented May 31, 2026

Copy link
Copy Markdown
Contributor

safe_outputs could fail after already applying successful items when a later assign_to_agent step hit a token/permission error. In practice this left runs in a partial-success state: issues were created, assignment failed, and the whole job concluded failure.

  • Failure classification

    • Treat assign_to_agent handler failures as report-only in the safe output handler manager.
    • Preserve existing behavior for all other safe output failures.
  • Job outcome semantics

    • Exclude assign_to_agent failures from the fatal safe_outputs failure count.
    • Continue surfacing them through the existing assignment error outputs and summaries so downstream failure reporting still has the signal.
  • Focused coverage

    • Add unit coverage for:
      • active vs skipped/deferred/cancelled failure classification
      • partitioning fatal failures from report-only assignment failures
const { fatalFailures, reportOnlyFailures } = partitionFailureResults(results);

// assign_to_agent failures are reported...
reportOnlyFailures.filter(r => r.type === "assign_to_agent");

// ...but do not fail the safe_outputs job
if (fatalFailures.length > 0) {
  core.setFailed(/* ... */);
}

Copilot AI and others added 3 commits May 31, 2026 12:28
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix safe_outputs job failure due to insufficient permissions Keep assign_to_agent failures from failing safe_outputs May 31, 2026
Copilot AI requested a review from pelikhan May 31, 2026 12:31
@pelikhan
pelikhan marked this pull request as ready for review May 31, 2026 12:33
Copilot AI review requested due to automatic review settings May 31, 2026 12:33

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 updates safe output handling so assign_to_agent failures are reported without causing the overall safe_outputs job to fail, while preserving fatal behavior for other safe output failures.

Changes:

  • Adds helper functions to classify failed processing results and partition fatal vs report-only failures.
  • Updates main() failure handling to exclude assign_to_agent failures from core.setFailed.
  • Adds focused unit tests for failure classification and partitioning behavior.
Show a summary per file
File Description
actions/setup/js/safe_output_handler_manager.cjs Adds failure classification helpers and applies report-only handling for assignment failures.
actions/setup/js/safe_output_handler_manager.test.cjs Adds tests covering active/skipped/deferred/cancelled failure classification and partitioning.

Copilot's findings

Tip

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

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

@pelikhan
pelikhan merged commit 858e064 into main May 31, 2026
22 of 28 checks passed
@pelikhan
pelikhan deleted the copilot/aw-failures-fix-safe-outputs-job branch May 31, 2026 12:39
@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

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

No ADR enforcement needed: PR #36112 does not have the 'implementation' label and has 0 new lines of code in business logic directories (≤100 threshold). Pre-fetched summary: has_implementation_label=false, requires_adr_by_default_volume=false, default_business_additions=0.

@github-actions github-actions Bot mentioned this pull request May 31, 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.

One non-blocking issue flagged (PR already merged).

### Findings

Medium: abstraction inconsistency in partitionFailureResults — the function re-implements the r?.type === "assign_to_agent" predicate inline instead of delegating to isReportOnlyFailureResult. If a second report-only handler type is ever added to isReportOnlyFailureResult, partitionFailureResults will silently disagree. The fix is one line: replace the inline filter predicates with calls to the exported function.

Everything else (classification logic, job-outcome semantics, test coverage for the new paths) looks correct.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 770K

* @returns {{fatalFailures: Array<any>, reportOnlyFailures: Array<any>}}
*/
function partitionFailureResults(results) {
const failedResults = results.filter(isFailedProcessingResult);

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.

partitionFailureResults duplicates the type check instead of calling isReportOnlyFailureResult, creating a maintenance trap where the two can silently diverge.

💡 Suggested fix

Replace the inline re-implementation:

// current — two separate places define what "report-only" means
const reportOnlyFailures = failedResults.filter(r => r?.type === "assign_to_agent");
const fatalFailures      = failedResults.filter(r => r?.type !== "assign_to_agent");

With a call to the already-exported predicate:

const reportOnlyFailures = failedResults.filter(isReportOnlyFailureResult);
const fatalFailures      = failedResults.filter(r => !isReportOnlyFailureResult(r));

Now there is a single source of truth: adding a second report-only type to isReportOnlyFailureResult automatically updates the partitioning, and the relationship between the two functions is explicit and verifiable.

@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.

Skills-Based Review 🧠

Applied /diagnose and /tdd — approving with minor suggestions.

📋 Key Themes & Highlights

Key Themes

  • DRY opportunity: partitionFailureResults inlines the assign_to_agent type check rather than delegating to isReportOnlyFailureResult, creating a latent drift risk if new report-only types are added later.
  • Test coverage gaps: the helpers' null/undefined guard cases and the negative path of isReportOnlyFailureResult (non-assign type, successful assign) are untested.

Positive Highlights

  • ✅ Clean extraction of three pure helpers with clear JSDoc — easy to read and reason about
  • core.setFailed is now gated only on fatalFailures, which is the right fix
  • ✅ Assignment failures are still surfaced via core.warning so downstream signal is preserved
  • ✅ Focused unit tests cover the happy path and skipped/deferred/cancelled exclusions well

The core fix is correct and well-structured. The inline comments flag small improvements that would tighten the test contract and remove the one duplication.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 1.6M

return { fatalFailures, reportOnlyFailures };
}

/**

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] partitionFailureResults duplicates the assign_to_agent type check instead of reusing isReportOnlyFailureResult — a DRY violation that could cause drift if a second report-only type is ever added.

💡 Suggested refactor
function partitionFailureResults(results) {
  const failedResults = results.filter(isFailedProcessingResult);
  const reportOnlyFailures = failedResults.filter(isReportOnlyFailureResult);
  return { fatalFailures, reportOnlyFailures };
}

This way the single definition of what counts as report-only lives in isReportOnlyFailureResult and partitionFailureResults is just composition.

describe("report-only assignment failures", () => {
it("recognizes only active failures as failed processing results", () => {
expect(isFailedProcessingResult({ success: false })).toBe(true);
expect(isFailedProcessingResult({ success: false, deferred: true })).toBe(false);

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 isFailedProcessingResult tests don't verify null/undefined guard cases, even though the implementation uses optional chaining (result?.) for that purpose.

💡 Suggested additions
expect(isFailedProcessingResult(null)).toBe(false);
expect(isFailedProcessingResult(undefined)).toBe(false);
expect(isFailedProcessingResult({ success: true })).toBe(false);

These pin the null-safety contract so a future refactor that drops optional chaining is caught immediately.

})
).toBe(true);
});

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] Missing a complementary test: isReportOnlyFailureResult should return false for a non-assign_to_agent failure (e.g. create_issue). Without it, the positive-only coverage could mask a bug where all failures are treated as report-only.

💡 Suggested addition
it('does not treat non-assign_to_agent failures as report-only', () => {
  expect(isReportOnlyFailureResult({ type: 'create_issue', success: false })).toBe(false);
  expect(isReportOnlyFailureResult({ type: 'assign_to_agent', success: true })).toBe(false);
});

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 5 new test(s) in safe_output_handler_manager.test.cjs: 5 design tests (behavioral contracts), 0 implementation tests, 0 guideline violations.

📊 Metrics & Test Classification (5 tests analyzed)
Metric Value
New/modified tests analyzed 5
✅ Design tests (behavioral contracts) 5 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 5 (100%)
Duplicate test clusters 0
Test inflation detected No (ratio: 65/58 ≈ 1.12)
🚨 Coding-guideline violations 0

Test Classification Details

Test File Classification Issues Detected
logCreatedItemFromResult > should log finalized review results and skip buffered review metadata safe_output_handler_manager.test.cjs:61 ✅ Design Covers both buffered (no callback) and finalized (callback with full payload) paths
recognizes only active failures as failed processing results safe_output_handler_manager.test.cjs:98 ✅ Design 4 assertions covering deferred/skipped/cancelled edge cases
treats failed assign_to_agent results as report-only safe_output_handler_manager.test.cjs:105 ✅ Design Core behavioral contract for the PR's main fix
does not treat skipped or cancelled assign_to_agent results as report-only safe_output_handler_manager.test.cjs:114 ✅ Design 3 edge-case assertions (skipped, cancelled, deferred)
partitions fatal failures away from assign_to_agent report-only failures safe_output_handler_manager.test.cjs:138 ✅ Design End-to-end contract on partitionFailureResults with mixed result types

Language Support

Tests analyzed:

  • 🟨 JavaScript (*.test.cjs): 5 tests (vitest)

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All new tests directly verify the behavioral contract introduced by this PR — that assign_to_agent failures are treated as report-only rather than fatal failures.

📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 1.8M ·

@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.

✅ Test Quality Sentinel: 100/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All 5 new tests directly verify the behavioral contract for assign_to_agent report-only failure handling.

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.

[aw-failures] safe_outputs job fails when agent self-assignment is blocked (insufficient permissions/token) — LintMonster

3 participants