Skip to content

feat: report failed non-builtin jobs as issues from conclusion job - #49959

Merged
pelikhan merged 4 commits into
mainfrom
copilot/update-conclusion-job-logic
Aug 3, 2026
Merged

feat: report failed non-builtin jobs as issues from conclusion job#49959
pelikhan merged 4 commits into
mainfrom
copilot/update-conclusion-job-logic

Conversation

Copilot AI commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

The conclusion job had no mechanism to surface failures in non-agent jobs (custom safe-output jobs, safe_outputs, detection, etc.). Failed jobs other than agent/activation would silently disappear unless someone inspected the run directly.

Changes

JavaScript (actions/setup/js/report_failed_jobs.cjs)

  • Queries listJobsForWorkflowRun for the current run, filtered to conclusion === 'failure'
  • Excludes builtin jobs already covered by handle_agent_failure: agent, activation, pre_activation, conclusion
  • Logs GitHub API rate limits before and after via fetchAndLogRateLimit
  • Renders issue body from template using renderTemplateFromFile; creates [aw] Failed jobs: <workflow> issue
  • Gracefully skips on missing actions:read or issues:write permissions

Template (actions/setup/md/failed_jobs_issue.md)

Separate template file with {workflow_name}, {workflow_source_url}, {run_url}, {failed_jobs_list} placeholders.

Compiler

  • safe_outputs_config_types.go / safe_outputs_config_global.go: new ReportFailedJobs *bool field backing report-failed-jobs frontmatter (default: true)
  • notify_comment_conclusion_helpers.go: buildConclusionReportFailedJobsStep — emits the step, skips when explicitly disabled
  • notify_comment.go: wires the step after handle_agent_failure; ensures conclusion job has at least actions: read + issues: write when the feature is on (preserves higher existing levels)

Opt-out

safe-outputs:
  report-failed-jobs: false

run: https://github.com/github/gh-aw/actions/runs/30815680828

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 9.88 AIC · ⌖ 4.97 AIC · ⊞ 8.3K ·
Comment /souschef to run again

Copilot AI and others added 2 commits August 3, 2026 11:50
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
… and template

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title feat: report failed non-builtin jobs from conclusion job feat: report failed non-builtin jobs as issues from conclusion job Aug 3, 2026
Copilot AI requested a review from pelikhan August 3, 2026 12:01
@pelikhan
pelikhan marked this pull request as ready for review August 3, 2026 12:17
Copilot AI review requested due to automatic review settings August 3, 2026 12:17
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

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

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

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

No test files were added or modified in this PR. Test Quality Sentinel skipped.

Verdict

skipped. No test files detected in the diff (PR contains only .lock.yml workflow file changes).

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 · 27.1 AIC · ⌖ 10.9 AIC · ⊞ 8.4K ·
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: No test files detected. PR contains only .lock.yml workflow file changes — sentinel skipped.

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

Adds conclusion-job reporting for failed non-builtin workflow jobs.

Changes:

  • Adds the reporting script and issue template.
  • Adds report-failed-jobs configuration and required permissions.
  • Regenerates affected workflow lockfiles.
Show a summary per file
File Description
actions/setup/js/report_failed_jobs.cjs Queries failed jobs and creates issues.
actions/setup/md/failed_jobs_issue.md Defines the failure issue body.
pkg/workflow/safe_outputs_config_types.go Adds the configuration field.
pkg/workflow/safe_outputs_config_global.go Parses the configuration value.
pkg/workflow/notify_comment.go Adds the step and permissions.
pkg/workflow/notify_comment_conclusion_helpers.go Generates the reporting step.
.github/workflows/*.lock.yml (all changed lockfiles) Regenerates conclusion steps and permissions.

Review details

Tip

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

  • Files reviewed: 275/275 changed files
  • Comments generated: 6
  • Review effort level: Balanced

ReportFailureAsIssue any `yaml:"report-failure-as-issue,omitempty"` // Controls failure issue creation: bool, templatable expression string, or []interface{} categories (parsed to ReportFailureAsIssueCategories/ExcludedCategories). Default: true
ReportFailureAsIssueCategories []string `yaml:"-"` // Parsed failure categories for report-failure-as-issue (internal use only, included categories)
ReportFailureAsIssueExcludedCategories []string `yaml:"-"` // Parsed excluded failure categories for report-failure-as-issue (internal use only, categories starting with "!")
ReportFailedJobs *bool `yaml:"report-failed-jobs,omitempty"` // Controls whether to report failed non-builtin jobs as issues (default: true). Set to false to disable.
Comment on lines +213 to +216
if reportFailedJobs, exists := outputMap["report-failed-jobs"]; exists {
if reportFailedJobsBool, ok := reportFailedJobs.(bool); ok {
config.ReportFailedJobs = &reportFailedJobsBool
safeOutputsConfigLog.Printf("Report failed jobs: %t", reportFailedJobsBool)
repo,
title: issueTitle,
body: issueBody,
labels: ["agentic-workflows"],
return nil, err
}
steps = append(steps, agentFailureSteps...)
steps = append(steps, c.buildConclusionReportFailedJobsStep(data, mainJobName)...)
Comment on lines +123 to +126
const workflowName = process.env.GH_AW_WORKFLOW_NAME || "unknown";
const workflowSourceURL = process.env.GH_AW_WORKFLOW_SOURCE_URL || "";
const runUrl = process.env.GH_AW_RUN_URL || "";
const { owner, repo } = context.repo;
Comment on lines +114 to +117
async function main() {
try {
// Check if reporting is enabled
const reportFailedJobs = parseBoolTemplatable(process.env.GH_AW_REPORT_FAILED_JOBS, true);
@github-actions github-actions Bot mentioned this pull request Aug 3, 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.

Review: feat: report failed non-builtin jobs as issues from conclusion job

The feature is well-structured and fills a real gap — failed non-builtin jobs were previously invisible unless you inspected the run directly. The compiler-side changes (permission injection, frontmatter field, step builder) look correct.

Two blocking issues in actions/setup/js/report_failed_jobs.cjs:

🔴 No deduplication (blocking)

Every invocation unconditionally creates a new issue. A scheduled workflow that fails on back-to-back runs, or a manual re-run, will accumulate [aw] Failed jobs: <workflow> issues for the same event. handle_agent_failure.cjs avoids this with findExistingFailureIssue, which searches for a recent open issue matching the workflow's XML marker. The same guard needs to be applied here before calling github.rest.issues.create.

🟡 timed_out jobs silently missed (blocking for correctness)

Line 89 filters conclusion === 'failure' only. GitHub Actions also uses timed_out (job-level timeout) as a failure-like conclusion for non-builtin jobs. Those will not be reported. Extend the check to include timed_out (and optionally action_required).

🔵 Permission-error matching is over-broad (non-blocking)

Both isIssueWritePermissionError and isActionsReadPermissionError match on '403' in the message string. A 403 from a repo with issues disabled (not a permissions problem) will be silently swallowed as a permission error instead of surfaced as a warning. Consider using a more precise match (e.g. checking the HTTP status code directly or matching "Resource not accessible by integration" exactly).

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 83.5 AIC · ⌖ 7.84 AIC · ⊞ 5.4K

setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
await main();
- name: Report failed jobs

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.

No deduplication of failure issuesreport_failed_jobs.cjs unconditionally creates a new issue every time this step runs. On re-runs or repeated scheduled failures the same workflow will accumulate multiple open [aw] Failed jobs: <workflow> issues.

handle_agent_failure.cjs uses findExistingFailureIssue to search for a recent open issue with a matching XML marker before creating a new one. The same dedup window check should be applied in report_failed_jobs.cjs.

@copilot please address this.

const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
await main();
- name: Report failed jobs
id: report_failed_jobs

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.

timed_out jobs silently ignored — In report_failed_jobs.cjs (line 89), only conclusion === 'failure' is checked. GitHub Actions jobs can also reach timed_out and action_required states that indicate a non-successful outcome. timed_out non-builtin jobs will not be reported.

Consider extending the filter to ['failure', 'timed_out'].includes(job.conclusion).

@copilot please address this.

@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 /tdd and /codebase-design — requesting changes on correctness and maintainability issues.

📋 Key Themes & Highlights

Key Themes

  • Duplicate issues on re-run: No deduplication guard means each conclusion-job re-run creates a new issue for the same run.
  • Overlapping error detectors: isIssueWritePermissionError and isActionsReadPermissionError share identical string patterns — a 403 from the jobs API can silently early-exit instead of warning.
  • Unbounded pagination: No page-cap safety net in getFailedNonBuiltinJobs.
  • Redundant env var emission: The else branch emitting GH_AW_REPORT_FAILED_JOBS: "true" is dead code since the JS already defaults to true.
  • Dual job-name forms: The builtin-names Set holds both pre_activation and pre-activation; normalise or document.

Positive Highlights

  • ✅ Graceful permission error handling — skips instead of failing the conclusion job
  • ✅ Rate-limit telemetry before/after job queries is good observability practice
  • ✅ Clean always() step condition ensures failures are captured even on partial runs
  • ✅ Opt-out via report-failed-jobs: false frontmatter is well-placed
  • ✅ Compiler correctly upgrades conclusion-job permissions without downgrading existing higher levels

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

* @param {unknown} error
* @returns {boolean}
*/
function isIssueWritePermissionError(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.

[/tdd] isIssueWritePermissionError and isActionsReadPermissionError share identical body logic — a 403 from the jobs API could match the issue-write check, silently skipping reporting when it should warn instead.

💡 Suggestion

Add unit tests for each function with errors that should only match one of the two, and consider tightening the issue-write check (e.g., scope the 403 match to a more specific string, or check status on a structured error object).

@copilot please address this.

page,
filter: "latest",
});

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 pagination loop has no upper bound — if jobs.length never falls below perPage (e.g. due to an API bug or very large run), this loops forever without a timeout or page cap.

💡 Suggestion

Add a safety cap, e.g. if (page > 100) { core.warning('Too many pages, aborting'); break; }, and add a test that simulates exactly perPage jobs on the last page to confirm the loop exits.

@copilot please address this.

}
core.warning(`Failed to query jobs for run: ${getErrorMessage(error)}`);
return;
} finally {

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] fetchAndLogRateLimit is called inside the finally block even when getFailedNonBuiltinJobs throws a permission error and the function returns early — this causes an extra API call after the early-return path, which may be surprising and could itself fail.

💡 Suggestion

Move the after-rate-limit log to after the try/catch, only calling it on the success path, or document why it's intentional in the finally block. Add a test for the permission-error path to confirm the expected number of rate-limit calls.

@copilot please address this.

* by the handle_agent_failure step (the agent job) or are framework-internal
* (conclusion = current job, pre_activation/activation = reported via agent failure issue flags).
*/
const BUILTIN_REPORTED_JOB_NAMES = new Set(["agent", "conclusion", "activation", "pre_activation", "pre-activation"]);

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.

[/codebase-design] BUILTIN_REPORTED_JOB_NAMES contains both pre_activation and pre-activation (underscore and hyphen variants). If the canonical job name changes or a new alias is added, this Set must be manually kept in sync across two string forms.

💡 Suggestion

Normalise job names before checking (e.g. job.name.replace(/-/g, '_')) and store only the underscore variant, or add a comment documenting where these names come from so future maintainers know how to update them.

@copilot please address this.

core.info(`Found ${failedJobs.length} failed non-builtin job(s): ${failedJobs.map(j => j.name).join(", ")}`);

// Render the issue body from template
const failedJobsList = formatFailedJobsList(failedJobs);

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] No deduplication: if the conclusion job runs twice (retry/re-run), a second [aw] Failed jobs: <workflow> issue is created for the same run. Existing failure-issue logic in this repo typically searches for an open issue with the same title before creating.

💡 Suggestion

Search for an existing open issue with the same title ([aw] Failed jobs: <workflow>) and update it instead of always creating a new one, consistent with report_failure.cjs patterns in this codebase.

@copilot please address this.

}
var envVars []string
envVars = append(envVars, buildWorkflowMetadataEnvVarsWithTrackerID(data.Name, data.Source, data.TrackerID, buildLocalWorkflowSourceURL(c.markdownPath))...)
envVars = append(envVars, " GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n")

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.

[/codebase-design] GH_AW_REPORT_FAILED_JOBS is emitted as "true" unconditionally when ReportFailedJobs is nil (default), but the JS already defaults to true via parseBoolTemplatable. The branch on lines 567-571 is dead code for the nil case — emitting the variable when it was not explicitly set adds noise and could confuse future readers who expect env vars to only be set when they carry a non-default value.

💡 Suggestion

Only append GH_AW_REPORT_FAILED_JOBS to envVars when data.SafeOutputs.ReportFailedJobs != nil (i.e., when the user explicitly set it), and remove the else branch that adds the redundant "true" line.

@copilot please address this.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot sous-chef triage:

Failed checks:

Please refresh the branch if needed and then run the pr-finisher skill.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 9.88 AIC · ⌖ 4.97 AIC · ⊞ 8.3K ·
Comment /souschef to run again

…int lint error

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Triage

Category: feature · Risk: high · Total score: 80/100

Impact Urgency Quality
42/50 20/30 18/20

Adds conclusion-job reporting for failed non-builtin jobs across 275 compiled workflow files — high blast radius (touches all lock.yml outputs) but high value for observability. CI pending.

Recommended action: fast_track — high-value change, warrants expedited human review given scale of generated-file diff.

Generated by 🔧 PR Triage Agent · auto · 55.7 AIC · ⌖ 4.24 AIC · ⊞ 8K ·

@pelikhan
pelikhan merged commit 0e47392 into main Aug 3, 2026
37 checks passed
@pelikhan
pelikhan deleted the copilot/update-conclusion-job-logic branch August 3, 2026 14:44
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.84.4

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.

4 participants