Skip to content

Fix safe-output summary outcome classification - #51484

Merged
dsyme merged 6 commits into
mainfrom
copilot/fix-safe-output-run-summary
Aug 9, 2026
Merged

Fix safe-output summary outcome classification#51484
dsyme merged 6 commits into
mainfrom
copilot/fix-safe-output-run-summary

Conversation

Copilot AI commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Safe-output run summaries could render expected skips as failures, count skipped no-ops as successes, and omit non-success outcome counts. This made policy skips hard to diagnose and caused aggregate counts to disagree with per-item details.

  • Shared outcome semantics

    • Added a normalized classifier for delegated, cancelled, deferred, skipped, warning, success, and failed results.
    • Reused it for aggregate status, failure partitioning, and detail rendering.
  • Outcome-aware summaries

    • Render skipped items as Skipped, not Failed.
    • Count applied, skipped, warnings, failed, cancelled, and deferred items separately.
    • Add a compact grouped overview before collapsible per-item details.
  • Safe diagnostics

    • Preserve handler-provided reasonCode, reason, target, and safeDetails.
    • Add structured required-label diagnostics for add_comment and add_labels.
Status: **completed_with_skips**

Applied: **5** · Skipped: **7** · Warnings: **0** · Failed: **0** · Cancelled: **0** · Deferred: **0**

| Outcome | Type | Count | Reason |
|---|---|---:|---|
| Skipped | Add Comment | 3 | Required labels missing |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

PR Triage

  • Category: chore (WIP)
  • Risk: low (0 files changed yet — PR just opened, agent still forming a plan)
  • Score: 15/100 (impact 5, urgency 5, quality 5)
  • CI: copilot check in_progress
  • Recommendation: defer — no diff yet; re-triage once the agent pushes commits and the PR leaves draft/WIP state.

Generated by 🔧 PR Triage Agent · auto · 51 AIC · ⌖ 2.57 AIC · ⊞ 8K ·

Co-authored-by: dsyme <7204669+dsyme@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix safe-output run summary misclassifications Fix safe-output summary outcome classification Aug 9, 2026
Copilot AI requested a review from dsyme August 9, 2026 00:46
@dsyme
dsyme marked this pull request as ready for review August 9, 2026 00:52
Copilot AI balanced review requested due to automatic review settings August 9, 2026 00:52
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Generated by Ponytail Reviewer for #51484

@github-actions

github-actions Bot commented Aug 9, 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-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • api.individual.githubcopilot.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "api.individual.githubcopilot.com"

See Network Configuration for more information.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

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

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (default_business_additions: 0).

🏗️ ADR gate enforced by Design Decision Gate 🏗️

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

Fixes safe-output outcome classification and diagnostics for skipped and non-successful operations.

Changes:

  • Adds shared outcome classification and aggregate counts.
  • Adds grouped summaries and structured diagnostics.
  • Enriches comment and label policy-skip results.
Show a summary per file
File Description
actions/setup/js/types/handler-factory.d.ts Expands handler result diagnostics.
actions/setup/js/safe_outputs_status.cjs Adds normalized outcome classification.
actions/setup/js/safe_output_summary.test.cjs Tests outcome-aware summaries.
actions/setup/js/safe_output_summary.cjs Renders counts, overview, and diagnostics.
actions/setup/js/safe_output_handler_manager.test.cjs Tests status outputs and preserved diagnostics.
actions/setup/js/safe_output_handler_manager.cjs Preserves skipped-result metadata and exports counts.
actions/setup/js/add_labels.cjs Adds structured policy-skip details.
actions/setup/js/add_comment.cjs Adds structured skip reasons and targets.

Review details

Tip

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

Suppressed comments (2)

actions/setup/js/safe_output_handler_manager.cjs:933

  • Diagnostic preservation currently applies only to skipped results. The ordinary success: false branch immediately below still drops the handler result, so the newly declared reasonCode, reason, target, and safeDetails fields never reach summaries for actual failures. Preserve the nested result there as well.
          ...(result.reasonCode ? { reasonCode: result.reasonCode } : {}),
          ...(result.errorCode ? { errorCode: result.errorCode } : {}),
          error: msg,
          result,

actions/setup/js/safe_output_summary.cjs:476

  • Manager-generated cancellations keep diagnostics on the processing record rather than in result.result (for example, threat aborts at safe_output_handler_manager.cjs:782-790). Passing only the nested value here makes the detail show UNCLASSIFIED instead of the available errorCode. Merge the wrapper and object-valued handler result before rendering, while preserving array results.
      result: result.result,
  • Files reviewed: 7/8 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment thread actions/setup/js/safe_outputs_status.cjs
Comment thread actions/setup/js/safe_output_summary.cjs Outdated
Comment thread actions/setup/js/safe_output_handler_manager.cjs
Comment thread actions/setup/js/safe_output_summary.cjs Outdated

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

Ponytail review: 1 finding.

net: -20 lines possible.

Generated by ✂️ Ponytail Reviewer for #51484 · auto · 32.3 AIC · ⌖ 4.25 AIC · ⊞ 6.8K
Comment /ponytail to run again

Comment thread actions/setup/js/safe_output_handler_manager.cjs

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

The classification logic and outcome precedence in classifySafeOutputResult is correct. The fix properly handles success:true, skipped:true results (TARGET_NOT_FOUND, TARGET_LOCKED) as skipped rather than success. Well-tested with appropriate coverage.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 37.7 AIC · ⌖ 6.77 AIC · ⊞ 5.5K

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

PR #51484 — "Fix safe-output summary outcome classification"
Actor: dsyme | Repository: github/gh-aw


Score: 100/100 ✅ Excellent

Metric Result Status
Design tests 5 / 5 (100%)
Edge cases 5 / 5 (100%)
Test inflation 1.23:1
Violations 0

Coverage Highlights

✅ New Behavioral Tests (5 Total)

Click to expand test classifications
# Test Name File Design Invariant Value Type
1 "computes skipped and warning counts without counting them as applied mutations" safe_output_handler_manager.test.cjs Status field classification integrity high_value design_test
2 "preserves summary-safe diagnostics from skipped handler results" safe_output_handler_manager.test.cjs Diagnostic data preservation through pipeline high_value design_test
3 "renders skipped policy diagnostics without classifying the item as failed" safe_output_summary.test.cjs Skipped outcome visual classification high_value design_test
4 "renders success true skipped warning outcomes as skipped rather than success" safe_output_summary.test.cjs Priority classification rule (skipped > success) high_value design_test
5 "writes aggregate counts and grouped overview matching per-item classifications" safe_output_summary.test.cjs End-to-end summary aggregation & grouping high_value design_test

📊 Line Count Analysis

File Test Δ Production Δ Ratio Status
safe_output_handler_manager +64 +39 1.64:1 ✅ Acceptable
safe_output_summary +136 +123 1.11:1 ✅ Excellent
Total +200 +162 1.23:1 ✅ Excellent

Test Quality Analysis

Strengths ✅

  1. 100% behavioral contracts — All new tests verify design invariants, not implementation details.

  2. Comprehensive edge-case coverage:

    • Mixed outcome types (success, skipped, failed, deferred, cancelled)
    • Contradictory state flags (success=true + skipped=true)
    • Diagnostic preservation & rendering
    • Multi-type aggregation in one summary
  3. Integration test included (Test Add workflow: githubnext/agentics/weekly-research #5) — Verifies all outcome types work together end-to-end.

  4. Good assertion variety:

    • Structure matching: toMatchObject, toEqual
    • Text patterns: toContain, not.toContain
    • Mock call verification: toHaveBeenCalledWith
  5. Excellent test inflation — 1.23:1 is well below the 2:1 threshold.

  6. No mock library violations — Only vitest mocks and vi.fn() stubs.

  7. Clear organization — Tests logically group related scenarios.

Test Maintenance

Existing tests were updated to verify new output fields:

  • computeSafeOutputsStatus tests now assert: itemsApplied, itemsSkipped, itemsWarnings, itemsCancelled, itemsDeferred
  • setSafeOutputsStatusOutputs test verifies all new output bindings
  • writeSafeOutputSummaries test checks new aggregate count format

This represents proper test maintenance for expanded APIs.


Verdict

APPROVE — This PR demonstrates excellent test quality:

  • 100% design tests, 0% implementation tests
  • All tests cover critical business logic invariants
  • Comprehensive edge-case scenarios
  • Healthy test inflation ratio
  • No coding violations

No issues flagged.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 26.5 AIC · ⌖ 3.02 AIC · ⊞ 7.7K ·
Comment /review to run again

@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. 5 design tests (100%), all cover critical business logic invariants. Test inflation 1.23:1 is excellent. No violations.

@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 /diagnosing-bugs, /tdd, and /codebase-design — requesting changes on three correctness issues and one test-assertion gap.

📋 Key Themes & Highlights

Key Themes

  • Delegated-skip heuristic is fragile (safe_outputs_status.cjs line 32): the three-condition implicit test will misclassify any new handler that returns { skipped: true, reason: '...' } without a nested result. An explicit delegated: true flag at the source would be safer.
  • skippedHandlerResults log bucket is broken for new code paths (safe_output_handler_manager.cjs line 1645): the existing !r.reason filter excludes all newly-enriched policy-skipped results, so the Skipped (no context or limit reached) log count will be wrong.
  • safeDetails rendering is coupled to handler-specific field names (safe_output_summary.cjs line 241): hardcoded requiredLabels/missingLabels keys mean new handlers with different shapes silently produce no diagnostics.
  • warning/skipped precedence (safe_outputs_status.cjs line 35): success: true, skipped: true, warning outcomes count as skips, not warnings — intent should be documented or the precedence adjusted.

Positive Highlights

  • classifySafeOutputResult is a clean centralization of outcome logic — shared by status computation, summary rendering, and aggregation.
  • ✅ Structured reasonCode / reason / target / safeDetails fields make diagnostics auditable without leaking raw exception text.
  • ✅ New tests cover the key regression cases (policy-skip rendered as Skipped, grouped overview counts, diagnostic preservation).
  • ✅ The generateOutcomeOverview grouped table is a nice UX improvement for bulk runs.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 86.2 AIC · ⌖ 6.95 AIC · ⊞ 7.1K
Comment /matt to run again

Comments that could not be inline-anchored

actions/setup/js/safe_output_handler_manager.cjs:1645

[/diagnosing-bugs] skippedHandlerResults filter uses !r.reason, but handler-skipped results now propagate reason into the processing record — so policy-skipped results (e.g. REQUIRED_LABELS_MISMATCH) will be excluded from this log bucket and the Skipped (no context or limit reached) count will be underreported.

<details>
<summary>💡 Suggested fix</summary>

Replace the !r.reason exclusion with explicit delegation-reason checks:

const skippedHandlerResults = processingRes…

</details>

<details><summary>actions/setup/js/safe_outputs_status.cjs:32</summary>

**[/diagnosing-bugs]** The `&quot;delegated&quot;` heuristic (`result?.skipped &amp;&amp; result?.reason &amp;&amp; result?.result === undefined`) is fragile: any new handler that returns `skipped: true` plus a `reason` string but omits a nested `result` object will be silently classified as delegated and excluded from all counts.

&lt;details&gt;
&lt;summary&gt;💡 Suggested hardening&lt;/summary&gt;

Delegated skips are synthetic records created by a specific code path in the handler manager. Consider tagging them explicitly at the sour…

</details>

<details><summary>actions/setup/js/safe_output_summary.cjs:241</summary>

**[/codebase-design]** `safeDetails` is decoded in `formatOutcomeDiagnostics` with hardcoded field access (`safeDetails?.requiredLabels`, `safeDetails?.missingLabels`). This works for the two current handlers but couples the rendering logic to handler-specific field names; a new handler with different `safeDetails` shape (e.g. `safeDetails.allowedBranches`) will silently produce no diagnostics.

&lt;details&gt;
&lt;summary&gt;💡 Suggested approach&lt;/summary&gt;

Either:

1. Document the `safeDetails` contract 

</details>

<details><summary>actions/setup/js/safe_output_summary.test.cjs:626</summary>

**[/tdd]** The `success: true, skipped: true` test verifies the rendered summary contains ` Add Comment - Skipped` but asserts `not.toContain(&#39;Success&#39;)`  this would also pass if the label were &#39;Failed&#39; or blank. A more precise assertion would check that the exact status text is `Skipped`:

```js
expect(summary).toContain(&#39;⚠️ Add Comment - Skipped (Message 2)&#39;);
expect(summary).not.toContain(&#39;- Success&#39;);
expect(summary).not.toContain(&#39;- Failed&#39;);

@copilot please address this.

actions/setup/js/safe_outputs_status.cjs:35

[/diagnosing-bugs] &quot;skipped&quot; is checked before &quot;warning&quot; in classifySafeOutputResult. A result that has both skipped: true and warning: &quot;...&quot; (e.g. TARGET_LOCKED in add_comment.cjs) will classify as &quot;skipped&quot;, but the OUTCOME_DISPLAY entry for &quot;skipped&quot; uses the same emoji (⚠️) as &quot;warning&quot;. This is consistent visually but means the aggregate itemsWarnings count will never include lock-skips, which may mislead callers expecting warnings to appear in the warnings bu…

@dsyme
dsyme merged commit 4fc0a30 into main Aug 9, 2026
35 checks passed
@dsyme
dsyme deleted the copilot/fix-safe-output-run-summary branch August 9, 2026 01:20
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for tackling the safe-output summary outcome classification fix. This is a well-scoped change that normalizes outcome semantics, adds outcome-aware summaries and structured diagnostics, and comes with solid new test coverage for mixed result scenarios. Ready for review.

Generated by ✅ Contribution Check · auto · 60.5 AIC · ⌖ 4.64 AIC · ⊞ 8.8K ·

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Safe-output run summary misclassifies skipped and warning outcomes as failures or successes

3 participants