Skip to content

feat: expose safe-output results as workflow_call outputs#18914

Merged
pelikhan merged 4 commits intomainfrom
copilot/expose-safe-output-results
Feb 28, 2026
Merged

feat: expose safe-output results as workflow_call outputs#18914
pelikhan merged 4 commits intomainfrom
copilot/expose-safe-output-results

Conversation

Copy link
Contributor

Copilot AI commented Feb 28, 2026

Workflows triggered via workflow_call had no way to surface results from safe-outputs (created issue numbers, PR URLs, commit SHAs, etc.) to callers. The compiled YAML never generated on.workflow_call.outputs, leaving downstream jobs unable to chain on those results.

Changes

JS runtime — emit individual step outputs

  • safe_outputs_action_outputs.cjs (new): Dedicated module exporting emitSafeOutputActionOutputs() that emits named step outputs for the first successful result of each type: created_issue_number/url, created_pr_number/url, comment_id/url, push_commit_sha/url
  • safe_output_unified_handler_manager.cjs: Calls emitSafeOutputActionOutputs() after processing
  • push_to_pull_request_branch.cjs: Returns commit_sha alongside the existing commit_url

Go compiler — wire outputs through the job and workflow levels

  • compiler_safe_outputs_job.go: Adds job-level output declarations for each configured safe output type, mapping to the new step outputs
  • compiler_workflow_call.go (new): injectWorkflowCallOutputs automatically adds on.workflow_call.outputs when workflow_call is a trigger and safe-outputs are configured. User-declared outputs in the frontmatter are preserved and take precedence.
  • compiler_yaml.go: Calls injectWorkflowCallOutputs during workflow body generation

Tests

  • safe_outputs_action_outputs.test.cjs (new): 12 unit tests covering all four safe output types, array unwrapping for add_comment, failed/empty result scenarios, and info logging verification
  • compiler_workflow_call_test.go (new): Unit and end-to-end tests for the injection logic (17 test cases)
  • compiled_lock_files_test.go (new): Validates all compiled .lock.yml files in .github/workflows/ against their source .md files, with 4 test functions covering 79+ workflows:
    • TestCompiledLockFiles_SafeOutputsJobOutputs — verifies the safe_outputs job exposes the correct named outputs for each configured safe-output type
    • TestCompiledLockFiles_WorkflowCallOutputs — verifies on.workflow_call.outputs is populated for workflow_call + safe-outputs workflows
    • TestCompiledLockFiles_NoSpuriousWorkflowCallOutputs — verifies non-workflow_call workflows don't get spurious outputs injected
    • TestCompiledLockFiles_SmokeWorkflowCallHasExpectedOutputs — focused sub-tests on the canonical smoke-workflow-call workflow

Result

A workflow with on.workflow_call + safe-outputs.create-issue now compiles to:

"on":
  workflow_call:
    outputs:
      created_issue_number:
        description: Number of the first created issue
        value: ${{ jobs.safe_outputs.outputs.created_issue_number }}
      created_issue_url:
        description: URL of the first created issue
        value: ${{ jobs.safe_outputs.outputs.created_issue_url }}

Callers can then chain on results:

jobs:
  follow-up:
    needs: run-agent
    steps:
      - run: echo "Created issue ${{ needs.run-agent.outputs.created_issue_number }}"
Original prompt

This section details on the original issue you should resolve

<issue_title>feat: expose safe-output results as workflow_call outputs</issue_title>
<issue_description>## Problem

When a gh-aw workflow is called via workflow_call, the caller has no way to access results from safe-outputs (created issue numbers, PR URLs, push status, etc.). The safe-outputs job sets job-level outputs, but the compiler doesn't generate on.workflow_call.outputs to expose them.

Proposal

  1. Publish individual safe-output results as named job outputs — instead of bundling everything into process_safe_outputs_temporary_id_map, each safe-output type should emit its own clearly-named outputs:

    • create_issuecreated_issue_number, created_issue_url
    • create_pull_requestcreated_pr_number, created_pr_url
    • push_to_pull_request_branchpush_commit_sha, push_commit_url
    • add_commentcomment_id, comment_url
    • etc.
  2. Wire job outputs to on.workflow_call.outputs — add frontmatter support to declare which outputs to expose:

    outputs:
      issue_number: safe_outputs.created_issue_number
      pr_url: safe_outputs.created_pr_url

    Or automatically expose all safe-output results without explicit declaration.

  3. Caller usage:

    jobs:
      run-agent:
        uses: ./.github/workflows/gh-aw-create-issue.lock.yml
      follow-up:
        needs: run-agent
        steps:
          - run: echo "Created issue ${{ needs.run-agent.outputs.issue_number }}"

Why

  • Enables chaining agent workflows (e.g., create issue → assign to agent → track results)
  • Allows callers to conditionally branch based on safe-output success/failure
  • Makes gh-aw workflows composable building blocks in larger automation</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

- Add individual named outputs to safe_output_unified_handler_manager.cjs
  for first successful result of each safe output type (create_issue,
  create_pull_request, add_comment, push_to_pull_request_branch)
- Add commit_sha to push_to_pull_request_branch.cjs return value
- Add individual job-level outputs in compiler_safe_outputs_job.go for
  created_issue_number/url, created_pr_number/url, comment_id/url,
  push_commit_sha/url
- Add injectWorkflowCallOutputs function in compiler_workflow_call.go
  to automatically inject on.workflow_call.outputs when workflow_call
  trigger is configured and safe-outputs are present
- Update generateWorkflowBody to call injectWorkflowCallOutputs
- Add comprehensive tests for the new functionality
- Recompile all workflow lock files to include new outputs

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Expose safe-output results as workflow_call outputs feat: expose safe-output results as workflow_call outputs Feb 28, 2026
@pelikhan
Copy link
Contributor

@copilot review changes, add tests.

create a test script that valides compiled lock.yml files in the repo

core.setOutput("issues_to_assign_copilot", "");
}

// Emit individual named outputs for the first successful result of each safe output type.
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot refactor into a separate file that populates the outputs

safe_outouts_action_outputs.cjs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Extracted into safe_outputs_action_outputs.cjs (commit 030b5b0). The new module exports emitSafeOutputActionOutputs(processingResult), and safe_output_unified_handler_manager.cjs now calls it instead of the inline block. Added safe_outputs_action_outputs.test.cjs with 12 unit tests covering all four safe output types.

Add compiled_lock_files_test.go with 4 test functions that validate
the compiled .lock.yml files in .github/workflows/ against their source
.md files:

- TestCompiledLockFiles_SafeOutputsJobOutputs: verifies the safe_outputs
  job exposes named outputs for each configured safe-output type (79
  workflows checked)
- TestCompiledLockFiles_WorkflowCallOutputs: verifies on.workflow_call.outputs
  is correctly populated for workflow_call + safe-output workflows
- TestCompiledLockFiles_NoSpuriousWorkflowCallOutputs: verifies non-
  workflow_call workflows don't get spurious outputs injected (78 workflows)
- TestCompiledLockFiles_SmokeWorkflowCallHasExpectedOutputs: focused
  test on the canonical smoke-workflow-call workflow

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

Copilot AI commented Feb 28, 2026

@copilot review changes, add tests.

create a test script that valides compiled lock.yml files in the repo

Added compiled_lock_files_test.go (commit 01ceb24) with 4 test functions that validate all compiled .lock.yml files in .github/workflows/ against their source .md files:

  • TestCompiledLockFiles_SafeOutputsJobOutputs — verifies the safe_outputs job exposes the correct named outputs for each configured safe-output type (79 workflows checked)
  • TestCompiledLockFiles_WorkflowCallOutputs — verifies on.workflow_call.outputs is populated for workflow_call + safe-outputs workflows
  • TestCompiledLockFiles_NoSpuriousWorkflowCallOutputs — verifies non-workflow_call workflows don't get spurious outputs injected (78 workflows)
  • TestCompiledLockFiles_SmokeWorkflowCallHasExpectedOutputs — focused sub-tests on smoke-workflow-call (the canonical example)

The tests use ParseWorkflowFile to correctly parse frontmatter YAML (avoiding false positives from YAML values that mention safe-output type names in nested config or prose), then compare against the actual lock file content.

@pelikhan pelikhan marked this pull request as ready for review February 28, 2026 22:09
Copilot AI review requested due to automatic review settings February 28, 2026 22:09
…ts.cjs

Move the block that emits individual named step outputs (created_issue_number/url,
created_pr_number/url, comment_id/url, push_commit_sha/url) from
safe_output_unified_handler_manager.cjs into a new dedicated module
safe_outputs_action_outputs.cjs, exported as emitSafeOutputActionOutputs().

Add safe_outputs_action_outputs.test.cjs with 12 test cases covering all four
safe output types, array unwrapping for add_comment, error/skip scenarios, and
info logging verification.

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

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class workflow_call outputs for safe-output results so reusable workflows can return created issue/PR/comment/push identifiers/URLs to callers.

Changes:

  • JS runtime now emits named step outputs for the first successful safe-output result per type (issue/PR/comment/push).
  • Go compiler wires those step outputs into safe_outputs job outputs and injects on.workflow_call.outputs when applicable.
  • Adds unit/E2E tests for injection behavior and broad validation against compiled .lock.yml workflows; regenerates affected lock files.

Reviewed changes

Copilot reviewed 104 out of 104 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/workflow/compiler_yaml.go Injects on.workflow_call.outputs into the rendered on: section when safe-outputs are configured.
pkg/workflow/compiler_workflow_call.go New injection/merge logic for on.workflow_call.outputs based on configured safe-output types.
pkg/workflow/compiler_workflow_call_test.go New unit + end-to-end tests for workflow_call outputs injection and mapping.
pkg/workflow/compiler_safe_outputs_job.go Adds named safe_outputs job outputs mapping to new step outputs.
pkg/workflow/compiled_lock_files_test.go New repo-wide validation tests ensuring lock files expose expected job/workflow_call outputs.
actions/setup/js/safe_output_unified_handler_manager.cjs Emits new named step outputs for created issue/PR/comment and push commit results.
actions/setup/js/push_to_pull_request_branch.cjs Includes commit_sha in handler result for downstream output export.
.github/workflows/workflow-skill-extractor.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/workflow-normalizer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/workflow-health-manager.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/weekly-safe-outputs-spec-review.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/weekly-editors-health-check.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/video-analyzer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/unbloat-docs.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/ubuntu-image-analyzer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/tidy.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/test-create-pr-error-handling.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/technical-doc-writer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/super-linter.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/sub-issue-closer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/step-name-alignment.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/stale-repo-identifier.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-workflow-call.lock.yml Regenerated compiled workflow to add on.workflow_call.outputs + new safe_outputs named outputs.
.github/workflows/smoke-test-tools.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-temporary-id.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-project.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-multi-pr.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-gemini.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-copilot.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-copilot-arm.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-codex.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-claude.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/smoke-agent.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/slide-deck-maintainer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/semantic-function-refactor.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/security-compliance.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/security-alert-burndown.campaign.g.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/scout.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/refiner.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/q.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/pr-triage-agent.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/poem-bot.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/plan.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/pdf-summary.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/mergefest.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/layout-spec-maintainer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/jsweep.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/issue-triage-agent.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/issue-monster.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/issue-arborist.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/instructions-janitor.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/hourly-ci-cleaner.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/gpclean.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/go-pattern-detector.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/go-logger.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/glossary-maintainer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/github-mcp-tools-report.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/functional-pragmatist.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/duplicate-code-detector.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/draft-pr-cleanup.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/discussion-task-miner.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/dictation-prompt.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/developer-docs-consolidator.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/dev.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/dev-hawk.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/dependabot-go-checker.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/dependabot-burner.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/delight.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/deep-report.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-workflow-updater.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-testify-uber-super-expert.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-team-status.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-syntax-error-quality.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-security-red-team.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-safe-outputs-conformance.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-safe-output-optimizer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-rendering-scripts-verifier.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-multi-device-docs-tester.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-mcp-concurrency-analysis.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-file-diet.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-fact.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-doc-updater.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-doc-healer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-cli-tools-tester.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-cli-performance.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-assign-issue-to-user.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/daily-architecture-diagram.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/craft.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/contribution-check.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/code-simplifier.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/code-scanning-fixer.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/cloclo.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/cli-version-checker.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/cli-consistency-checker.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/ci-doctor.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/ci-coach.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/changeset.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/breaking-change-checker.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/brave.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/bot-detection.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
.github/workflows/archie.lock.yml Regenerated compiled workflow to include new safe_outputs named outputs.
Comments suppressed due to low confidence (2)

pkg/workflow/compiled_lock_files_test.go:178

  • Same masking issue here: parse errors are treated as a skip, so a workflow that becomes unparsable won’t fail this validation. Consider requiring ParseWorkflowFile to succeed for any workflow being validated (especially those with workflow_call + safe-outputs).
    actions/setup/js/safe_output_unified_handler_manager.cjs:1161
  • The logic that picks the “first successful result” can select a success result that doesn’t actually contain the fields you export (e.g., add_comment can return {success: true, skipped: true, warning: ...} without commentId/url). In that case, comment_id/comment_url (or other outputs) won’t be exported even if a later result of the same type did create something. Consider selecting the first result that contains the expected keys (or scanning arrays for the first element with the expected keys) instead of the first success.

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

Comment on lines +128 to +136
safeOutputs, _, parseErr := parseSafeOutputsForLockFileTest(mdPath)
if parseErr != nil || safeOutputs == nil {
continue // skip workflows without safe-outputs or that fail to parse
}

lockBytes, err := os.ReadFile(lockPath)
if err != nil {
continue // lock file may not exist yet
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

This test silently skips workflows when parsing fails or when the corresponding .lock.yml file can’t be read. That can mask regressions (a broken workflow frontmatter or a missing compiled lock file would still let the suite pass). Consider failing the test for parse errors and missing lock files for any workflow that declares safe-outputs.

This issue also appears on line 175 of the same file.

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +87
if existingOutputs, hasOutputs := workflowCallMap["outputs"].(map[string]any); hasOutputs {
for k, v := range existingOutputs {
// User-defined entries may be maps with description+value or plain strings
if outputMap, ok := v.(map[string]any); ok {
entry := workflowCallOutputEntry{}
if desc, ok := outputMap["description"].(string); ok {
entry.Description = desc
}
if val, ok := outputMap["value"].(string); ok {
entry.Value = val
}
mergedOutputs[k] = entry
}
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

This merge only preserves existing user-defined workflow_call outputs when each entry is a map (description/value). The comment notes outputs may also be plain strings, but those would currently be dropped. Either update the comment to match the supported schema, or preserve non-map entries (e.g., by merging into a map[string]any and keeping unknown value types as-is).

Copilot uses AI. Check for mistakes.
@pelikhan pelikhan merged commit 19c329d into main Feb 28, 2026
104 checks passed
@pelikhan pelikhan deleted the copilot/expose-safe-output-results branch February 28, 2026 22:16
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.

feat: expose safe-output results as workflow_call outputs

3 participants