Safe Outputs
The safe-outputs: (validated GitHub operations) element of your workflow’s frontmatter declares that your agentic workflow should conclude with optional automated actions based on the agentic workflow’s output. This enables your workflow to write content that is then automatically processed to create GitHub issues, comments, pull requests, or add labels—all without giving the agentic portion of the workflow any write permissions.
Why Safe Outputs?
Section titled “Why Safe Outputs?”Safe outputs enforce security through separation: agents run read-only and request actions via structured output, while separate permission-controlled jobs execute those requests. This provides least privilege, defense against prompt injection, auditability, and controlled limits per operation.
Example:
safe-outputs: create-issue:The agent requests issue creation; a separate job with issues: write creates it.
Available Safe Output Types
Section titled “Available Safe Output Types”Issues & Discussions
Section titled “Issues & Discussions”- Create Issue (
create-issue) — Create GitHub issues (max: 1) - Update Issue (
update-issue) — Update issue status, title, or body (max: 1) - Close Issue (
close-issue) — Close issues with comment (max: 1) - Link Sub-Issue (
link-sub-issue) — Link issues as sub-issues (max: 1) - Create Discussion (
create-discussion) — Create GitHub discussions (max: 1) - Update Discussion (
update-discussion) — Update discussion title, body, or labels (max: 1) - Close Discussion (
close-discussion) — Close discussions with comment and resolution (max: 1)
Pull Requests
Section titled “Pull Requests”- Create PR (
create-pull-request) — Create pull requests with code changes (max: 1) - Update PR (
update-pull-request) — Update PR title or body (max: 1) - Close PR (
close-pull-request) — Close pull requests without merging (max: 10) - PR Review Comments (
create-pull-request-review-comment) — Create review comments on code lines (max: 10) - Push to PR Branch (
push-to-pull-request-branch) — Push changes to PR branch (max: 1, same-repo only)
Labels, Assignments & Reviews
Section titled “Labels, Assignments & Reviews”- Add Comment (
add-comment) — Post comments on issues, PRs, or discussions (max: 1) - Hide Comment (
hide-comment) — Hide comments on issues, PRs, or discussions (max: 5) - Add Labels (
add-labels) — Add labels to issues or PRs (max: 3) - Remove Labels (
remove-labels) — Remove labels from issues or PRs (max: 3) - Add Reviewer (
add-reviewer) — Add reviewers to pull requests (max: 3) - Assign Milestone (
assign-milestone) — Assign issues to milestones (max: 1) - Assign to Agent (
assign-to-agent) — Assign Copilot agents to issues or PRs (max: 1) - Assign to User (
assign-to-user) — Assign users to issues (max: 1)
Projects, Releases & Assets
Section titled “Projects, Releases & Assets”- Create Project (
create-project) — Create new GitHub Projects boards (max: 1, cross-repo) - Update Project (
update-project) — Manage GitHub Projects boards (max: 10, same-repo only) - Create Project Status Update (
create-project-status-update) — Create project status updates - Update Release (
update-release) — Update GitHub release descriptions (max: 1) - Upload Assets (
upload-asset) — Upload files to orphaned git branch (max: 10, same-repo only)
Security & Agent Tasks
Section titled “Security & Agent Tasks”- Dispatch Workflow (
dispatch-workflow) — Trigger other workflows with inputs (max: 3, same-repo only) - Code Scanning Alerts (
create-code-scanning-alert) — Generate SARIF security advisories (max: unlimited, same-repo only) - Autofix Code Scanning Alerts (
autofix-code-scanning-alert) — Create automated fixes for code scanning alerts (max: 10, same-repo only) - Create Agent Session (
create-agent-session) — Create Copilot agent sessions (max: 1)
System Types (Auto-Enabled)
Section titled “System Types (Auto-Enabled)”- No-Op (
noop) — Log completion message for transparency (max: 1, same-repo only) - Missing Tool (
missing-tool) — Report missing tools (max: unlimited, same-repo only) - Missing Data (
missing-data) — Report missing data required to achieve goals (max: unlimited, same-repo only)
Custom Safe Output Jobs (jobs:)
Section titled “Custom Safe Output Jobs (jobs:)”Create custom post-processing jobs registered as Model Context Protocol (MCP) tools. Support standard GitHub Actions properties and auto-access agent output via $GH_AW_AGENT_OUTPUT. See Custom Safe Output Jobs.
Issue Creation (create-issue:)
Section titled “Issue Creation (create-issue:)”Creates GitHub issues based on workflow output.
safe-outputs: create-issue: title-prefix: "[ai] " # prefix for titles labels: [automation, agentic] # labels to attach assignees: [user1, copilot] # assignees (use 'copilot' for bot) max: 5 # max issues (default: 1) expires: 7 # auto-close after 7 days (or false to disable) group: true # group as sub-issues under parent close-older-issues: true # close previous issues from same workflow target-repo: "owner/repo" # cross-repositoryAuto-Expiration
Section titled “Auto-Expiration”The expires field auto-closes issues after a time period. Supports integers (days), relative formats (2h, 7d, 2w, 1m, 1y), or false to disable expiration. Generates agentics-maintenance.yml workflow that runs at the minimum required frequency based on the shortest expiration time across all workflows:
- 1 day or less → every 2 hours
- 2 days → every 6 hours
- 3-4 days → every 12 hours
- 5+ days → daily
Hours less than 24 are treated as 1 day minimum for expiration calculation.
To explicitly disable expiration (useful when create-issue has a default expiration), use expires: false:
Issue Grouping
Section titled “Issue Grouping”The group field (default: false) automatically organizes multiple issues as sub-issues under a parent issue. When enabled:
- Parent issues are automatically created and managed using the workflow ID as the group identifier
- Child issues are linked to the parent using GitHub’s sub-issue relationships
- Maximum of 64 sub-issues per parent issue
- Parent issues include metadata tracking all sub-issues
This is useful for workflows that create multiple related issues, such as planning workflows that break down epics into tasks, or batch processing workflows that create issues for individual items.
Example:
safe-outputs: create-issue: title-prefix: "[plan] " labels: [plan, ai-generated] max: 5 group: trueIn this example, if the workflow creates 5 issues, all will be automatically grouped under a parent issue, making it easy to track related work items together.
Temporary IDs for Issue References
Section titled “Temporary IDs for Issue References”Use temporary IDs (aw_ + 12 hex chars) to reference parent issues before creation. References like #aw_abc123def456 in bodies are replaced with actual numbers. The parent field creates sub-issue relationships.
Auto-Close Older Issues
Section titled “Auto-Close Older Issues”The close-older-issues field (default: false) automatically closes previous open issues from the same workflow when a new issue is created. This is useful for workflows that generate recurring reports or status updates, ensuring only the latest issue remains open.
safe-outputs: create-issue: title-prefix: "[weekly-report] " labels: [report, automation] close-older-issues: trueWhen enabled:
- Searches for open issues containing the same workflow-id marker in their body
- Closes found issues as “not planned” with a comment linking to the new issue
- Maximum 10 older issues will be closed
- Only runs if the new issue creation succeeds
Searching for Workflow-Created Items
Section titled “Searching for Workflow-Created Items”All items created by workflows (issues, pull requests, discussions, and comments) include a hidden workflow-id marker in their body:
<!-- gh-aw-workflow-id: WORKFLOW_NAME -->You can use this marker to find all items created by a specific workflow on GitHub.com.
Search Examples:
Find all open issues created by the daily-team-status workflow:
repo:owner/repo is:issue is:open "gh-aw-workflow-id: daily-team-status" in:bodyFind all pull requests created by the security-audit workflow:
repo:owner/repo is:pr "gh-aw-workflow-id: security-audit" in:bodyFind all items (issues, PRs, discussions) from any workflow in your organization:
org:your-org "gh-aw-workflow-id:" in:bodyFind comments from a specific workflow:
repo:owner/repo "gh-aw-workflow-id: bot-responder" in:commentsClose Issue (close-issue:)
Section titled “Close Issue (close-issue:)”Closes GitHub issues with an optional comment and state reason. Filters by labels and title prefix control which issues can be closed.
safe-outputs: close-issue: target: "triggering" # "triggering" (default), "*", or number required-labels: [automated] # only close with any of these labels required-title-prefix: "[bot]" # only close matching prefix max: 20 # max closures (default: 1) target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires issue event), "*" (any issue), or number (specific issue).
State Reasons: completed, not_planned, reopened (default: completed).
Comment Creation (add-comment:)
Section titled “Comment Creation (add-comment:)”Posts comments on issues, PRs, or discussions. Defaults to triggering item; use target: "*" for any, or number for specific items. When combined with create-issue, create-discussion, or create-pull-request, includes “Related Items” section.
safe-outputs: add-comment: max: 3 # max comments (default: 1) target: "*" # "triggering" (default), "*", or number discussion: true # target discussions target-repo: "owner/repo" # cross-repository hide-older-comments: true # hide previous comments from same workflow allowed-reasons: [outdated] # restrict hiding reasons (optional)Hide Older Comments
Section titled “Hide Older Comments”Set hide-older-comments: true to minimize previous comments from the same workflow (identified by GITHUB_WORKFLOW) before posting new ones. Useful for status updates. Allowed reasons: spam, abuse, off_topic, outdated (default), resolved.
Append-Only Status Comments
Section titled “Append-Only Status Comments”By default, gh-aw posts an activation comment when a workflow starts, then updates that same comment with the final status.
If you prefer an append-only timeline (never editing existing comments), set:
safe-outputs: messages: append-only-comments: trueWhen enabled, the workflow completion notifier creates a new comment instead of editing the activation comment.
Hide Comment (hide-comment:)
Section titled “Hide Comment (hide-comment:)”Collapses comments in GitHub UI with reason. Requires GraphQL node IDs (e.g., IC_kwDOABCD123456), not REST numeric IDs. Reasons: spam, abuse, off_topic, outdated, resolved.
safe-outputs: hide-comment: max: 5 # max comments (default: 5) target-repo: "owner/repo" # cross-repositoryAdd Labels (add-labels:)
Section titled “Add Labels (add-labels:)”Adds labels to issues or PRs. Specify allowed to restrict to specific labels.
safe-outputs: add-labels: allowed: [bug, enhancement] # restrict to specific labels max: 3 # max labels (default: 3) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryRemove Labels (remove-labels:)
Section titled “Remove Labels (remove-labels:)”Removes labels from issues or PRs. Specify allowed to restrict which labels can be removed. If a label is not present on the item, it will be silently skipped.
safe-outputs: remove-labels: allowed: [automated, stale] # restrict to specific labels (optional) max: 3 # max operations (default: 3) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires issue/PR event), "*" (any issue/PR), or number (specific issue/PR).
When allowed is omitted or set to null, any labels can be removed. Use allowed to restrict removal to specific labels only, providing control over which labels agents can manipulate.
Example use case: Label lifecycle management where agents add temporary labels during triage and remove them once processed.
safe-outputs: add-labels: allowed: [needs-triage, automation] remove-labels: allowed: [needs-triage] # agents can remove triage label after processingAdd Reviewer (add-reviewer:)
Section titled “Add Reviewer (add-reviewer:)”Adds reviewers to pull requests. Specify reviewers to restrict to specific GitHub usernames.
safe-outputs: add-reviewer: reviewers: [user1, copilot] # restrict to specific reviewers max: 3 # max reviewers (default: 3) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires PR event), "*" (any PR), or number (specific PR).
Use reviewers: copilot to assign the Copilot PR reviewer bot. Requires a PAT stored as COPILOT_GITHUB_TOKEN or GH_AW_GITHUB_TOKEN (legacy).
Assign Milestone (assign-milestone:)
Section titled “Assign Milestone (assign-milestone:)”Assigns issues to milestones. Specify allowed to restrict to specific milestone titles.
safe-outputs: assign-milestone: allowed: [v1.0, v2.0] # restrict to specific milestone titles max: 1 # max assignments (default: 1) target-repo: "owner/repo" # cross-repositoryIssue Updates (update-issue:)
Section titled “Issue Updates (update-issue:)”Updates issue status, title, or body. Only explicitly enabled fields can be updated. Status must be “open” or “closed”. The operation field controls how body updates are applied: append (default), prepend, replace, or replace-island.
safe-outputs: update-issue: status: # enable status updates title: # enable title updates body: # enable body updates max: 3 # max updates (default: 1) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires issue event), "*" (any issue), or number (specific issue).
When using target: "*", the agent must provide issue_number or item_number in the output to identify which issue to update.
Operation Types (for body updates):
append(default): Adds content to the end with separator and attributionprepend: Adds content to the start with separator and attributionreplace: Completely replaces existing body with new content and attributionreplace-island: Updates a specific section marked with HTML comments
Agent output format: {"type": "update_issue", "issue_number": 123, "operation": "append", "body": "..."}. The operation field is optional (defaults to append).
Pull Request Updates (update-pull-request:)
Section titled “Pull Request Updates (update-pull-request:)”Updates PR title or body. Both fields are enabled by default. The operation field controls how body updates are applied: append (default), prepend, or replace.
safe-outputs: update-pull-request: title: true # enable title updates (default: true) body: true # enable body updates (default: true) max: 1 # max updates (default: 1) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires PR event), "*" (any PR), or number (specific PR).
When using target: "*", the agent must provide pull_request_number in the output to identify which pull request to update.
Operation Types:
append(default): Adds content to the end with separator and attributionprepend: Adds content to the start with separator and attributionreplace: Completely replaces existing body with new content and attribution
Title updates always replace the existing title. Disable fields by setting to false.
Link Sub-Issue (link-sub-issue:)
Section titled “Link Sub-Issue (link-sub-issue:)”Links issues as sub-issues using GitHub’s parent-child issue relationships. Supports filtering by labels and title prefixes for both parent and sub issues.
safe-outputs: link-sub-issue: parent-required-labels: [epic] # parent must have these labels parent-title-prefix: "[Epic]" # parent must match prefix sub-required-labels: [task] # sub must have these labels sub-title-prefix: "[Task]" # sub must match prefix max: 1 # max links (default: 1) target-repo: "owner/repo" # cross-repositoryAgent output includes parent_issue_number and sub_issue_number. Validation ensures both issues exist and meet label/prefix requirements before linking.
Project Creation (create-project:)
Section titled “Project Creation (create-project:)”Creates new GitHub Projects V2 boards. Requires PAT or GitHub App token (GH_AW_PROJECT_GITHUB_TOKEN)—default GITHUB_TOKEN lacks Projects v2 access. Supports optional view configuration to create custom project views at creation time.
safe-outputs: create-project: max: 1 # max operations (default: 1) github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }} target-owner: "myorg" # default target owner (optional) title-prefix: "Campaign" # default title prefix (optional) views: # optional: auto-create views - name: "Sprint Board" layout: board filter: "is:issue is:open" - name: "Task Tracker" layout: tableWhen views are configured, they are created automatically after project creation. GitHub’s default “View 1” will remain, and configured views are created as additional views.
The target-owner field is an optional default. When configured, the agent can omit the owner field in tool calls, and the default will be used. The agent can still override by providing an explicit owner value.
Without default (agent must provide owner):
create_project({ title: "Campaign: Security Q1 2025", owner: "myorg", owner_type: "org", // "org" or "user" (default: "org") item_url: "https://github.com/myorg/repo/issues/123" // Optional issue to add});With default configured (agent only needs title):
create_project({ title: "Campaign: Security Q1 2025" // owner uses configured default // owner_type defaults to "org" // Can still override: owner: "...", owner_type: "user"});Optionally include item_url (GitHub issue URL) to add the issue as the first project item. Exposes outputs: project-id, project-number, project-title, project-url, item-id (if item added).
Project Board Updates (update-project:)
Section titled “Project Board Updates (update-project:)”Manages GitHub Projects boards. Requires PAT or GitHub App token (GH_AW_PROJECT_GITHUB_TOKEN)—default GITHUB_TOKEN lacks Projects v2 access. Update-only by default; set create_if_missing: true to create boards (requires appropriate token permissions).
safe-outputs: update-project: project: "https://github.com/orgs/myorg/projects/42" # required: target project URL max: 20 # max operations (default: 10) github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }} views: # optional: auto-create views - name: "Sprint Board" layout: board filter: "is:issue is:open" - name: "Task Tracker" layout: table - name: "Campaign Roadmap" layout: roadmapConfiguration options:
project(required in configuration): Default project URL shown in examples. Note: Agent output messages must explicitly include theprojectfield - the configured value is for documentation purposes only.max: Maximum number of operations per run (default: 10).github-token: Custom token with Projects permissions (required for Projects v2 access).views: Optional array of project views to create automatically.- Optional
campaign_idin agent output appliesz_campaign_<id>labels for Campaign Workflows. - Exposes outputs:
project-id,project-number,project-url,campaign-id,item-id.
Supported Field Types
Section titled “Supported Field Types”GitHub Projects V2 supports various custom field types. The following field types are automatically detected and handled:
TEXT— Text fields (default)DATE— Date fields (format:YYYY-MM-DD)NUMBER— Numeric fields (story points, estimates, etc.)ITERATION— Sprint/iteration fields (matched by iteration title)SINGLE_SELECT— Dropdown/select fields (creates missing options automatically)
Example field usage:
fields: status: "In Progress" # SINGLE_SELECT field start_date: "2026-01-04" # DATE field story_points: 8 # NUMBER field sprint: "Sprint 42" # ITERATION field (by title) priority: "High" # SINGLE_SELECT fieldCreating Project Views
Section titled “Creating Project Views”Project views can be created automatically by declaring them in the views array. Views are created when the workflow runs, after processing update_project items from the agent.
View configuration:
safe-outputs: update-project: github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }} views: - name: "Sprint Board" # required: view name layout: board # required: table, board, or roadmap filter: "is:issue is:open" # optional: filter query - name: "Task Tracker" layout: table filter: "is:issue is:pr" - name: "Campaign Timeline" layout: roadmapView properties:
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | View name (e.g., “Sprint Board”, “Task Tracker”) |
layout | string | Yes | View layout: table, board, or roadmap |
filter | string | No | Filter query (e.g., is:issue is:open, label:bug) |
visible-fields | array | No | Field IDs to display (table/board only, not roadmap) |
Layout types:
table— List view with customizable columns for detailed trackingboard— Kanban-style cards grouped by status or custom fieldroadmap— Timeline visualization with date-based swimlanes
Filter syntax examples:
is:issue is:open— Open issues onlyis:pr— Pull requests onlyis:issue is:pr— Both issues and PRslabel:bug— Items with bug labelassignee:@me— Items assigned to viewer
Views are created automatically during workflow execution. The workflow must include at least one update_project operation to provide the target project URL. For campaign workflows, see Campaign Guides.
Project Status Updates (create-project-status-update:)
Section titled “Project Status Updates (create-project-status-update:)”Creates status updates on GitHub Projects boards to communicate campaign progress, findings, and trends. Status updates appear in the project’s Updates tab and provide a historical record of execution. Requires PAT or GitHub App token (GH_AW_PROJECT_GITHUB_TOKEN)—default GITHUB_TOKEN lacks Projects v2 access.
safe-outputs: create-project-status-update: project: "https://github.com/orgs/myorg/projects/73" # required: target project URL max: 1 # max updates per run (default: 1) github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }}Configuration options:
project(required in configuration): Default project URL shown in examples. Note: Agent output messages must explicitly include theprojectfield - the configured value is for documentation purposes only.max: Maximum number of status updates per run (default: 1).github-token: Custom token with Projects permissions (required for Projects v2 access).- Typically used by Campaign Workflows to automatically post run summaries.
Required Fields
Section titled “Required Fields”| Field | Type | Description |
|---|---|---|
project | URL | Full GitHub project URL (e.g., https://github.com/orgs/myorg/projects/73). Required in every agent output message. |
body | Markdown | Status update content with campaign summary, findings, and next steps |
Optional Fields
Section titled “Optional Fields”| Field | Type | Default | Description |
|---|---|---|---|
status | Enum | ON_TRACK | Status indicator: ON_TRACK, AT_RISK, OFF_TRACK, COMPLETE, INACTIVE |
start_date | Date | Today | Run start date (format: YYYY-MM-DD) |
target_date | Date | Today | Projected completion or milestone date (format: YYYY-MM-DD) |
Example Usage
Section titled “Example Usage”create-project-status-update: project: "https://github.com/orgs/myorg/projects/73" status: "ON_TRACK" start_date: "2026-01-06" target_date: "2026-01-31" body: | ## Campaign Run Summary
**Discovered:** 25 items (15 issues, 10 PRs) **Processed:** 10 items added to project, 5 updated **Completion:** 60% (30/50 total tasks)
### Key Findings - Documentation coverage improved to 88% - 3 critical accessibility issues identified - Worker velocity: 1.2 items/day
### Trends - Velocity stable at 8-10 items/week - Blocked items decreased from 5 to 2 - On track for end-of-month completion
### Next Steps - Continue processing remaining 15 items - Address 2 blocked items in next run - Target 95% documentation coverage by end of monthStatus Indicators
Section titled “Status Indicators”ON_TRACK: Campaign progressing as planned, meeting velocity targetsAT_RISK: Potential issues identified (blocked items, slower velocity, dependencies)OFF_TRACK: Campaign behind schedule, requires intervention or re-planningCOMPLETE: All campaign objectives met, no further work neededINACTIVE: Campaign paused or not actively running
Exposes outputs: status-update-id, project-id, status.
Pull Request Creation (create-pull-request:)
Section titled “Pull Request Creation (create-pull-request:)”Creates PRs with code changes. Falls back to issue if creation fails (e.g., org settings block it). expires field (same-repo only) auto-closes after period: integers (days) or 2h, 7d, 2w, 1m, 1y (hours < 24 treated as 1 day).
safe-outputs: create-pull-request: title-prefix: "[ai] " # prefix for titles labels: [automation] # labels to attach reviewers: [user1, copilot] # reviewers (use 'copilot' for bot) draft: true # create as draft (default: true) expires: 14 # auto-close after 14 days (same-repo only) if-no-changes: "warn" # "warn" (default), "error", or "ignore" target-repo: "owner/repo" # cross-repositoryClose Pull Request (close-pull-request:)
Section titled “Close Pull Request (close-pull-request:)”Closes PRs without merging with optional comment. Filter by labels and title prefix. Target: "triggering" (PR event), "*" (any), or number.
safe-outputs: close-pull-request: target: "triggering" # "triggering" (default), "*", or number required-labels: [automated, stale] # only close with these labels required-title-prefix: "[bot]" # only close matching prefix max: 10 # max closures (default: 1) target-repo: "owner/repo" # cross-repositoryPR Review Comments (create-pull-request-review-comment:)
Section titled “PR Review Comments (create-pull-request-review-comment:)”Creates review comments on specific code lines in PRs. Supports single-line and multi-line comments.
safe-outputs: create-pull-request-review-comment: max: 3 # max comments (default: 10) side: "RIGHT" # "LEFT" or "RIGHT" (default: "RIGHT") target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryCode Scanning Alerts (create-code-scanning-alert:)
Section titled “Code Scanning Alerts (create-code-scanning-alert:)”Creates security advisories in SARIF format and submits to GitHub Code Scanning. Supports severity: error, warning, info, note.
safe-outputs: create-code-scanning-alert: max: 50 # max findings (default: unlimited)Autofix Code Scanning Alerts (autofix-code-scanning-alert:)
Section titled “Autofix Code Scanning Alerts (autofix-code-scanning-alert:)”Creates automated fixes for code scanning alerts. Agent outputs fix suggestions that are submitted to GitHub Code Scanning.
safe-outputs: autofix-code-scanning-alert: max: 10 # max autofixes (default: 10)Push to PR Branch (push-to-pull-request-branch:)
Section titled “Push to PR Branch (push-to-pull-request-branch:)”Pushes changes to a PR’s branch. Validates via title-prefix and labels to ensure only approved PRs receive changes.
safe-outputs: push-to-pull-request-branch: target: "*" # "triggering" (default), "*", or number title-prefix: "[bot] " # require title prefix labels: [automated] # require all labels if-no-changes: "warn" # "warn" (default), "error", or "ignore"When create-pull-request or push-to-pull-request-branch are enabled, file editing tools (Edit, Write, NotebookEdit) and git commands are added.
Release Updates (update-release:)
Section titled “Release Updates (update-release:)”Updates GitHub release descriptions: replace (complete replacement), append (add to end), or prepend (add to start).
safe-outputs: update-release: max: 1 # max releases (default: 1, max: 10) target-repo: "owner/repo" # cross-repository github-token: ${{ secrets.CUSTOM_TOKEN }} # custom tokenAgent output format: {"type": "update_release", "tag": "v1.0.0", "operation": "replace", "body": "..."}. The tag field is optional for release events (inferred from context). Workflow needs read access; only the generated job receives write permissions.
Asset Uploads (upload-asset:)
Section titled “Asset Uploads (upload-asset:)”Uploads files (screenshots, charts, reports) to orphaned git branch with predictable URLs: https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{filename}. Agent registers files via upload_asset tool; separate job with contents: write commits them.
safe-outputs: upload-asset: branch: "assets/my-workflow" # default: "assets/${{ github.workflow }}" max-size: 5120 # KB (default: 10240 = 10MB) allowed-exts: [.png, .jpg, .svg] # default: [.png, .jpg, .jpeg] max: 20 # default: 10Branch Requirements: New branches require assets/ prefix for security. Existing branches allow any name. Create custom branches manually:
git checkout --orphan my-custom-branch && git rm -rf . && git commit --allow-empty -m "Initialize" && git push origin my-custom-branchSecurity: File path validation (workspace//tmp only), extension allowlist, size limits, SHA-256 verification, orphaned branch isolation, minimal permissions.
Outputs: published_count, branch_name. Limits: Same-repo only, max 50MB/file, 100 assets/run.
No-Op Logging (noop:)
Section titled “No-Op Logging (noop:)”Enabled by default. Allows agents to produce completion messages when no actions are needed, preventing silent workflow completion.
safe-outputs: create-issue: # noop enabled automatically noop: false # explicitly disableAgent output: {"type": "noop", "message": "Analysis complete - no issues found"}. Messages appear in the workflow conclusion comment or step summary.
Missing Tool Reporting (missing-tool:)
Section titled “Missing Tool Reporting (missing-tool:)”Enabled by default. Automatically detects and reports tools lacking permissions or unavailable functionality.
safe-outputs: create-issue: # missing-tool enabled automatically missing-tool: false # explicitly disableMissing Data Reporting (missing-data:)
Section titled “Missing Data Reporting (missing-data:)”Enabled by default. Allows AI agents to report missing data required to achieve their goals, encouraging truthfulness over hallucination.
safe-outputs: missing-data: create-issue: true # create GitHub issues for missing data title-prefix: "[data]" # prefix for issue titles (default: "[missing data]") labels: [data, blocked] # labels to attach to issues max: 10 # max reports per run (default: unlimited)Why Missing Data Matters
AI agents work best when they acknowledge data gaps instead of inventing information. By explicitly reporting missing data, agents:
- Ensure accuracy: Prevent hallucinations and incorrect outputs
- Enable improvement: Help teams identify gaps in documentation, APIs, or configuration
- Demonstrate responsibility: Show honest behavior that should be encouraged
Agent Output Format
{ "type": "missing_data", "data_type": "user_preferences", "reason": "User preferences database not accessible", "context": "Needed to customize dashboard layout", "alternatives": "Could use default settings"}Required Fields: data_type, reason
Optional Fields: context, alternatives
Issue Creation
When create-issue: true, the agent creates or updates GitHub issues documenting missing data with:
- Detailed explanation of what data is needed and why
- Context about how the data would be used
- Possible alternatives if the data cannot be provided
- Encouragement message praising the agent’s truthfulness
This rewards honest AI behavior and helps teams improve data accessibility for future agent runs.
Discussion Creation (create-discussion:)
Section titled “Discussion Creation (create-discussion:)”Creates discussions with optional category (slug, name, or ID; defaults to first available). expires field auto-closes after period (integers, 2h, 7d, 2w, 1m, 1y, or false to disable; hours < 24 treated as 1 day) as “OUTDATED” with comment. Generates maintenance workflow with dynamic frequency based on shortest expiration time (see Auto-Expiration section above).
Category Naming Standard: Use lowercase, plural category names (e.g., audits, general, reports) for consistency and better searchability. GitHub Discussion category IDs (starting with DIC_) are also supported.
safe-outputs: create-discussion: title-prefix: "[ai] " # prefix for titles category: "general" # category slug, name, or ID (use lowercase) expires: 3 # auto-close after 3 days (or false to disable) max: 3 # max discussions (default: 1) target-repo: "owner/repo" # cross-repositoryClose Discussion (close-discussion:)
Section titled “Close Discussion (close-discussion:)”Closes GitHub discussions with optional comment and resolution reason. Filters by category, labels, and title prefix control which discussions can be closed.
safe-outputs: close-discussion: target: "triggering" # "triggering" (default), "*", or number required-category: "Ideas" # only close in category required-labels: [resolved] # only close with labels required-title-prefix: "[ai]" # only close matching prefix max: 1 # max closures (default: 1) target-repo: "owner/repo" # cross-repositoryTarget: "triggering" (requires discussion event), "*" (any discussion), or number (specific discussion).
Resolution Reasons: RESOLVED, DUPLICATE, OUTDATED, ANSWERED.
Discussion Updates (update-discussion:)
Section titled “Discussion Updates (update-discussion:)”Updates discussion title, body, or labels. Only explicitly enabled fields can be updated.
safe-outputs: update-discussion: title: # enable title updates body: # enable body updates labels: # enable label updates allowed-labels: [bug, idea] # restrict to specific labels max: 1 # max updates (default: 1) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryField Enablement: Include title:, body:, or labels: keys to enable updates for those fields. Without these keys, the field cannot be updated. Setting allowed-labels implicitly enables label updates.
Target: "triggering" (requires discussion event), "*" (any discussion), or number (specific discussion).
When using target: "*", the agent must provide discussion_number in the output to identify which discussion to update.
Workflow Dispatch (dispatch-workflow:)
Section titled “Workflow Dispatch (dispatch-workflow:)”Triggers other workflows in the same repository using GitHub’s workflow_dispatch event. This enables agent orchestration patterns, such as campaign workflows that coordinate multiple worker workflows.
Shorthand Syntax:
safe-outputs: dispatch-workflow: [worker-workflow, scanner-workflow]Detailed Syntax:
safe-outputs: dispatch-workflow: workflows: [worker-workflow, scanner-workflow] max: 3 # maximum dispatches (default: 1, max: 50)Configuration
Section titled “Configuration”workflows(required) — List of workflow names (without.mdextension) that the agent is allowed to dispatch. Each workflow must exist in the same repository and support theworkflow_dispatchtrigger.max(optional) — Maximum number of workflow dispatches allowed (default: 1, maximum: 50). This prevents excessive workflow triggering.
Validation Rules
Section titled “Validation Rules”At compile time, the compiler validates:
-
Workflow existence — Each workflow in the
workflowslist must exist as either:- A markdown workflow file (
.md) - A compiled lock file (
.lock.yml) - A standard GitHub Actions workflow (
.yml)
- A markdown workflow file (
-
workflow_dispatch trigger — Each workflow must include
workflow_dispatchin itson:trigger section:on: [push, workflow_dispatch] # oron:push:workflow_dispatch:inputs:campaign_id:description: "Campaign identifier"required: true -
No self-reference — A workflow cannot dispatch itself to prevent infinite loops.
-
File resolution — The compiler resolves the correct file extension (
.lock.ymlor.yml) at compile time and embeds it in the safe output configuration, ensuring the runtime handler dispatches the correct workflow file.
How It Works: MCP Tool Generation
Section titled “How It Works: MCP Tool Generation”When you configure dispatch-workflow, the compiler automatically generates MCP (Model Context Protocol) tools that the AI agent can call. Each workflow in your workflows list becomes a callable tool:
Example Configuration:
safe-outputs: dispatch-workflow: workflows: [deploy-app, run-tests]Generated MCP Tools:
deploy_apptool — Dispatches the deploy-app workflowrun_teststool — Dispatches the run-tests workflow
The compiler:
- Reads workflow_dispatch inputs from the target workflow’s YAML
- Generates MCP tool schemas with matching input parameters
- Validates workflow files exist and support workflow_dispatch
- Embeds tool definitions in the compiled workflow
This means the AI agent automatically knows what inputs each workflow expects and can call them directly as tools.
Defining Workflow Inputs
Section titled “Defining Workflow Inputs”To enable the agent to provide inputs when dispatching workflows, define workflow_dispatch inputs in the target workflow:
Target Workflow Example (deploy-app.md):
---on: workflow_dispatch: inputs: environment: description: "Target deployment environment" required: true type: choice options: [staging, production] version: description: "Version to deploy" required: true type: string dry_run: description: "Perform dry run without actual deployment" required: false type: boolean default: false---
# Deploy Application Workflow
Deploys the application to the specified environment...Agent Output Format:
When the agent calls the generated MCP tool, it produces:
{ "type": "dispatch_workflow", "workflow_name": "deploy-app", "inputs": { "environment": "staging", "version": "v1.2.3", "dry_run": false }}All input values are automatically converted to strings as required by GitHub’s workflow_dispatch API:
- Objects are JSON-stringified
- Numbers and booleans are converted to strings
nullandundefinedbecome empty strings
Rate Limiting
Section titled “Rate Limiting”To respect GitHub API rate limits, the handler automatically enforces a 5-second delay between consecutive workflow dispatches. The first dispatch has no delay.
Best Practices
Section titled “Best Practices”1. Always Define Explicit Inputs
When creating workflows that will be dispatched, explicitly define all required inputs in the workflow_dispatch section:
---on: workflow_dispatch: inputs: task_id: description: "Unique task identifier" required: true type: string priority: description: "Task priority level" required: false type: choice options: [low, medium, high] default: medium---This ensures:
- The MCP tool schema includes all expected parameters
- The agent knows what information to provide
- GitHub validates inputs at dispatch time
2. Use Descriptive Input Descriptions
Clear descriptions help the AI agent understand what information to provide:
# ✅ GOOD - Clear descriptionrepository_url: description: "Full GitHub repository URL (e.g., https://github.com/owner/repo)" required: true type: string
# ❌ BAD - Vague descriptionrepo: description: "Repository" type: string3. Use Choice Types for Limited Options
When inputs have a fixed set of valid values, use type: choice:
action: description: "Action to perform" required: true type: choice options: [analyze, fix, report]This prevents the agent from providing invalid values and makes the interface clearer.
4. Provide Sensible Defaults
For optional inputs, provide defaults that work for the most common use case:
timeout: description: "Maximum execution time in minutes" required: false type: number default: 30Troubleshooting
Section titled “Troubleshooting”Problem: “Workflow file not found”
Error: dispatch-workflow: workflow 'my-workflow' not found in .github/workflows/
Solutions:
- Ensure the workflow file exists in
.github/workflows/ - Use the workflow name without extension (e.g.,
my-workflow, notmy-workflow.md) - Compile markdown workflows before dispatching:
gh aw compile my-workflow
Problem: “Workflow does not support workflow_dispatch trigger”
Error: dispatch-workflow: workflow 'my-workflow' does not support workflow_dispatch trigger
Solution: Add workflow_dispatch to the on: section of the target workflow:
on: push: workflow_dispatch: inputs: # Define your inputs hereProblem: “Required input not provided”
The workflow is dispatched but GitHub rejects it due to missing required inputs.
Solution: Ensure the target workflow defines its inputs and they match what the agent is providing:
- Check the target workflow’s
workflow_dispatch.inputssection - Mark required inputs with
required: true - The agent will automatically know to provide these inputs based on the MCP tool schema
Problem: “Agent doesn’t know what inputs to provide”
The agent dispatches the workflow but doesn’t include necessary inputs.
Solutions:
- Define inputs explicitly in the target workflow’s
workflow_dispatchsection - Add clear descriptions to help the agent understand what each input is for
- Mark required inputs with
required: true - Update your dispatcher workflow’s prompt to mention specific inputs if needed
Example of well-defined inputs:
---on: workflow_dispatch: inputs: campaign_id: description: "Unique identifier for this campaign run (e.g., 'campaign-2024-01-15-001')" required: true type: string target_repos: description: "JSON array of repository names to process (e.g., '[\"repo1\", \"repo2\"]')" required: true type: string dry_run: description: "If true, validate configuration without executing actions" required: false type: boolean default: false---Security Considerations
Section titled “Security Considerations”- Same-repository only — Cannot dispatch workflows in other repositories. This prevents cross-repository workflow triggering which could be a security risk.
- Allowlist enforcement — Only workflows explicitly listed in the
workflowsconfiguration can be dispatched. Requests for unlisted workflows are rejected. - Compile-time validation — Workflows are validated at compile time to catch configuration errors early.
Use Cases
Section titled “Use Cases”Campaign Orchestration:
safe-outputs: dispatch-workflow: workflows: [seeder-worker, processor-worker] max: 2An orchestrator workflow can dispatch seeder and worker workflows to discover and process campaign work items.
Multi-Stage Pipelines:
safe-outputs: dispatch-workflow: [deploy-staging, run-tests]A workflow can trigger deployment and testing workflows as separate, trackable runs.
Agent Session Creation (create-agent-session:)
Section titled “Agent Session Creation (create-agent-session:)”Creates Copilot agent sessions. Requires COPILOT_GITHUB_TOKEN or GH_AW_GITHUB_TOKEN PAT—default GITHUB_TOKEN lacks permissions.
Assign to Agent (assign-to-agent:)
Section titled “Assign to Agent (assign-to-agent:)”Programmatically assigns GitHub Copilot agents to existing issues or pull requests through workflow automation. This safe output automates the standard GitHub workflow for assigning issues to Copilot. Requires fine-grained PAT with actions, contents, issues, pull requests write access stored as GH_AW_AGENT_TOKEN, or GitHub App token. Supported agents: copilot (copilot-swe-agent).
Auto-resolves target from workflow context (issue/PR events) when issue_number or pull_number not explicitly provided. Restrict with allowed list. Target: "triggering" (default), "*" (any), or number.
safe-outputs: assign-to-agent: name: "copilot" # default agent (default: "copilot") allowed: [copilot] # restrict to specific agents (optional) max: 1 # max assignments (default: 1) target: "triggering" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryBehavior:
target: "triggering"— Auto-resolves fromgithub.event.issue.numberorgithub.event.pull_request.numbertarget: "*"— Requires explicitissue_numberorpull_numberin agent outputtarget: "123"— Always uses issue/PR #123
Assignee Filtering:
When allowed list is configured, existing agent assignees not in the list are removed while regular user assignees are preserved.
Assign to User (assign-to-user:)
Section titled “Assign to User (assign-to-user:)”Assigns users to issues. Restrict with allowed list. Target: "triggering" (issue event), "*" (any), or number. Supports single or multiple assignees.
safe-outputs: assign-to-user: allowed: [user1, user2] # restrict to specific users max: 3 # max assignments (default: 1) target: "*" # "triggering" (default), "*", or number target-repo: "owner/repo" # cross-repositoryCross-Repository Operations
Section titled “Cross-Repository Operations”Many safe outputs support target-repo. Requires PAT (github-token or GH_AW_GITHUB_TOKEN)—default GITHUB_TOKEN is current-repo only. Use specific names (no wildcards).
safe-outputs: github-token: ${{ secrets.CROSS_REPO_PAT }} create-issue: target-repo: "org/tracking-repo"Automatically Added Tools
Section titled “Automatically Added Tools”When create-pull-request or push-to-pull-request-branch are configured, file editing tools (Edit, MultiEdit, Write, NotebookEdit) and git commands (checkout, branch, switch, add, rm, commit, merge) are automatically enabled.
Security and Sanitization
Section titled “Security and Sanitization”Auto-sanitization: XML escaped, HTTPS only, domain allowlist (GitHub by default), 0.5MB/65k line limits, control char stripping.
safe-outputs: allowed-domains: [api.github.com] # GitHub domains always included allowed-github-references: [] # Escape all GitHub referencesDomain Filtering (allowed-domains): Controls which domains are allowed in URLs. URLs from other domains are replaced with (redacted).
Reference Escaping (allowed-github-references): Controls which GitHub repository references (#123, owner/repo#456) are allowed in workflow output. When configured, references to unlisted repositories are escaped with backticks to prevent GitHub from creating timeline items. This is particularly useful for SideRepoOps workflows to prevent automation from cluttering your main repository’s timeline.
Configuration options:
[]— Escape all references (prevents all timeline items)["repo"]— Allow only the target repository’s references["repo", "owner/other-repo"]— Allow specific repositories- Not specified (default) — All references allowed
Example for clean automation:
safe-outputs: allowed-github-references: [] # Escape all references create-issue: target-repo: "my-org/main-repo"With [], references like #123 become `#123` and other/repo#456 becomes `other/repo#456`, preventing timeline clutter while preserving the information.
Global Configuration Options
Section titled “Global Configuration Options”Custom GitHub Token (github-token:)
Section titled “Custom GitHub Token (github-token:)”Token precedence: GH_AW_GITHUB_TOKEN → GITHUB_TOKEN (default). Override globally or per safe output:
safe-outputs: github-token: ${{ secrets.CUSTOM_PAT }} # global create-issue: create-pull-request: github-token: ${{ secrets.PR_PAT }} # per-outputGitHub App Token (app:)
Section titled “GitHub App Token (app:)”Use GitHub App tokens for enhanced security: on-demand minting, auto-revocation, fine-grained permissions, better attribution. Supports config import from shared workflows.
safe-outputs: app: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} owner: "my-org" # optional: installation owner repositories: ["repo1", "repo2"] # optional: scope to repos create-issue:Maximum Patch Size (max-patch-size:)
Section titled “Maximum Patch Size (max-patch-size:)”Limits git patch size for PR operations (1-10,240 KB, default: 1024 KB):
safe-outputs: max-patch-size: 512 # max patch size in KB create-pull-request:Assigning to Copilot
Section titled “Assigning to Copilot”Use assignees: copilot or reviewers: copilot for bot assignment. Requires GH_AW_AGENT_TOKEN (or fallback to GH_AW_GITHUB_TOKEN/GITHUB_TOKEN)—uses GraphQL API to assign the bot.
Custom Runner Image
Section titled “Custom Runner Image”Specify custom runner for safe output jobs (default: ubuntu-slim): runs-on: ubuntu-22.04
Threat Detection
Section titled “Threat Detection”Auto-enabled. Analyzes output for prompt injection, secret leaks, malicious patches. See Threat Detection Guide.
Agentic Campaign Workflows
Section titled “Agentic Campaign Workflows”Combine create-issue + update-project for coordinated initiatives. Returns campaign ID, applies z_campaign_<id> labels, syncs boards. See Campaign Workflows.
Custom Messages (messages:)
Section titled “Custom Messages (messages:)”Customize notifications using template variables and Markdown. Import from shared workflows (local overrides imported).
safe-outputs: messages: footer: "> 🤖 Generated by [{workflow_name}]({run_url})" append-only-comments: true run-started: "🚀 Processing {event_type}..." run-success: "✅ Completed successfully" run-failure: "❌ Encountered {status}" create-issue:Templates: footer, footer-install, staged-title, staged-description, run-started, run-success, run-failure
Options: append-only-comments (default: false)
Variables: {workflow_name}, {run_url}, {triggering_number}, {workflow_source}, {workflow_source_url}, {event_type}, {status}, {operation}
Related Documentation
Section titled “Related Documentation”- Threat Detection Guide - Complete threat detection documentation and examples
- Frontmatter - All configuration options for workflows
- Workflow Structure - Directory layout and organization
- Command Triggers - Special /my-bot triggers and context text
- CLI Commands - CLI commands for workflow management