This is the full developer documentation for GitHub Agentic Workflows
# GitHub Agentic Workflows
> Repository automation, running the coding agents you know and love, with strong guardrails in GitHub Actions.
Imagine a world where improvements to your repositories are automatically delivered each morning, ready for you to review. Issues are automatically triaged, CI failures analyzed, documentation maintained and tests improved. All defined via simple markdown files.
GitHub Agentic Workflows deliver this: repository automation, running the coding agents you know and love, in GitHub Actions, with strong guardrails and security-first design principles.
Use GitHub Copilot, Claude by Anthropic, Gemini from Google or OpenAI Codex for event-triggered and scheduled jobs to improve your repository. GitHub Agentic Workflows [augment](https://github.github.com/gh-aw/reference/faq/#determinism) your existing, deterministic CI/CD with [Continuous AI](https://githubnext.com/projects/continuous-ai) capabilities.
Developed by GitHub and Microsoft, workflows run with added guardrails, using safe outputs and sandboxed execution to help keep your repository safe.
> ⓘ Note: GitHub Agentic Workflows is in early development and may change significantly. Using agentic workflows requires careful attention to security considerations and careful human supervision, and even then things can still go wrong. Use it with caution, and at your own risk.
## Key Features
[Section titled “Key Features”](#key-features)
### [Automated Markdown Workflows](/gh-aw/introduction/overview/#natural-language-to-github-actions)
[Write automation in markdown instead of complex YAML](/gh-aw/introduction/overview/#natural-language-to-github-actions)
### [AI-Powered Decision Making](/gh-aw/introduction/how-they-work/)
[Workflows that understand context and adapt to situations](/gh-aw/introduction/how-they-work/)
### [GitHub Integration](/gh-aw/reference/github-tools/)
[Deep integration with Actions, Issues, PRs, Discussions, and repository management](/gh-aw/reference/github-tools/)
### [Safety First](/gh-aw/introduction/architecture/)
[Sandboxed execution with minimal permissions and safe output processing](/gh-aw/introduction/architecture/)
### [Multiple AI Engines](/gh-aw/reference/engines/)
[Support for Copilot, Claude, Codex, and custom AI processors](/gh-aw/reference/engines/)
### [Continuous AI](/gh-aw/introduction/how-they-work/)
[Systematic, automated application of AI to software collaboration](/gh-aw/introduction/how-they-work/)
## Guardrails Built-In
[Section titled “Guardrails Built-In”](#guardrails-built-in)
AI agents can be manipulated into taking unintended actions—through malicious repository content, compromised tools, or prompt injection. GitHub Agentic Workflows addresses this with five security layers that work together to contain the impact of a confused or compromised agent.
### Read-only tokens
[Section titled “Read-only tokens”](#read-only-tokens)
The AI agent receives a GitHub token scoped to read-only permissions. Even if the agent attempts to create a pull request, push code, or delete a file, the underlying token simply doesn’t allow it. The agent can observe your repository; it cannot change it.
### Zero secrets in the agent
[Section titled “Zero secrets in the agent”](#zero-secrets-in-the-agent)
The agent process never receives write tokens, API keys, or other sensitive credentials. Those secrets exist only in separate, isolated jobs that run *after* the agent has finished and its output has passed review. A compromised agent has nothing to steal and no credentials to misuse.
### Containerized with a network firewall
[Section titled “Containerized with a network firewall”](#containerized-with-a-network-firewall)
The agent runs inside an isolated container. A built-in network firewall—the [Agent Workflow Firewall](/gh-aw/introduction/architecture/#agent-workflow-firewall-awf)—routes all outbound traffic through a Squid proxy enforcing an explicit domain allowlist. Traffic to any other destination is dropped at the kernel level, so a compromised agent cannot exfiltrate data or call out to unexpected servers.
### Safe outputs with strong guardrails
[Section titled “Safe outputs with strong guardrails”](#safe-outputs-with-strong-guardrails)
The agent cannot write to GitHub directly. Instead, it produces a structured artifact describing its intended actions—for example, “create an issue with this title and body.” A separate job with [scoped write permissions](/gh-aw/reference/safe-outputs/) reads that artifact and applies only what your workflow explicitly permits: hard limits per operation (such as a maximum of one issue per run), required title prefixes, and label constraints. The agent requests; a gated job decides.
### Agentic threat detection
[Section titled “Agentic threat detection”](#agentic-threat-detection)
Before any output is applied, a dedicated [threat detection job](/gh-aw/reference/threat-detection/) runs an AI-powered scan of the agent’s proposed changes. It checks for prompt injection attacks, leaked credentials, and malicious code patterns. If anything looks suspicious, the workflow fails immediately and nothing is written to your repository.
See the [Security Architecture](/gh-aw/introduction/architecture/) for a full breakdown of the layered defense-in-depth model.
## Example: Daily Issues Report
[Section titled “Example: Daily Issues Report”](#example-daily-issues-report)
Here’s a simple workflow that runs daily to create an upbeat status report:
```markdown
---
on:
schedule: daily
permissions:
contents: read
issues: read
pull-requests: read
safe-outputs:
create-issue:
title-prefix: "[team-status] "
labels: [report, daily-status]
close-older-issues: true
---
## Daily Issues Report
Create an upbeat daily status report for the team as a GitHub issue.
## What to include
- Recent repository activity (issues, PRs, discussions, releases, code changes)
- Progress tracking, goal reminders and highlights
- Project status and recommendations
- Actionable next steps for maintainers
```
The `gh aw` cli hardens this to a traditional GitHub Actions Workflow (.lock.yml) that runs an AI coding agent (Copilot CLI, Claude Code, Codex, …) in a containerized environment on a schedule or manually. The AI coding agent reads your repository context, analyzes issues, generates visualizations, and creates reports. All defined in natural language rather than complex code.
## Gallery
[Section titled “Gallery”](#gallery)
### [Issue & PR Management](/gh-aw/blog/2026-01-13-meet-the-workflows-issue-management/)
[Automated triage, labeling, and project coordination](/gh-aw/blog/2026-01-13-meet-the-workflows-issue-management/)
### [Continuous Documentation](/gh-aw/blog/2026-01-13-meet-the-workflows-documentation/)
[Continuous documentation maintenance and consistency](/gh-aw/blog/2026-01-13-meet-the-workflows-documentation/)
### [Continuous Improvement](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/)
[Daily code simplification, refactoring, and style improvements](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/)
### [Metrics & Analytics](/gh-aw/blog/2026-01-13-meet-the-workflows-metrics-analytics/)
[Daily reports, trend analysis, and workflow health monitoring](/gh-aw/blog/2026-01-13-meet-the-workflows-metrics-analytics/)
### [Quality & Testing](/gh-aw/blog/2026-01-13-meet-the-workflows-quality-hygiene/)
[CI failure diagnosis, test improvements, and quality checks](/gh-aw/blog/2026-01-13-meet-the-workflows-quality-hygiene/)
### [Multi-Repository](/gh-aw/examples/multi-repo/)
[Feature sync and cross-repo tracking workflows](/gh-aw/examples/multi-repo/)
## Getting Started
[Section titled “Getting Started”](#getting-started)
Install the extension, add a sample workflow, and trigger your first run - all from the command line in minutes.
Your browser doesn't support HTML5 video. [Download Install and add workflow in CLI demo video](/gh-aw/videos/install-and-add-workflow-in-cli.mp4).
## Creating Workflows
[Section titled “Creating Workflows”](#creating-workflows)
Create custom agentic workflows directly from the GitHub web interface using natural language.
Your browser doesn't support HTML5 video. [Download Create workflow on GitHub demo video](/gh-aw/videos/create-workflow-on-github.mp4).
# Agent Factory
> Experimental agentic workflows used by the team to learn and build.
These are experimental agentic workflows used by the GitHub Next team to learn, build, and use agentic workflows. [Browse source files](https://github.com/github/gh-aw/tree/main/.github/workflows).
| Workflow | Agent | Status | Schedule | Command |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------: | :-------: |
| [\[aw\] Failure Investigator (6h)](https://github.com/github/gh-aw/blob/main/.github/workflows/aw-failure-investigator.md) | claude | [![\[aw\] Failure Investigator (6h)](https://github.com/github/gh-aw/actions/workflows/aw-failure-investigator.lock.yml/badge.svg)](https://github.com/github/gh-aw/actions/workflows/aw-failure-investigator.lock.yml) | `every 6h` | - |
| [/cloclo](https://github.com/github/gh-aw/blob/main/.github/workflows/cloclo.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/cloclo.lock.yml) | - | `/cloclo` |
| [ACE Editor Session](https://github.com/github/gh-aw/blob/main/.github/workflows/ace-editor.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/ace-editor.lock.yml) | - | - |
| [Agent Container Smoke Test](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-test-tools.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-test-tools.lock.yml) | - | - |
| [Agent Performance Analyzer - Meta-Orchestrator](https://github.com/github/gh-aw/blob/main/.github/workflows/agent-performance-analyzer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/agent-performance-analyzer.lock.yml) | - | - |
| [Agent Persona Explorer](https://github.com/github/gh-aw/blob/main/.github/workflows/agent-persona-explorer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/agent-persona-explorer.lock.yml) | - | - |
| [Agentic Workflow Audit Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/audit-workflows.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/audit-workflows.lock.yml) | - | - |
| [Agentic Workflow Portfolio Yield](https://github.com/github/gh-aw/blob/main/.github/workflows/aw-portfolio-yield.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/aw-portfolio-yield.lock.yml) | - | - |
| [AI Moderator](https://github.com/github/gh-aw/blob/main/.github/workflows/ai-moderator.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/ai-moderator.lock.yml) | - | - |
| [Approach Validator](https://github.com/github/gh-aw/blob/main/.github/workflows/approach-validator.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/approach-validator.lock.yml) | - | - |
| [Archie](https://github.com/github/gh-aw/blob/main/.github/workflows/archie.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/archie.lock.yml) | - | `/archie` |
| [Architecture Diagram Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-architecture-diagram.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-architecture-diagram.lock.yml) | - | - |
| [Architecture Guardian](https://github.com/github/gh-aw/blob/main/.github/workflows/architecture-guardian.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/architecture-guardian.lock.yml) | - | - |
| [Artifacts Summary](https://github.com/github/gh-aw/blob/main/.github/workflows/artifacts-summary.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/artifacts-summary.lock.yml) | - | - |
| [Auto-Assign Issue](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-assign-issue-to-user.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-assign-issue-to-user.lock.yml) | - | - |
| [Auto-Triage Issues](https://github.com/github/gh-aw/blob/main/.github/workflows/auto-triage-issues.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/auto-triage-issues.lock.yml) | - | - |
| [Basic Research Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/research.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/research.lock.yml) | - | - |
| [Blog Auditor](https://github.com/github/gh-aw/blob/main/.github/workflows/blog-auditor.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/blog-auditor.lock.yml) | - | - |
| [Bot Detection](https://github.com/github/gh-aw/blob/main/.github/workflows/bot-detection.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/bot-detection.lock.yml) | `every 6h` | - |
| [Brave Web Search Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/brave.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/brave.lock.yml) | - | - |
| [Breaking Change Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/breaking-change-checker.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/breaking-change-checker.lock.yml) | - | - |
| [Changeset Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/changeset.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/changeset.lock.yml) | - | - |
| [Chaos PR Bundle Fuzzer](https://github.com/github/gh-aw/blob/main/.github/workflows/chaos-pr-bundle-fuzzer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/chaos-pr-bundle-fuzzer.lock.yml) | - | - |
| [CI Cleaner](https://github.com/github/gh-aw/blob/main/.github/workflows/hourly-ci-cleaner.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/hourly-ci-cleaner.lock.yml) | - | - |
| [CI Failure Doctor](https://github.com/github/gh-aw/blob/main/.github/workflows/ci-doctor.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/ci-doctor.lock.yml) | - | - |
| [CI Optimization Coach](https://github.com/github/gh-aw/blob/main/.github/workflows/ci-coach.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/ci-coach.lock.yml) | `daily around 13:00 on weekdays` | - |
| [Claude Code User Documentation Review](https://github.com/github/gh-aw/blob/main/.github/workflows/claude-code-user-docs-review.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/claude-code-user-docs-review.lock.yml) | - | - |
| [CLI Consistency Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/cli-consistency-checker.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/cli-consistency-checker.lock.yml) | `daily around 13:00 on weekdays` | - |
| [CLI Version Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/cli-version-checker.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/cli-version-checker.lock.yml) | - | - |
| [Code Refiner](https://github.com/github/gh-aw/blob/main/.github/workflows/refiner.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/refiner.lock.yml) | - | - |
| [Code Scanning Fixer](https://github.com/github/gh-aw/blob/main/.github/workflows/code-scanning-fixer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/code-scanning-fixer.lock.yml) | - | - |
| [Code Simplifier](https://github.com/github/gh-aw/blob/main/.github/workflows/code-simplifier.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/code-simplifier.lock.yml) | - | - |
| [Codex GitHub Remote MCP Test](https://github.com/github/gh-aw/blob/main/.github/workflows/codex-github-remote-mcp-test.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/codex-github-remote-mcp-test.lock.yml) | - | - |
| [Commit Changes Analyzer](https://github.com/github/gh-aw/blob/main/.github/workflows/commit-changes-analyzer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/commit-changes-analyzer.lock.yml) | - | - |
| [Constraint Solving — Problem of the Day](https://github.com/github/gh-aw/blob/main/.github/workflows/constraint-solving-potd.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/constraint-solving-potd.lock.yml) | - | - |
| [Contribution Check](https://github.com/github/gh-aw/blob/main/.github/workflows/contribution-check.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/contribution-check.lock.yml) | - | - |
| [Copilot Agent PR Analysis](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-agent-analysis.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/copilot-agent-analysis.lock.yml) | - | - |
| [Copilot Agent Prompt Clustering Analysis](https://github.com/github/gh-aw/blob/main/.github/workflows/prompt-clustering-analysis.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/prompt-clustering-analysis.lock.yml) | - | - |
| [Copilot CLI Deep Research Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-cli-deep-research.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/copilot-cli-deep-research.lock.yml) | - | - |
| [Copilot Opt](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-opt.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/copilot-opt.lock.yml) | `weekly on monday` | - |
| [Copilot PR Conversation NLP Analysis](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-pr-nlp-analysis.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/copilot-pr-nlp-analysis.lock.yml) | `daily around 10:00 on weekdays` | - |
| [Copilot PR Prompt Pattern Analysis](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-pr-prompt-analysis.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/copilot-pr-prompt-analysis.lock.yml) | - | - |
| [Copilot Session Insights](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-session-insights.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/copilot-session-insights.lock.yml) | - | - |
| [Copilot Token Usage Optimizer](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-token-optimizer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/copilot-token-optimizer.lock.yml) | `daily around 14:00 on weekdays` | - |
| [Daily A/B Testing Advisor](https://github.com/github/gh-aw/blob/main/.github/workflows/ab-testing-advisor.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/ab-testing-advisor.lock.yml) | - | - |
| [Daily Agent of the Day Blog Writer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-agent-of-the-day-blog-writer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-agent-of-the-day-blog-writer.lock.yml) | - | - |
| [Daily AgentRx Trace Optimizer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-agentrx-trace-optimizer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-agentrx-trace-optimizer.lock.yml) | - | - |
| [Daily AstroStyleLite Markdown Spellcheck](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-astrostylelite-markdown-spellcheck.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-astrostylelite-markdown-spellcheck.lock.yml) | - | - |
| [Daily AW Cross-Repo Compile Check](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-aw-cross-repo-compile-check.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-aw-cross-repo-compile-check.lock.yml) | - | - |
| [Daily Cache Strategy Analyzer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-cache-strategy-analyzer.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/daily-cache-strategy-analyzer.lock.yml) | - | - |
| [Daily Caveman Optimizer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-caveman-optimizer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-caveman-optimizer.lock.yml) | - | - |
| [Daily Choice Type Test](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-choice-test.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-choice-test.lock.yml) | `daily around 12:00 on weekdays` | - |
| [Daily CLI Performance Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-cli-performance.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-cli-performance.lock.yml) | - | - |
| [Daily CLI Tools Exploratory Tester](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-cli-tools-tester.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-cli-tools-tester.lock.yml) | - | - |
| [Daily Code Metrics and Trend Tracking Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-code-metrics.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-code-metrics.lock.yml) | - | - |
| [Daily Community Attribution Updater](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-community-attribution.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-community-attribution.lock.yml) | - | - |
| [Daily Compiler Quality Check](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-compiler-quality.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-compiler-quality.lock.yml) | - | - |
| [Daily Compiler Threat Spec Optimizer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-compiler-threat-spec-optimizer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-compiler-threat-spec-optimizer.lock.yml) | - | - |
| [Daily Copilot PR Merged Report](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-pr-merged-report.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/copilot-pr-merged-report.lock.yml) | `daily around 15:00 on weekdays` | - |
| [Daily Copilot Token Usage Audit](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-token-audit.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/copilot-token-audit.lock.yml) | `daily around 12:00 on weekdays` | - |
| [Daily Documentation Healer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-doc-healer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-doc-healer.lock.yml) | - | - |
| [Daily Documentation Updater](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-doc-updater.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-doc-updater.lock.yml) | - | - |
| [Daily Fact About gh-aw](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-fact.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/daily-fact.lock.yml) | `daily around 14:00 on weekdays` | - |
| [Daily File Diet](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-file-diet.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-file-diet.lock.yml) | - | - |
| [Daily Firewall Logs Collector and Reporter](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-firewall-report.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-firewall-report.lock.yml) | - | - |
| [Daily Go Function Namer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-function-namer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-function-namer.lock.yml) | - | - |
| [Daily Grafana OTel Instrumentation Advisor](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-grafana-otel-instrumentation-advisor.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-grafana-otel-instrumentation-advisor.lock.yml) | - | - |
| [Daily Hippo Learn](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-hippo-learn.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-hippo-learn.lock.yml) | `daily around 7:00` | - |
| [Daily Issues Report Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-issues-report.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-issues-report.lock.yml) | - | - |
| [Daily Malicious Code Scan Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-malicious-code-scan.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-malicious-code-scan.lock.yml) | - | - |
| [Daily MCP Tool Concurrency Analysis](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-mcp-concurrency-analysis.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-mcp-concurrency-analysis.lock.yml) | - | - |
| [Daily Model Inventory Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-model-inventory.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-model-inventory.lock.yml) | - | - |
| [Daily News](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-news.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-news.lock.yml) | `daily around 9:00 on weekdays` | - |
| [Daily Observability Report for AWF Firewall and MCP Gateway](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-observability-report.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/daily-observability-report.lock.yml) | - | - |
| [Daily OTel Instrumentation Advisor](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-otel-instrumentation-advisor.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-otel-instrumentation-advisor.lock.yml) | - | - |
| [Daily Project Performance Summary Generator (Using MCP Scripts)](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-performance-summary.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-performance-summary.lock.yml) | - | - |
| [Daily Regulatory Report Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-regulatory.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-regulatory.lock.yml) | - | - |
| [Daily Reliability Review](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-reliability-review.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-reliability-review.lock.yml) | - | - |
| [Daily Rendering Scripts Verifier](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-rendering-scripts-verifier.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-rendering-scripts-verifier.lock.yml) | - | - |
| [Daily Safe Output Integrator](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-safe-output-integrator.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-safe-output-integrator.lock.yml) | - | - |
| [Daily Safe Output Tool Optimizer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-safe-output-optimizer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-safe-output-optimizer.lock.yml) | - | - |
| [Daily Safe Outputs Conformance Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-safe-outputs-conformance.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-safe-outputs-conformance.lock.yml) | - | - |
| [Daily Secrets Analysis Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-secrets-analysis.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-secrets-analysis.lock.yml) | - | - |
| [Daily Security Observability Report](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-security-observability.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-security-observability.lock.yml) | - | - |
| [Daily Security Red Team Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-security-red-team.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-security-red-team.lock.yml) | - | - |
| [Daily Semgrep Scan](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-semgrep-scan.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-semgrep-scan.lock.yml) | - | - |
| [Daily Sentrux Report](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-sentrux-report.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-sentrux-report.lock.yml) | - | - |
| [Daily Skill Optimizer Improvements](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-skill-optimizer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-skill-optimizer.lock.yml) | - | - |
| [Daily SPDD Spec Planner](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-spdd-spec-planner.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-spdd-spec-planner.lock.yml) | - | - |
| [Daily Sub-Agent Optimizer](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-subagent-optimizer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-subagent-optimizer.lock.yml) | - | - |
| [Daily Syntax Error Quality Check](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-syntax-error-quality.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-syntax-error-quality.lock.yml) | - | - |
| [Daily Team Evolution Insights](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-team-evolution-insights.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-team-evolution-insights.lock.yml) | - | - |
| [Daily Team Status](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-team-status.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-team-status.lock.yml) | `daily around 9:00 on weekdays` | - |
| [Daily Testify Uber Super Expert](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-testify-uber-super-expert.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-testify-uber-super-expert.lock.yml) | - | - |
| [Daily Token Consumption Report (Sentry OTel)](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-token-consumption-report.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-token-consumption-report.lock.yml) | - | - |
| [Daily Workflow Updater](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-workflow-updater.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-workflow-updater.lock.yml) | - | - |
| [daily-experiment-report](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-experiment-report.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-experiment-report.lock.yml) | - | - |
| [DataFlow PR & Discussion Dataset Builder](https://github.com/github/gh-aw/blob/main/.github/workflows/dataflow-pr-discussion-dataset.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dataflow-pr-discussion-dataset.lock.yml) | - | - |
| [Dead Code Removal Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/dead-code-remover.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dead-code-remover.lock.yml) | - | - |
| [DeepReport - Intelligence Gathering Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/deep-report.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/deep-report.lock.yml) | `daily around 15:00 on weekdays` | - |
| [Delight](https://github.com/github/gh-aw/blob/main/.github/workflows/delight.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/delight.lock.yml) | - | - |
| [Dependabot Burner](https://github.com/github/gh-aw/blob/main/.github/workflows/dependabot-burner.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dependabot-burner.lock.yml) | - | - |
| [Dependabot Campaign](https://github.com/github/gh-aw/blob/main/.github/workflows/dependabot-campaign.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dependabot-campaign.lock.yml) | - | - |
| [Dependabot Dependency Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/dependabot-go-checker.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dependabot-go-checker.lock.yml) | `20 9 * * 1,3,5` | - |
| [Dependabot Local Repair](https://github.com/github/gh-aw/blob/main/.github/workflows/dependabot-repair.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dependabot-repair.lock.yml) | - | - |
| [Dependabot Worker](https://github.com/github/gh-aw/blob/main/.github/workflows/dependabot-worker.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dependabot-worker.lock.yml) | - | - |
| [Deployment Incident Monitor](https://github.com/github/gh-aw/blob/main/.github/workflows/deployment-incident-monitor.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/deployment-incident-monitor.lock.yml) | - | - |
| [Design Decision Gate](https://github.com/github/gh-aw/blob/main/.github/workflows/design-decision-gate.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/design-decision-gate.lock.yml) | - | - |
| [Dev](https://github.com/github/gh-aw/blob/main/.github/workflows/dev.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dev.lock.yml) | `daily around 9:00` | - |
| [Dev Hawk](https://github.com/github/gh-aw/blob/main/.github/workflows/dev-hawk.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dev-hawk.lock.yml) | - | - |
| [Developer Documentation Consolidator](https://github.com/github/gh-aw/blob/main/.github/workflows/developer-docs-consolidator.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/developer-docs-consolidator.lock.yml) | - | - |
| [Dictation Prompt Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/dictation-prompt.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/dictation-prompt.lock.yml) | `weekly on sunday around 6:00` | - |
| [Discussion Task Miner - Code Quality Improvement Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/discussion-task-miner.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/discussion-task-miner.lock.yml) | - | - |
| [Documentation Noob Tester](https://github.com/github/gh-aw/blob/main/.github/workflows/docs-noob-tester.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/docs-noob-tester.lock.yml) | - | - |
| [Documentation Unbloat](https://github.com/github/gh-aw/blob/main/.github/workflows/unbloat-docs.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/unbloat-docs.lock.yml) | - | - |
| [Draft PR Cleanup](https://github.com/github/gh-aw/blob/main/.github/workflows/draft-pr-cleanup.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/draft-pr-cleanup.lock.yml) | - | - |
| [Duplicate Code Detector](https://github.com/github/gh-aw/blob/main/.github/workflows/duplicate-code-detector.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/duplicate-code-detector.lock.yml) | - | - |
| [Example: Properly Provisioned Permissions](https://github.com/github/gh-aw/blob/main/.github/workflows/example-permissions-warning.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/example-permissions-warning.lock.yml) | - | - |
| [Firewall Test Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/firewall.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/firewall.lock.yml) | - | - |
| [Functional Pragmatist](https://github.com/github/gh-aw/blob/main/.github/workflows/functional-pragmatist.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/functional-pragmatist.lock.yml) | `25 9 * * 2,4` | - |
| [GEO Optimizer Daily Audit](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-geo-optimizer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-geo-optimizer.lock.yml) | - | - |
| [GitHub API Consumption Report Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/api-consumption-report.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/api-consumption-report.lock.yml) | - | - |
| [GitHub MCP Remote Server Tools Report Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/github-mcp-tools-report.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/github-mcp-tools-report.lock.yml) | - | - |
| [GitHub MCP Structural Analysis](https://github.com/github/gh-aw/blob/main/.github/workflows/github-mcp-structural-analysis.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/github-mcp-structural-analysis.lock.yml) | `daily around 11:00 on weekdays` | - |
| [GitHub Remote MCP Authentication Test](https://github.com/github/gh-aw/blob/main/.github/workflows/github-remote-mcp-auth-test.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/github-remote-mcp-auth-test.lock.yml) | - | - |
| [Glossary Maintainer](https://github.com/github/gh-aw/blob/main/.github/workflows/glossary-maintainer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/glossary-maintainer.lock.yml) | `daily around 10:00 on weekdays` | - |
| [Go Fan](https://github.com/github/gh-aw/blob/main/.github/workflows/go-fan.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/go-fan.lock.yml) | - | - |
| [Go Logger Enhancement](https://github.com/github/gh-aw/blob/main/.github/workflows/go-logger.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/go-logger.lock.yml) | - | - |
| [Go Pattern Detector](https://github.com/github/gh-aw/blob/main/.github/workflows/go-pattern-detector.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/go-pattern-detector.lock.yml) | `daily around 14:00 on weekdays` | - |
| [GPL Dependency Cleaner (gpclean)](https://github.com/github/gh-aw/blob/main/.github/workflows/gpclean.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/gpclean.lock.yml) | - | - |
| [Grumpy Code Reviewer](https://github.com/github/gh-aw/blob/main/.github/workflows/grumpy-reviewer.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/grumpy-reviewer.lock.yml) | - | - |
| [Hippo Embed](https://github.com/github/gh-aw/blob/main/.github/workflows/hippo-embed.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/hippo-embed.lock.yml) | - | - |
| [Instructions Janitor](https://github.com/github/gh-aw/blob/main/.github/workflows/instructions-janitor.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/instructions-janitor.lock.yml) | - | - |
| [Issue Arborist](https://github.com/github/gh-aw/blob/main/.github/workflows/issue-arborist.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/issue-arborist.lock.yml) | - | - |
| [Issue Monster](https://github.com/github/gh-aw/blob/main/.github/workflows/issue-monster.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/issue-monster.lock.yml) | - | - |
| [Issue Summary to Notion](https://github.com/github/gh-aw/blob/main/.github/workflows/notion-issue-summary.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/notion-issue-summary.lock.yml) | - | - |
| [Issue Triage Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/issue-triage-agent.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/issue-triage-agent.lock.yml) | - | - |
| [jsweep - JavaScript Unbloater](https://github.com/github/gh-aw/blob/main/.github/workflows/jsweep.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/jsweep.lock.yml) | - | - |
| [Layout Specification Maintainer](https://github.com/github/gh-aw/blob/main/.github/workflows/layout-spec-maintainer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/layout-spec-maintainer.lock.yml) | - | - |
| [Linter Miner](https://github.com/github/gh-aw/blob/main/.github/workflows/linter-miner.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/linter-miner.lock.yml) | - | - |
| [LintMonster](https://github.com/github/gh-aw/blob/main/.github/workflows/lint-monster.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/lint-monster.lock.yml) | - | - |
| [Lockfile Statistics Analysis Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/lockfile-stats.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/lockfile-stats.lock.yml) | - | - |
| [Matt Pocock Skills Reviewer](https://github.com/github/gh-aw/blob/main/.github/workflows/mattpocock-skills-reviewer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/mattpocock-skills-reviewer.lock.yml) | - | - |
| [MCP Inspector Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/mcp-inspector.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/mcp-inspector.lock.yml) | - | - |
| [Mergefest](https://github.com/github/gh-aw/blob/main/.github/workflows/mergefest.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/mergefest.lock.yml) | - | - |
| [Metrics Collector - Infrastructure Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/metrics-collector.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/metrics-collector.lock.yml) | - | - |
| [Multi-Device Docs Tester](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-multi-device-docs-tester.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/daily-multi-device-docs-tester.lock.yml) | - | - |
| [Necromancer](https://github.com/github/gh-aw/blob/main/.github/workflows/necromancer.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/necromancer.lock.yml) | - | - |
| [Organization Health Report](https://github.com/github/gh-aw/blob/main/.github/workflows/org-health-report.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/org-health-report.lock.yml) | - | - |
| [OTLP Data Quality Validator](https://github.com/github/gh-aw/blob/main/.github/workflows/otlp-data-quality-validator.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/otlp-data-quality-validator.lock.yml) | - | - |
| [Outcome Collector](https://github.com/github/gh-aw/blob/main/.github/workflows/outcome-collector.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/outcome-collector.lock.yml) | - | - |
| [Package Specification Enforcer](https://github.com/github/gh-aw/blob/main/.github/workflows/spec-enforcer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/spec-enforcer.lock.yml) | - | - |
| [Package Specification Extractor](https://github.com/github/gh-aw/blob/main/.github/workflows/spec-extractor.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/spec-extractor.lock.yml) | - | - |
| [Package Specification Librarian](https://github.com/github/gh-aw/blob/main/.github/workflows/spec-librarian.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/spec-librarian.lock.yml) | - | - |
| [Plan Command](https://github.com/github/gh-aw/blob/main/.github/workflows/plan.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/plan.lock.yml) | - | - |
| [Poem Bot - A Creative Agentic Workflow](https://github.com/github/gh-aw/blob/main/.github/workflows/poem-bot.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/poem-bot.lock.yml) | - | - |
| [PR Code Quality Reviewer](https://github.com/github/gh-aw/blob/main/.github/workflows/pr-code-quality-reviewer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/pr-code-quality-reviewer.lock.yml) | - | - |
| [PR Description Updater](https://github.com/github/gh-aw/blob/main/.github/workflows/pr-description-caveman.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/pr-description-caveman.lock.yml) | - | - |
| [PR Nitpick Reviewer](https://github.com/github/gh-aw/blob/main/.github/workflows/pr-nitpick-reviewer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/pr-nitpick-reviewer.lock.yml) | - | - |
| [PR Sous Chef](https://github.com/github/gh-aw/blob/main/.github/workflows/pr-sous-chef.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/pr-sous-chef.lock.yml) | - | - |
| [PR Triage Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/pr-triage-agent.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/pr-triage-agent.lock.yml) | - | - |
| [Python Data Visualization Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/python-data-charts.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/python-data-charts.lock.yml) | - | - |
| [Q](https://github.com/github/gh-aw/blob/main/.github/workflows/q.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/q.lock.yml) | - | `/q` |
| [Rebuild the documentation after making changes](https://github.com/github/gh-aw/blob/main/.github/workflows/technical-doc-writer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/technical-doc-writer.lock.yml) | - | - |
| [Refactoring Cadence](https://github.com/github/gh-aw/blob/main/.github/workflows/refactoring-cadence.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/refactoring-cadence.lock.yml) | - | - |
| [Release](https://github.com/github/gh-aw/blob/main/.github/workflows/release.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/release.lock.yml) | - | - |
| [Repository Audit & Agentic Workflow Opportunity Analyzer](https://github.com/github/gh-aw/blob/main/.github/workflows/repo-audit-analyzer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/repo-audit-analyzer.lock.yml) | - | - |
| [Repository Quality Improvement Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/repository-quality-improver.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/repository-quality-improver.lock.yml) | `daily around 13:00 on weekdays` | - |
| [Repository Tree Map Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/repo-tree-map.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/repo-tree-map.lock.yml) | - | - |
| [Resource Summarizer Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/pdf-summary.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/pdf-summary.lock.yml) | - | - |
| [Safe Output Health Monitor](https://github.com/github/gh-aw/blob/main/.github/workflows/safe-output-health.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/safe-output-health.lock.yml) | - | - |
| [Schema Consistency Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/schema-consistency-checker.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/schema-consistency-checker.lock.yml) | - | - |
| [Schema Feature Coverage Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/schema-feature-coverage.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/schema-feature-coverage.lock.yml) | - | - |
| [Scout](https://github.com/github/gh-aw/blob/main/.github/workflows/scout.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/scout.lock.yml) | - | `/scout` |
| [Security Compliance Campaign](https://github.com/github/gh-aw/blob/main/.github/workflows/security-compliance.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/security-compliance.lock.yml) | - | - |
| [Security Review Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/security-review.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/security-review.lock.yml) | - | - |
| [Semantic Function Refactoring](https://github.com/github/gh-aw/blob/main/.github/workflows/semantic-function-refactor.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/semantic-function-refactor.lock.yml) | - | - |
| [Sergo - Serena Go Expert](https://github.com/github/gh-aw/blob/main/.github/workflows/sergo.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/sergo.lock.yml) | - | - |
| [Slide Deck Maintainer](https://github.com/github/gh-aw/blob/main/.github/workflows/slide-deck-maintainer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/slide-deck-maintainer.lock.yml) | `daily around 16:00 on weekdays` | - |
| [Smoke Agent: all/merged](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-all-merged.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-all-merged.lock.yml) | - | - |
| [Smoke Agent: all/none](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-all-none.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-all-none.lock.yml) | - | - |
| [Smoke Agent: public/approved](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-public-approved.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-public-approved.lock.yml) | - | - |
| [Smoke Agent: public/none](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-public-none.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-public-none.lock.yml) | - | - |
| [Smoke Agent: scoped/approved](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-scoped-approved.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-scoped-approved.lock.yml) | - | - |
| [Smoke Call Workflow](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-call-workflow.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-call-workflow.lock.yml) | - | - |
| [Smoke CI](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-ci.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-ci.lock.yml) | - | - |
| [Smoke Claude](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-claude.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/smoke-claude.lock.yml) | - | - |
| [Smoke Codex](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-codex.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-codex.lock.yml) | - | - |
| [Smoke Copilot](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-copilot.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-copilot.lock.yml) | - | - |
| [Smoke Copilot ARM64](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-copilot-arm.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-copilot-arm.lock.yml) | - | - |
| [Smoke Create Cross-Repo PR](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-create-cross-repo-pr.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-create-cross-repo-pr.lock.yml) | - | - |
| [Smoke Crush](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-crush.md) | crush | [](https://github.com/github/gh-aw/actions/workflows/smoke-crush.lock.yml) | - | - |
| [Smoke Gemini](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-gemini.md) | gemini | [](https://github.com/github/gh-aw/actions/workflows/smoke-gemini.lock.yml) | - | - |
| [Smoke Multi PR](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-multi-pr.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-multi-pr.lock.yml) | - | - |
| [Smoke OpenCode](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-opencode.md) | opencode | [](https://github.com/github/gh-aw/actions/workflows/smoke-opencode.lock.yml) | - | - |
| [Smoke OTEL](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-otel-backends.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-otel-backends.lock.yml) | - | - |
| [Smoke Pi](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-pi.md) | pi | [](https://github.com/github/gh-aw/actions/workflows/smoke-pi.lock.yml) | - | - |
| [Smoke Project](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-project.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-project.lock.yml) | - | - |
| [Smoke Service Ports](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-service-ports.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-service-ports.lock.yml) | - | - |
| [Smoke Temporary ID](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-temporary-id.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-temporary-id.lock.yml) | - | - |
| [Smoke Update Cross-Repo PR](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-update-cross-repo-pr.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-update-cross-repo-pr.lock.yml) | - | - |
| [Smoke Workflow Call](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-workflow-call.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-workflow-call.lock.yml) | - | - |
| [Smoke Workflow Call with Inputs](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-workflow-call-with-inputs.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-workflow-call-with-inputs.lock.yml) | - | - |
| [Stale PR Cleanup](https://github.com/github/gh-aw/blob/main/.github/workflows/stale-pr-cleanup.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/stale-pr-cleanup.lock.yml) | - | - |
| [Stale Repository Identifier](https://github.com/github/gh-aw/blob/main/.github/workflows/stale-repo-identifier.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/stale-repo-identifier.lock.yml) | - | - |
| [Static Analysis Report](https://github.com/github/gh-aw/blob/main/.github/workflows/static-analysis-report.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/static-analysis-report.lock.yml) | - | - |
| [Step Name Alignment](https://github.com/github/gh-aw/blob/main/.github/workflows/step-name-alignment.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/step-name-alignment.lock.yml) | - | - |
| [Sub-Issue Closer](https://github.com/github/gh-aw/blob/main/.github/workflows/sub-issue-closer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/sub-issue-closer.lock.yml) | - | - |
| [Super Linter Report](https://github.com/github/gh-aw/blob/main/.github/workflows/super-linter.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/super-linter.lock.yml) | `daily around 14:00 on weekdays` | - |
| [Terminal Stylist](https://github.com/github/gh-aw/blob/main/.github/workflows/terminal-stylist.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/terminal-stylist.lock.yml) | - | - |
| [Test Create PR Error Handling](https://github.com/github/gh-aw/blob/main/.github/workflows/test-create-pr-error-handling.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/test-create-pr-error-handling.lock.yml) | - | - |
| [Test Dispatcher Workflow](https://github.com/github/gh-aw/blob/main/.github/workflows/test-dispatcher.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/test-dispatcher.lock.yml) | - | - |
| [Test Project URL Explicit Requirement](https://github.com/github/gh-aw/blob/main/.github/workflows/test-project-url-default.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/test-project-url-default.lock.yml) | - | - |
| [Test Quality Sentinel](https://github.com/github/gh-aw/blob/main/.github/workflows/test-quality-sentinel.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/test-quality-sentinel.lock.yml) | - | - |
| [Test Workflow](https://github.com/github/gh-aw/blob/main/.github/workflows/test-workflow.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/test-workflow.lock.yml) | - | - |
| [The Daily Repository Chronicle](https://github.com/github/gh-aw/blob/main/.github/workflows/daily-repo-chronicle.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/daily-repo-chronicle.lock.yml) | `daily around 16:00 on weekdays` | - |
| [The Great Escapi](https://github.com/github/gh-aw/blob/main/.github/workflows/firewall-escape.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/firewall-escape.lock.yml) | - | - |
| [Tidy](https://github.com/github/gh-aw/blob/main/.github/workflows/tidy.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/tidy.lock.yml) | `daily around 7:00` | - |
| [Typist - Go Type Analysis](https://github.com/github/gh-aw/blob/main/.github/workflows/typist.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/typist.lock.yml) | - | - |
| [Ubuntu Actions Image Analyzer](https://github.com/github/gh-aw/blob/main/.github/workflows/ubuntu-image-analyzer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/ubuntu-image-analyzer.lock.yml) | - | - |
| [UK AI Operational Resilience](https://github.com/github/gh-aw/blob/main/.github/workflows/uk-ai-operational-resilience.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/uk-ai-operational-resilience.lock.yml) | - | - |
| [Update Astro](https://github.com/github/gh-aw/blob/main/.github/workflows/update-astro.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/update-astro.lock.yml) | - | - |
| [Video Analysis Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/video-analyzer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/video-analyzer.lock.yml) | - | - |
| [Visual Regression Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/visual-regression-checker.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/visual-regression-checker.lock.yml) | - | - |
| [Weekly Blog Post Writer](https://github.com/github/gh-aw/blob/main/.github/workflows/weekly-blog-post-writer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/weekly-blog-post-writer.lock.yml) | - | - |
| [Weekly Editors Health Check](https://github.com/github/gh-aw/blob/main/.github/workflows/weekly-editors-health-check.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/weekly-editors-health-check.lock.yml) | - | - |
| [Weekly Issue Summary](https://github.com/github/gh-aw/blob/main/.github/workflows/weekly-issue-summary.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/weekly-issue-summary.lock.yml) | `weekly on monday around 15:00` | - |
| [Weekly Safe Outputs Specification Review](https://github.com/github/gh-aw/blob/main/.github/workflows/weekly-safe-outputs-spec-review.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/weekly-safe-outputs-spec-review.lock.yml) | `weekly on monday` | - |
| [Weekly Workflow Analysis](https://github.com/github/gh-aw/blob/main/.github/workflows/example-workflow-analyzer.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/example-workflow-analyzer.lock.yml) | - | - |
| [Workflow Craft Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/craft.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/craft.lock.yml) | - | `/my` |
| [Workflow Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/workflow-generator.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/workflow-generator.lock.yml) | - | - |
| [Workflow Health Manager - Meta-Orchestrator](https://github.com/github/gh-aw/blob/main/.github/workflows/workflow-health-manager.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/workflow-health-manager.lock.yml) | - | - |
| [Workflow Normalizer](https://github.com/github/gh-aw/blob/main/.github/workflows/workflow-normalizer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/workflow-normalizer.lock.yml) | - | - |
| [Workflow Skill Extractor](https://github.com/github/gh-aw/blob/main/.github/workflows/workflow-skill-extractor.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/workflow-skill-extractor.lock.yml) | - | - |
Note
Badges update automatically. Click badges for run details or workflow names for source files.
# Welcome to Peli's Agent Factory
> It's basically a candy shop chocolate factory of agentic workflows.

Welcome, welcome, WELCOME to Peli’s Agent Factory!
Imagine a software repository where AI agents work alongside your team - not replacing developers, but handling the repetitive, time-consuming tasks that slow down collaboration and forward progress.
Peli’s Agent Factory is our exploration of what happens when you take the design philosophy of **“let’s create a new automated agentic workflow for that”** as the answer to almost every opportunity that arises! What happens when you **max out on automated agentic workflows** - when you make and use dozens of specialized, automated AI agentic workflows and use them in practice.
Software development is changing rapidly. This is our attempt to understand how automated agentic AI can make software teams more efficient, collaborative, and more enjoyable.
It’s basically a candy shop chocolate factory of agentic workflows. And we’d like to share it with you.
Let’s explore together!
## What Is Peli’s Agent Factory?
[Section titled “What Is Peli’s Agent Factory?”](#what-is-pelis-agent-factory)
Peli’s factory is a collection of [**automated agentic workflows**](https://gh.io/gh-aw) we use in practice. We have built and operated **over 100 automated agentic workflows** within the [`github/gh-aw`](https://github.com/github/gh-aw) repository. These were used mostly in the context of the [`github/gh-aw`](https://github.com/github/gh-aw) project itself, but some have also been applied at scale in GitHub internal repositories. These weren’t hypothetical demos - they were working agents that:
* [Triage incoming issues](/gh-aw/blog/2026-01-13-meet-the-workflows/)
* [Diagnose CI failures](/gh-aw/blog/2026-01-13-meet-the-workflows-quality-hygiene/)
* [Maintain documentation](/gh-aw/blog/2026-01-13-meet-the-workflows-documentation/)
* [Improve test coverage](/gh-aw/blog/2026-01-13-meet-the-workflows-testing-validation/)
* [Monitor security compliance](/gh-aw/blog/2026-01-13-meet-the-workflows-security-compliance/)
* [Optimize workflow efficiency](/gh-aw/blog/2026-01-13-meet-the-workflows-metrics-analytics/)
* [Execute multi-day projects](/gh-aw/blog/2026-01-13-meet-the-workflows-multi-phase/)
* Even [write poetry to boost team morale](/gh-aw/blog/2026-01-13-meet-the-workflows-creative-culture/)
Some workflows are [“read-only analysts”](/gh-aw/blog/2026-01-13-meet-the-workflows-metrics-analytics/). Others [proactively propose changes through pull requests](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/). Some are [meta-agents that monitor and improve the health of other workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-metrics-analytics/).
We know we’re taking things to an extreme here. Most repositories won’t need dozens of agentic workflows. No one can read all these outputs (except, of course, another workflow). But by pushing the boundaries, we learned valuable lessons about what works, what doesn’t, and how to design safe, effective agentic workflows that teams can trust and use.
## Why Build a Factory?
[Section titled “Why Build a Factory?”](#why-build-a-factory)
When we started exploring agentic workflows, we faced a fundamental question: **What should repository-level automated agentic workflows actually do?**
Rather than trying to build one “perfect” agent, we took a broad, heterogeneous approach:
1. **Embrace diversity** - Create many specialized workflows as we identified opportunities
2. **Use them continuously** - Run them in real development workflows
3. **Observe what works** - Find which patterns work and which fail
4. **Share the knowledge** - Catalog the structures that make agents safe and effective
The factory becomes both an experiment and a reference collection - a living library of patterns that others can study, adapt, and remix. Each workflow is written in natural language using Markdown, then converted into secure [GitHub Actions](https://github.com/features/actions) that run with carefully scoped permissions with guardrails. Everything is observable, auditable, and remixable.
## Meet the Workflows
[Section titled “Meet the Workflows”](#meet-the-workflows)
In our first series, [Meet the Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows/), we’ll take you on a tour of the most interesting agents in the factory. Each article is bite-sized. If you’d like to skip ahead, here’s the full list of articles in the series:
1. [Meet a Simple Triage Workflow](/gh-aw/blog/2026-01-13-meet-the-workflows/)
2. [Introducing Continuous Simplicity](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/)
3. [Introducing Continuous Refactoring](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-refactoring/)
4. [Introducing Continuous Style](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-style/)
5. [Introducing Continuous Improvement](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-improvement/)
6. [Introducing Continuous Documentation](/gh-aw/blog/2026-01-13-meet-the-workflows-documentation/)
After that we have a cornucopia of specialized workflow categories for you to dip into:
* [Meet the Issue & PR Management Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-issue-management/)
* [Meet the Fault Investigation Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-quality-hygiene/)
* [Meet the Metrics & Analytics Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-metrics-analytics/)
* [Meet the Operations & Release Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-operations-release/)
* [Meet the Security-related Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-security-compliance/)
* [Meet the Teamwork & Culture Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-creative-culture/)
* [Meet the Interactive & ChatOps Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-interactive-chatops/)
* [Meet the Testing & Validation Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-testing-validation/)
* [Meet the Tool & Infrastructure Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-tool-infrastructure/)
* [Introducing Multi-Phase Improver Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-multi-phase/)
* [Meet the Organization & Cross-Repo Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-organization/)
* [Go Deep with Advanced Analytics & ML Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-advanced-analytics/)
* [Go Deep with Project Coordination Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows-campaigns/)
Every post comes with instructions about how to add the workflow to your own repository, or customize and remix it to create your own variant.
## What We’re Learning
[Section titled “What We’re Learning”](#what-were-learning)
Running this many agents in production is a learning experience! We’ve watched agents succeed spectacularly and fail in instructive ways. Over the next few weeks, we’ll also be sharing what we’ve learned through a series of detailed articles. We’ll be looking at the design and operational patterns we’ve discovered, security lessons, and practical guides for building your own workflows.
To give a taste, some key lessons are emerging:
* **Repository-level automation is powerful** - Agents embedded in the development workflow can have outsized impact
* **Specialization reveals possibilities** - Focused agents allowed us to find more useful applications of automation than a single monolithic coding agent
* **Guardrails enable innovation** - Strict constraints actually make it easier to experiment safely
* **Meta-agents are valuable** - Agents that watch other agents become incredibly valuable
* **Cost-quality tradeoffs are real** - Longer analyses aren’t always better
We’ll dive deeper into these lessons in upcoming articles.
## Try It Yourself
[Section titled “Try It Yourself”](#try-it-yourself)
Want to start with automated agentic workflows on GitHub? See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[Meet the Workflows](/gh-aw/blog/2026-01-13-meet-the-workflows/)** - The 19-part tour of the workflows
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Credits
[Section titled “Credits”](#credits)
**Peli’s Agent Factory** is by GitHub Next, Microsoft Research and collaborators, including Peli de Halleux, Don Syme, Mara Kiefer, Edward Aftandilian, Russell Horton, Jiaxiao Zhou. This is part of GitHub Next’s exploration of [Continuous AI](https://githubnext.com/projects/continuous-ai) - making AI-enriched automation as routine as CI/CD.
## Factory Status
[Section titled “Factory Status”](#factory-status)
[Current Factory Status](/gh-aw/agent-factory-status/)
# Meet the Workflows: Issue Triage
> A curated tour of triage and summarization workflows in the factory

Welcome back to [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
We’re the GitHub Next team. Over the past months, we’ve built and operated a collection of automated agentic workflows. These aren’t just demos - these are real agents doing actual work in our [`github/gh-aw`](https://github.com/github/gh-aw) repository and others.
Think of this as your guided tour through our agent factory. We’re showcasing the workflows that caught our attention. Every workflow links to its source markdown file, so you can peek under the hood and see exactly how it works.
## Starting Simple: Automated Issue Triage
[Section titled “Starting Simple: Automated Issue Triage”](#starting-simple-automated-issue-triage)
To start the tour, let’s begin with one of the simpler workflows that **handles incoming activity** - issue triage.
Issue triage represents a “hello world” of automated agentic workflows: practical, immediately useful, relatively simple, and impactful. It’s used as the starter example in other agentic automation technologies like [Claude Code in GitHub Actions](https://code.claude.com/docs/en/github-actions).
When a new issue is opened, the triage agent analyzes its content, does research in the codebase and other issues, responds with a comment, and applies appropriate labels based on predefined categories. This helps maintainers quickly understand the nature of incoming issues without manual review.
Let’s take a look at the full **[Issue Triage Agent](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/issue-triage-agent.md?plain=1)**:
```markdown
---
timeout-minutes: 5
on:
issue:
types: [opened, reopened]
permissions:
issues: read
tools:
github:
toolsets: [issues, labels]
safe-outputs:
add-labels:
allowed: [bug, feature, enhancement, documentation, question, help-wanted, good-first-issue]
add-comment: {}
---
# Issue Triage Agent
List open issues in ${{ github.repository }} that have no labels. For each
unlabeled issue, analyze the title and body, then add one of the allowed
labels: `bug`, `feature`, `enhancement`, `documentation`, `question`,
`help-wanted`, or `good-first-issue`.
Skip issues that:
- Already have any of these labels
- Have been assigned to any user (especially non-bot users)
Do research on the issue in the context of the codebase and, after
adding the label to an issue, mention the issue author in a comment, explain
why the label was added and give a brief summary of how the issue may be
addressed.
```
Note how concise this is - it’s like reading a to-do list for the agent. The workflow runs whenever a new issue is opened or reopened. It checks for unlabeled issues, analyzes their content, and applies appropriate labels based on content analysis. It even leaves a friendly comment explaining the label choice.
In the frontmatter, we define [permissions](/gh-aw/reference/frontmatter/#permissions-permissions), [tools](/gh-aw/reference/tools/), and [safe outputs](/gh-aw/reference/safe-outputs/). This ensures the agent only has access to what it needs and can’t perform any unsafe actions. The natural language instructions in the body guide the agent’s behavior in a clear, human-readable way.
Issue triage workflows in public repositories may need to process issues from all contributors. By default, `min-integrity: approved` restricts agent visibility to owners, members, and collaborators. If you are a maintainer in a public repository and need your triage agent to see and label issues from users without push access, set `min-integrity: none` in your GitHub tools configuration. See [Integrity Filtering](/gh-aw/reference/integrity/) for security considerations and best practices.
We’ve deliberately kept this workflow ultra-simple. In practice, in your own repo, **customization** is key. Triage differs in every repository. Tailoring workflows to your specific context will make them more effective. Generic agents are okay, but customized ones are often a better fit.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add this workflow to your own repository and remix it as follows:
**Issue Triage Agent:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/issue-triage-agent.md
```
Then edit and remix the workflow specification to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Next Up: Code Quality & Refactoring Workflows
[Section titled “Next Up: Code Quality & Refactoring Workflows”](#next-up-code-quality--refactoring-workflows)
Now that we’ve explored how triage workflows help us stay on top of incoming activity, let’s turn to something far more radical and powerful: agents that continuously improve code.
Continue reading: [Continuous Simplicity →](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/)
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
***
*This is part 1 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Advanced Analytics & ML
> A curated tour of workflows that use ML to extract insights from agent behavior

*Ooh!* Time to plunge into the *data wonderland* at [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! Where numbers dance and patterns sing!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-organization/), we explored organization and cross-repo workflows that operate at enterprise scale - analyzing dozens of repositories together to find patterns and outliers that single-repo analysis would miss. We learned that perspective matters: what looks normal in isolation might signal drift at scale.
Beyond tracking basic metrics (run time, cost, success rate), we wanted deeper insights into *how* our agents actually behave and *how* developers interact with them. What patterns emerge from thousands of agent prompts? What makes some PR conversations more effective than others? How do usage patterns reveal improvement opportunities? This is where we brought out the big guns: machine learning, natural language processing, sentiment analysis, and clustering algorithms. Advanced analytics workflows don’t just count things - they understand them, finding patterns and insights that direct observation would never reveal.
## Advanced Analytics & ML Workflows
[Section titled “Advanced Analytics & ML Workflows”](#advanced-analytics--ml-workflows)
These agents use sophisticated analysis techniques to extract insights:
* **[Copilot Session Insights](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/copilot-session-insights.md?plain=1)** - Analyzes Copilot coding agent usage patterns and metrics - **32 analysis discussions**
* **[Copilot PR NLP Analysis](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/copilot-pr-nlp-analysis.md?plain=1)** - Natural language processing on PR conversations
* **[Prompt Clustering Analysis](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/prompt-clustering-analysis.md?plain=1)** - Clusters and categorizes agent prompts using ML - **27 analysis discussions**
* **[Copilot Agent Analysis](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/copilot-agent-analysis.md?plain=1)** - Deep analysis of agent behavior patterns - **48 daily analysis discussions**
Prompt Clustering Analysis has created **27 analysis discussions** using ML to categorize thousands of agent prompts - for example, [#6918](https://github.com/github/gh-aw/discussions/6918) clustering agent prompts to identify patterns and optimization opportunities. It revealed patterns we never noticed (“oh, 40% of our prompts are about error handling”).
Copilot PR NLP Analysis applies natural language processing to PR conversations, performing sentiment analysis and identifying linguistic patterns across agent interactions. It found that PRs with questions in the title get faster review.
Copilot Session Insights has created **32 analysis discussions** examining Copilot coding agent usage patterns and metrics across the workflow ecosystem. It identifies common patterns and failure modes.
Copilot Coding Agent Analysis has created **48 daily analysis discussions** providing deep analysis of agent behavior patterns - for example, [#6913](https://github.com/github/gh-aw/discussions/6913) with the daily Copilot coding agent analysis.
What we learned: **meta-analysis is powerful** - using AI to analyze AI systems reveals insights that direct observation misses. These workflows helped us understand not just what our agents do, but *how* they behave and how users interact with them.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix it as follows:
**Copilot Session Insights:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/copilot-agent-analysis.md
```
**Copilot PR NLP Analysis:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/copilot-pr-nlp-analysis
```
**Prompt Clustering Analysis:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/prompt-clustering-analysis.md
```
**Copilot Agent Analysis:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/copilot-agent-analysis.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Project Coordination Workflows
[Section titled “Next Up: Project Coordination Workflows”](#next-up-project-coordination-workflows)
We’ve reached the final stop: coordinating multiple agents toward shared, complex goals across extended timelines.
Continue reading: [Project Coordination Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-campaigns/)
***
*This is part 18 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Project Coordination
> A curated tour of workflows that coordinate multi-agent projects

My dear friends, we’ve arrived at the *grand finale* - the most spectacular room of all in [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
We’ve journeyed through 18 categories of workflows - from triage bots to code quality improvers, from security guards to creative poets, culminating in [advanced analytics](/gh-aw/blog/2026-01-13-meet-the-workflows-advanced-analytics/) that use machine learning to understand agent behavior patterns. Each workflow handles its individual task admirably.
But here’s the ultimate challenge: how do you coordinate *multiple* agents working toward a shared goal? How do you break down a large initiative like “migrate all workflows to a new engine” into trackable sub-tasks that different agents can tackle? How do you monitor progress, alert on delays, and ensure the whole is greater than the sum of its parts? This final post explores planning, task-decomposition and project coordination workflows - the orchestration layer that proves AI agents can handle not just individual tasks, but entire structured projects requiring careful coordination and progress tracking.
## Planning & Project Coordination Workflows
[Section titled “Planning & Project Coordination Workflows”](#planning--project-coordination-workflows)
These agents coordinate multi-agent plans and projects:
* **[Plan Command](https://github.com/github/gh-aw/tree/2c1f68a721ae7b3b67d0c2d93decf1fa5bcf7ee3/.github/workflows/plan.md?plain=1)** - Breaks down issues into actionable sub-tasks via `/plan` command - **514 merged PRs out of 761 proposed (67% merge rate)**
* **[Discussion Task Miner](https://github.com/github/gh-aw/tree/2c1f68a721ae7b3b67d0c2d93decf1fa5bcf7ee3/.github/workflows/discussion-task-miner.md?plain=1)** - Extracts actionable tasks from discussion threads - **60 merged PRs out of 105 proposed (57% merge rate)**
Plan Command has contributed **514 merged PRs out of 761 proposed (67% merge rate)**, providing on-demand task decomposition that breaks complex issues into actionable sub-tasks. This is the **highest-volume workflow by attribution** in the entire factory. Developers can comment `/plan` on any issue to get an AI-generated breakdown into actionable sub-issues that agents can work on. A verified example causal chain: [Discussion #7631](https://github.com/github/gh-aw/discussions/7631) → [Issue #8058](https://github.com/github/gh-aw/issues/8058) → [PR #8110](https://github.com/github/gh-aw/pull/8110).
Discussion Task Miner has contributed **60 merged PRs out of 105 proposed (57% merge rate)**, continuously scanning discussions to extract actionable tasks that might otherwise be lost. The workflow demonstrates perfect causal chain attribution: when it creates an issue from a discussion, and Copilot Coding Assistant later fixes that issue, the resulting PR is correctly attributed to Discussion Task Miner. A verified example: [Discussion #13934](https://github.com/github/gh-aw/discussions/13934) → [Issue #14084](https://github.com/github/gh-aw/issues/14084) → [PR #14129](https://github.com/github/gh-aw/pull/14129). Recent merged examples include [fixing firewall SSL-bump field extraction](https://github.com/github/gh-aw/pull/13920) and [adding security rationale to permissions documentation](https://github.com/github/gh-aw/pull/13918).
We learned that individual agents are great at focused tasks, but orchestrating multiple agents toward a shared goal requires careful architecture. Project coordination isn’t just about breaking down work - it’s about discovering work (Task Miner), planning work (Plan Command), and tracking work (Workflow Health Manager).
These workflows implement patterns like epic issues, progress tracking, and deadline management. They prove that AI agents can handle not just individual tasks, but entire projects when given proper coordination infrastructure.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Plan Command:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/plan.md
```
**Discussion Task Miner:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/discussion-task-miner.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
***
## What We’ve Learned
[Section titled “What We’ve Learned”](#what-weve-learned)
Throughout this 19-part journey, we’ve explored workflows spanning from simple triage bots to sophisticated multi-phase improvers, from security guards to creative poets, from individual task automation to organization-wide orchestration.
The key insight? **AI agents are most powerful when they’re specialized, well-coordinated, and designed for their specific context.** No single agent does everything - instead, we have an ecosystem where each agent excels at its particular job, and they work together through careful orchestration.
We’ve learned that observability is essential, that incremental progress beats heroic efforts, that security needs careful boundaries, and that even “fun” workflows can drive meaningful engagement. We’ve discovered that AI agents can maintain documentation, manage campaigns, analyze their own behavior, and continuously improve codebases - when given the right architecture and guardrails.
As you build your own agentic workflows, remember: start small, measure everything, iterate based on real usage, and don’t be afraid to experiment. The workflows we’ve shown you evolved through experimentation and real-world use. Yours will too.
*This is part 19 (final) of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Continuous Improvement
> Agents that take a holistic view of repository health

Welcome back to [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
In our [previous posts](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/), we’ve explored autonomous cleanup agents. Now we complete the picture with agents that analyze dependencies, type safety, and overall repository quality.
## Continuous Improvement Workflows
[Section titled “Continuous Improvement Workflows”](#continuous-improvement-workflows)
* **[Go Module Usage Expert (aka Go Fan)](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/go-fan.md?plain=1)** - Daily Go module usage reviewer
* **[Typist](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/typist.md?plain=1)** - Analyzes type usage patterns for improved safety
* **[Functional Pragmatist](https://github.com/github/gh-aw/blob/main/.github/workflows/functional-programming-enhancer.md?plain=1)** - Applies functional techniques pragmatically
* **[Repository Quality Improver](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/repository-quality-improver.md?plain=1)** - Holistic code quality analysis
### Go Module Usage Expert: The Dependency Enthusiast
[Section titled “Go Module Usage Expert: The Dependency Enthusiast ”](#go-module-usage-expert-the-dependency-enthusiast-)
The **Go Module Usage Expert** is perhaps the most uniquely characterized workflow in the factory - an “enthusiastic Go module expert” who performs daily deep-dive reviews of the project’s Go dependencies. This isn’t just dependency scanning - it’s thoughtful analysis of **how well we’re using the tools we’ve chosen**.
Most dependency tools focus on vulnerabilities or outdated versions. Go Module Usage Expert asks deeper and more positive questions: Are we using this module’s best features? Have recent updates introduced better patterns we should adopt? Could we use a more appropriate module for this use case? Are we following the module’s recommended practices?
Go Module Usage Expert uses an intelligent selection algorithm. It extracts direct dependencies from `go.mod`, fetches GitHub metadata for each dependency including last update time, sorts by recency to prioritize recently updated modules, uses round-robin selection to cycle through modules ensuring comprehensive coverage, and maintains persistent memory through cache-memory to track which modules were recently reviewed.
This ensures recently updated modules get reviewed first since new features might be relevant, all modules eventually get reviewed so nothing is forgotten, and reviews don’t repeat unnecessarily thanks to cache tracking.
For each module, Go Module Usage Expert researches the repository (releases, docs, best practices), analyzes actual usage patterns using Serena, and generates actionable recommendations. It saves summaries under `scratchpad/mods/` and opens GitHub Discussions.
The output of Go Module Usage Expert is a discussion, which is then often “task mined” for actionable tasks using the [ResearchPlanAssignOps](https://github.github.com/gh-aw/patterns/research-plan-assign-ops/) design pattern.
Let’s take a look at an example of how this works:
1. Go Module Usage Expert created the [Go Module Review: actionlint](https://github.com/github/gh-aw/discussions/7472) discussion after noticing the `actionlint` module was updated.
2. Peli [requested the Plan agent](https://github.com/github/gh-aw/discussions/7472#discussioncomment-15342254) mine for actionable tasks.
3. This created [a parent issue](https://github.com/github/gh-aw/issues/7648) and 5 sub-tasks.
4. The subtasks were then solved by further workflow runs. An example PR is [Implement parallel multi-file actionlint execution](https://github.com/github/gh-aw/issues/7649).
Through this multi-agent causal chain pattern, Go Module Usage Expert has generated **58 merged PRs out of 74 proposed (78% merge rate)** across 67 module reviews. Notable chains include: spinner improvements (4 PRs from [briandowns/spinner review](https://github.com/github/gh-aw/discussions/5094)), MCP SDK v1.2.0 upgrade (5 PRs from [go-sdk review](https://github.com/github/gh-aw/discussions/7710)), and terminal styling overhaul (3 PRs from [lipgloss review](https://github.com/github/gh-aw/discussions/5158)).
### Typist: The Type Safety Advocate
[Section titled “Typist: The Type Safety Advocate”](#typist-the-type-safety-advocate)
The **Typist** analyzes Go type usage patterns with a singular focus: improving type safety. It hunts for untyped code that should be strongly typed, and identifies duplicated type definitions that create confusion.
Typist looks for untyped usages: `interface{}` or `any` where specific types would be better, untyped constants that should have explicit types, and type assertions that could be eliminated with better design. It also hunts for duplicated type definitions - the same types defined in multiple packages, similar types with different names, and type aliases that could be unified.
Using grep patterns and Serena’s semantic analysis, it discovers type definitions, identifies semantic duplicates, analyzes untyped usage patterns, and generates refactoring recommendations.
Typist also uses the [ResearchPlanAssignOps](https://github.github.com/gh-aw/patterns/research-plan-assign-ops/) pattern. This means the job of Typist is not to fix code, but to analyze code and recommend possible improvements.
Let’s take a look at an example of this in practice:
* Typist created the [Typist - Go Type Consistency Analysis Report](https://github.com/github/gh-aw/discussions/4082). This used grep and other tools to perform a comprehensive analysis examining 208 non-test Go files.
* The report found 477 instances of `map[string]any` usage, 36 untyped constants and 30+ uses `any` in function signatures.
* [Peli requested `/plan` on that issue](https://github.com/github/gh-aw/discussions/4082#discussioncomment-14983559), causing the Plan agent to do further research and create 5 issues for work to be done such as [Create unified ToolsConfig struct in tools\_types.go](https://github.com/github/gh-aw/issues/4155).
* 4/5 of these issues were then solved by Copilot. For example [Add unified ToolsConfig struct to replace map\[string\]any pattern](https://github.com/github/gh-aw/pull/4158).
Through this multi-agent causal chain, Typist has produced **19 merged PRs out of 25 proposed (76% merge rate)** from 57 discussions → 22 issues → 25 PRs. The blog example (Discussion #4082 → Issue #4155 → PR #4158) is a verified causal chain.
The static v. dynamic typing debate has raged for decades. Today’s hybrid languages like Go, C#, TypeScript and F# support both strong and dynamic typing. Continuous typing improvement offers **a new and refreshing perspective on this old debate**: rather than enforcing strict typing upfront, we can develop quickly with flexibility, then let autonomous agents like Typist trail behind, strengthening type safety over time. This allows us to get the best of both worlds: rapid development without getting bogged down in type design, while still achieving strong typing and safety as the codebase matures.
### Functional Pragmatist: The Pragmatic Purist
[Section titled “Functional Pragmatist: The Pragmatic Purist ”](#functional-pragmatist-the-pragmatic-purist-)
**Functional Pragmatist** applies moderate functional programming techniques to improve code clarity and safety, balancing pragmatism with functional principles.
The workflow focuses on seven patterns: immutability, functional initialization, transformative operations (map/filter/reduce), functional options pattern, avoiding shared mutable state, pure functions, and reusable logic wrappers.
It searches for opportunities (mutable variables, imperative loops, initialization anti-patterns, global state), scores by safety/clarity/testability improvements, uses Serena for deep analysis, and implements changes like converting to composite literals, using functional options, eliminating globals, extracting pure functions, and creating reusable wrappers (Retry, WithTiming, Memoize).
The workflow is pragmatic: Go’s simple style is respected, for-loops stay when clearer, and abstraction is added only where it genuinely improves code. It runs Tuesday and Thursday mornings, systematically improving patterns over time.
An example PR from our own use of this workflow is [Apply functional programming and immutability improvements](https://github.com/github/gh-aw/pull/12921).
Functional Pragmatist (originally named “Functional Enhancer”) is a recent addition - so far it has created **2 PRs (both merged, 100% merge rate)**, demonstrating that its pragmatic approach to functional patterns is well-received.
### Repository Quality Improver: The Holistic Analyst
[Section titled “Repository Quality Improver: The Holistic Analyst”](#repository-quality-improver-the-holistic-analyst)
**Repository Quality Improver** takes the widest view, selecting a different *focus area* each day to analyze the repository from that perspective.
It uses cache memory to ensure diverse coverage: 60% custom areas (repository-specific concerns), 30% standard categories (code quality, documentation, testing, security, performance), and 10% revisits for consistency.
Standard categories cover fundamentals. Custom areas are repository-specific: error message consistency, CLI flag naming conventions, workflow YAML generation patterns, console output formatting, configuration validation.
The workflow loads recent history, selects the next area, spends 20 minutes on deep analysis, generates discussions with recommendations, and saves state. It looks for cross-cutting concerns that don’t fit neatly into other categories but impact overall quality.
Example reports from our own use of this workflow are:
* [Repository Quality Improvement - CI/CD Optimization](https://github.com/github/gh-aw/discussions/6863)
* [Repository Quality Improvement Report - Performance](https://github.com/github/gh-aw/discussions/13280).
Through its multi-agent causal chain (59 discussions → 30 issues → 40 PRs), Repository Quality Improver has produced **25 merged PRs out of 40 proposed (62% merge rate)**, taking a holistic view of quality from multiple angles.
## The Power of Continuous Improvement
[Section titled “The Power of Continuous Improvement”](#the-power-of-continuous-improvement)
These workflows complete the autonomous improvement picture: Go Module Usage Expert keeps dependencies fresh, Typist strengthens type safety, Functional Pragmatist applies functional techniques, and Repository Quality Improver maintains coherence.
Combined with earlier workflows, we have agents improving code at every level: line-level output (Terminal Stylist), function-level complexity (Code Simplifier), file-level organization (Semantic Function Refactor), pattern-level consistency (Go Pattern Detector), functional clarity (Functional Pragmatist), type safety (Typist), module dependencies (Go Module Usage Expert), and repository coherence (Repository Quality Improver).
This is the future of code quality: not periodic cleanup sprints, but continuous autonomous improvement across every dimension simultaneously.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Go Module Usage Expert:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/go-fan.md
```
**Typist:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/typist.md
```
**Functional Pragmatist:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/main/.github/workflows/functional-programming-enhancer.md
```
**Repository Quality Improver:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/repository-quality-improver.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Next Up: Continuous Documentation
[Section titled “Next Up: Continuous Documentation”](#next-up-continuous-documentation)
Beyond code quality, we need to keep documentation accurate and up-to-date as code evolves. How do we maintain docs that stay current?
Continue reading: [Continuous Documentation Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-documentation/)
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
***
*This is part 5 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Continuous Refactoring
> Agents that identify structural improvements and systematically refactor code

Welcome back to [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/), we met automated agents that detect complexity and propose simpler solutions. These work tirelessly in the background, cleaning things up. Now let’s explore similar agents that take a deeper structural view, extending the automation to *structural refactoring*.
## Continuous Refactoring
[Section titled “Continuous Refactoring”](#continuous-refactoring)
Our next two agents continuously analyze code structure, suggesting systematic improvements:
* **[Semantic Function Refactor](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/semantic-function-refactor.md?plain=1)** - Spots refactoring opportunities we might have missed
* **[Large File Simplifier](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-file-diet.md?plain=1)** - Monitors file sizes and proposes splitting oversized files
The **Semantic Function Refactor** workflow combines agentic AI with code analysis tools to analyze and address the structure of the entire codebase. It analyzes all Go source files in the `pkg/` directory to identify functions that might be in the wrong place.
As codebases evolve, functions sometimes end up in files where they don’t quite belong. Humans struggle to notice these organizational issues because we work on one file at a time and focus on making code work rather than on where it lives.
The workflow performs comprehensive discovery by
1. algorithmically collecting all function names from non-test Go files, then
2. agentically grouping functions semantically by name and purpose.
It then identifies functions that don’t fit their current file’s theme as outliers, uses Serena-powered semantic code analysis to detect potential duplicates, and creates issues recommending consolidated refactoring. These issues can then be reviewed and addressed by coding agents.
The workflow follows a “one file per feature” principle: files should be named after their primary purpose, and functions within each file should align with that purpose. It closes existing open issues with the `[refactor]` prefix before creating new ones. This prevents issue accumulation and ensures recommendations stay current.
In our extended use of Semantic Function Refactoring, the workflow has driven **112 merged PRs out of 142 proposed (79% merge rate)** through causal chains - creating 99 refactoring issues that downstream agents turn into code changes. For example, [issue #12291](https://github.com/github/gh-aw/issues/12291) analyzing code organization opportunities led to [PR #12363 splitting permissions.go into focused modules](https://github.com/github/gh-aw/pull/12363) (928→133 lines).
An example PR from our own use of this workflow is [Move misplaced extraction functions to frontmatter\_extraction.go](https://github.com/github/gh-aw/pull/7043).
### Large File Simplifier: The Size Monitor
[Section titled “Large File Simplifier: The Size Monitor”](#large-file-simplifier-the-size-monitor)
Large files are a common code smell - they often indicate unclear boundaries, mixed responsibilities, or accumulated complexity. The **Large File Simplifier** workflow monitors file sizes daily and creates actionable issues when files grow too large.
The workflow runs on weekdays, analyzing all Go source files in the `pkg/` directory. It identifies the largest file, checks if it exceeds healthy size thresholds, and creates a detailed issue proposing how to split it into smaller, more focused files.
What makes this workflow effective is its focus and prioritization. Instead of overwhelming developers with issues about every large file, it creates at most one issue, targeting the largest offender. The workflow also skips if an open `[file-diet]` issue already exists, preventing duplicate work.
In our extended use, Large File Simplifier (also known as “Daily File Diet”) has driven **26 merged PRs out of 33 proposed (79% merge rate)** through causal chains - creating 37 file-diet issues targeting the largest files, which downstream agents turn into modular code changes. For example, [issue #12535](https://github.com/github/gh-aw/issues/12535) targeting add\_interactive.go led to [PR #12545 refactoring it into 6 domain-focused modules](https://github.com/github/gh-aw/pull/12545).
The workflow uses Serena for semantic code analysis to understand function relationships and propose logical boundaries for splitting. It both counts lines and analyzes the code structure to suggest meaningful module boundaries that make sense.
## The Power of Continuous Refactoring
[Section titled “The Power of Continuous Refactoring”](#the-power-of-continuous-refactoring)
These workflows demonstrate how AI agents can continuously maintain institutional knowledge about code organization. The benefits compound over time: better organization makes code easier to find, consistent patterns reduce cognitive load, reduced duplication improves maintainability, and clean structure attracts further cleanliness. They’re particularly valuable in AI-assisted development, where code gets written quickly and organizational concerns can take a backseat to functionality.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Semantic Function Refactor:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/semantic-function-refactor.md
```
**Large File Simplifier:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-file-diet.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Next Up: Continuous Style
[Section titled “Next Up: Continuous Style”](#next-up-continuous-style)
Beyond structure and organization, there’s another dimension of code quality: presentation and style. How do we maintain beautiful, consistent console output and formatting?
Continue reading: [Meet the Workflows: Continuous Style →](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-style/)
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
***
*This is part 3 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Continuous Simplicity
> Agents that detect complexity and propose simpler solutions

Ah, what marvelous timing! Come, come, let me show you the *next wonders* in [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows/), we explored how a simple triage workflow helps us stay on top of incoming activity - automatically labeling issues and reducing cognitive load.
Now let’s meet the agents that work quietly in the background to keep code simple and clean. These workflows embody a powerful principle: **code quality is not a destination, it’s a continuous practice**. While developers race ahead implementing features and fixing bugs, autonomous cleanup agents trail behind, constantly sweeping, polishing, and simplifying. Let’s meet the agents that hunt for complexity.
## Continuous Simplicity
[Section titled “Continuous Simplicity”](#continuous-simplicity)
The next two agents represent different aspects of code simplicity: detecting *overcomplicated code* and *duplicated logic*:
* **[Automatic Code Simplifier](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/code-simplifier.md?plain=1)** - Analyzes recently modified code and creates PRs with simplifications
* **[Duplicate Code Detector](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/duplicate-code-detector.md?plain=1)** - Uses Serena’s semantic analysis to identify duplicate code patterns
The **Automatic Code Simplifier** runs daily, analyzing recently modified code for opportunities to simplify without changing functionality. It looks at what changed in the last few commits and asks: “Could this be clearer? Could it be shorter? Could it be more idiomatic?”
This workflow is particularly valuable after rapid development sessions. When you’re racing to implement a feature or fix a bug, code often becomes more complex than necessary. Variables get temporary names, logic becomes nested, error handling gets verbose. The workflow tirelessly cleans up after these development sessions, creating PRs that preserve functionality while improving clarity, consistency, and maintainability.
The kinds of simplifications it proposes range from extracting repeated logic into helper functions to converting nested if-statements to early returns. It spots opportunities to simplify boolean expressions, use standard library functions instead of custom implementations, and consolidate similar error handling patterns.
Code Simplifier is a recent addition - so far it has created **6 PRs (5 merged, 83% merge rate)**, such as [extracting an action mode helper to reduce code duplication](https://github.com/github/gh-aw/pull/13982) and [simplifying validation config code for clarity](https://github.com/github/gh-aw/pull/13118).
The **Duplicate Code Detector** uses traditional, road-tested semantic code analysis in conjunction with agentic reasoning to find duplicate patterns. It understands code *meaning* rather than just textual similarity, catching patterns where:
* The same logic appears with different variable names
* Similar functions exist across different files
* Repeated patterns could be extracted into utilities
* Structure is duplicated even if implementation differs
What makes this workflow special is its use of semantic analysis through [Serena](https://oraios.github.io/serena/) - a powerful coding agent toolkit capable of turning an LLM into a fully-featured agent that works directly on your codebase. When we use Serena, we understand code at the compiler-resolved level, not just syntax.
The workflow focuses on recent changes in the latest commits, intelligently filtering out test files, workflows, and non-code files. It creates issues only for significant duplication: patterns spanning more than 10 lines or appearing in 3 or more locations. It performs a multi-phase analysis. It starts by setting up Serena’s semantic environment for the repository, then finds changed `.go` and `.cjs` files while excluding tests and workflows. Using `get_symbols_overview` and `find_symbol`, it understands structure, identifies similar function signatures and logic blocks, and compares symbol overviews across files for deeper similarities. It creates issues with the `[duplicate-code]` prefix and limits itself to 3 issues per run, preventing overwhelm. Issues include specific file references, code snippets, and refactoring suggestions.
In our extended use of Duplicate Code Detector, the agent has raised **76 merged PRs out of 96 proposed (79% merge rate)**, demonstrating sustained practical value of semantic code analysis. Recent examples include [refactoring expired-entity cleanup scripts to share expiration processing](https://github.com/github/gh-aw/pull/13420) and [refactoring safe-output update handlers to eliminate duplicate control flow](https://github.com/github/gh-aw/pull/8791).
## Continuous AI for Simplicity - A New Paradigm
[Section titled “Continuous AI for Simplicity - A New Paradigm”](#continuous-ai-for-simplicity---a-new-paradigm)
Together, these workflows point towards **an emerging shift in how we maintain code quality**. Instead of periodic “cleanup sprints” or waiting for code reviews to catch complexity, we have agents that clean up after us and continuously monitor and propose improvements. This is especially valuable in AI-assisted development. When developers use AI to write code faster, these cleanup agents ensure speed doesn’t sacrifice simplicity. They understand the same patterns that humans recognize but apply them consistently across the entire codebase, every day.
The workflows never take a day off, never get tired, and never let technical debt accumulate. They embody the principle that *good enough* can always become *better*, and that incremental improvements compound over time.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Automatic Code Simplifier:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/code-simplifier.md
```
**Duplicate Code Detector:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/duplicate-code-detector.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Next Up: Continuous Refactoring
[Section titled “Next Up: Continuous Refactoring”](#next-up-continuous-refactoring)
Simplification is just the beginning. Beyond removing complexity, we can use agents to continuously improve code in many more ways. Our next posts explore this topic.
Continue reading: [Continuous Refactoring →](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-refactoring/)
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
***
*This is part 2 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Continuous Style
> The agent that makes console output beautiful and consistent

Welcome back to [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
In our [previous posts](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/), we’ve explored how autonomous cleanup agents work continuously in the background, simplifying code and improving structure. Today’s post is dedicated to one agent, and the larger admirable concept it represents: continuously making things *beautiful*.
## A Continuous Style Workflow
[Section titled “A Continuous Style Workflow”](#a-continuous-style-workflow)
Today’s post is dedicated to one agent, and the larger concept it represents: the **[Terminal Stylist](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/terminal-stylist.md?plain=1)** workflow. This agent’s purpose is to **make things look better**, by reviewing and enhancing the style of command-line interface (CLI) output.
Command-line interfaces are a primary interaction point for developer tools. When output is inconsistent or noisy, it still “works,” but it adds friction. When it’s well-styled, information becomes scannable, color highlights what matters, layouts remain readable across light and dark themes, and the overall experience feels professional.
Under the hood, the workflow looks for non-test Go files with console-related code and patterns such as `fmt.Print*`, `console.*`, and Lipgloss usage. It then checks for consistency in formatting helpers (especially for errors), sensible TTY-aware rendering, and accessible color choices. When it finds rough edges, it proposes concrete improvements, such as replacing plain output like `fmt.Println("Error: compilation failed")` with `fmt.Fprintln(os.Stderr, console.FormatErrorMessage("Compilation failed"))`, or swapping ad-hoc ANSI coloring for adaptive Lipgloss styles.
Rather than opening issues or PRs, the Terminal Stylist posts GitHub Discussions in the “General” category. Styling changes are often subjective, and discussions make it easier to converge on the right balance between simplicity and polish.
Terminal Stylist demonstrates multi-agent collaboration at its best. The workflow created **31 daily analysis reports** as discussions, which were then mined by Discussion Task Miner and Plan Command into **25 actionable issues**. Those issues spawned **16 merged PRs (80% merge rate)** improving console output across the codebase - from [Charmbracelet best practices adoption](https://github.com/github/gh-aw/pull/9928) to [progress bars](https://github.com/github/gh-aw/pull/8731) to [stderr routing fixes](https://github.com/github/gh-aw/pull/12302). Terminal Stylist never creates PRs directly; instead, it identifies opportunities that other agents implement, showing how workflows can collaborate through GitHub’s discussion → issue → PR pipeline.
The Terminal Stylist is proof that autonomous cleanup agents can have surprisingly specific taste. It focuses on terminal UI craft, using the Charmbracelet ecosystem (especially Lipgloss and Huh) to keep the CLI not just correct, but pleasant to use.
## The Art of Continuous Style
[Section titled “The Art of Continuous Style”](#the-art-of-continuous-style)
The Terminal Stylist shows that autonomous improvement isn’t limited to structure and correctness; it also covers user experience. By continuously reviewing output patterns, it helps new features match the project’s visual language, keeps styling aligned with evolving libraries, and nudges the CLI toward accessibility and clarity.
This is especially useful in AI-assisted development, where quick suggestions tend to default to `fmt.Println`. The Terminal Stylist cleans up after the AI, bringing that output back in line with the project’s conventions.
Continuous Style is a new frontier in code quality. It recognizes that how code *looks* matters just as much as how it *works*. By automating style reviews, we ensure that every interaction with our tools feels polished and professional.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add this workflow to your own repository and remix it as follows:
**Terminal Stylist:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/terminal-stylist.md
```
Then edit and remix the workflow specification to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Next Up: Continuous Improvement
[Section titled “Next Up: Continuous Improvement”](#next-up-continuous-improvement)
Beyond simplicity, structure, and style, there’s a final dimension: holistic quality improvement. How do we analyze dependencies, type safety, and overall repository health?
Continue reading: [Continuous Improvement Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-improvement/)
## Learn More
[Section titled “Learn More”](#learn-more)
Learn more about **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)**, try the **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** guide, and explore **[Charmbracelet](https://charm.sh/)**, the terminal UI ecosystem referenced by the Terminal Stylist.
***
*This is part 4 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Teamwork & Culture
> A curated tour of creative and culture workflows that bring joy to work

*Oh, my dear friends!* Let’s explore the *playful workshop* - the most fun corner of [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-security-compliance/), we explored security and compliance workflows - the essential guardrails that manage vulnerability campaigns, validate network security, and prevent credential exposure. These workflows let us sleep soundly knowing our agents operate within safe boundaries.
But here’s the thing: work doesn’t have to be all business. While we’ve built serious, production-critical workflows for quality, releases, and security, we also discovered something unexpected - AI agents can bring joy, build team culture, and create moments of delight. Not every workflow needs to solve a critical problem; some can simply make your day better. Let’s explore the playful side of our agent factory, where we learned that personality and fun drive engagement just as powerfully as utility.
## Teamwork & Culture Workflows
[Section titled “Teamwork & Culture Workflows”](#teamwork--culture-workflows)
These agents facilitate team communication and remind us that work can be fun:
* **[Daily Team Status](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-team-status.md?plain=1)** - Shares team mood and status updates - **22 issues**, **17 discussions** (plus 2 causal chain PRs!)
* **[Daily News](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-news.md?plain=1)** - Curates relevant news for the team - **45 news digest discussions**
* **[Poem Bot](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/poem-bot.md?plain=1)** - Responds to `/poem-bot` commands with creative verses (yes, really)
* **[Weekly Issue Summary](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/weekly-issue-summary.md?plain=1)** - Creates digestible summaries complete with charts and trends - **5 weekly analysis discussions**
* **[Daily Repo Chronicle](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-repo-chronicle.md?plain=1)** - Narrates the day’s activity like a storyteller - **6 chronicle discussions**
The Poem Bot started as a whimsy in our Copilot for PRs project in 2022. Someone said “wouldn’t it be funny if we had an agent that writes poems about our code?” and then we built it. Poem Bot responds to `/poem-bot` commands with creative verses about code, adding a touch of whimsy to the development workflow. We learned that AI agents don’t have to be all business - they can build culture and create moments of joy.
Daily News has created **45 news digest discussions** curating relevant developments for the team - for example, [#6932](https://github.com/github/gh-aw/discussions/6932) with the daily status roundup. It shares links, adds commentary and connects them to our work.
Daily Team Status has created **22 issues** and **17 discussions** sharing daily team status updates - for example, [#6930](https://github.com/github/gh-aw/discussions/6930) with the daily team status report. Two of its issues even led to merged PRs by downstream agents, showing that even “soft” workflows can drive concrete improvements.
Weekly Issue Summary has created **5 weekly analysis discussions** with digestible summaries, charts, and trends - for example, [#5844](https://github.com/github/gh-aw/discussions/5844) analyzing the week of December 1-8, 2025.
Daily Repo Chronicle has created **6 chronicle discussions** narrating the repository’s activity like a storyteller - for example, [#6750](https://github.com/github/gh-aw/discussions/6750) chronicling a development surge with 42 active PRs.
A theme here is the **reduction of cognitive load**. Having agents summarize and narrate daily activity means we don’t have to mentally parse long lists of issues or PRs. Instead, we get digestible stories that highlight what’s important. This frees up mental bandwidth for actual work.
Another theme is that **tone** can help make things more enjoyable. The Daily Repo Chronicle started writing summaries in a narrative, almost journalistic style. The outputs from AI agents don’t have to be robotic - they can have personality while still being informative.
These communication workflows help build team cohesion and remind us that work can be delightful.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Daily Team Status:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-team-status.md
```
**Daily News:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-news.md
```
**Poem Bot:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/poem-bot.md
```
**Weekly Issue Summary:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/weekly-issue-summary.md
```
**Daily Repo Chronicle:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-repo-chronicle.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Summon an Agent on Demand
[Section titled “Next Up: Summon an Agent on Demand”](#next-up-summon-an-agent-on-demand)
Scheduled workflows are great, but sometimes you need help *right now*. Enter ChatOps and interactive workflows.
Continue reading: [Interactive & ChatOps Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-interactive-chatops/)
***
*This is part 12 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Continuous Documentation
> A curated tour of workflows that maintain high-quality documentation

Step right up, step right up, and enter the *documentation chamber* of [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! Pure imagination meets technical accuracy in this most delightful corner of our establishment!
In our [previous posts](/gh-aw/blog/2026-01-13-meet-the-workflows-continuous-simplicity/), we explored autonomous cleanup agents - workflows that continuously improve code quality by simplifying complexity, refactoring structure, polishing style, and maintaining overall repository health. These agents never take a day off, quietly working to make our codebase better.
Now let’s address one of software development’s eternal challenges: keeping documentation accurate and up-to-date. Code evolves rapidly; docs… not so much. Terminology drifts, API examples become outdated, slide decks grow stale, and blog posts reference deprecated features. The question isn’t “can AI agents write good documentation?” but rather “can they maintain it as code changes?” Documentation and content workflows challenge conventional wisdom about AI-generated technical content. Spoiler: the answer involves human review, but it’s way better than the alternative (no docs at all).
## Continuous Documentation Workflows
[Section titled “Continuous Documentation Workflows”](#continuous-documentation-workflows)
These agents maintain high-quality documentation and content:
* **[Daily Documentation Updater](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-doc-updater.md?plain=1)** - Reviews and updates documentation to ensure accuracy and completeness - **57 merged PRs out of 59 proposed (96% merge rate)**
* **[Glossary Maintainer](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/glossary-maintainer.md?plain=1)** - Keeps glossary synchronized with codebase - **10 merged PRs out of 10 proposed (100% merge rate)**
* **[Documentation Unbloat](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/unbloat-docs.md?plain=1)** - Reviews and simplifies documentation by reducing verbosity - **88 merged PRs out of 103 proposed (85% merge rate)**
* **[Documentation Noob Tester](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/docs-noob-tester.md?plain=1)** - Tests documentation as a new user would, identifying confusing steps - **9 merged PRs (43% merge rate)** via causal chain
* **[Slide Deck Maintainer](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/slide-deck-maintainer.md?plain=1)** - Maintains presentation slide decks - **2 merged PRs out of 5 proposed (40% merge rate)**
* **[Multi-device Docs Tester](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-multi-device-docs-tester.md?plain=1)** - Tests documentation site across mobile, tablet, and desktop devices - **2 merged PRs out of 2 proposed (100% merge rate)**
* **[Blog Auditor](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/blog-auditor.md?plain=1)** - Verifies blog posts are accessible and contain expected content - **6 audits completed** (5 passed, 1 flagged issues)
Documentation is where we challenged conventional wisdom. Can AI agents write *good* documentation?
The **Technical Doc Writer** generates API docs from code, but more importantly, it *maintains* them - updating docs when code changes. The Glossary Maintainer caught terminology drift (“we’re using three different terms for the same concept”).
The **Slide Deck Maintainer** keeps our presentation materials current without manual updates.
The **Multi-device Docs Tester** uses Playwright to verify our documentation site works across phones, tablets, and desktops - testing responsive layouts, accessibility, and interactive elements. It catches visual regressions and layout issues that only appear on specific screen sizes.
The **Blog Auditor** ensures our blog posts stay accurate as the codebase evolves - it flags outdated code examples and broken links. Blog Auditor is a **validation-only workflow** that creates audit reports rather than code changes. It has run **6 audits** (5 passed, [1 flagged out-of-date content](https://github.com/github/gh-aw/issues/2162)), confirming blog accuracy.
Documentation Noob Tester deserves special mention for its exploratory nature. It has produced **9 merged PRs out of 21 proposed (43% merge rate)** through a causal chain: 62 discussions analyzed → 21 issues created → 21 PRs. The lower merge rate reflects this workflow’s exploratory nature - it identifies many potential improvements, some of which are too ambitious for immediate implementation. For example, [Discussion #8477](https://github.com/github/gh-aw/discussions/8477) led to [Issue #8486](https://github.com/github/gh-aw/issues/8486) which spawned PRs [#8716](https://github.com/github/gh-aw/pull/8716) and [#8717](https://github.com/github/gh-aw/pull/8717), both merged.
AI-generated docs need human/agent review, but they’re dramatically better than *no* docs (which is often the alternative). Validation can be automated to a large extent, freeing writers to focus on content shaping, topic, clarity, tone, and accuracy.
In this collection of agents, we took a heterogeneous approach - some workflows generate content, others maintain it, and still others validate it. Other approaches are possible - all tasks can be rolled into a single agent. We found that it’s easier to explore the space by using multiple agents, to separate concerns, and that encouraged us to use agents for other communication outputs such as blogs and slides.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Daily Documentation Updater:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-doc-updater.md
```
**Glossary Maintainer:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/glossary-maintainer.md
```
**Documentation Unbloat:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/unbloat-docs.md
```
**Documentation Noob Tester:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/docs-noob-tester.md
```
**Slide Deck Maintainer:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/slide-deck-maintainer.md
```
**Multi-device Docs Tester:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-multi-device-docs-tester.md
```
**Blog Auditor:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/blog-auditor.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Issue & PR Management Workflows
[Section titled “Next Up: Issue & PR Management Workflows”](#next-up-issue--pr-management-workflows)
Beyond writing code and docs, we need to manage the flow of issues and pull requests. How do we keep collaboration smooth and efficient?
Continue reading: [Issue & PR Management Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-issue-management/)
***
*This is part 6 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Interactive & ChatOps
> A curated tour of interactive workflows that respond to commands

*Onwards, onwards!* Let’s keep exploring the wonders of [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! To the *command center* where instant magic happens!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-creative-culture/), we explored creative and culture workflows - agents that bring joy, build team culture, and create moments of delight. We discovered that AI agents don’t have to be all business; they can have personality while making work more enjoyable.
But sometimes you need help *right now*, at the exact moment you’re stuck on a problem. You don’t want to wait for a scheduled run - you want to summon an expert agent with a command. That’s where interactive workflows and ChatOps come in. These agents respond to slash commands and GitHub reactions, providing on-demand assistance with full context of the current situation.
We learned that the right agent at the right moment with the right information is a valuable addition to an agent portfolio.
## Interactive & ChatOps Workflows
[Section titled “Interactive & ChatOps Workflows”](#interactive--chatops-workflows)
These agents respond to commands, providing on-demand assistance whenever you need it:
* **[Q](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/q.md?plain=1)** - Workflow optimizer that investigates performance and creates PRs - **69 merged PRs out of 88 proposed (78% merge rate)**
* **[Grumpy Reviewer](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/grumpy-reviewer.md?plain=1)** - Performs critical code reviews with personality - creates issues for downstream agents
* **[Workflow Generator](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/workflow-generator.md?plain=1)** - Creates new workflows from issue requests - scaffolds workflow files
Interactive workflows changed how we think about agent invocation. Instead of everything running on a schedule, these respond to slash commands and reactions - `/q` summons the workflow optimizer, a reaction triggers analysis. Q (yes, named after the James Bond quartermaster) became our go-to troubleshooter - it has contributed **69 merged PRs out of 88 proposed (78% merge rate)**, responding to commands and investigating workflow issues on demand. Recent examples include [fixing the daily-fact workflow action-tag](https://github.com/github/gh-aw/pull/14127) and [configuring PR triage reports with 1-day expiration](https://github.com/github/gh-aw/pull/13903).
The Grumpy Reviewer performs opinionated code reviews, creating issues that flag security risks and code quality concerns (e.g., [#13990](https://github.com/github/gh-aw/issues/13990) about risky event triggers) for downstream agents to fix. It gave us surprisingly valuable feedback with a side of sass (“This function is so nested it has its own ZIP code”).
Workflow Generator creates new agentic workflows from issue requests, scaffolding the markdown workflow files that other agents then refine (e.g., [#13379](https://github.com/github/gh-aw/issues/13379) requesting AWF mode changes).
We learned that **context is king** - these agents work because they’re invoked at the right moment with the right context, not because they run on a schedule.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Q:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/q.md
```
**Grumpy Reviewer:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/grumpy-reviewer.md
```
**Workflow Generator:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/workflow-generator.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Testing & Validation Workflows
[Section titled “Next Up: Testing & Validation Workflows”](#next-up-testing--validation-workflows)
While ChatOps agents respond to commands, we also need workflows that continuously verify our systems still function as expected.
Continue reading: [Testing & Validation Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-testing-validation/)
***
*This is part 13 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Issue & PR Management
> A curated tour of workflows that enhance GitHub collaboration

*Ah!* Let’s discuss the art of managing issues and pull requests at [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! A most delicious topic indeed!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-documentation/), we explored documentation and content workflows - agents that maintain glossaries, technical docs, slide decks, and blog content. We learned how we took a heterogeneous approach to documentation agents - some workflows generate content, others maintain it, and still others validate it.
Now let’s talk about the daily rituals of software development: managing issues and pull requests. GitHub provides excellent primitives for collaboration, but there’s ceremony involved - linking related issues, merging main into PR branches, assigning work, closing completed sub-issues, optimizing templates. These are small papercuts individually, but they can add up to significant friction.
## Issue & PR Management Workflows
[Section titled “Issue & PR Management Workflows”](#issue--pr-management-workflows)
These agents enhance issue and pull request workflows:
* **[Issue Arborist](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/issue-arborist.md?plain=1)** - Links related issues as sub-issues - **77 discussion reports** and **18 parent issues** created
* **[Issue Monster](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/issue-monster.md?plain=1)** - Assigns issues to the asynchronous [GitHub Copilot coding agent](https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent) one at a time - **task dispatcher** for the whole system
* **[Mergefest](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/mergefest.md?plain=1)** - Automatically merges main branch into PR branches - **orchestrator workflow**
* **[Sub Issue Closer](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/sub-issue-closer.md?plain=1)** - Closes completed sub-issues automatically - **orchestrator workflow**
The Issue Arborist is an **organizational workflow** that has created **77 discussion reports** (titled “\[Issue Arborist] Issue Arborist Report”) and **18 parent issues** to group related sub-issues. It keeps the issue tracker organized by automatically linking related issues, building a dependency tree we’d never maintain manually. For example, [#12037](https://github.com/github/gh-aw/issues/12037) grouped engine documentation updates.
The Issue Monster is the **task dispatcher** - it assigns issues to the GitHub platform’s asynchronous [Copilot coding agent](https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent) one at a time. It doesn’t create PRs itself, but enables every other agent’s work by feeding them tasks. This prevents the chaos of parallel work on the same codebase.
Mergefest is an **orchestrator workflow** that automatically merges main into PR branches, keeping long-lived PRs up to date without manual intervention. It eliminates the “please merge main” dance.
Sub Issue Closer automatically closes completed sub-issues when their parent issue is resolved, keeping the issue tracker clean.
Issue and PR management workflows don’t replace GitHub’s features; they enhance them, removing ceremony and making collaboration feel smoother.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Issue Arborist:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/issue-arborist.md
```
**Issue Monster:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/issue-monster.md
```
**Mergefest:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/mergefest.md
```
**Sub Issue Closer:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/sub-issue-closer.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Fault Investigation Workflows
[Section titled “Next Up: Fault Investigation Workflows”](#next-up-fault-investigation-workflows)
Next up we look at agents that maintain codebase health - spotting problems before they escalate.
Continue reading: [Fault Investigation Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-quality-hygiene/)
***
*This is part 7 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Metrics & Analytics
> A curated tour of metrics and analytics workflows that turn data into insights

Excellent journey! Now it’s time to plunge into the *observatory* - the nerve center of [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-quality-hygiene/), we explored quality and hygiene workflows - the vigilant caretakers that investigate failed CI runs, detect schema drift, and catch breaking changes before users do. These workflows maintain codebase health by spotting problems before they escalate.
When you’re running dozens of AI agents, how do you know if they’re actually working well? How do you spot performance issues, cost problems, or quality degradation? That’s where metrics and analytics workflows come in - they’re the agents that monitor other agents. The aim is to turn raw activity data into actionable insights.
## Metrics & Analytics Workflows
[Section titled “Metrics & Analytics Workflows”](#metrics--analytics-workflows)
Let’s take a look at these three workflows:
* **[Metrics Collector](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/metrics-collector.md?plain=1)** - Tracks daily performance across the entire agent ecosystem
* **[Portfolio Analyst](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/portfolio-analyst.md?plain=1)** - Identifies cost reduction opportunities
* **[Audit Workflows](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/audit-workflows.md?plain=1)** - A meta-agent that audits all the other agents’ runs
The Metrics Collector has created **41 daily metrics discussions** tracking performance across the agent ecosystem - for example, [#6986](https://github.com/github/gh-aw/discussions/6986) with the daily code metrics report. It became our central nervous system, gathering performance data that feeds into higher-level orchestrators.
Portfolio Analyst has created **7 portfolio analysis discussions** identifying cost reduction opportunities and token optimization patterns - for example, [#6499](https://github.com/github/gh-aw/discussions/6499) with a weekly portfolio analysis. The workflow has identified workflows that were costing us money unnecessarily (turns out some agents were way too chatty with their LLM calls).
Audit Workflows is our most prolific discussion-creating agent with **93 audit report discussions** and **9 issues**, acting as a meta-agent that analyzes logs, costs, errors, and success patterns across all other workflow runs. Four of its issues led to PRs by downstream agents.
Observability isn’t optional when you’re running dozens of AI agents - it’s the difference between a well-oiled machine and an expensive black box.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Metrics Collector:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/metrics-collector.md
```
**Portfolio Analyst:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/portfolio-analyst.md
```
**Audit Workflows:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/audit-workflows.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Operations & Release Workflows
[Section titled “Next Up: Operations & Release Workflows”](#next-up-operations--release-workflows)
Now that we can measure and optimize our agent ecosystem, let’s talk about the moment of truth: actually shipping software to users.
Continue reading: [Operations & Release Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-operations-release/)
***
*This is part 9 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Multi-Phase Improvers
> A curated tour of multi-phase workflows that tackle long-running projects

Let’s continue our journey through [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-tool-infrastructure/), we explored infrastructure workflows - the meta-monitoring layer that validates MCP servers, checks tool configurations, and ensures the platform itself stays healthy. These workflows watch the watchers, providing visibility into the invisible plumbing.
Most workflows we’ve seen so far run once and complete: analyze this PR, triage that issue, test this deployment. They’re ephemeral - they execute, produce results, and disappear. But what about projects that are too big to tackle in a single run? What about initiatives that require research, setup, and incremental implementation? Traditional CI/CD is built for stateless execution, but we discovered something powerful: workflows that maintain state across days, working a little bit each day like a persistent team member who never takes breaks. Welcome to our most ambitious experiment - multi-phase improvers that prove AI agents can handle complex, long-running projects.
## Multi-Phase Improver Workflows
[Section titled “Multi-Phase Improver Workflows”](#multi-phase-improver-workflows)
These are some of our most ambitious agents - they tackle big projects over multiple days:
* **[Daily Backlog Burner](https://github.com/githubnext/agentics/blob/main/workflows/daily-backlog-burner.md?plain=1)** - Systematically works through issues and PRs, one day at a time
* **[Daily Perf Improver](https://github.com/githubnext/agentics/blob/main/workflows/daily-perf-improver.md?plain=1)** - Three-phase performance optimization (research, setup, implement)
* **[Daily QA](https://github.com/githubnext/agentics/blob/main/workflows/daily-qa.md?plain=1)** - Continuous quality assurance that never sleeps
* **[Daily Accessibility Review](https://github.com/githubnext/agentics/blob/main/workflows/daily-accessibility-review.md?plain=1)** - WCAG compliance checking with Playwright
* **[PR Fix](https://github.com/githubnext/agentics/blob/main/workflows/pr-fix.md?plain=1)** - On-demand slash command to fix failing CI checks (super handy!)
This is where we got experimental with agent persistence and multi-day workflows. Traditional CI runs are ephemeral, but these workflows maintain state across days using repo-memory. The Daily Perf Improver runs in three phases - research (find bottlenecks), setup (create profiling infrastructure), implement (optimize). It’s like having a performance engineer who works a little bit each day. The Daily Backlog Burner systematically tackles our issue backlog - one issue per day, methodically working through technical debt. We learned that **incremental progress beats heroic sprints** - these agents never get tired, never get distracted, and never need coffee breaks. The PR Fix workflow is our emergency responder - when CI fails, invoke `/pr-fix` and it investigates and attempts repairs.
These workflows prove that AI agents can handle complex, long-running projects when given the right architecture.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Daily Backlog Burner:**
```bash
gh aw add-wizard githubnext/agentics/workflows/daily-backlog-burner.md
```
**Daily Perf Improver:**
```bash
gh aw add-wizard githubnext/agentics/workflows/daily-perf-improver.md
```
**Daily QA:**
```bash
gh aw add-wizard githubnext/agentics/workflows/daily-qa.md
```
**Daily Accessibility Review:**
```bash
gh aw add-wizard githubnext/agentics/workflows/daily-accessibility-review.md
```
**PR Fix:**
```bash
gh aw add-wizard githubnext/agentics/workflows/pr-fix.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Organization & Cross-Repo Workflows
[Section titled “Next Up: Organization & Cross-Repo Workflows”](#next-up-organization--cross-repo-workflows)
Single-repository workflows are powerful, but what happens when you scale to an entire organization with dozens of repositories?
Continue reading: [Organization & Cross-Repo Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-organization/)
***
*This is part 16 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Operations & Release
> A curated tour of operations and release workflows that ship software

Ah! Right this way to our next chamber in [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! The chamber where our AI agents enhance the magical moment of *shipping software*.
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-metrics-analytics/), we explored metrics and analytics workflows - the agents that monitor other agents, turning raw activity data into actionable insights.
## Operations & Release Workflows
[Section titled “Operations & Release Workflows”](#operations--release-workflows)
The agents that help us actually ship software:
* **[Changeset](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/changeset.md?plain=1)** - Manages version bumps and changelog entries for releases - **22 merged PRs out of 28 proposed (78% merge rate)**
* **[Daily Workflow Updater](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-workflow-updater.md?plain=1)** - Keeps GitHub Actions and dependencies current
Shipping software is stressful enough without worrying about whether you formatted your release notes correctly.
Changeset Generator has contributed **22 merged PRs out of 28 proposed (78% merge rate)**, automating version bumps and changelog generation for every release. It analyzes commits since the last release, determines the appropriate version bump (major, minor, patch), and updates the changelog accordingly.
Daily Workflow Updater keeps GitHub Actions and dependencies current, ensuring workflows don’t fall behind on security patches or new features.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Changeset:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/changeset.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Security-related Workflows
[Section titled “Next Up: Security-related Workflows”](#next-up-security-related-workflows)
After all this focus on shipping, we need to talk about the guardrails: how do we ensure these powerful agents operate safely?
Continue reading: [Security-related Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-security-compliance/)
***
*This is part 10 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Organization & Cross-Repo
> A curated tour of workflows that operate at organization scale

Let’s zoom out at [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-multi-phase/), we explored multi-phase improver workflows - our most ambitious agents that tackle big projects over multiple days, maintaining state and making incremental progress. These workflows proved that AI agents can handle complex, long-running initiatives when given the right architecture.
But all that sophisticated functionality has focused on a single repository. What happens when you zoom out to organization scale? What insights emerge when you analyze dozens or hundreds of repositories together? What looks perfectly normal in one repo might be a red flag across an organization. Organization and cross-repo workflows operate at enterprise scale, requiring careful permission management, thoughtful rate limiting, and different analytical lenses. Let’s explore workflows that see the forest, not just the trees.
## Organization & Cross-Repo Workflows
[Section titled “Organization & Cross-Repo Workflows”](#organization--cross-repo-workflows)
These agents work at organization scale, across multiple repositories:
* **[Org Health Report](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/org-health-report.md?plain=1)** - Organization-wide repository health metrics - **4 organization health discussions** created
* **[Stale Repo Identifier](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/stale-repo-identifier.md?plain=1)** - Identifies inactive repositories - **2 issues** flagging truly stale repos
* **[Ubuntu Image Analyzer](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/ubuntu-image-analyzer.md?plain=1)** - Documents GitHub Actions runner environments - **4 merged PRs out of 8 proposed (50% merge rate)**
Scaling agents across an entire organization changes the game. Org Health Report has created **4 organization health discussions** analyzing dozens of repositories at scale - for example, [#6777](https://github.com/github/gh-aw/discussions/6777) with the December 2025 organization health report. It identifies patterns and outliers (“these three repos have no tests, these five haven’t been updated in months”).
Stale Repo Identifier has created **2 issues** flagging truly stale repositories for organizational hygiene - for example, [#5384](https://github.com/github/gh-aw/issues/5384) identifying Skills-Based-Volunteering-Public as truly stale. It helps find abandoned projects that should be archived or transferred.
We learned that **cross-repo insights are different** - what looks fine in one repository might be an outlier across the organization. These workflows require careful permission management (reading across repos needs organization-level tokens) and thoughtful rate limiting (you can hit API limits fast when analyzing 50+ repos).
Ubuntu Image Analyzer has contributed **4 merged PRs out of 8 proposed (50% merge rate)**, documenting GitHub Actions runner environments to keep the team informed about available tools and versions. It’s wonderfully meta - it documents the very environment that runs our agents.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Org Health Report:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/org-health-report.md
```
**Stale Repo Identifier:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/stale-repo-identifier.md
```
**Ubuntu Image Analyzer:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/ubuntu-image-analyzer.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Advanced Analytics & ML Workflows
[Section titled “Next Up: Advanced Analytics & ML Workflows”](#next-up-advanced-analytics--ml-workflows)
Cross-repo insights reveal patterns, but we wanted to go even deeper - using machine learning to understand agent behavior.
Continue reading: [Advanced Analytics & ML Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-advanced-analytics/)
***
*This is part 17 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Fault Investigation
> A curated tour of proactive fault investigation workflows that maintain codebase health

*Ah, splendid!* Welcome back to [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! Come, let me show you the chamber where vigilant caretakers investigate faults before they escalate!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-issue-management/), we explored issue and PR management workflows.
Now let’s shift from collaboration ceremony to fault investigation.
While issue workflows help us handle what comes in, fault investigation workflows act as vigilant caretakers - spotting problems before they escalate and keeping our codebase healthy. These are the agents that investigate failed CI runs, detect schema drift, and catch breaking changes before users do.
## Fault Investigation Workflows
[Section titled “Fault Investigation Workflows”](#fault-investigation-workflows)
These are our diligent caretakers - the agents that spot problems before they become bigger problems:
* **[CI Doctor](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/ci-doctor.md?plain=1)** - Investigates failed workflows and opens diagnostic issues - **9 merged PRs out of 13 proposed (69% merge rate)**
* **[Schema Consistency Checker](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/schema-consistency-checker.md?plain=1)** - Detects when schemas, code, and docs drift apart - **55 analysis discussions** created
* **[Breaking Change Checker](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/breaking-change-checker.md?plain=1)** - Watches for changes that might break things for users - creates alert issues
The CI Doctor (also known as “CI Failure Doctor”) was one of our most important workflows. Instead of drowning in CI failure notifications, we now get *timely*, *investigated* failures with actual diagnostic insights. The agent doesn’t just tell us something broke - it analyzes logs, identifies patterns, searches for similar past issues, and even suggests fixes - even before the human has read the failure notification. CI Failure Doctor has contributed **9 merged PRs out of 13 proposed (69% merge rate)**, including fixes like [adding Go module download pre-flight checks](https://github.com/github/gh-aw/pull/13740) and [adding retry logic to prevent proxy 403 failures](https://github.com/github/gh-aw/pull/13155). We learned that agents excel at the tedious investigation work that humans find draining.
The Schema Consistency Checker has created **55 analysis discussions** examining schema drift between JSON schemas, Go structs, and documentation - for example, [#7020](https://github.com/github/gh-aw/discussions/7020) analyzing conditional logic consistency across the codebase. It caught drift that would have taken us days to notice manually.
Breaking Change Checker is a newer workflow that monitors for backward-incompatible changes and creates alert issues (e.g., [#14113](https://github.com/github/gh-aw/issues/14113) flagging CLI version updates) before they reach production.
These “hygiene” workflows became our first line of defense, catching issues before they reached users.
The CI Doctor has inspired a growing range of similar workflows inside GitHub, where agents proactively do depth investigations of site incidents and failures. This is the future of operational excellence: AI agents kicking in immediately to do depth investigation, for faster organizational response.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**CI Doctor:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/ci-doctor.md
```
**Schema Consistency Checker:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/schema-consistency-checker.md
```
**Breaking Change Checker:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/breaking-change-checker.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Metrics & Analytics Workflows
[Section titled “Next Up: Metrics & Analytics Workflows”](#next-up-metrics--analytics-workflows)
Next up, we look at workflows which help us understand if the agent collection as a whole is working well That’s where metrics and analytics workflows come in.
Continue reading: [Metrics & Analytics Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-metrics-analytics/)
***
*This is part 8 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Security-related
> A curated tour of security and compliance workflows that enforce safe boundaries

*Splendid!* How great to have you back at [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! Now, let me show you the *guardian chamber* - where the watchful protectors stand vigil!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-operations-release/), we explored operations and release workflows that handle the critical process of shipping software - building, testing, generating release notes, and publishing. These workflows need to be rock-solid reliable because they represent the moment when our work reaches users.
But reliability alone isn’t enough - we also need *security*. When AI agents can access APIs, modify code, and interact with external services, security becomes paramount. How do we ensure agents only access authorized resources? How do we track vulnerabilities and enforce compliance deadlines? How do we prevent credential exposure? That’s where security and compliance workflows become our essential guardrails - the watchful guardians that let us sleep soundly at night.
## Security-related Workflows
[Section titled “Security-related Workflows”](#security-related-workflows)
These agents are our security guards, keeping watch and enforcing the rules:
* **[Security Compliance](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/security-compliance.md?plain=1)** - Runs vulnerability campaigns with deadline tracking
* **[Firewall](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/firewall.md?plain=1)** - Tests network security and validates rules - **59 daily firewall report discussions**, **5 smoke test issues**
* **[Daily Secrets Analysis](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-secrets-analysis.md?plain=1)** - Scans for exposed credentials (yes, it happens)
* **[Daily Malicious Code Scan](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-malicious-code-scan.md?plain=1)** - Reviews recent code changes for suspicious patterns
* **[Static Analysis Report](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/static-analysis-report.md?plain=1)** - Daily security scans using zizmor, poutine, and actionlint - **57 analysis discussions** plus **12 Zizmor security reports**
Security Compliance manages vulnerability remediation campaigns with deadline tracking, ensuring security issues are addressed within defined SLAs - perfect for those “audit in 3 weeks” panic moments.
The Firewall workflow has created **59 daily firewall report discussions** and **5 smoke test issues**, validating that our agents can’t access unauthorized resources - for example, [#6943](https://github.com/github/gh-aw/discussions/6943) with the daily firewall analysis. It’s the bouncer that enforces network rules.
Daily Secrets Analysis scans for exposed credentials in commits and discussions, providing an automated security net against accidental secret exposure - catching those “oops, I committed my API key” moments before they become incidents.
Daily Malicious Code Scan reviews recent code changes for suspicious patterns, adding an automated defense layer against supply chain attacks.
Static Analysis Report has created **57 analysis discussions** plus **12 Zizmor security reports**, running comprehensive daily security audits using industry-standard tools - for example, [#6973](https://github.com/github/gh-aw/discussions/6973) with the latest static analysis findings and [#3033](https://github.com/github/gh-aw/discussions/3033) with a Zizmor security analysis. This shows how traditional security tools can be integrated into an AI agent workflow.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Security Compliance:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/security-compliance.md
```
**Firewall:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/firewall.md
```
**Daily Secrets Analysis:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-secrets-analysis.md
```
**Daily Malicious Code Scan:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-malicious-code-scan.md
```
**Static Analysis Report:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/static-analysis-report.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Teamwork & Culture Workflows
[Section titled “Next Up: Teamwork & Culture Workflows”](#next-up-teamwork--culture-workflows)
After all this serious talk, let’s explore the fun side: agents that bring joy and build team culture.
Continue reading: [Teamwork & Culture Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-creative-culture/)
***
*This is part 11 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Testing & Validation
> A curated tour of testing workflows that keep everything running smoothly

*Right this way!* Let’s continue our grand tour of [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! Into the *verification chamber* where nothing escapes scrutiny!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-interactive-chatops/), we explored ChatOps workflows - agents that respond to slash commands and GitHub reactions, providing on-demand assistance with full context.
But making code *better* is only half the battle. We also need to ensure it keeps *working*. As we refactor, optimize, and evolve our codebase, how do we know we haven’t broken something? How do we catch regressions before users do? That’s where testing and validation workflows come in - the skeptical guardians that continuously verify our systems still function as expected. We learned that AI infrastructure needs constant health checks, because what worked yesterday might silently fail today. These workflows embody **trust but verify**.
## Testing & Validation Workflows
[Section titled “Testing & Validation Workflows”](#testing--validation-workflows)
These agents keep everything running smoothly through continuous testing:
### Code Quality & Test Validation
[Section titled “Code Quality & Test Validation”](#code-quality--test-validation)
* **[Daily Testify Uber Super Expert](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-testify-uber-super-expert.md?plain=1)** - Analyzes test files daily and suggests testify-based improvements - **19 issues created**, **13 led to merged PRs (100% causal chain merge rate)**
* **[Daily Test Improver](https://github.com/githubnext/agentics/blob/main/workflows/daily-test-improver.md?plain=1)** - Identifies coverage gaps and implements new tests incrementally
* **[Daily Compiler Quality Check](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-compiler-quality.md?plain=1)** - Analyzes compiler code to ensure it meets quality standards
### User Experience & Integration Testing
[Section titled “User Experience & Integration Testing”](#user-experience--integration-testing)
* **[Daily Multi-Device Docs Tester](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-multi-device-docs-tester.md?plain=1)** - Tests documentation across devices with Playwright - **2 merged PRs out of 2 proposed (100% merge rate)**
* **[CLI Consistency Checker](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/cli-consistency-checker.md?plain=1)** - Inspects the CLI for inconsistencies, typos, and documentation gaps - **80 merged PRs out of 102 proposed (78% merge rate)**
### CI/CD Optimization
[Section titled “CI/CD Optimization”](#cicd-optimization)
* **[CI Coach](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/ci-coach.md?plain=1)** - Analyzes CI pipelines and suggests optimizations - **9 merged PRs out of 9 proposed (100% merge rate)**
* **[Workflow Health Manager](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/workflow-health-manager.md?plain=1)** - Meta-orchestrator monitoring health of all agentic workflows - **40 issues created**, **5 direct PRs + 14 causal chain PRs merged**
The Daily Testify Expert has created **19 issues** analyzing test quality, and **13 of those issues led to merged PRs** by downstream agents - a perfect 100% causal chain merge rate. For example, [issue #13701](https://github.com/github/gh-aw/issues/13701) led to [#13722](https://github.com/github/gh-aw/pull/13722) modernizing console render tests with testify assertions. The Daily Test Improver works alongside it to identify coverage gaps and implement new tests.
The Multi-Device Docs Tester uses Playwright to test our documentation on different screen sizes - it has created **2 PRs (both merged)**, including [adding —network host to Playwright Docker containers](https://github.com/github/gh-aw/pull/7158). It found mobile rendering issues we never would have caught manually. The CLI Consistency Checker has contributed **80 merged PRs out of 102 proposed (78% merge rate)**, maintaining consistency in CLI interface and documentation. Recent examples include [removing undocumented CLI commands](https://github.com/github/gh-aw/pull/12762) and [fixing upgrade command documentation](https://github.com/github/gh-aw/pull/11559).
CI Optimization Coach has contributed **9 merged PRs out of 9 proposed (100% merge rate)**, optimizing CI pipelines for speed and efficiency with perfect execution. Examples include [removing unnecessary test dependencies](https://github.com/github/gh-aw/pull/13925) and [fixing duplicate test execution](https://github.com/github/gh-aw/pull/8176).
The Workflow Health Manager has created **40 issues** monitoring the health of all other workflows, with **25 of those issues leading to 34 PRs** (14 merged) by downstream agents - plus **5 direct PRs merged**. For example, [issue #14105](https://github.com/github/gh-aw/issues/14105) about a missing runtime file led to [#14127](https://github.com/github/gh-aw/pull/14127) fixing the workflow configuration.
These workflows embody the principle: **trust but verify**. Just because it worked yesterday doesn’t mean it works today.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**Daily Testify Uber Super Expert:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-testify-uber-super-expert.md
```
**Daily Test Improver:**
```bash
gh aw add-wizard githubnext/agentics/daily-test-improver
```
**Daily Compiler Quality Check:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-compiler-quality.md
```
**Daily Multi-Device Docs Tester:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/daily-multi-device-docs-tester.md
```
**CLI Consistency Checker:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/cli-consistency-checker.md
```
**CI Coach:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/ci-coach.md
```
**Workflow Health Manager:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/workflow-health-manager.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Monitoring the Monitors
[Section titled “Next Up: Monitoring the Monitors”](#next-up-monitoring-the-monitors)
But what about the infrastructure itself? Who watches the watchers? Time to go meta.
Continue reading: [Tool & Infrastructure Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-tool-infrastructure/)
***
*This is part 14 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Meet the Workflows: Tool & Infrastructure
> A curated tour of infrastructure workflows that monitor the agentic systems

*Delighted to have you back* on our journey through [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/)! Now, prepare yourself for something *quite peculiar* - the room where we watch the watchers!
In our [previous post](/gh-aw/blog/2026-01-13-meet-the-workflows-testing-validation/), we explored testing and validation workflows that continuously verify our systems function correctly - running smoke tests, checking documentation across devices, and catching regressions before users notice them. We learned that trust must be verified.
But here’s a question that kept us up at night: what if the *infrastructure itself* fails? What if MCP servers are misconfigured, tools become unavailable, or agents can’t access the capabilities they need? Testing the *application* is one thing; monitoring the *platform* that runs AI agents is another beast entirely. Tool and infrastructure workflows provide meta-monitoring - they watch the watchers, validate configurations, and ensure the invisible plumbing stays functional. Welcome to the layer where we monitor agents monitoring agents monitoring code. Yes, it gets very meta.
## Tool & Infrastructure Workflows
[Section titled “Tool & Infrastructure Workflows”](#tool--infrastructure-workflows)
These agents monitor and analyze the agentic infrastructure itself:
* **[MCP Inspector](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/mcp-inspector.md?plain=1)** - Validates Model Context Protocol configurations - ensures agents can access tools
* **[GitHub MCP Tools Report](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/github-mcp-tools-report.md?plain=1)** - Analyzes available MCP tools - **5 merged PRs out of 6 proposed (83% merge rate)**
* **[Agent Performance Analyzer](https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/agent-performance-analyzer.md?plain=1)** - Meta-orchestrator for agent quality - **29 issues created, 14 leading to PRs (8 merged)**
Infrastructure for AI agents is different from traditional infrastructure - you need to validate that tools are available, properly configured, and actually working. The MCP Inspector continuously validates Model Context Protocol server configurations because a misconfigured MCP server means an agent can’t access the tools it needs.
GitHub MCP Tools Report Generator has contributed **5 merged PRs out of 6 proposed (83% merge rate)**, analyzing MCP tool availability and keeping tool configurations up to date. For example, [PR #13169](https://github.com/github/gh-aw/pull/13169) updates MCP server tool configurations.
Agent Performance Analyzer has created **29 issues** identifying performance problems across the agent ecosystem, and **14 of those issues led to PRs** (8 merged) by downstream agents - for example, it detected that draft PRs accounted for 9.6% of open PRs, created issue #12168, which led to [#12174](https://github.com/github/gh-aw/pull/12174) implementing automated draft cleanup.
We learned that **layered observability** is crucial: you need monitoring at the infrastructure level (are servers up?), the tool level (can agents access what they need?), and the agent level (are they performing well?).
These workflows provide visibility into the invisible.
## Using These Workflows
[Section titled “Using These Workflows”](#using-these-workflows)
You can add these workflows to your own repository and remix them. Get going with our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/), then run one of the following:
**MCP Inspector:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/mcp-inspector.md
```
**GitHub MCP Tools Report:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/github-mcp-tools-report.md
```
**Agent Performance Analyzer:**
```bash
gh aw add-wizard https://github.com/github/gh-aw/blob/v0.45.5/.github/workflows/agent-performance-analyzer.md
```
Then edit and remix the workflow specifications to meet your needs, regenerate the lock file using `gh aw compile`, and push to your repository. See our [Quick Start](https://github.github.com/gh-aw/setup/quick-start/) for further installation and setup instructions.
You can also [create your own workflows](/gh-aw/setup/creating-workflows/).
## Learn More
[Section titled “Learn More”](#learn-more)
* **[GitHub Agentic Workflows](https://github.github.com/gh-aw/)** - The technology behind the workflows
* **[Quick Start](https://github.github.com/gh-aw/setup/quick-start/)** - How to write and compile workflows
## Next Up: Multi-Phase Improver Workflows
[Section titled “Next Up: Multi-Phase Improver Workflows”](#next-up-multi-phase-improver-workflows)
Most workflows we’ve seen are stateless - they run, complete, and disappear. But what if agents could maintain memory across days?
Continue reading: [Multi-Phase Improver Workflows →](/gh-aw/blog/2026-01-13-meet-the-workflows-multi-phase/)
***
*This is part 15 of a 19-part series exploring the workflows in Peli’s Agent Factory.*
# Weekly Update – March 18, 2026
> Seven releases in seven days: guard policy overhaul, new triggers, GHE improvements, and a healthy dose of quality-of-life fixes.
It’s been a busy week in [github/gh-aw](https://github.com/github/gh-aw) — seven releases shipped between March 13 and March 17, covering everything from a security model overhaul to a new label-based trigger and a long-overdue terminal resize fix. Let’s dig in.
## Releases This Week
[Section titled “Releases This Week”](#releases-this-week)
### [v0.61.0](https://github.com/github/gh-aw/releases/tag/v0.61.0) — March 17
[Section titled “v0.61.0 — March 17”](#v0610--march-17)
The freshest release focuses on reliability and developer experience:
* **Automatic debug logging** ([#21406](https://github.com/github/gh-aw/pull/21406)): Set `ACTIONS_RUNNER_DEBUG=true` on your runner and full debug logging activates automatically — no more manually adding `DEBUG=*` to every troubleshooting run.
* **Cross-repo project item updates** ([#21404](https://github.com/github/gh-aw/pull/21404)): `update_project` now accepts a `target_repo` parameter, so org-level project boards can update fields on items from any repository.
* **GHE Cloud data residency support** ([#21408](https://github.com/github/gh-aw/pull/21408)): Compiled workflows now auto-inject a `GH_HOST` step, fixing `gh` CLI failures on `*.ghe.com` instances.
* **CI build artifacts** ([#21440](https://github.com/github/gh-aw/pull/21440)): The `build` CI job now uploads the compiled `gh-aw` binary as a downloadable artifact — handy for testing PRs without a local build.
### [v0.60.0](https://github.com/github/gh-aw/releases/tag/v0.60.0) — March 17
[Section titled “v0.60.0 — March 17”](#v0600--march-17)
This release rewires the security model. **Breaking change**: automatic `lockdown=true` is gone. Instead, the runtime now auto-configures guard policies on the GitHub MCP server — `min_integrity=approved` for public repos, `min_integrity=none` for private/internal. Remove any explicit `lockdown: false` from your frontmatter; it’s no longer needed.
Other highlights:
* **GHES domain auto-allowlisting** ([#21301](https://github.com/github/gh-aw/pull/21301)): When `engine.api-target` points to a GHES instance, the compiler automatically adds GHES API hostnames to the firewall. No more silent blocks after every recompile.
* **`github-app:` auth in APM dependencies** ([#21286](https://github.com/github/gh-aw/pull/21286)): APM `dependencies:` can now use `github-app:` auth for cross-org private package access.
### [v0.59.0](https://github.com/github/gh-aw/releases/tag/v0.59.0) — March 16
[Section titled “v0.59.0 — March 16”](#v0590--march-16)
A feature-packed release with two breaking changes (field renames in `safe-outputs.allowed-domains`) and several new capabilities:
* **Label Command Trigger** ([#21118](https://github.com/github/gh-aw/pull/21118)): Activate a workflow by adding a label to an issue, PR, or discussion. The label is automatically removed so it can be reapplied to re-trigger.
* **`gh aw domains` command** ([#21086](https://github.com/github/gh-aw/pull/21086)): Inspect the effective network domain configuration for all your workflows, with per-domain ecosystem annotations.
* **Pre-activation step injection** — New `on.steps` and `on.permissions` frontmatter fields let you inject custom steps and permissions into the activation job for advanced scenarios.
### Earlier in the Week
[Section titled “Earlier in the Week”](#earlier-in-the-week)
* [v0.58.3](https://github.com/github/gh-aw/releases/tag/v0.58.3) (March 15): MCP write-sink guard policy for non-GitHub MCP servers, Copilot pre-flight diagnostic for GHES, and a richer run details step summary.
* [v0.58.2](https://github.com/github/gh-aw/releases/tag/v0.58.2) (March 14): GHES auto-detection in `audit` and `add-wizard`, `excluded-files` support for `create-pull-request`, and clearer `run` command errors.
* [v0.58.1](https://github.com/github/gh-aw/releases/tag/v0.58.1) / [v0.58.0](https://github.com/github/gh-aw/releases/tag/v0.58.0) (March 13): `call-workflow` safe output for chaining workflows, `checkout: false` for agent jobs, custom OpenAI/Anthropic API endpoints, and 92 merged PRs in v0.58.0 alone.
## Notable Pull Requests
[Section titled “Notable Pull Requests”](#notable-pull-requests)
* **[Top-level `github-app` fallback](https://github.com/github/gh-aw/pull/21510)** ([#21510](https://github.com/github/gh-aw/pull/21510)): Define your GitHub App config once at the top level and let it propagate to safe-outputs, checkout, MCP, APM, and activation — instead of repeating it in every section.
* **[GitHub App-only permission scopes](https://github.com/github/gh-aw/pull/21511)** ([#21511](https://github.com/github/gh-aw/pull/21511)): 31 new `PermissionScope` constants cover repository, org, and user-level GitHub App permissions (e.g., `administration`, `members`, `environments`).
* **[Custom Huh theme](https://github.com/github/gh-aw/pull/21557)** ([#21557](https://github.com/github/gh-aw/pull/21557)): All 11 interactive CLI forms now use a Dracula-inspired theme consistent with the rest of the CLI’s visual identity.
* **[Weekly blog post writer workflow](https://github.com/github/gh-aw/pull/21575)** ([#21575](https://github.com/github/gh-aw/pull/21575)): Yes, the workflow that wrote this post was itself merged this week. Meta!
* **[CI job timeout limits](https://github.com/github/gh-aw/pull/21601)** ([#21601](https://github.com/github/gh-aw/pull/21601)): All 25 CI jobs that relied on GitHub’s 6-hour default now have explicit timeouts, preventing a stuck test from silently burning runner compute.
## Agent of the Week: auto-triage-issues
[Section titled “ Agent of the Week: auto-triage-issues”](#-agent-of-the-week-auto-triage-issues)
The first-ever Agent of the Week goes to the workflow that handles the unglamorous but essential job of keeping the issue tracker from becoming a swamp.
`auto-triage-issues` runs on a schedule and fires on every new issue, reading each one and deciding how to categorize it. This week it ran five times — three successful runs and two that were triggered by push events to a feature branch (which apparently fire the workflow but don’t give it much to work with). On its scheduled run this morning, it found zero open issues in the repository, so it created a tidy summary discussion to announce the clean state, as instructed. On an earlier issues-triggered run, it attempted to triage issue [#21572](https://github.com/github/gh-aw/pull/21572) but hit empty results from GitHub MCP tools on all three read attempts — so it gracefully called `missing_data` and moved on rather than hallucinating a label.
Across its recent runs it made 131 `search_repositories` calls. We’re not sure why it finds repository searches so compelling, but clearly it’s very thorough about knowing its neighborhood before making any decisions.
**Usage tip**: Pair `auto-triage-issues` with a notify workflow on specific labels (e.g., `security` or `needs-repro`) so the right people get pinged automatically without anyone having to watch the inbox.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/auto-triage-issues.md)
## Try It Out
[Section titled “Try It Out”](#try-it-out)
Update to [v0.61.0](https://github.com/github/gh-aw/releases/tag/v0.61.0) to get all the improvements from this packed week. If you run workflows on GHES or in GHE Cloud, the new auto-detection and `GH_HOST` injection features are especially worth trying. As always, contributions and feedback are welcome in [github/gh-aw](https://github.com/github/gh-aw).
# Weekly Update – March 23, 2026
> Eight releases this week: security hardening, custom Actions as safe-output tools, a 20-second speed boost, and timezone support for scheduled workflows.
Another week, another flurry of releases in [github/gh-aw](https://github.com/github/gh-aw). Eight versions shipped between March 18 and March 21, pushing security hardening, extensibility, and performance improvements across the board. Here’s what you need to know.
## Releases This Week
[Section titled “Releases This Week”](#releases-this-week)
### [v0.62.5](https://github.com/github/gh-aw/releases/tag/v0.62.5) — March 21
[Section titled “v0.62.5 — March 21”](#v0625--march-21)
The latest release leads with two important security fixes:
* **Supply chain protection**: The Trivy vulnerability scanner action was removed after a supply chain compromise was discovered ([#22007](https://github.com/github/gh-aw/pull/22007), [#22065](https://github.com/github/gh-aw/pull/22065)). Scanning has been replaced with a safer alternative.
* **Public repo integrity hardening** ([#21969](https://github.com/github/gh-aw/pull/21969)): GitHub App authentication no longer exempts public repositories from the minimum-integrity guard policy, closing a gap where untrusted content could bypass integrity checks.
On the feature side:
* **Timezone support for `on.schedule`** ([#22018](https://github.com/github/gh-aw/pull/22018)): Cron entries now accept an optional `timezone` field — finally, no more mental UTC arithmetic when you want your workflow to run “at 9 AM Pacific”.
* **Boolean expression optimizer** ([#22025](https://github.com/github/gh-aw/pull/22025)): Condition trees are optimized at compile time, generating cleaner `if:` expressions in compiled workflows.
* **Wildcard `target-repo` in safe-output handlers** ([#21877](https://github.com/github/gh-aw/pull/21877)): Use `target-repo: "*"` to write a single handler definition that works across any repository.
### [v0.62.3](https://github.com/github/gh-aw/releases/tag/v0.62.3) — March 20
[Section titled “v0.62.3 — March 20”](#v0623--march-20)
This one is a standout for extensibility and speed:
* **Custom Actions as Safe Output Tools** ([#21752](https://github.com/github/gh-aw/pull/21752)): You can now expose any GitHub Action as an MCP tool via the new `safe-outputs.actions` block. The compiler resolves `action.yml` at compile time to derive the tool schema and inject it into the agent — no custom wiring needed. This opens the door to a whole ecosystem of reusable safe-output handlers built from standard Actions.
* **\~20 seconds faster per workflow run** ([#21873](https://github.com/github/gh-aw/pull/21873)): A bump to `DefaultFirewallVersion` v0.24.5 eliminates a 10-second shutdown delay for both the agent container and the threat detection container. That’s 20 free seconds on every single run.
* **`trustedBots` support in MCP Gateway** ([#21865](https://github.com/github/gh-aw/pull/21865)): Pass an allowlist of additional GitHub bot identities to the MCP Gateway, enabling safe cross-bot collaboration in guarded environments.
* **`gh-aw-metadata` v3** ([#21899](https://github.com/github/gh-aw/pull/21899)): Lock files now embed the configured agent ID/model in the `gh-aw-metadata` comment, making audits much easier.
### [v0.62.2](https://github.com/github/gh-aw/releases/tag/v0.62.2) — March 19
[Section titled “v0.62.2 — March 19”](#v0622--march-19)
! **Breaking change alert**: `lockdown: true` is gone. It has been replaced by the more expressive `min-integrity` field. If you have `lockdown: false` in your frontmatter, remove it — it’s no longer recognized. The new integrity-level system gives you finer control over what content can trigger your workflows.
This release also introduces **integrity filtering for log analysis** — the `gh aw logs` command can now filter to only runs where DIFC integrity events were triggered, making security investigations much faster.
### [v0.62.0](https://github.com/github/gh-aw/releases/tag/v0.62.0) — March 19
[Section titled “v0.62.0 — March 19”](#v0620--march-19)
The GitHub MCP guard policy graduates to **general availability**. The policy automatically configures appropriate access controls on the GitHub MCP server at runtime — no manual `lockdown` configuration required. Also new: **inline custom safe-output scripts**, letting you define JavaScript handlers directly in your workflow frontmatter without a separate file.
### [v0.61.x](https://github.com/github/gh-aw/releases/tag/v0.61.2) — March 18
[Section titled “v0.61.x — March 18”](#v061x--march-18)
Three patch releases covered:
* Signed-commit support for protected branches ([v0.61.1](https://github.com/github/gh-aw/releases/tag/v0.61.1))
* Broader ecosystem domain coverage for language package registries ([v0.61.2](https://github.com/github/gh-aw/releases/tag/v0.61.2))
* Critical `workflow_dispatch` expression evaluation fix ([v0.61.2](https://github.com/github/gh-aw/releases/tag/v0.61.2))
## Notable Pull Requests
[Section titled “Notable Pull Requests”](#notable-pull-requests)
Several important fixes landed today (March 23):
* **[Propagate `assign_copilot` failures to agent failure comment](https://github.com/github/gh-aw/pull/22371)** ([#22371](https://github.com/github/gh-aw/pull/22371)): When `assign_copilot_to_created_issues` fails (e.g., bad credentials), the failure context is now surfaced in the agent failure issue so you can actually diagnose it.
* **[Post failure comment when agent assignment fails](https://github.com/github/gh-aw/pull/22347)** ([#22347](https://github.com/github/gh-aw/pull/22347)): A follow-up to the above — the failure now also posts a comment directly on the target issue or PR for immediate visibility.
* **[Hot-path regexp and YAML parse elimination](https://github.com/github/gh-aw/pull/22359)** ([#22359](https://github.com/github/gh-aw/pull/22359)): Redundant regexp compilations and YAML re-parses on the hot path have been eliminated, improving throughput for high-volume workflow execution.
* **[`blocked-users` and `approval-labels` in guard policy](https://github.com/github/gh-aw/pull/22360)** ([#22360](https://github.com/github/gh-aw/pull/22360)): The `tools.github` guard policy now supports `blocked-users` and `approval-labels` fields, giving you more granular control over who can trigger guarded workflows.
* **[Pull merged workflow files after GitHub confirms readiness](https://github.com/github/gh-aw/pull/22335)** ([#22335](https://github.com/github/gh-aw/pull/22335)): A race condition where merged workflow files were pulled before GitHub reported the workflow as ready has been fixed.
## Agent of the Week: contribution-check
[Section titled “ Agent of the Week: contribution-check”](#-agent-of-the-week-contribution-check)
Your tireless four-hourly guardian of PR quality — reads every open pull request and evaluates it against `CONTRIBUTING.md` for compliance and completeness.
`contribution-check` ran five times this week (once every four hours, as scheduled) and processed a steady stream of incoming PRs, creating issues for contributors who needed guidance, adding labels, and leaving review comments. Four of five runs completed in under 5 minutes with 6–9 turns. The fifth run, however, apparently found the task of reviewing PRs during a particularly active Sunday evening so intellectually stimulating that it worked through 50 turns and consumed 1.55 million tokens — roughly 5× its usual appetite — before the safe\_outputs step politely called it a night. It still managed to file issues, label PRs, and post comments on the way out. Overachiever.
One earlier run also hit a minor hiccup: the pre-agent filter step forgot to write its output file, leaving the agent with nothing to evaluate. Rather than fabricating a list of PRs to review, it dutifully reported “missing data” and moved on. Sometimes the bravest thing is knowing when there’s nothing to do.
**Usage tip**: The `contribution-check` pattern works best when your `CONTRIBUTING.md` is explicit and opinionated — the more specific your guidelines, the more actionable its feedback will be for contributors.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/contribution-check.md)
## Try It Out
[Section titled “Try It Out”](#try-it-out)
Update to [v0.62.5](https://github.com/github/gh-aw/releases/tag/v0.62.5) to pick up the security fixes and timezone support. If you’ve been holding off on migrating from `lockdown: true`, now’s the time — check the [v0.62.2 release notes](https://github.com/github/gh-aw/releases/tag/v0.62.2) for the migration path. As always, contributions and feedback are welcome in [github/gh-aw](https://github.com/github/gh-aw).
# Weekly Update – March 30, 2026
> Six releases in seven days: audit superpowers, integrity-aware cache-memory, a serious security sweep, and runner flexibility for compile-stable jobs.
Six releases shipped in [github/gh-aw](https://github.com/github/gh-aw) between March 24 and March 30 — that’s almost one a day. From expanded audit tooling to integrity-isolated cache storage and a wave of security fixes, this was a dense week. Here’s the rundown.
## Releases This Week
[Section titled “Releases This Week”](#releases-this-week)
### [v0.64.4](https://github.com/github/gh-aw/releases/tag/v0.64.4) — March 30
[Section titled “v0.64.4 — March 30”](#v0644--march-30)
The freshest release ships with quality-of-life wins for workflow authors:
* **`runs-on-slim` for compile-stable jobs** ([#23490](https://github.com/github/gh-aw/pull/23490)): Override the runner for `compile-stable` framework jobs with a new `runs-on-slim` key, giving you fine-grained control over which machine handles compilation.
* **Sibling nested imports fixed** ([#23475](https://github.com/github/gh-aw/pull/23475)): `./file.md` imports now resolve relative to the importing file’s directory, not the working directory. Modular workflows that import sibling files were silently broken before — now they’re not.
* **Custom tools in `` prompt** ([#23487](https://github.com/github/gh-aw/pull/23487)): Custom jobs, scripts, and actions are now listed in the agent’s `` prompt block so the AI actually knows they exist.
* **Compile-time validation of safe-output job ordering** ([#23486](https://github.com/github/gh-aw/pull/23486)): Misconfigured `needs:` ordering on custom safe-output jobs is now caught at compile time.
* **MCP Gateway v0.2.9** ([#23513](https://github.com/github/gh-aw/pull/23513)) and **firewall v0.25.4** ([#23514](https://github.com/github/gh-aw/pull/23514)) bumped for all compiled workflows.
### [v0.64.3](https://github.com/github/gh-aw/releases/tag/v0.64.3) — March 29
[Section titled “v0.64.3 — March 29”](#v0643--march-29)
A security-heavy release with one major architectural upgrade:
**Integrity-aware cache-memory** is the headline. Cache storage now uses dedicated git branches — `merged`, `approved`, `unapproved`, and `none` — to enforce integrity isolation at the storage level. A run operating at `unapproved` integrity can no longer read data written by a `merged`-integrity run, and any change to your `allow-only` guard policy automatically invalidates stale cache entries. If you upgrade and see a cache miss on your first run, that’s intentional — legacy data has no integrity provenance and must be regenerated.
**`patch-format: bundle`** ([#23338](https://github.com/github/gh-aw/pull/23338)) is the other highlight: code-push flows now support `git bundle` as an alternative to `git am`, preserving merge commits, authorship, and per-commit messages that were previously dropped.
Security fixes:
* **Secret env var exclusion** ([#23360](https://github.com/github/gh-aw/pull/23360)): AWF now strips all secret-bearing env vars (tokens, API keys, MCP secrets) from the agent container’s visible environment, closing a potential prompt-injection exfiltration path in `pull_request_target` workflows.
* **Argument injection fix** ([#23374](https://github.com/github/gh-aw/pull/23374)): Package and image names in `gh aw compile --validate-packages` are validated before being passed to `npm view`, `pip index versions`, `uv pip show`, and `docker`.
### [v0.64.2](https://github.com/github/gh-aw/releases/tag/v0.64.2) — March 26
[Section titled “v0.64.2 — March 26”](#v0642--march-26)
The `gh aw logs` command gained cross-run report generation via the new `--format` flag:
**`gh aw logs --format`** aggregates firewall behavior across multiple workflow runs and produces an executive summary, domain inventory, and per-run breakdown:
```bash
gh aw logs agent-task --format markdown --count 10 # Markdown
gh aw logs --format markdown --json # JSON for dashboards
gh aw logs --format pretty # Console output
```
This release also includes a **YAML env injection security fix** ([#23055](https://github.com/github/gh-aw/pull/23055)): all `env:` emission sites in the compiler now use `%q`-escaped YAML scalars, preventing newlines or quote characters in frontmatter values from injecting sibling env variables into `.lock.yml` files.
### [v0.64.1](https://github.com/github/gh-aw/releases/tag/v0.64.1) — March 26
[Section titled “v0.64.1 — March 26”](#v0641--march-26)
**`gh aw audit diff`** ([#22996](https://github.com/github/gh-aw/pull/22996)) lets you compare two workflow runs side-by-side — firewall behavior, MCP tool invocations, token usage, and duration — to spot regressions and behavioral drift before they become incidents:
```bash
gh aw audit diff --format markdown
```
Five new sections also landed in the standard `gh aw audit` report: Engine Configuration, Prompt Analysis, Session & Agent Performance, Safe Output Summary, and MCP Server Health. One report now gives you the full picture.
### [v0.64.0](https://github.com/github/gh-aw/releases/tag/v0.64.0) — March 25
[Section titled “v0.64.0 — March 25”](#v0640--march-25)
**Bot-actor concurrency isolation**: Workflows combining `safe-outputs.github-app` with `issue_comment`-capable triggers now automatically get bot-isolated concurrency keys, preventing the workflow from cancelling itself mid-run when the bot posts a comment that re-triggers the same workflow.
### [v0.63.1](https://github.com/github/gh-aw/releases/tag/v0.63.1) — March 24
[Section titled “v0.63.1 — March 24”](#v0631--march-24)
A focused patch adding the **`skip-if-check-failing`** pre-activation gate — workflows can now bail out before the agent runs if a named CI check is currently failing, avoiding wasted inference on a broken codebase. Also ships an improved fuzzy schedule algorithm with weighted preferred windows and peak avoidance to reduce queue contention on shared runners.
***
## Agent of the Week: auto-triage-issues
[Section titled “ Agent of the Week: auto-triage-issues”](#-agent-of-the-week-auto-triage-issues)
The self-appointed gatekeeper of the issue tracker — reads every new issue and assigns labels so the right people see it.
This week, `auto-triage-issues` handled three runs. Two of them were textbook efficiency: triggered the moment a new issue landed, ran the pre-activation check, decided there was nothing worth labeling, and wrapped up in under 42 seconds flat. No fuss, no drama. Then came the Monday scheduled sweep. That run went a different direction: 18 turns, 817,000 tokens, and after all that contemplation… a failure. Somewhere between turn one and turn eighteen, the triage workflow decided this batch of issues deserved its most thoughtful analysis yet, burned through a frontier model’s patience, and still couldn’t quite close the loop.
It’s the classic overachiever problem — sometimes the issues that look the simplest turn out to be the ones that take all day.
**Usage tip**: If your `auto-triage-issues` scheduled runs are consistently expensive, the new `agentic_fraction` metric in `gh aw audit` can help you identify which turns are pure data-gathering and could be moved to deterministic shell steps.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/auto-triage-issues.md)
***
## Try It Out
[Section titled “Try It Out”](#try-it-out)
Update to [v0.64.4](https://github.com/github/gh-aw/releases/tag/v0.64.4) today with `gh extension upgrade aw`. The integrity-aware cache-memory migration will trigger a one-time cache miss on first run — expected and safe. As always, questions and contributions are welcome in [github/gh-aw](https://github.com/github/gh-aw).
# Weekly Update – April 6, 2026
> Ten releases in seven days: full OpenTelemetry distributed tracing, a new report_incomplete safe output, Claude Code 1.0.0 support, and security hardening across the board.
Ten releases landed in [github/gh-aw](https://github.com/github/gh-aw) between March 31 and April 6 — a relentless pace that delivered production-ready distributed tracing, new safe output signals, and a sweeping security cleanup. Here’s what shipped.
## Release Highlights
[Section titled “Release Highlights”](#release-highlights)
### [v0.67.1](https://github.com/github/gh-aw/releases/tag/v0.67.1) — OpenTelemetry Overhaul & Security Hardening (April 6)
[Section titled “v0.67.1 — OpenTelemetry Overhaul & Security Hardening (April 6)”](#v0671--opentelemetry-overhaul--security-hardening-april-6)
The headline release of the week polishes the OTLP tracing story introduced in v0.67.0 and adds a wave of security fixes.
* **Accurate span names and real job durations** ([#24823](https://github.com/github/gh-aw/pull/24823)): Job lifecycle spans now use the actual job name (e.g. `gh-aw.agent.conclusion`) and record real execution time — previously spans always reported 2–5 ms due to a missing `startMs`.
* **OTLP payload sanitization**: Sensitive values (`token`, `secret`, `key`, `auth`, etc.) in span attributes are automatically redacted before sending to any OTLP collector.
* **OTLP headers masking** ([#24805](https://github.com/github/gh-aw/pull/24805)): `OTEL_EXPORTER_OTLP_HEADERS` is masked with `::add-mask::` in every job, preventing auth tokens from leaking into GitHub Actions debug logs.
* **MCP Gateway OpenTelemetry** ([#24697](https://github.com/github/gh-aw/pull/24697)): The MCP Gateway now receives OpenTelemetry config derived from `observability.otlp` frontmatter and the `actions/setup` trace IDs, correlating all MCP tool-call traces under the workflow root trace.
* **`report_incomplete` safe output** ([#24796](https://github.com/github/gh-aw/pull/24796)): A new first-class signal lets agents surface infrastructure or tool failures without being misclassified as successful runs. When an agent emits `report_incomplete`, the safe-outputs handler activates failure handling regardless of agent exit code.
* **`checks` as a first-class MCP tool** ([#24818](https://github.com/github/gh-aw/pull/24818)): The `checks` tool is now registered in the gh-aw MCP server, returning a normalized CI verdict (`success`, `failed`, `pending`, `no_checks`, `policy_blocked`).
* **Token/secret injection prevention**: 422 instances of `${{ secrets.* }}` interpolated directly into `run:` blocks were moved to `env:` mappings across lock files.
* **Claude Code 1.0.0 compatibility** ([#24807](https://github.com/github/gh-aw/pull/24807)): Removed the `--disable-slash-commands` flag that was dropped in Claude Code 1.0.0.
### [v0.67.0](https://github.com/github/gh-aw/releases/tag/v0.67.0) — OTLP Trace Export & GitHub API Rate Limit Analytics (April 5)
[Section titled “v0.67.0 — OTLP Trace Export & GitHub API Rate Limit Analytics (April 5)”](#v0670--otlp-trace-export--github-api-rate-limit-analytics-april-5)
The milestone release that first shipped distributed tracing support:
* **`observability.otlp` frontmatter**: Workflows can now export structured OpenTelemetry spans to any OTLP-compatible backend (Honeycomb, Grafana Tempo, Sentry) with a single frontmatter block. Every job emits setup and conclusion spans; cross-job trace correlation is wired automatically with a single trace ID from the activation job.
* **GitHub API rate limit analytics**: `gh aw audit`, `gh aw logs`, and `gh aw audit diff` now show GitHub API quota consumed per run, per resource.
* **Environment Variable Reference**: A new comprehensive reference section covers all CLI configuration variables.
### [v0.66.1](https://github.com/github/gh-aw/releases/tag/v0.66.1) — Richer `gh aw logs` & Breaking Change (April 4)
[Section titled “v0.66.1 — Richer gh aw logs & Breaking Change (April 4)”](#v0661--richer-gh-aw-logs--breaking-change-april-4)
**! Breaking change**: `gh aw audit report` has been removed. Cross-run security reports are now generated directly by `gh aw logs --format`. The new `--last` flag aliases `--count` to ease migration.
* **Flat run classification** in `gh aw logs --json`: Each run now carries a top-level `classification` string (`"risky"`, `"normal"`, `"baseline"`, or `"unclassified"`), eliminating null-guard gymnastics.
* **Per-tool-call metrics in logs**: Granular token usage, failure counts, and latency per tool — perfect for identifying which tools consume the most resources.
### [v0.66.0](https://github.com/github/gh-aw/releases/tag/v0.66.0) — Token Usage Artifacts & Threat Detection Extensibility (April 3)
[Section titled “v0.66.0 — Token Usage Artifacts & Threat Detection Extensibility (April 3)”](#v0660--token-usage-artifacts--threat-detection-extensibility-april-3)
* **Token Usage Artifact** ([#24315](https://github.com/github/gh-aw/pull/24315)): Agent token usage is now uploaded as a workflow artifact, making it easy to track spend over time.
* Workflow reliability and threat detection extensibility improvements shipped alongside.
### Earlier in the week
[Section titled “Earlier in the week”](#earlier-in-the-week)
[v0.65.7](https://github.com/github/gh-aw/releases/tag/v0.65.7) through [v0.65.2](https://github.com/github/gh-aw/releases/tag/v0.65.2) (March 31–April 3) focused on cross-repo workflow reliability, MCP gateway keepalive configuration, safe-outputs improvements, and token optimization tooling.
***
## Agent of the Week: agentic-observability-kit
[Section titled “ Agent of the Week: agentic-observability-kit”](#-agent-of-the-week-agentic-observability-kit)
The tireless watchdog that monitors your entire fleet of agentic workflows and escalates when things go sideways.
Every day, `agentic-observability-kit` pulls logs from all running workflows, classifies their behavior, and posts a structured observability report as a GitHub Discussion — then files issues when patterns of waste or failure cross defined thresholds. This past week it had a particularly eventful run: on April 6 it spotted that `smoke-copilot` and `smoke-claude` had each burned through 675K–1.7M tokens across multiple runs (flagged as `resource_heavy_for_domain` with high severity), and it filed an issue titled *“Smoke Copilot and Smoke Claude repeatedly resource-heavy”* before anyone on the team had noticed. It also caught that the GitHub Remote MCP Authentication Test workflow had a 100% failure rate across two runs — one of which completed at zero tokens, suggesting a config or auth problem rather than an agent misbehaving.
In a delightfully meta moment, the observability kit itself hit token-limit errors while trying to ingest its own log data — it made four attempts with progressively smaller `count` and `max_tokens` parameters before it could fit the output into context. It got there in the end.
**Usage tip**: Pair `agentic-observability-kit` with Slack or email notifications so escalation issues trigger an alert — otherwise the issues it files can sit unread while the token bill quietly grows.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/agentic-observability-kit.md)
***
## Try It Out
[Section titled “Try It Out”](#try-it-out)
Update to [v0.67.1](https://github.com/github/gh-aw/releases/tag/v0.67.1) and start exporting traces from your workflows today — all it takes is an `observability.otlp` block in your frontmatter. Feedback and contributions are always welcome in [github/gh-aw](https://github.com/github/gh-aw).
# Weekly Update – April 13, 2026
> Five releases this week: engine.bare context control, a critical Copilot CLI hotfix, cross-job distributed tracing, and a wave of security hardening.
It was a busy week in [github/gh-aw](https://github.com/github/gh-aw) — five releases shipped between April 6 and April 10, addressing everything from a critical Copilot CLI reliability crisis to shiny new workflow composition features. Here’s the full rundown.
## Release Highlights
[Section titled “Release Highlights”](#release-highlights)
### [v0.68.1](https://github.com/github/gh-aw/releases/tag/v0.68.1) — April 10
[Section titled “v0.68.1 — April 10”](#v0681--april-10)
The headline of this patch is a **critical Copilot CLI reliability hotfix**. Workflows using the Copilot engine were hanging indefinitely or producing zero-byte output due to an incompatibility introduced in v1.0.22 of the Copilot CLI. [v0.68.1](https://github.com/github/gh-aw/releases/tag/v0.68.1) pins the CLI back to v1.0.21 — the last confirmed-working version — and gets everyone’s workflows running again ([#25689](https://github.com/github/gh-aw/pull/25689)).
Beyond the hotfix, this release also ships:
* **`engine.bare` frontmatter field** ([#25661](https://github.com/github/gh-aw/pull/25661)): Set `bare: true` to suppress automatic context loading — `AGENTS.md` and user instructions for Copilot, `CLAUDE.md` memory files for Claude. Great when you want the AI to start from a clean slate.
* **Improved stale lock file diagnostics** ([#25571](https://github.com/github/gh-aw/pull/25571)): When the activation job detects a stale hash, it now emits step-by-step `[hash-debug]` log lines and opens an actionable issue guiding you to fix it.
* **`actions/github-script` upgraded to v9** ([#25553](https://github.com/github/gh-aw/pull/25553)): Scripts now get `getOctokit` as a built-in context parameter, removing the need for manual `@actions/github` imports in safe-output handlers.
* **Squash-merge fallback in `gh aw add`** ([#25609](https://github.com/github/gh-aw/pull/25609)): If a repo disallows merge commits, the setup PR now automatically falls back to squash merge instead of failing.
* **Security: `agent-stdio.log` permissions hardened** — Log files are now pre-created with `0600` permissions before `tee` writes, preventing world-readable exposure of MCP gateway bearer tokens.
### [v0.68.0](https://github.com/github/gh-aw/releases/tag/v0.68.0) — April 10
[Section titled “v0.68.0 — April 10”](#v0680--april-10)
This release brings [distributed tracing](https://github.com/github/gh-aw/releases/tag/v0.68.0) improvements and a cleaner comment API:
* **OpenTelemetry cross-job trace hierarchy** ([#25540](https://github.com/github/gh-aw/pull/25540)): Parent span IDs now propagate through `aw_context` across jobs, giving you end-to-end distributed trace visibility for multi-job workflows in backends like Tempo, Honeycomb, and Datadog.
* **Simplified discussion comment API** ([#25532](https://github.com/github/gh-aw/pull/25532)): The deprecated `add-comment.discussion` boolean has been removed in favor of the clearer `discussions: true/false` syntax. Run `gh aw fix --write` to migrate existing workflows.
* **Security: heredoc content validation** ([#25510](https://github.com/github/gh-aw/pull/25510)): `ValidateHeredocContent` checks now cover five user-controlled heredoc insertion sites, closing a class of potential injection vectors.
### [v0.67.4](https://github.com/github/gh-aw/releases/tag/v0.67.4) — April 9
[Section titled “v0.67.4 — April 9”](#v0674--april-9)
This one led with **five new agentic workflow templates**: [approach-validator](https://github.com/github/gh-aw/pull/25354), [test-quality-sentinel](https://github.com/github/gh-aw/pull/25353), [refactoring-cadence](https://github.com/github/gh-aw/pull/25352), [architecture-guardian](https://github.com/github/gh-aw/pull/25334), and [design-decision-gate](https://github.com/github/gh-aw/pull/25323). These expand the built-in library for code quality, ADR enforcement, and architectural governance. The release also included Copilot driver retry logic and a `--runner-guard` compilation flag.
### [v0.67.3](https://github.com/github/gh-aw/releases/tag/v0.67.3) — April 8
[Section titled “v0.67.3 — April 8”](#v0673--april-8)
The star of this release is the new **`pre-steps` frontmatter field** — inject steps that run *before* checkout and the agent inside the same job. This is the recommended pattern for token-minting actions (e.g., `actions/create-github-app-token`, `octo-sts`) that need to check out external repos. Because the minted token stays in the same job, it never gets masked when crossing a job boundary. Also shipped: `${{ github.aw.import-inputs.* }}` expression support in the `imports:` section, and `assignees` support on `create-pull-request` fallback issues.
### [v0.67.2](https://github.com/github/gh-aw/releases/tag/v0.67.2) — April 6
[Section titled “v0.67.2 — April 6”](#v0672--april-6)
Reliability-focused: cross-repo workflow hash checks, checkout tokens no longer silently dropped on newer runners, `curl`/`wget` flag-bearing invocations now allowed in `network.allowed` workflows, and a `timeout-minutes` schema cap at 360.
## Notable Merged Pull Requests
[Section titled “Notable Merged Pull Requests”](#notable-merged-pull-requests)
Beyond the releases, the past week also delivered:
* **[#25923](https://github.com/github/gh-aw/pull/25923)**: Image artifacts can now be uploaded without zip archiving using `skip-archive: true`, and the resulting artifact URLs are surfaced as outputs — enabling workflows to embed images directly in Markdown comments.
* **[#25908](https://github.com/github/gh-aw/pull/25908)**: A new scheduled `cleanup-cache-memory` job was added to the agentics maintenance workflow to prune outdated cache-memory entries automatically (and can be triggered on demand).
* **[#25914](https://github.com/github/gh-aw/pull/25914) + [#25972](https://github.com/github/gh-aw/pull/25972)**: OTel exception span events now emit `exception.type` alongside `exception.message` and individual error attributes are queryable — no more digging through pipe-delimited strings in Grafana.
* **[#25960](https://github.com/github/gh-aw/pull/25960)**: Fixed a sneaky bug where `push_repo_memory` would run on every bot-triggered no-op because `always()` bypassed skip propagation.
* **[#25971](https://github.com/github/gh-aw/pull/25971)**: Raw subprocess output from `gh aw compile --validate` is now sanitized before being embedded into issue bodies, closing a Markdown injection vector.
## Agent of the Week: auto-triage-issues
[Section titled “ Agent of the Week: auto-triage-issues”](#-agent-of-the-week-auto-triage-issues)
The quiet backbone of issue hygiene — reads every new issue and applies the right labels so the right people see it.
This week `auto-triage-issues` proved it’s doing its job almost too well. In the scheduled run on April 13, it scanned all open issues and found exactly **zero** unlabeled issues — reporting a 100% label coverage rate with zero action required. It had already handled the labeling in near-real-time as issues arrived, including one run on April 12 where it correctly tagged a freshly opened issue with `enhancement`, `mcp`, `compiler`, and `security` in a single pass. Four labels, zero hesitation.
That “security” label is doing a lot of work — the workflow spotted MCP and compiler concerns that genuinely deserved the tag, not just keyword-matched on it. We’ll take it.
**Usage tip**: Pair `auto-triage-issues` with label-based notification rules so your team gets automatically paged for `security` or `critical` issues without anyone having to babysit the issue tracker.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/auto-triage-issues.md)
## Try It Out
[Section titled “Try It Out”](#try-it-out)
Update to [v0.68.1](https://github.com/github/gh-aw/releases/tag/v0.68.1) today to get the Copilot CLI hotfix and the new `engine.bare` control. As always, contributions and feedback are welcome in [github/gh-aw](https://github.com/github/gh-aw).
# Weekly Update – April 20, 2026
> This week brings five releases packed with a new OpenCode engine, pre-agent steps, cache-memory security hardening, and much more.
What a week for [github/gh-aw](https://github.com/github/gh-aw)! Five releases dropped between April 13 and April 17, delivering a new AI engine, key security improvements, and a wave of reliability fixes. Here’s what you need to know.
## Release Highlights
[Section titled “Release Highlights”](#release-highlights)
### [v0.68.7](https://github.com/github/gh-aw/releases/tag/v0.68.7) — April 17
[Section titled “v0.68.7 — April 17”](#v0687--april-17)
A targeted fix-and-polish release with one standout new addition:
* **`on.roles` single-string support** ([#26789](https://github.com/github/gh-aw/pull/26789)): You can now write `roles: write` instead of `roles: [write]`. Previously this produced a confusing compiler error — now it just works.
* **Codex chroot fix** ([#26787](https://github.com/github/gh-aw/pull/26787)): Codex workflows on restricted filesystems were failing silently. Runtime state now lives in `/tmp` where it can actually be written.
* **Cross-repo compatibility checks** ([#26802](https://github.com/github/gh-aw/pull/26802)): A new daily Claude workflow automatically discovers repositories using gh-aw and runs compile checks against the latest build. Compatibility regressions now get caught before they reach users.
### [v0.68.6](https://github.com/github/gh-aw/releases/tag/v0.68.6) — April 17
[Section titled “v0.68.6 — April 17”](#v0686--april-17)
The headline release of the week, with a brand-new engine and important security improvements:
* **OpenCode engine** — Set `engine: opencode` to use [OpenCode](https://opencode.ai) as your agentic engine, joining Copilot, Claude, and Codex as first-class options.
* **`engine.bare` mode** — Set `engine.bare: true` to skip loading `AGENTS.md`. Perfect for triage, reporting, and ops workflows where repository code context just adds noise.
* **Pre-agent steps** — The new `pre-agent-steps` frontmatter field lets you run custom GitHub Actions steps before the AI agent starts — great for authentication, environment setup, or any prerequisite work.
* **`cache-memory` working-tree sanitization** — Before each agent run, the working tree is now scanned and cleaned of planted executables and disallowed files from cached memory. This closes a real supply-chain attack vector.
### [v0.68.5](https://github.com/github/gh-aw/releases/tag/v0.68.5) — April 16
[Section titled “v0.68.5 — April 16”](#v0685--april-16)
Quality-of-life improvements and more security hardening:
* **MCP config at `.github/mcp.json`** ([#26665](https://github.com/github/gh-aw/pull/26665)): The MCP configuration file has moved from `.mcp.json` (repo root) to `.github/mcp.json`, aligning with standard GitHub configuration conventions. The `init` flow creates the new path automatically.
* **`shared/reporting-otlp.md` import bundle** ([#26655](https://github.com/github/gh-aw/pull/26655)): One import now replaces two for telemetry-enabled reporting workflows.
* **Environment-level secrets fixed** ([#26650](https://github.com/github/gh-aw/pull/26650)): The `environment:` frontmatter field now correctly propagates to the activation job.
### [v0.68.4](https://github.com/github/gh-aw/releases/tag/v0.68.4) — April 16
[Section titled “v0.68.4 — April 16”](#v0684--april-16)
A substantial patch resolving 21 community-reported issues:
* **BYOK Copilot mode** ([#26544](https://github.com/github/gh-aw/pull/26544)): New `byok-copilot` feature flag wires offline Copilot support.
* **Side repo maintenance workflow** ([#26382](https://github.com/github/gh-aw/pull/26382)): The compiler now auto-generates `agentics-maintenance.yml` for target repositories in side repository patterns.
* **MCP servers as local CLIs** ([#25928](https://github.com/github/gh-aw/pull/25928)): MCP servers can now be mounted as local CLI commands after the gateway starts, enabling richer tool integrations.
### [v0.68.3](https://github.com/github/gh-aw/releases/tag/v0.68.3) — April 14
[Section titled “v0.68.3 — April 14”](#v0683--april-14)
Observability and reliability improvements:
* **Model-not-supported detection** ([#26229](https://github.com/github/gh-aw/pull/26229)): When a model is unavailable for your plan, the workflow now stops retrying and surfaces a clear error instead of spinning indefinitely.
* **Time Between Turns (TBT) metric** ([#26321](https://github.com/github/gh-aw/pull/26321)): `gh aw audit` and `gh aw logs` now report TBT — a key indicator of whether LLM prompt caching is working for your workflows.
* **`env` and `checkout` fields in shared imports** ([#26113](https://github.com/github/gh-aw/pull/26113), [#26292](https://github.com/github/gh-aw/pull/26292)): Shared importable workflows now support both `env:` and `checkout:` fields, eliminating common workarounds.
## Agent of the Week: auto-triage-issues
[Section titled “ Agent of the Week: auto-triage-issues”](#-agent-of-the-week-auto-triage-issues)
The unsung hero of issue hygiene — reads every unlabeled issue and applies the right labels so the right people see it, automatically, on a schedule.
This week `auto-triage-issues` kept its usual steady pace, triaging issues as they came in. In one run, it spotted issue [#27290](https://github.com/github/gh-aw/issues/27290) — a question about ecosystem groups in the frontmatter/compilation pipeline — and correctly labeled it `compiler` within 24 seconds flat. In another run, it encountered an issue that the integrity policy had filtered before the agent could even read the title, so it did the responsible thing: skipped labeling, created a summary discussion, and politely told the maintainers to take a look themselves.
Even when it can’t act, it doesn’t just silently fail — it leaves a breadcrumb so nothing falls through the cracks.
**Usage tip**: Pair `auto-triage-issues` with a `notify` workflow on high-priority labels (like `security` or `breaking-change`) so your team gets paged for the things that actually matter.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/auto-triage-issues.md)
## Try It Out
[Section titled “Try It Out”](#try-it-out)
With [v0.68.7](https://github.com/github/gh-aw/releases/tag/v0.68.7) now available, it’s a great time to update and explore the new OpenCode engine, `engine.bare` mode, or pre-agent steps. As always, feedback and contributions are very welcome in [github/gh-aw](https://github.com/github/gh-aw).
# Weekly Update – April 27, 2026
> v0.71.1 lands with critical bug fixes, v0.71.0 adds threat-detection improvements and Claude engine updates, plus a spotlight on the auto-triage-issues workflow.
Another productive week in [github/gh-aw](https://github.com/github/gh-aw)! Two releases dropped — v0.71.0 and v0.71.1 — bringing reliability fixes across the board, from threat-detection improvements to the Claude engine to a loop that was quietly consuming millions of tokens. Here’s what shipped.
## Release: [v0.71.1](https://github.com/github/gh-aw/releases/tag/v0.71.1)
[Section titled “Release: v0.71.1”](#release-v0711)
Released April 24th, this patch release is all about correctness:
* **`protected-files` object form now compiles correctly** ([#28341](https://github.com/github/gh-aw/pull/28341)): Workflows using the documented `{policy, exclude}` object syntax were being rejected at compile time. That’s fixed — the schema now accepts both the string shorthand and the full object form.
* **Pre-agent skills no longer overwritten on `pull_request` triggers** ([#28290](https://github.com/github/gh-aw/pull/28290)): Skills installed by `pre-agent-steps` were silently clobbered because the “Restore agent config folders” step ran *after* them. Step ordering is now correct.
* **Incremental diff for `push_to_pull_request_branch` patch size** ([#28198](https://github.com/github/gh-aw/pull/28198)): The max patch size check now measures only the incremental change since the last push, not the full diff from the default branch. No more spurious size-limit rejections on long-running branches.
* **`jsweep` infinite loop fixed** ([#28353](https://github.com/github/gh-aw/pull/28353)): A workflow was calling `create_pull_request` in a loop, racking up 4.64M tokens per run. It now exits after creating a PR.
## Release: [v0.71.0](https://github.com/github/gh-aw/releases/tag/v0.71.0)
[Section titled “Release: v0.71.0”](#release-v0710)
Released April 23rd, focused on runtime reliability and new capabilities:
* **Node.js setup added to threat-detection jobs** ([#28160](https://github.com/github/gh-aw/pull/28160)): The `node: command not found` error in Copilot threat-detection workflows is gone — Node.js setup is now emitted before `copilot_driver.cjs`.
* **OTLP tracing for cancelled runs** ([#28172](https://github.com/github/gh-aw/pull/28172)): Manually cancelled runs now emit a proper OpenTelemetry span, so you get full duration visibility even when a run is cut short.
* **Claude engine: `bypassPermissions` → `acceptEdits`** ([#28047](https://github.com/github/gh-aw/pull/28047)): Migrates away from the deprecated flag and fixes missing MCP server entries in `--allowed-tools`, keeping Claude-powered workflows fully functional.
## Notable Merged PRs
[Section titled “Notable Merged PRs”](#notable-merged-prs)
Beyond the releases, this week also saw some useful quality-of-life improvements merged directly to main:
* **[Add `gh aw run` guidance and CLI commands reference](https://github.com/github/gh-aw/pull/28616)**: Better docs for running workflows locally — a common source of confusion.
* **[Accessibility fix: skip link anchor](https://github.com/github/gh-aw/pull/28618)**: Renamed `#_top` → `#main-content` to meet WCAG 2.4.1 requirements.
* **[Fix `daily-cache-strategy-analyzer` false alarm](https://github.com/github/gh-aw/pull/28617)**: The workflow was raising spurious alerts at startup when the cache was simply empty. Now it checks properly before sounding the alarm.
## Agent of the Week: auto-triage-issues
[Section titled “ Agent of the Week: auto-triage-issues”](#-agent-of-the-week-auto-triage-issues)
The tireless sentinel of the issue tracker — reads every open issue and classifies it so the right people see it.
This week, `auto-triage-issues` ran **three times in a single day** (April 27th alone), faithfully scanning for untriaged issues each time on a scheduled basis. Across its runs, it averaged just 4–6 turns per execution, keeping things lean while still making 6 GitHub API calls per run. The workflow even improved its own efficiency mid-day — dropping from 6 turns in the morning run down to 4 turns by afternoon, apparently learning to get to the point faster. The observability metrics politely noted it might be “partially reducible to deterministic automation,” but honestly, where’s the fun in that?
One of its runs earned an honorable mention from the agentic assessment system: “This Triage run looks stable enough that deterministic automation may be a simpler fit.” The workflow responded by running again an hour later, exactly the same as before. Iconic.
**Usage tip**: Pair `auto-triage-issues` with a label-based notification workflow so the right team members get pinged the moment a new issue is categorized.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/auto-triage-issues.md)
## Try It Out
[Section titled “Try It Out”](#try-it-out)
Update to [v0.71.1](https://github.com/github/gh-aw/releases/tag/v0.71.1) today and check out all the fixes. Feedback and contributions are always welcome over at [github/gh-aw](https://github.com/github/gh-aw).
# Weekly Update – May 4, 2026
> This week brings v0.71.3 with parameterized safe-outputs, the new A/B experiments framework, and a codex harness upgrade.
Happy May the Fourth! Here’s a look at what shipped in [github/gh-aw](https://github.com/github/gh-aw) this week — a busy one packed with experiment infrastructure, compiler fixes, and engine improvements.
## Release: v0.71.3
[Section titled “Release: v0.71.3”](#release-v0713)
[v0.71.3](https://github.com/github/gh-aw/releases/tag/v0.71.3) landed on April 30th, capping off a week of rapid iteration. This release delivers major improvements to safe-outputs reusability, more resilient Copilot driver behavior, and solid self-hosted runner support.
### What’s New
[Section titled “ What’s New”](#-whats-new)
* **Parameterized safe-outputs for reusable workflows** ([#29171](https://github.com/github/gh-aw/issues/29171)): `workflow_call` inputs can now control `safe-outputs.threat-detection`, boolean flags, PR policy fields, and list constraints. Build reusable workflows that callers can configure without forking.
* **Configurable MCP gateway session timeout**: Set `engine.mcp.session-timeout` in your workflow frontmatter to keep long-running MCP sessions alive. No more premature timeouts on deep analysis workflows.
* **Auto-inject `create_issue` safe output**: Workflows without explicit safe-output configuration now automatically get a `create_issue` safe output, slashing boilerplate for common workflows.
* **Repo Mind Light shared workflow**: A shared `repo-mind-light.md` workflow is now available for reuse across daily issue/PR agentic workflows ([#29063](https://github.com/github/gh-aw/issues/29063)).
* **Team reviewers on `add_reviewer`**: The `add_reviewer` MCP tool now supports setting `team_reviewers` on pull requests ([#29228](https://github.com/github/gh-aw/issues/29228)).
* **Self-hosted runner support for non-default home directories**: Workflows now work correctly on self-hosted runners where the service account home is not `/home/runner` ([#27260](https://github.com/github/gh-aw/issues/27260)).
## Notable Pull Requests
[Section titled “Notable Pull Requests”](#notable-pull-requests)
Several impactful PRs landed this week beyond the release:
* **[Compiler detects single-quoted bash commands that crash Copilot CLI](https://github.com/github/gh-aw/pull/30040)**: The compiler now catches and sanitizes single-quoted bash tool commands before they reach the Copilot CLI, preventing cryptic runtime crashes. A small fix with a big quality-of-life impact.
* **[Default Codex harness with retry logic](https://github.com/github/gh-aw/pull/30035)**: The Codex engine now ships a default `codex_harness.cjs` with built-in retry logic, making Codex-powered workflows more resilient out of the box.
* **[A/B experiments framework](https://github.com/github/gh-aw/pull/30020)**: A hidden `experiments` CLI command lets you read experiment state from storage repo branches, enabling controlled A/B testing of workflow behavior across runs.
* **[Statistical analysis for experiments](https://github.com/github/gh-aw/pull/30029)**: The `experiments analyze` command now computes statistical significance, so you can tell whether a prompt change actually improved things — or just got lucky.
* **[Multiple OTLP endpoints](https://github.com/github/gh-aw/pull/30021)**: The `endpoint` field in OTLP configuration is now polymorphic — send telemetry to multiple backends simultaneously.
* **[Fix: round-robin random start on cache miss](https://github.com/github/gh-aw/pull/30005)**: Round-robin workflows now randomly select their starting item when the cache is cold, preventing all instances from piling onto the first item at startup.
## Agent of the Week: ab-testing-advisor
[Section titled “ Agent of the Week: ab-testing-advisor”](#-agent-of-the-week-ab-testing-advisor)
The world’s most meta workflow — it finds workflows that *don’t* run experiments yet, and proposes experiments for them.
This week `ab-testing-advisor` ran three times, each time scanning the entire workflow catalog for experiment-free candidates, picking one, and writing a detailed GitHub issue with a full A/B experiment campaign. On May 2nd alone it created two issues: one proposing a [`prompt_style` A/B test for the `daily-news` workflow](https://github.com/github/gh-aw/issues/29660) (which it diagnosed as “highly prescriptive” and worth loosening up), and another ([#29661](https://github.com/github/gh-aw/issues/29661)) calling for improvements to the experiment infrastructure itself — the advisor advising on how to improve the advisor. Very on-brand.
It spent roughly 500k tokens per run carefully reading workflow files, thinking through experiment dimensions, and writing crisp implementation specs. For a workflow that runs daily and quietly, it’s doing serious intellectual heavy lifting behind the scenes.
**Usage tip**: Use `ab-testing-advisor` as inspiration for your own repos — it’s a great example of a meta-workflow that uses AI to drive continuous improvement of *other* AI workflows.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/ab-testing-advisor.md)
## Try It Out
[Section titled “Try It Out”](#try-it-out)
Update to [v0.71.3](https://github.com/github/gh-aw/releases/tag/v0.71.3) today to get parameterized safe-outputs, the new experiment infrastructure, and all the reliability fixes. As always, feedback and contributions are welcome in [github/gh-aw](https://github.com/github/gh-aw).
# Weekly Update – May 11, 2026
> Four releases in one week: gh aw lint, inline sub-agents default-on, a new forecast command, and Claude /tmp access — plus the story of our tireless Auto-Triage Issues agent.
It was a busy week in [github/gh-aw](https://github.com/github/gh-aw)! Four releases landed between May 4 and May 7, paired with a wave of pull requests that delivered new commands, security hardening, and developer-experience polish. Here’s everything that shipped.
## Releases This Week
[Section titled “Releases This Week”](#releases-this-week)
### [v0.72.1](https://github.com/github/gh-aw/releases/tag/v0.72.1) — May 7
[Section titled “v0.72.1 — May 7”](#v0721--may-7)
The headline feature is a new `gh aw lint` command that runs [actionlint](https://github.com/rhysd/actionlint) directly against your existing `.lock.yml` files — no recompile required. It’s a lightweight CI gate you can drop into any pipeline to catch syntax errors early. Pass `--shellcheck` or `--pyflakes` for deeper script analysis, or point it at specific files with `--dir`.
Other highlights:
* **Shared workflow `engine.mcp.tool-timeout` inheritance** ([#30634](https://github.com/github/gh-aw/issues/30634)): Shared workflows that wrap slow MCP servers can now declare timeout values once and have consumers inherit them automatically — no more duplicating `engine.mcp.tool-timeout` in every downstream workflow.
* **First-party coding-agent skill** ([#27259](https://github.com/github/gh-aw/issues/27259)): Copilot, Claude, and other coding agents now get structured guidance on creating, debugging, and updating agentic workflows via a router skill shipped with `gh aw`.
* **`&&` preserved in compiled expressions** ([#30695](https://github.com/github/gh-aw/issues/30695)): A sneaky Go HTML-escaping bug was silently turning `&&` into `\u0026\u0026` inside `.lock.yml` files, corrupting `${{ ... && ... }}` expressions. Fixed.
### [v0.72.0](https://github.com/github/gh-aw/releases/tag/v0.72.0) — May 6
[Section titled “v0.72.0 — May 6”](#v0720--may-6)
Inline sub-agents are now **default-on** — the `features.inline-agents: true` flag is deprecated. Run `gh aw fix --write` to auto-remove it from existing workflows via the new `features-inline-agents-removal` codemod.
This release also fixed a community-reported `push_to_pull_request_branch` rerun failure: when an agent reran and its patch reintroduced a file already on the branch, `git am --3way` produced an unresolvable add/add conflict. The fix detects add/add-only conflicts and resolves them by taking the patch side automatically.
### [v0.71.6](https://github.com/github/gh-aw/releases/tag/v0.71.6) and [v0.71.5](https://github.com/github/gh-aw/releases/tag/v0.71.5) — May 5–6
[Section titled “v0.71.6 and v0.71.5 — May 5–6”](#v0716-and-v0715--may-56)
These patch releases addressed Claude engine stability (no more mid-session crashes from “Fast mode unavailable”), fixed multi-line `engine.env` block-scalar values that compiled to broken YAML, added gateway RPC message rendering in step summaries, and switched inline sub-agent blocks to the `small` model alias by default to reduce cost and latency.
## Notable Pull Requests
[Section titled “Notable Pull Requests”](#notable-pull-requests)
Beyond the releases, several PRs merged this week are worth highlighting:
* **[`gh aw forecast` command (experimental)](https://github.com/github/gh-aw/pull/31377)** — A new command for projecting workflow effective token usage before you run it. Useful for budgeting and capacity planning.
* **[Grant Claude default `/tmp` read/write in sandboxed workflows](https://github.com/github/gh-aw/pull/31357)** — Claude-engine workflows can now read and write to `/tmp` by default in sandboxed environments, eliminating a common pain point when agents need temporary scratch space.
* **[Rename `rate-limit` → `user-rate-limit` and `max-runs` → `max-runs-per-window`](https://github.com/github/gh-aw/pull/31390)** — Clearer naming for rate-limiting configuration fields.
* **[OTel `gen_ai.response.finish_reasons`](https://github.com/github/gh-aw/pull/31332)** — Agent spans now emit finish reasons (e.g., `stop`, `length`, `tool_calls`) as an OpenTelemetry attribute, improving observability dashboards.
* **[Synthetic OTel exception events for silent failures](https://github.com/github/gh-aw/pull/31334)** — When a workflow fails but the agent produces no readable output, a synthetic exception event is now emitted so traces still surface the failure.
## Agent of the Week: auto-triage-issues
[Section titled “ Agent of the Week: auto-triage-issues”](#-agent-of-the-week-auto-triage-issues)
The unsung inbox manager of the repository — reads every new issue the moment it’s opened and figures out where it belongs.
This week `auto-triage-issues` ran three times in quick succession (May 9–10), successfully triaging two issues and stumbling on a third that triggered a failure — a small battle scar it wore with dignity. In its successful runs it stayed impressively lean: nine API requests, \~270 K input tokens pulled from cache, and a turnaround of under 40 seconds per issue. It never wastes a compute cycle it doesn’t have to.
The run summary noted with mild concern that `auto-triage-issues` is so reliable and narrow in its tool usage that it might be “overkill for agentic” — meaning deterministic automation could theoretically do its job. The workflow appears to have taken this note personally and immediately triaged the next issue without comment.
**Usage tip**: Pair `auto-triage-issues` with a `notify` or `discussion` workflow on high-priority labels so the right people are paged the moment a critical bug or security issue lands.
→ [View the workflow on GitHub](https://github.com/github/gh-aw/blob/main/.github/workflows/auto-triage-issues.md)
## Try It Out
[Section titled “Try It Out”](#try-it-out)
Update to [v0.72.1](https://github.com/github/gh-aw/releases/tag/v0.72.1) today — `gh extension upgrade gh-aw` — and try the new `gh aw lint` and experimental `gh aw forecast` commands. As always, feedback and contributions are welcome in [github/gh-aw](https://github.com/github/gh-aw).
# Agent of the Day – May 15, 2026
> Meet the AI Moderator: a Codex-powered workflow that reviews every PR, issue, and comment for policy compliance — automatically.
Every open-source repo has the same invisible tax: someone has to watch the door. Label the PR. Check if the commenter is a member or an outsider. Hide the policy violation before it spreads. Flag the ambiguous case for a human. It’s repetitive, important, and easy to miss at 2 AM when CI is green and you’re trying to ship.
That’s the gap the AI Moderator workflow fills — automatically, on every event, before a human even opens their notifications.
***
## Agent of the Day: AI Moderator
[Section titled “Agent of the Day: AI Moderator”](#agent-of-the-day-ai-moderator)
The AI Moderator is a Codex-powered agentic workflow in the `github/gh-aw` repository. It fires on pull requests, new issues, and comments — running a structured investigation each time to determine who’s knocking, what they brought, and what action to take. Label it. Hide it. Escalate it. Or stand down.
It’s not a simple rule-based bot. It reasons.
On a recent run — [Actions run 25924881974](https://github.com/github/gh-aw/actions/runs/25924881974) — the agent woke up when [PR #32406](https://github.com/github/gh-aw/pull/32406) landed: a work-in-progress branch titled *“Experiment with output format in daily compiler quality”* from `copilot/ab-advisorexperiment-output-format`. Sixteen turns later, it had done its job.
### What it actually did
[Section titled “What it actually did”](#what-it-actually-did)
The agent didn’t guess. It looked things up.
It started by orienting itself — calling `github___get_me` to confirm its own identity, then `github-search_repositories` to verify the repo context it was operating in. From there it fanned out: `github-list_branches`, `github-list_tags`, `github-list_releases`, `github-get_teams`, `github-get_team_members`. It was building a picture of who belongs here and what the repo looks like right now.
Then it turned to the PR itself. It pulled the PR details with `github___pull_request_read`, searched related issues with `github___search_issues` and `github___search_pull_requests`, reviewed the commit history via `github___list_commits`, and read any linked issue context through `github-issue_read`. That’s a broad sweep — the kind a human reviewer would do informally, but inconsistently. The agent did it every time, in the same order, with a logged record of each step.
The conclusion: `action_required`. The agent applied labels through `safeoutputs-add_labels`, hid at least one comment using `safeoutputs___hide_comment`, and raised a flag with `safeoutputs-report_incomplete` to signal that follow-up was needed. Where checks passed cleanly, it called `safeoutputs-noop` — explicit confirmation that nothing warranted action, not just silence.
### Sixteen turns, and that’s notable
[Section titled “Sixteen turns, and that’s notable”](#sixteen-turns-and-thats-notable)
The audit system tracks behavioral baselines. On the same day, a reference run ([25924730956](https://github.com/github/gh-aw/actions/runs/25924730956)) completed with zero turns and a `success` conclusion. This run took 16. The delta was flagged automatically as a `turns_increase` requiring review.
That flag matters. It means the system caught a meaningful deviation in how the agent behaved — not a failure, but a signal worth inspecting. Did the PR have unusual characteristics? Was the team membership lookup more complex than usual? The audit trail is there. The observation is already logged.
This is what makes agentic workflows different from scripts: the behavior changes with the input, and the monitoring has to account for that.
### Why it’s worth watching
[Section titled “Why it’s worth watching”](#why-its-worth-watching)
Community moderation is one of those problems where the cost of under-investing is invisible until it isn’t. A missed label means a misrouted PR. A comment that should have been hidden lingers. An external contributor gets treated the same as a maintainer when they shouldn’t.
The AI Moderator closes that gap without requiring a human to be on-call for it. It checks team membership — not just assumed from a username, but verified against `github-get_team_members`. It applies structured outputs through the `safeoutputs` interface, which means every action is auditable. And when it can’t confidently resolve a case, it says so explicitly via `report_incomplete`, rather than silently doing nothing.
Fast, too. This run completed in seconds.
### Try it
[Section titled “Try it”](#try-it)
The workflow is part of the `github/gh-aw` agentic workflows project — a growing collection of Codex-powered agents built to automate the unglamorous parts of software engineering. If your team maintains a repository and you’re tired of playing gatekeeper manually, this is a good place to start.
Head to [github.com/github/gh-aw](https://github.com/github/gh-aw) to see the workflows, read the specs, and explore what’s already running in production.
***
*Agent of the Day is a recurring look at agentic workflows built and run inside the GitHub engineering org.*
# Agent of the Day – May 20, 2026
> Architecture Guardian workflow intelligently skips analysis when no code changes are detected
You know that sinking feeling when your CI pipeline kicks off a full build-test-deploy cycle because someone fixed a typo in the README? Or when your security scanner churns through every line of code at 2 AM, finds nothing new, and emails you a 47-page report that’s identical to yesterday’s?
Yeah, we’ve all been there. The robot dutifully did its job. You dutifully archived the notification. Nobody won.
Enter **Architecture Guardian**, a scheduled workflow that’s learned the ancient DevOps virtue of knowing when *not* to run.
## The Setup: Daily Architecture Audits
[Section titled “The Setup: Daily Architecture Audits”](#the-setup-daily-architecture-audits)
This workflow runs every weekday around 14:00 UTC with a straightforward mission: scan Go and JavaScript source files for architecture drift, naming violations, or structural anti-patterns that might’ve slipped through code review. It’s the kind of governance check that *should* run regularly—but doesn’t need to re-analyze the entire codebase when nothing has changed.
On [run 26171885477](https://github.com/github/gh-aw/actions/runs/26171885477), Architecture Guardian demonstrated exactly how a smart agent should behave: it showed up, looked around, realized there was no work to do, and gracefully bowed out.
## The Smart Skip: 5.5 Minutes of Doing Nothing (Efficiently)
[Section titled “The Smart Skip: 5.5 Minutes of Doing Nothing (Efficiently)”](#the-smart-skip-55-minutes-of-doing-nothing-efficiently)
Here’s what happened under the hood:
The workflow spun up, spent three agent turns checking for recent changes, and concluded: **zero Go or JavaScript files modified in the last 24 hours**. Instead of proceeding with the full architecture scan—parsing files, running static analysis, generating reports—it called `safeoutputs.noop` with a clear message:
> “No Go or JavaScript source files changed in the last 24 hours. Architecture scan skipped.”
Total runtime? 5.5 minutes. Token usage? 123k—mostly spent confirming the skip was valid. No unnecessary compute, no noise in the logs, no pointless notifications.
Compare that to a naïve scheduled job that runs the full analysis every single day regardless of activity. Over a month of weekdays (roughly 22 runs), this skip-when-idle logic could save hours of compute time and thousands of tokens on quiet days.
## The Read-Only Posture: Analysis, Not Automation Chaos
[Section titled “The Read-Only Posture: Analysis, Not Automation Chaos”](#the-read-only-posture-analysis-not-automation-chaos)
Architecture Guardian operates in **read-only mode**—it never writes back to GitHub, never auto-fixes violations, never opens PRs. It’s pure analysis. When it *does* find issues, it surfaces them cleanly for human review. When it finds nothing (or nothing *new*), it stays silent.
This run hit some network friction—3 blocked requests out of 8 total, a 38% block rate—but still completed successfully. The agent adapted, worked within constraints, and delivered its finding: nothing to report.
Two anomalous event patterns flagged during the run suggest the reliability monitoring is working as intended, catching edge cases for future iteration.
## Why This Matters: Respecting Developer Time
[Section titled “Why This Matters: Respecting Developer Time”](#why-this-matters-respecting-developer-time)
The real win isn’t the 5.5 minutes saved on one run. It’s the **cognitive load reduction**. When your scheduled jobs only notify you about *actual changes*, you start trusting them again. The alert fatigue drops. The “mark all as read” reflex fades.
Architecture Guardian isn’t trying to impress you with how much work it can do. It’s trying to impress you by doing *only the work that matters*.
That’s automation maturity.

***
**Want workflows that know when to quit while they’re ahead?** Check out the [gh-aw project on GitHub](https://github.com/github/gh-aw) and see how agentic workflows can respect your time as much as your architecture.
# Error Messages
> Write actionable, constructive error messages with examples.
# Error message style guide
[Section titled “Error message style guide”](#error-message-style-guide)
Use actionable messages that explain what went wrong, what is expected, and how to fix it.
## Prefer constructive language
[Section titled “Prefer constructive language”](#prefer-constructive-language)
* Avoid: `invalid`, `cannot`, `must`, `failed` without guidance.
* Prefer adding: `expected`, `requires`, `should`, `example`.
✓ `invalid repo format 'gh-aw' — expected 'owner/repo' format (for example: 'github/gh-aw')`
✗ `invalid repo format`
## When to use `NewValidationError` vs `fmt.Errorf`
[Section titled “When to use NewValidationError vs fmt.Errorf”](#when-to-use-newvalidationerror-vs-fmterrorf)
Use `NewValidationError(field, value, reason, suggestion)` in validation code (`*_validation.go`) so users get a structured reason and suggestion.
Use `fmt.Errorf` for operational wrapping (`%w`) outside validation logic when you include specific context and recovery guidance.
## Error type selection
[Section titled “Error type selection”](#error-type-selection)
* `NewValidationError(...)`: bad input/config shape, missing fields, unsupported values.
* `NewOperationError(...)`: runtime actions fail (fetching, file IO, network, command execution).
* `NewConfigurationError(...)`: safe-outputs/config wiring errors.
* `fmt.Errorf(...%w...)`: wrap lower-level errors with actionable context.
## Suggestion text requirements
[Section titled “Suggestion text requirements”](#suggestion-text-requirements)
Good suggestions:
1. Say what to change
2. Include a concrete YAML/code example
3. Prefer ✓/✗ examples when ambiguity is likely
Example:
```text
Use a supported engine.
✓ Example:
engine: copilot
✗ Avoid:
engine: unknown
```
## YAML example guidance
[Section titled “YAML example guidance”](#yaml-example-guidance)
* Keep examples minimal and valid YAML
* Use real field names from frontmatter
* Quote only when required by YAML syntax
# Multi-Repository Examples
> Complete examples for managing workflows across multiple GitHub repositories, including feature synchronization, cross-repo tracking, quality monitoring, and organization-wide updates.
Multi-repository operations enable coordinating work across multiple GitHub repositories while maintaining security and proper access controls. These examples demonstrate common patterns for cross-repo workflows.
## Featured Examples
[Section titled “Featured Examples”](#featured-examples)
### [Triage from Side Repo](/gh-aw/examples/multi-repo/triage-from-side-repo/)
[Section titled “Triage from Side Repo”](#triage-from-side-repo)
Runs automated issue triage on a main repository from an isolated side repository, with a slash-command bridge for real-time `/triage` response. Keeps all automation logic separate from the main codebase. Use when you want to experiment with agentic triage without touching your main repository.
### [Code Quality Monitoring](/gh-aw/examples/multi-repo/code-quality-monitoring/)
[Section titled “Code Quality Monitoring”](#code-quality-monitoring)
Runs weekly code quality analysis from a side repository by checking out the target codebase locally, running linters and complexity checks, and creating focused actionable issues. Use for ongoing quality gates across repositories you don’t want to modify.
### [Feature Synchronization](/gh-aw/examples/multi-repo/feature-sync/)
[Section titled “Feature Synchronization”](#feature-synchronization)
Automates code synchronization from main repositories to sub-repositories or downstream services through pull requests with change detection, path filters, and bidirectional sync support. Use for monorepo alternatives, shared component libraries, multi-platform deployments, or fork maintenance.
### [Cross-Repository Issue Tracking](/gh-aw/examples/multi-repo/issue-tracking/)
[Section titled “Cross-Repository Issue Tracking”](#cross-repository-issue-tracking)
Centralizes issue tracking by automatically creating tracking issues in a central repository with status synchronization and multi-component coordination. Use for component-based architecture visibility, multi-team coordination, cross-project initiatives, or upstream dependency tracking.
### [Dependabot Rollout](/gh-aw/examples/multi-repo/dependabot-rollout/)
[Section titled “Dependabot Rollout”](#dependabot-rollout)
Rolls out a customized Dependabot configuration across many repositories using an orchestrator + worker pair from a central control repository. The orchestrator filters and prioritizes targets, then dispatches workers that analyze each repo and create tailored pull requests. Use for org-wide config standardization, security patch rollouts, or any scheduled multi-repo operation.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) - Design patterns for multi-repository workflows
* [Cross-Repository Reference](/gh-aw/reference/cross-repository/) - Checkout and target-repo configuration
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - Configuration options
* [GitHub Tools](/gh-aw/reference/github-tools/) - API access configuration
* [Security Best Practices](/gh-aw/introduction/architecture/) - Authentication and security
* [Reusing Workflows](/gh-aw/guides/packaging-imports/) - Sharing workflows
# Code Quality Monitoring
> Run weekly code quality analysis on a main repository from a side repository, checking out the code locally to run linters and producing actionable issues.
This example shows how to run weekly code quality checks on `my-org/main-repo` from a dedicated side repository. The agent checks out the target repository, runs linters and complexity analysis locally, and creates prioritized issues in the main repo — keeping automation infrastructure entirely separate from the codebase it monitors.
## How It Works
[Section titled “How It Works”](#how-it-works)
```
flowchart LR
subgraph side["Side repo (automation)"]
schedule([Weekly schedule]) --> agent[Quality agent]
agent -->|checkout| clone[Local clone\nof main-repo]
clone --> lint[Run linters /\nanalyze code]
end
lint -->|create-issue| main[main-repo]
```
The agent:
1. Checks out `main-repo` into the workflow runner
2. Runs linters, counts complexity, and scans for security patterns
3. Creates focused, actionable issues in the main repo for significant findings
## Setup
[Section titled “Setup”](#setup)
### 1. Create the Side Repository
[Section titled “1. Create the Side Repository”](#1-create-the-side-repository)
```bash
gh repo create my-org/main-repo-quality --private
gh repo clone my-org/main-repo-quality
cd main-repo-quality
```
### 2. Create the Authentication Token
[Section titled “2. Create the Authentication Token”](#2-create-the-authentication-token)
Create a fine-grained PAT (`GH_AW_MAIN_REPO_TOKEN`) scoped **only to `my-org/main-repo`** with these permissions:
| Permission | Level | Purpose |
| ---------- | ------------ | ----------------------- |
| Contents | Read-only | Checkout the repository |
| Issues | Read & write | Create quality issues |
Store it as a secret in the **side repository**:
```bash
gh secret set GH_AW_MAIN_REPO_TOKEN --repo my-org/main-repo-quality
```
Note
The default `GITHUB_TOKEN` cannot access other repositories. The explicit token must be set on both `checkout` and `safe-outputs`.
For enhanced security, use a [GitHub App token](/gh-aw/reference/auth/#using-a-github-app-for-authentication) — minted on demand and automatically revoked after each job.
### 3. Create the Workflow
[Section titled “3. Create the Workflow”](#3-create-the-workflow)
In the side repository, create `.github/workflows/code-quality.md`:
````aw
---
on: weekly on monday
permissions:
contents: read
checkout:
repository: my-org/main-repo
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
path: repo
current: true
tools:
github:
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
toolsets: [repos, pull_requests]
bash:
- "npx:*"
- "eslint:*"
- "pip:*"
safe-outputs:
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
create-issue:
target-repo: "my-org/main-repo"
title-prefix: "[quality] "
labels: [code-quality, automation]
max: 10
---
# Weekly Code Quality Review
The target repository has been checked out to `${{ github.workspace }}/repo`. Start by navigating there:
```
cd ${{ github.workspace }}/repo
```
## What to Analyze
### 1. JavaScript / TypeScript (if package.json exists)
```bash
npx eslint . --format json --max-warnings 0 2>/dev/null | head -200
```
Look for:
- Files with >5 ESLint errors (flag for immediate fix)
- Patterns that indicate missing error handling (`catch(e) {}`, empty catch blocks)
- Unused imports and variables accumulating across many files
### 2. Complexity (any language)
Count lines per file and flag files over 500 lines — they are candidates for splitting. Use `wc -l` on source files:
```bash
find . -name "*.ts" -o -name "*.js" -o -name "*.py" | xargs wc -l | sort -rn | head -20
```
### 3. Python (if requirements.txt or pyproject.toml exists)
```bash
pip install flake8 --quiet && flake8 . --count --statistics 2>/dev/null | tail -20
```
Flag modules with >10 flake8 errors.
### 4. Dependency staleness
Check for packages with known security advisories using GitHub tools — look at open Dependabot alerts on `my-org/main-repo`.
### 5. Recent PR patterns
Use GitHub tools to look at the last 10 merged PRs. Note recurring themes: are tests consistently skipped? Are the same files always modified together (coupling indicator)?
## What to Create
Create **one issue per distinct finding category** (not one issue per file). Each issue should:
- Name the specific files or modules involved (link to them via GitHub URL)
- Explain why it matters (performance, maintainability, security)
- Suggest a concrete first step to address it
- Include a severity: High (security/crashes), Medium (maintainability), Low (style)
Skip findings with fewer than 3 instances — they are not worth the noise.
## What to Skip
Do not create issues for:
- Style preferences without an established linter rule
- Files with a `// quality-exempt` comment
- Test files (`*.test.*`, `*.spec.*`, `__tests__/`)
````
Compile: `gh aw compile`.
## Customizing the Analysis
[Section titled “Customizing the Analysis”](#customizing-the-analysis)
### Running Type Checkers
[Section titled “Running Type Checkers”](#running-type-checkers)
Add TypeScript checking to the bash tools and prompt:
```aw
---
tools:
bash:
- "npx:*"
- "tsc:*"
---
# ...
Run `npx tsc --noEmit 2>&1 | head -50` and flag any type errors in non-test files.
```
### Targeting a Specific Directory
[Section titled “Targeting a Specific Directory”](#targeting-a-specific-directory)
Use `path:` in checkout and navigate into a subdirectory:
```aw
---
checkout:
repository: my-org/monorepo
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
path: repo
current: true
---
# ...
Navigate to `${{ github.workspace }}/repo/packages/api` and run analysis only on that package.
```
### Checking Out Multiple Repositories
[Section titled “Checking Out Multiple Repositories”](#checking-out-multiple-repositories)
Compare quality trends across related repos:
```aw
---
checkout:
- repository: my-org/service-alpha
path: alpha
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
- repository: my-org/service-beta
path: beta
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
current: true # Issues created here
---
# ...
Compare complexity metrics between alpha/ and beta/ and create a comparative report issue.
```
## Important: `current: true` and Working Directory
[Section titled “Important: current: true and Working Directory”](#important-current-true-and-working-directory)
`current: true` tells the agent which repository to treat as the primary target for GitHub operations (issue creation, PR references). It does **not** automatically change the working directory. Always include an explicit `cd` in the prompt:
```plaintext
cd ${{ github.workspace }}/repo
```
Without it, the agent starts in `$GITHUB_WORKSPACE` (the side repo) and may analyze the wrong directory.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Side repository pattern and other topologies
* [Triage from Side Repo](/gh-aw/examples/multi-repo/triage-from-side-repo/) — Issue triage from a side repo
* [Cross-Repository Operations](/gh-aw/reference/cross-repository/) — Checkout configuration and `current: true`
* [Authentication](/gh-aw/reference/auth/) — PAT and GitHub App setup
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Issue creation with `max` and labels
# Dependabot Rollout
> Roll out a customized Dependabot configuration across many repositories using an orchestrator and worker workflow pair from a central control repository.
This example shows how to roll out a new Dependabot configuration across 100 repositories using the [central control plane pattern](/gh-aw/patterns/multi-repo-ops/#the-central-control-plane-pattern-org-wide-rollouts). An **orchestrator** workflow filters and prioritizes target repositories, then dispatches a **worker** workflow that analyzes each repo and creates an intelligently customized pull request.
Both workflows live in a single private control repository.
## How It Works
[Section titled “How It Works”](#how-it-works)
```
flowchart LR
subgraph central["Central control repo"]
schedule([Weekly schedule]) --> orch[Orchestrator\nfilter & prioritize]
end
orch -->|dispatch_workflow| w1[Worker: Repo A\ncreate PR]
orch -->|dispatch_workflow| w2[Worker: Repo B\ncreate PR]
orch -->|dispatch_workflow| w3[Worker: Repo N\ncreate PR]
```
1. The orchestrator runs weekly, scans org repos, skips ones that already have Dependabot configured, and dispatches up to 5 workers per run.
2. Each worker checks out the target repo, analyzes its structure, and creates a customized `dependabot.yml` pull request — or opens an issue if Renovate or other conflicts are detected.
## Setup
[Section titled “Setup”](#setup)
### 1. Create the Orchestrator
[Section titled “1. Create the Orchestrator”](#1-create-the-orchestrator)
In your central control repository, create `.github/workflows/dependabot-rollout-orchestrator.md`:
```aw
---
on:
schedule: weekly on monday
tools:
github:
github-token: ${{ secrets.GH_AW_READ_ORG_TOKEN }}
toolsets: [repos]
safe-outputs:
dispatch-workflow:
workflows: [dependabot-rollout]
max: 5
---
# Dependabot Rollout Orchestrator
Categorize and orchestrate Dependabot rollout across repositories.
**Target repos**: All repos in the organization
## Task
1. **Filter** - Parse repos (from input or variable), check each for existing `.github/dependabot.yml`, keep only repos without it
2. **Categorize** - Read repo contents to assess complexity:
- Simple: Single package.json, <50 dependencies, standard structure
- Complex: Multiple package.json files, >100 deps, or multiple ecosystems
- Conflicting: Has Renovate config or custom update scripts
- Security: Open security alerts or public with dependencies
3. **Prioritize** - Order repos by rollout preference: simple → security → complex → conflicting
4. **Dispatch** - Dispatch `dependabot-rollout` worker for every prioritized repository
5. **Summarize** - Report total candidates, categorization breakdown, selected repos with rationale
```
Compile this workflow: `gh aw compile`. Then create the `GH_AW_READ_ORG_TOKEN` secret — a fine-grained PAT with `Contents: Read-only` scoped to all target repositories. See [Authentication](/gh-aw/reference/auth/) for PAT and GitHub App setup.
### 2. Create the Worker
[Section titled “2. Create the Worker”](#2-create-the-worker)
Create the worker workflow `.github/workflows/dependabot-rollout.md` in the same central repository. It checks out each target repo via `checkout:` and creates a customized PR (or issue) via cross-repo safe outputs:
````aw
---
on:
workflow_dispatch:
inputs:
target_repo:
description: 'Target repository (owner/repo format)'
required: true
type: string
run-name: Dependabot rollout for ${{ github.event.inputs.target_repo }}
concurrency:
group: gh-aw-${{ github.workflow }}-${{ github.event.inputs.target_repo }}
engine:
concurrency:
group: gh-aw-copilot-${{ github.workflow }}-${{ github.event.inputs.target_repo }}
checkout:
repository: ${{ github.event.inputs.target_repo }}
github-token: ${{ secrets.ORG_REPO_CHECKOUT_TOKEN }}
current: true
permissions:
contents: read
issues: read
pull-requests: read
tools:
github:
github-token: ${{ secrets.GH_AW_READ_ORG_TOKEN }}
toolsets: [repos]
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: ${{ github.event.inputs.target_repo }}
title-prefix: '[dependabot] '
max: 1
create-issue:
target-repo: ${{ github.event.inputs.target_repo }}
title-prefix: '[dependabot-config] '
max: 1
---
# Intelligent Dependabot Configuration
You are creating a **customized** Dependabot configuration based on analyzing this specific repository.
**Target Repository**: ${{ github.event.inputs.target_repo }}
## Why AI is Required
You must analyze the repository structure and create an intelligent, customized configuration - not a generic template.
## Step 1: Analyze Repository
**Check for conflicts:**
- Does `.github/dependabot.yml` already exist? → Stop, create issue explaining it exists
- Does `.github/renovate.json` or `renovate.json` exist? → Create issue about migrating from Renovate
- Are there custom dependency update scripts? → Create issue suggesting Dependabot alternative
**Analyze package manager complexity:**
For **npm** (if package.json exists):
- Count total dependencies (dependencies + devDependencies)
- Check for monorepo: Are there multiple package.json files in subdirectories?
- Simple: <20 dependencies, single package.json
- Complex: >100 dependencies OR monorepo structure
For **Python** (requirements.txt, setup.py, pyproject.toml):
- Count dependencies
- Check for multiple requirement files
For **Go** (go.mod):
- Note if present
For **GitHub Actions** (.github/workflows/*.yml):
- Count workflow files
**Security context:**
- Use GitHub tools to check for open security alerts
- If critical alerts exist, prioritize security updates
## Step 2: Create Customized Configuration
Based on your analysis, create an appropriate config:
### Simple Repository (<20 npm deps, no monorepo)
```yaml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily" # Low complexity = more frequent
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
```
### Complex Repository (>100 deps OR security alerts)
```yaml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly" # High complexity = less frequent
groups:
production:
patterns: ["*"]
exclude-patterns: ["@types/*", "@jest/*"]
dev-dependencies:
patterns: ["@types/*", "@jest/*", "eslint*"]
```
### Monorepo (multiple package.json)
```yaml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/packages/frontend"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/packages/backend"
schedule:
interval: "weekly"
```
## Step 3: Deliver Configuration
**If config is straightforward (no Renovate conflict):**
- Create `.github/dependabot.yml` with your customized config
- Create pull request with:
- Title: "[dependabot] Add customized Dependabot configuration"
- Body explaining: dependency count, why weekly vs daily, grouping strategy, etc.
**If Renovate detected:**
- Create issue explaining migration benefits and proposed config
- Include generated config in issue body
**If no package managers found:**
- Create issue: "No supported package managers detected"
## Key: Explain Your Reasoning
In the PR/issue body, explain **why** you chose this specific configuration (not a generic template).
````
Compile: `gh aw compile`.
### 3. Create Secrets
[Section titled “3. Create Secrets”](#3-create-secrets)
Create two fine-grained PATs scoped to target repositories (see [Authentication](/gh-aw/reference/auth/) for full setup):
| Secret | Permissions | Purpose |
| ------------------------- | ---------------------------------------------------------- | ----------------------------------------- |
| `ORG_REPO_CHECKOUT_TOKEN` | `Contents: Read & write`, `Actions: Read & write` | Checkout target repos |
| `GH_AW_CROSS_REPO_PAT` | `Contents: Write`, `Issues: Write`, `Pull Requests: Write` | Create PRs and issues |
| `GH_AW_READ_ORG_TOKEN` | `Contents: Read-only` | Read org repos in orchestrator and worker |
## Running the Rollout
[Section titled “Running the Rollout”](#running-the-rollout)
After setup, the orchestrator runs automatically every Monday, processing up to 5 repositories per run. To trigger manually:
```bash
gh workflow run dependabot-rollout-orchestrator.lock.yml
```
Track progress by reviewing the Actions runs and the PRs created in each target repository.
## Best Practices
[Section titled “Best Practices”](#best-practices)
* Keep `max: 5` on the orchestrator during initial rollout; increase once you’ve validated the worker output
* Add `[dependabot]` title-prefix to make PRs easy to filter across repositories
* Use `concurrency` groups to prevent duplicate worker runs for the same target repo
* Review a few worker PRs manually before trusting the full automation
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Central control plane pattern and other topologies
* [Feature Synchronization](/gh-aw/examples/multi-repo/feature-sync/) — Upstream-to-downstream sync example
* [Cross-Repository Issue Tracking](/gh-aw/examples/multi-repo/issue-tracking/) — Hub-and-spoke tracking example
* [Cross-Repository Operations](/gh-aw/reference/cross-repository/) — Checkout and `target-repo` configuration
* [Authentication](/gh-aw/reference/auth/) — PAT and GitHub App setup
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Secure write operations
# Feature Synchronization
> Synchronize features from a main repository to sub-repositories or downstream services with automated pull requests.
Feature synchronization workflows propagate changes from a main repository to related sub-repositories, ensuring downstream projects stay current with upstream improvements while maintaining proper change tracking through pull requests.
## When to Use
[Section titled “When to Use”](#when-to-use)
Use feature sync when maintaining related projects in separate repositories (monorepo alternative), propagating library updates to dependent projects, updating platform-specific repos after core changes, or keeping downstream forks synchronized with upstream.
## How It Works
[Section titled “How It Works”](#how-it-works)
```
flowchart LR
subgraph upstream["Upstream repo"]
push([Push to main]) --> agent[Sync agent]
end
agent -->|create-pull-request| ds1[downstream-service]
agent -->|create-pull-request| ds2[api-service]
agent -->|create-pull-request| ds3[mobile-backend]
```
The workflow monitors specific paths in the main repository and creates pull requests in target repositories when changes occur, adapting the changes for each target’s structure while maintaining full audit trails.
## Basic Feature Sync
[Section titled “Basic Feature Sync”](#basic-feature-sync)
Synchronize changes from shared directory to downstream repository:
```aw
---
on:
push:
branches: [main]
paths:
- 'shared/**'
permissions:
contents: read
actions: read
tools:
github:
toolsets: [repos]
edit:
bash:
- "git:*"
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: "myorg/downstream-service"
title-prefix: "[sync] "
labels: [auto-sync, upstream-update]
reviewers: [team-lead]
draft: true
---
# Sync Shared Components to Downstream Service
When shared components change, synchronize them to `myorg/downstream-service`. Review the git diff, read current versions from the target repo, adapt paths if needed, and create a PR with descriptive commit messages linking to original commits. Include structural adaptations and migration notes for breaking changes.
```
## Multi-Target Sync
[Section titled “Multi-Target Sync”](#multi-target-sync)
Synchronize to multiple repositories simultaneously:
```aw
---
on:
push:
branches: [main]
paths:
- 'core/**'
permissions:
contents: read
actions: read
tools:
github:
toolsets: [repos]
edit:
bash:
- "git:*"
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
max: 3
title-prefix: "[core-sync] "
labels: [automated-sync]
draft: true
---
# Sync Core Library to All Services
When core library files change, create PRs in dependent services (`myorg/api-service`, `myorg/web-frontend`, `myorg/mobile-backend`). For each target, check if they use the changed modules, adapt imports/paths, and create a PR with compatibility notes and links to source commits.
```
## Release-Based Sync
[Section titled “Release-Based Sync”](#release-based-sync)
Synchronize when new releases are published:
```aw
---
on:
release:
types: [published]
permissions:
contents: read
actions: read
tools:
github:
toolsets: [repos]
edit:
bash:
- "git:*"
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: "myorg/production-service"
title-prefix: "[upgrade] "
labels: [version-upgrade, auto-generated]
reviewers: [release-manager]
draft: false
---
# Upgrade Production Service to New Release
When a new release is published (version ${{ github.event.release.tag_name }}), create an upgrade PR that updates version references, applies API changes from release notes, updates configuration for breaking changes, and includes a migration guide with testing recommendations.
```
## Selective File Sync
[Section titled “Selective File Sync”](#selective-file-sync)
Synchronize only specific file types or patterns:
```aw
---
on:
push:
branches: [main]
paths:
- 'types/**/*.ts'
- 'interfaces/**/*.ts'
permissions:
contents: read
actions: read
tools:
github:
toolsets: [repos]
edit:
bash:
- "git:*"
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: "myorg/client-sdk"
title-prefix: "[types] "
labels: [type-definitions]
draft: true
---
# Sync TypeScript Type Definitions
Synchronize TypeScript type definitions to client SDK. Identify changed `.ts` files in `types/` and `interfaces/`, update them in `myorg/client-sdk` while preserving client-specific extensions, validate no breaking changes, and document any compatibility concerns.
```
## Bidirectional Sync with Conflict Detection
[Section titled “Bidirectional Sync with Conflict Detection”](#bidirectional-sync-with-conflict-detection)
Handle bidirectional synchronization with conflict awareness:
```aw
---
on:
push:
branches: [main]
paths:
- 'shared-config/**'
permissions:
contents: read
actions: read
tools:
github:
toolsets: [repos, pull_requests]
edit:
bash:
- "git:*"
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: "myorg/sister-project"
title-prefix: "[config-sync] "
labels: [config-update, needs-review]
draft: true
---
# Bidirectional Config Sync
Synchronize shared configuration between this project and `myorg/sister-project`, which may be modified independently. Compare timestamps and change history; if conflicts are detected, create a PR marked for manual review with conflict notes. If no conflict, apply changes automatically and record sync timestamp.
```
## Feature Branch Sync
[Section titled “Feature Branch Sync”](#feature-branch-sync)
Synchronize feature branches between repositories:
```aw
---
on:
pull_request:
types: [opened, synchronize]
branches:
- 'feature/**'
permissions:
contents: read
pull-requests: read
actions: read
tools:
github:
toolsets: [repos, pull_requests]
edit:
bash:
- "git:*"
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: "myorg/integration-tests"
title-prefix: "[feature-test] "
labels: [feature-branch, auto-sync]
draft: true
---
# Sync Feature Branch for Integration Testing
When feature branch ${{ github.event.pull_request.head.ref }} (PR #${{ github.event.pull_request.number }}) is updated, create a matching branch in the integration test repo, sync relevant changes, update test configurations, and create a PR linking to the source with test scenarios and integration points.
```
## Scheduled Sync Check
[Section titled “Scheduled Sync Check”](#scheduled-sync-check)
Regularly check for sync drift and create catch-up PRs:
```aw
---
on: weekly on monday
permissions:
contents: read
actions: read
tools:
github:
toolsets: [repos, pull_requests]
edit:
bash:
- "git:*"
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: "myorg/downstream-fork"
title-prefix: "[weekly-sync] "
labels: [scheduled-sync]
draft: true
---
# Weekly Sync Check
Check for accumulated changes needing synchronization to downstream fork. Find the last sync PR, identify all commits since then, categorize changes (features, fixes, docs), and create a comprehensive PR grouping commits by category with breaking changes highlighted and migration guidance.
```
## Authentication Setup
[Section titled “Authentication Setup”](#authentication-setup)
Cross-repo sync workflows require authentication via PAT or GitHub App.
### PAT Configuration
[Section titled “PAT Configuration”](#pat-configuration)
Create a PAT with `repo`, `contents: write`, and `pull-requests: write` permissions, then store it as a repository secret:
```bash
gh aw secrets set GH_AW_CROSS_REPO_PAT --value "ghp_your_token_here"
```
### GitHub App Configuration
[Section titled “GitHub App Configuration”](#github-app-configuration)
For enhanced security, use GitHub App installation tokens. See [Using a GitHub App for Authentication](/gh-aw/reference/auth/#using-a-github-app-for-authentication) for complete configuration including repository scoping options.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps Design Pattern](/gh-aw/patterns/multi-repo-ops/) - Complete multi-repo overview
* [Cross-Repo Issue Tracking](/gh-aw/examples/multi-repo/issue-tracking/) - Issue management patterns
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - Pull request configuration
* [GitHub Tools](/gh-aw/reference/github-tools/) - Repository access tools
# Cross-Repository Issue Tracking
> Centralize issue tracking across multiple repositories with automated tracking issue creation and status synchronization.
Cross-repository issue tracking enables organizations to maintain a centralized view of work across multiple component repositories. When issues are created in component repos, tracking issues are automatically created in a central repository, providing visibility without requiring direct access to all repositories.
## When to Use
[Section titled “When to Use”](#when-to-use)
Use cross-repo issue tracking for component-based architectures where multiple teams need centralized visibility, when tracking external dependencies, coordinating cross-project initiatives, or aggregating metrics from distributed repositories.
## How It Works
[Section titled “How It Works”](#how-it-works)
```
flowchart LR
subgraph comp["Component repos"]
ev1([Issue opened
component-alpha]) --> agent1[Tracking agent]
ev2([Issue opened
component-beta]) --> agent2[Tracking agent]
end
agent1 -->|create-issue| central[central-tracker]
agent2 -->|create-issue| central
```
Workflows in component repositories create tracking issues in a central repository when local issues are opened, updated, or closed. The central repository maintains references to all component issues, enabling organization-wide visibility and reporting.
## Basic Tracking Issue Creation
[Section titled “Basic Tracking Issue Creation”](#basic-tracking-issue-creation)
Create tracking issues in central repository when component issues are opened:
```
flowchart LR
subgraph comp["component-alpha"]
ev([Issue opened]) --> agent[Tracking agent]
end
agent -->|create-issue| central["central-tracker\n[component-alpha] ..."]
```
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
actions: read
tools:
github:
toolsets: [issues]
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-issue:
target-repo: "myorg/central-tracker"
title-prefix: "[component-alpha] "
labels: [from-component-alpha, tracking-issue]
---
# Create Central Tracking Issue
When an issue is opened in component-alpha, create a corresponding
tracking issue in the central tracker.
**Original issue:** ${{ github.event.issue.html_url }}
**Issue number:** ${{ github.event.issue.number }}
**Content:** "${{ steps.sanitized.outputs.text }}"
Create tracking issue with link to original, component identifier, summary, suggested priority, and labels `from-component-alpha` and `tracking-issue`.
```
## Status Synchronization
[Section titled “Status Synchronization”](#status-synchronization)
Update tracking issues when component issues change status:
```
flowchart LR
subgraph comp["component-alpha"]
ev(["Issue closed /\nreopened / labeled"]) --> agent[Tracking agent]
end
agent -->|find tracking issue| central[central-tracker]
agent -->|add-comment| central
```
```aw
---
on:
issues:
types: [closed, reopened, labeled, unlabeled]
permissions:
contents: read
actions: read
tools:
github:
toolsets: [issues]
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
add-comment:
target-repo: "myorg/central-tracker"
target: "*" # Find related tracking issue
---
# Update Central Tracking Issue Status
When this component issue changes status, update the central tracking issue.
**Original issue:** ${{ github.event.issue.html_url }}
**Action:** ${{ github.event.action }}
Search for tracking issue in `myorg/central-tracker` and add comment with status update (✓ resolved, reopened, or label changes), issue link, and timestamp.
```
## Multi-Component Tracking
[Section titled “Multi-Component Tracking”](#multi-component-tracking)
Track issues that span multiple component repositories:
```
flowchart LR
subgraph comp["Component repos"]
ev([Cross-component\nissue opened]) --> agent[Tracking agent]
end
agent -->|create-issue primary| central[central-tracker]
agent -->|create-issue child| a[component-alpha]
agent -->|create-issue child| b[component-beta]
```
```aw
---
on:
issues:
types: [opened]
# Triggered when issue has 'cross-component' label
permissions:
contents: read
actions: read
tools:
github:
toolsets: [issues]
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-issue:
max: 3 # May create issues in multiple tracking repos
target-repo: "myorg/central-tracker"
title-prefix: "[cross-component] "
labels: [cross-component, needs-coordination]
---
# Track Cross-Component Issues
When an issue is marked as cross-component, create coordinated tracking issues.
**Original issue:** ${{ github.event.issue.html_url }}
Identify affected components, create primary tracking issue in central tracker with affected components list and coordination requirements, and create child issues in component repos if needed. Tag team leads and schedule coordination meeting for high-priority issues.
```
## External Dependency Tracking
[Section titled “External Dependency Tracking”](#external-dependency-tracking)
Track issues from external/upstream repositories:
```
flowchart LR
subgraph trigger["Manual trigger"]
ev([workflow_dispatch\nexternal URL]) --> agent[Tracking agent]
end
agent -->|web-fetch| ext[External issue]
agent -->|create-issue| tracker["dependency-tracker\n[upstream] ..."]
```
```aw
---
on:
workflow_dispatch:
inputs:
external_issue_url:
description: 'URL of external issue to track'
required: true
type: string
permissions:
contents: read
tools:
github:
toolsets: [issues]
web-fetch:
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-issue:
target-repo: "myorg/dependency-tracker"
title-prefix: "[upstream] "
labels: [external-dependency, upstream-issue]
---
# Track External Dependency Issue
Create tracking issue for external dependency problem.
**External issue URL:** ${{ github.event.inputs.external_issue_url }}
Fetch external issue details, identify affected internal projects, and create tracking issue with external link, status, impact assessment, affected repositories, and monitoring plan. Set weekly reminder and notify affected teams.
```
## Automated Triage and Routing
[Section titled “Automated Triage and Routing”](#automated-triage-and-routing)
Triage component issues and route to appropriate trackers:
```
flowchart LR
subgraph comp["component-alpha"]
ev([Issue opened]) --> agent[Triage agent]
end
agent -->|security| sec[security-tracker]
agent -->|feature| feat[feature-tracker]
agent -->|bug| bugs[bug-tracker]
agent -->|infra| ops[ops-tracker]
```
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
actions: read
tools:
github:
toolsets: [issues]
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-issue:
max: 2
title-prefix: "[auto-triaged] "
---
# Triage and Route to Tracking Repos
Analyze new issues and create tracking issues in appropriate repositories.
**Original issue:** ${{ github.event.issue.html_url }}
**Content:** "${{ steps.sanitized.outputs.text }}"
Analyze issue severity and route to appropriate tracker: security issues to `myorg/security-tracker`, features to `myorg/feature-tracker`, bugs to `myorg/bug-tracker`, or infrastructure to `myorg/ops-tracker`. Include original link, triage reasoning, priority, affected components, and SLA targets.
```
## Aggregated Reporting
[Section titled “Aggregated Reporting”](#aggregated-reporting)
Create weekly summary of tracked issues:
```
flowchart LR
schedule([Weekly schedule]) --> agent[Report agent]
subgraph sources["Component repos"]
a[component-alpha]
b[component-beta]
n[component-N]
end
agent -->|query issues| a & b & n
agent -->|create-discussion| central["central-tracker\nweekly summary"]
```
```aw
---
on: weekly on monday
permissions:
contents: read
tools:
github:
toolsets: [issues]
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-discussion:
target-repo: "myorg/central-tracker"
category: "Status Reports"
title-prefix: "[weekly] "
---
# Weekly Cross-Repo Issue Summary
Generate weekly summary of tracked issues across all component repositories.
Summarize issues from all component repositories including open counts by priority, issues opened/closed this week, stale issues (>30 days), and blockers. Create discussion with executive summary, per-repo breakdown, trending analysis, and action items formatted as markdown table.
```
## Bidirectional Linking
[Section titled “Bidirectional Linking”](#bidirectional-linking)
Maintain references between component and tracking issues:
```
flowchart LR
subgraph comp["component-alpha"]
ev([Issue opened]) --> agent[Tracking agent]
original[original issue]
end
agent -->|create-issue| central["central-tracker\n[linked] ..."]
agent -->|add-comment| original
```
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
actions: read
tools:
github:
toolsets: [issues]
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-issue:
target-repo: "myorg/central-tracker"
title-prefix: "[linked] "
add-comment:
max: 1
---
# Create Tracking Issue with Bidirectional Links
Create tracking issue and add comment to original with link.
**Original issue:** ${{ github.event.issue.html_url }}
Create tracking issue in `myorg/central-tracker` with title "[linked] ${{ github.event.issue.title }}" and body linking to original. Add comment to original issue with tracking link. This enables easy navigation, automatic GitHub reference detection, and clear audit trail.
```
## Priority-Based Routing
[Section titled “Priority-Based Routing”](#priority-based-routing)
Route issues to different trackers based on priority:
```
flowchart LR
subgraph comp["component-alpha"]
ev(["Issue opened /\nlabeled"]) --> agent[Priority router]
end
agent -->|P0| inc[incidents]
agent -->|P1| p1[priority-tracker]
agent -->|P2| central[central-tracker]
agent -->|P3| backlog[backlog]
```
```aw
---
on:
issues:
types: [opened, labeled]
permissions:
contents: read
actions: read
tools:
github:
toolsets: [issues]
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-issue:
max: 1
title-prefix: "[priority-routed] "
---
# Route Issues Based on Priority
Route issues to appropriate tracking repository based on priority level.
**Original issue:** ${{ github.event.issue.html_url }}
**Labels:** Check for priority labels (P0, P1, P2, P3)
Route by priority: P0 → `myorg/incidents`, P1 → `myorg/priority-tracker`, P2 → `myorg/central-tracker`, P3 → `myorg/backlog`. Include original link, priority, SLA expectations, and escalation path. For P0, alert on-call team and include incident response checklist.
```
## Authentication Setup
[Section titled “Authentication Setup”](#authentication-setup)
Cross-repo issue tracking requires appropriate authentication:
### PAT Configuration
[Section titled “PAT Configuration”](#pat-configuration)
```bash
# Create PAT with issues and repository read permissions
gh aw secrets set GH_AW_CROSS_REPO_PAT --value "ghp_your_token_here"
```
**Required Permissions:**
* `repo` (for private repositories)
* `public_repo` (for public repositories)
### GitHub App Configuration
[Section titled “GitHub App Configuration”](#github-app-configuration)
For enhanced security, use GitHub App installation tokens. See [Using a GitHub App for Authentication](/gh-aw/reference/auth/#using-a-github-app-for-authentication) for complete configuration including repository scoping options.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps Design Pattern](/gh-aw/patterns/multi-repo-ops/) - Complete multi-repo overview
* [Feature Synchronization](/gh-aw/examples/multi-repo/feature-sync/) - Code sync patterns
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - Issue creation configuration
* [GitHub Tools](/gh-aw/reference/github-tools/) - API access configuration
# Triage from Side Repo
> Run automated issue triage on a main repository from an isolated automation repository, with a slash-command bridge for real-time response.
This example shows how to run automated triage on `my-org/main-repo` from a dedicated side repository. The side repo hosts all automation workflows; the main repo receives only the resulting labels and comments. A slash-command bridge is included for real-time `/triage` response alongside the scheduled triage run.
## How It Works
[Section titled “How It Works”](#how-it-works)
```
flowchart LR
subgraph side["Side repo (automation)"]
schedule([Every 6h / dispatch]) --> triage[Triage agent]
end
triage -->|add-labels / add-comment| main[main-repo]
subgraph main_repo["main-repo"]
slash(["/triage comment"]) --> relay[Relay workflow]
end
relay -->|workflow_dispatch| triage
```
* **Scheduled triage** runs every 6 hours from the side repo, finding unlabeled issues and adding appropriate labels and a triage comment.
* **Slash-command triage** is triggered by `/triage` in the main repo. Because GitHub webhooks only fire in the repository where the event occurs, a thin relay workflow in the main repo forwards the command to the side repo via `workflow_dispatch`.
## Setup
[Section titled “Setup”](#setup)
### 1. Create the Side Repository
[Section titled “1. Create the Side Repository”](#1-create-the-side-repository)
```bash
gh repo create my-org/main-repo-automation --private
gh repo clone my-org/main-repo-automation
cd main-repo-automation
```
### 2. Create the Authentication Token
[Section titled “2. Create the Authentication Token”](#2-create-the-authentication-token)
Create a fine-grained PAT (`GH_AW_MAIN_REPO_TOKEN`) scoped **only to `my-org/main-repo`** with these permissions:
| Permission | Level | Purpose |
| ---------- | ------------ | -------------------------------------- |
| Issues | Read & write | Read issues, add labels and comments |
| Contents | Read-only | Read repo structure (for GitHub tools) |
Store it as a secret in the **side repository**:
```bash
gh secret set GH_AW_MAIN_REPO_TOKEN --repo my-org/main-repo-automation
```
Note
The default `GITHUB_TOKEN` cannot access other repositories. You must use this additional token for both `tools.github` and `safe-outputs`.
For enhanced security, use a [GitHub App token](/gh-aw/reference/auth/#using-a-github-app-for-authentication) instead of a PAT — tokens are minted on demand and automatically revoked after each job.
### 3. Create the Scheduled Triage Workflow
[Section titled “3. Create the Scheduled Triage Workflow”](#3-create-the-scheduled-triage-workflow)
In the side repository, create `.github/workflows/triage.md`:
```aw
---
on: every 6h
permissions:
contents: read
safe-outputs:
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
add-labels:
target-repo: "my-org/main-repo"
allowed-labels:
- bug
- enhancement
- question
- documentation
- good first issue
- wontfix
- duplicate
- needs-info
add-comment:
target-repo: "my-org/main-repo"
target: "*"
tools:
github:
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
toolsets: [issues]
---
# Triage Main Repository Issues
Find all unlabeled issues in my-org/main-repo opened in the last 7 days.
For each issue:
1. Read the title and body carefully
2. Assign one primary label (bug / enhancement / question / documentation / good first issue)
3. Add a second label if clearly applicable (e.g., duplicate, needs-info, wontfix)
4. Post a brief triage comment explaining the label choice and any suggested next step
Limit to 20 issues per run to avoid rate limits.
```
Compile: `gh aw compile`.
### 4. Create the Slash-Command Bridge
[Section titled “4. Create the Slash-Command Bridge”](#4-create-the-slash-command-bridge)
Because webhook events only fire in the repository where they occur, you need two workflows for slash-command support.
**Step 1** — Relay workflow in **`my-org/main-repo`** (`.github/workflows/triage-relay.yml`):
Note
This is a plain GitHub Actions YAML file, not a compiled agentic workflow. Create it directly as `.yml`.
```yaml
name: Triage relay
on:
issue_comment:
types: [created]
jobs:
relay:
if: github.event.comment.body == '/triage' && github.event.issue.pull_request == null
runs-on: ubuntu-latest
steps:
- name: Forward to automation repo
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_AW_SIDE_REPO_TOKEN }}
script: |
await github.rest.actions.createWorkflowDispatch({
owner: 'my-org',
repo: 'main-repo-automation',
workflow_id: 'triage-on-demand.lock.yml',
ref: 'main',
inputs: {
issue_number: String(context.issue.number),
issue_url: context.payload.issue.html_url,
}
});
```
This relay needs a `GH_AW_SIDE_REPO_TOKEN` secret in `main-repo` — a PAT with `Actions: write` on `main-repo-automation`.
**Step 2** — On-demand triage workflow in the **side repo** (`.github/workflows/triage-on-demand.md`):
```aw
---
on:
workflow_dispatch:
inputs:
issue_number:
description: "Issue number to triage"
required: true
issue_url:
description: "Issue URL for context"
required: true
permissions:
contents: read
safe-outputs:
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
add-labels:
target-repo: "my-org/main-repo"
allowed-labels:
- bug
- enhancement
- question
- documentation
- good first issue
- wontfix
- duplicate
- needs-info
add-comment:
target-repo: "my-org/main-repo"
target: "${{ github.event.inputs.issue_number }}"
tools:
github:
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
toolsets: [issues]
---
# Triage Issue on Demand
Triage issue #${{ github.event.inputs.issue_number }} in my-org/main-repo.
Read the issue at ${{ github.event.inputs.issue_url }}, assign the most appropriate label, and post a brief comment explaining the triage decision.
```
Compile: `gh aw compile`.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Side repository pattern and other topologies
* [IssueOps](/gh-aw/patterns/issue-ops/) — Event-driven issue automation in the main repo
* [ChatOps](/gh-aw/patterns/chat-ops/) — Slash command workflows
* [Cross-Repository Operations](/gh-aw/reference/cross-repository/) — `target-repo` configuration
* [Authentication](/gh-aw/reference/auth/) — PAT and GitHub App setup
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Labels, comments, and allowed-labels
# AWF Reflect Route
> Use the AWF /reflect route to discover gateway inference endpoints and available models at runtime.
Experimental
The AWF `/reflect` route and its response shape are currently experimental and subject to change. Do not rely on this API for production use or in shared workflow logic.
Inside the AWF runtime network, the AWF API proxy exposes `GET /reflect` at `http://api-proxy:10000/reflect`.
Use this route when building shared workflows, tools, or extensions that need runtime model routing.
## Why use `/reflect`
[Section titled “Why use /reflect”](#why-use-reflect)
`/reflect` returns the currently configured inference providers and their model availability for the active run. This allows a shared workflow or tool to:
* Discover which gateway endpoints are available
* Check whether each endpoint is configured
* Read or refresh model availability
* Select a provider/model dynamically at runtime
Caution
Do not hardcode direct upstream model API URLs in shared workflow logic. All inference requests should go through the AWF gateway so usage remains controllable and observable for cost control, tracking, and optimization.
## Response shape
[Section titled “Response shape”](#response-shape)
The response includes an `endpoints` array and a `models_fetch_complete` flag:
* `endpoints[].provider`: provider identifier (e.g., `openai`, `anthropic`, `copilot`, `gemini`)
* `endpoints[].base_url`: gateway base URL for inference calls
* `endpoints[].configured`: whether credentials/config are present for that provider
* `endpoints[].models`: discovered model IDs, or `null` when model discovery is not yet complete
* `endpoints[].models_url`: gateway URL used to query models for that provider
* `models_fetch_complete`: whether startup model discovery is complete
## Recommended selection flow for shared tools
[Section titled “Recommended selection flow for shared tools”](#recommended-selection-flow-for-shared-tools)
1. Query `/reflect` at start of execution.
2. Filter endpoints to `configured: true`.
3. Prefer endpoints with a non-empty `models` list.
4. Match requested model aliases/patterns against available models.
5. Route inference to the selected endpoint `base_url`.
6. If `models` is `null`, retry discovery with bounded backoff (for example, every 3 seconds up to 5 attempts) before failing.
This keeps shared tooling portable across repositories and environments where available providers differ.
## Example request
[Section titled “Example request”](#example-request)
```bash
curl -s http://api-proxy:10000/reflect
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MCP Gateway](/gh-aw/reference/mcp-gateway/)
* [Cost Management](/gh-aw/reference/cost-management/)
* [Model Aliases & Multipliers](/gh-aw/reference/model-tables/)
# CorrectionOps
> Improve agentic workflows from trusted human corrections without retraining the underlying model
Experimental
CorrectionOps is an experimental pattern. The guidance and workflow shape on this page may change as the pattern is tested in more real-world workflows.
CorrectionOps is a workflow pattern that improves the workflow *around* the model rather than retraining it. It stores predictions at decision time, compares them with later trusted human truth, and uses that evidence to update instructions, routing, thresholds, and rollout decisions.
The basic loop:
1. Save what the workflow predicted
2. Collect what humans later decided
3. Use the difference to improve the workflow
## When to Use CorrectionOps
[Section titled “When to Use CorrectionOps”](#when-to-use-correctionops)
Use CorrectionOps when humans still make or correct the real decision and you want the workflow to improve iteratively — by updating instructions, routing, thresholds, or rollout state — rather than all at once. Typical fits include labeling and classification, routing and prioritization, moderation and approvals, and summaries or recommendations that humans later correct.
It is especially useful when the rollout path is gradual: start with `staged: true`, keep evaluation and reporting in Ops, use corrections to improve the workflow, and promote to direct writes only when the evidence is strong enough.
## How It Works
[Section titled “How It Works”](#how-it-works)
A clean CorrectionOps setup has two long-lived surfaces. Production stays authoritative. Ops hosts prediction, correction intake, reporting, instruction updates, and rollout control — early on without writing back to production, later with direct writes once promoted.
Most implementations reduce to three workflow classes: a thin relay that forwards stable facts into ops, a prediction workflow that persists snapshots and writes safely, and a compare/report/decide workflow that checks later human truth and updates the system when the evidence is strong enough.
Keep relays, snapshot resolution, diffing, and grouping deterministic. Use the agent for semantic judgment, not for reconstructing event history or inferring provenance after the fact.
## Example: Issue Labeling
[Section titled “Example: Issue Labeling”](#example-issue-labeling)
```
flowchart TB
subgraph ProductionRepo[Production Repo]
A[Issue or item in production]
D[Later human correction in production]
B[Thin relay]
end
subgraph OpsRepo[Ops Repo]
C[Store prediction snapshot]
E[Collect correction evidence]
F[Build deterministic diff]
G[Publish report or open instruction PR]
H[Make rollout decision]
end
A -->|item-created event| B
B --> C
D -->|truth-feedback event| E
C --> F
E --> F
F --> G
G --> H
H -.->|improves next run| A
```
A single CorrectionOps worker can carry the pattern when permissions and triggers fit cleanly together:
```aw
---
on:
schedule: daily
workflow_dispatch:
repository_dispatch:
types: [truth-feedback]
permissions:
contents: read
issues: read
safe-outputs:
create-issue:
create-pull-request:
---
# CorrectionOps Worker
Read persisted predictions and later trusted truth, compare them deterministically, then either publish a health report or open a draft PR updating instructions.
```
Unlike Reinforcement Learning from Human Feedback (RLHF), which updates model weights, CorrectionOps changes instruction files, routing rules, deterministic checks, thresholds, or rollout decisions — no separate evaluation repository required.
### Full Workflow Pieces
[Section titled “Full Workflow Pieces”](#full-workflow-pieces)
The example above breaks into four pieces:
#### 1. Relay In The Source Repo
[Section titled “1. Relay In The Source Repo”](#1-relay-in-the-source-repo)
Forwards stable facts and provenance into ops only — no diffs, no human-intent inference, no correctness decisions.
prod-repo/.github/workflows/relay-correction-signals.yml
```yaml
name: Relay Correction Signals
on:
issues:
types: [opened, labeled, unlabeled]
jobs:
relay:
runs-on: ubuntu-latest
steps:
- name: Forward stable facts to ops
uses: actions/github-script@v8
with:
github-token: ${{ secrets.OPS_DISPATCH_TOKEN }}
script: |
await github.rest.repos.createDispatchEvent({
owner: 'org',
repo: 'ops-repo',
event_type: context.payload.action === 'opened' ? 'item-created' : 'truth-feedback',
client_payload: {
data: {
source_repository: `${context.repo.owner}/${context.repo.repo}`,
source_type: 'issue',
item_number: context.payload.issue.number,
item_title: context.payload.issue.title,
item_url: context.payload.issue.html_url,
event_type: context.payload.action,
label: context.payload.label?.name || null,
actor: context.actor,
actor_type: context.actor.endsWith('[bot]') ? 'bot' : 'human',
occurred_at: new Date().toISOString(),
},
},
});
```
#### 2. Prediction Workflow In Ops
[Section titled “2. Prediction Workflow In Ops”](#2-prediction-workflow-in-ops)
Consumes normalized inputs, applies the current instructions, and persists a durable snapshot for later comparison.
ops-repo/.github/workflows/predict-items.md
```aw
---
name: Predict Items
on:
schedule: daily
workflow_dispatch:
repository_dispatch:
types: [item-created]
tools:
github:
toolsets: [issues, repos]
safe-outputs:
create-issue:
update-issue:
---
# Predict Items
Read prepared items from `/tmp/gh-aw/agent/item-scan`, apply the current instructions, write review artifacts through safe outputs in Ops, and append a prediction snapshot containing the source identifier, predicted action, instruction version, and timestamp.
```
#### 3. Compare, Report, And Decide In Ops
[Section titled “3. Compare, Report, And Decide In Ops”](#3-compare-report-and-decide-in-ops)
Reads predictions and later human truth, builds deterministic diffs first, then asks the agent to summarize patterns or propose instruction updates.
ops-repo/.github/workflows/review-corrections.md
```aw
---
name: Review Corrections
on:
schedule: weekly
workflow_dispatch:
inputs:
mode:
description: report or adaptation
required: false
default: report
type: choice
options: [report, adaptation]
safe-outputs:
create-issue:
create-pull-request:
---
# Review Corrections
Read `correction-diffs.json` from `/tmp/gh-aw/agent/correction-review`. In `report` mode, publish a health summary. In `adaptation` mode, open a draft PR updating the instruction file only when the grouped evidence is strong enough.
```
#### 4. Optional Deterministic Collector
[Section titled “4. Optional Deterministic Collector”](#4-optional-deterministic-collector)
Add a separate collector when the later-truth boundary needs its own trigger, permissions, or serialized write path.
ops-repo/.github/workflows/collect-corrections.yml
```yaml
name: Collect Corrections
on:
repository_dispatch:
types: [truth-feedback]
jobs:
collect:
runs-on: ubuntu-latest
steps:
- name: Resolve authoritative truth and store correction evidence
run: ./scripts/store-correction-evidence.sh
```
### Stable Contracts To Define First
[Section titled “Stable Contracts To Define First”](#stable-contracts-to-define-first)
Before adding rollout logic or adaptation prompts, define four small deterministic contracts:
1. relay payload: the minimal source identity, object identity, event type, actor facts, and timestamps forwarded into ops
2. prediction snapshot: the durable record of what the workflow predicted and under which instruction version
3. correction review input: the deterministic diff artifact used by reporting and adaptation
4. rollout gate contract: what evidence or approvals are required before direct production writes are enabled
The production object changes across use cases, but the CorrectionOps shape does not.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Staged Mode](/gh-aw/reference/staged-mode/) for the optional safe-write rollout guidance inside CorrectionOps
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) for separating workflow infrastructure from the production repository
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) for coordinating workflows across repository boundaries
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) for controlling write targets and protections
* [GitHub Tools](/gh-aw/reference/github-tools/) for cross-repository reads and operations
# Monitoring with Projects
> Use GitHub Projects + safe-outputs to track and monitor workflow work items and progress.
Experimental
The monitoring with projects pattern is experimental and subject to change.
Use this pattern when you want a durable “source of truth” for what your agentic workflows discovered, decided, and did.
## What this pattern is
[Section titled “What this pattern is”](#what-this-pattern-is)
* **Projects** are the dashboard: a GitHub Projects v2 board holds issues/PRs and custom fields.
* **Monitoring** is the behavior: workflows continuously add/update items, and periodically post status updates.
## Building blocks
[Section titled “Building blocks”](#building-blocks)
### 1) Track items with `update-project`
[Section titled “1) Track items with update-project”](#1-track-items-with-update-project)
Enable the safe output and point it at your project URL:
```yaml
safe-outputs:
update-project:
project: https://github.com/orgs/myorg/projects/123
max: 10
github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }}
```
* Adds issues/PRs to the board and updates custom fields.
* Can also create views and custom fields when configured.
See the full reference: [/reference/safe-outputs/#project-board-updates-update-project](/gh-aw/reference/safe-outputs/#project-board-updates-update-project)
### 2) Post run summaries with `create-project-status-update`
[Section titled “2) Post run summaries with create-project-status-update”](#2-post-run-summaries-with-create-project-status-update)
Use project status updates to communicate progress and next steps:
```yaml
safe-outputs:
create-project-status-update:
project: https://github.com/orgs/myorg/projects/123
max: 1
github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }}
```
This is useful for scheduled workflows (daily/weekly) or orchestrator workflows.
See the full reference: [/reference/safe-outputs/#project-status-updates-create-project-status-update](/gh-aw/reference/safe-outputs/#project-status-updates-create-project-status-update)
### 3) Correlate work with a Tracker Id field
[Section titled “3) Correlate work with a Tracker Id field”](#3-correlate-work-with-a-tracker-id-field)
If you want to correlate multiple runs, add a custom field like **Tracker Id** (text) and populate it from your workflow prompt/output (for example, a run ID, issue number, or “initiative” key).
## Run failure issues
[Section titled “Run failure issues”](#run-failure-issues)
When a workflow run fails, the system automatically posts a failure notification on the triggering issue or pull request. To track failures as searchable GitHub issues, enable `create-issue` in `safe-outputs`:
```yaml
safe-outputs:
create-issue:
title-prefix: "[failed] "
labels: [automation, failed]
```
The issue body includes the workflow name, run URL, and failure status, making it easy to find and triage recurring failures.
### Grouping failures as sub-issues
[Section titled “Grouping failures as sub-issues”](#grouping-failures-as-sub-issues)
When multiple workflow runs fail, the `group-reports` option links each failure report as a sub-issue under a shared parent issue titled “\[aw] Failed runs”. This is useful for scheduled or high-frequency workflows where failures can accumulate.
```yaml
safe-outputs:
create-issue:
title-prefix: "[failed] "
labels: [automation, failed]
group-reports: true # Group failure reports under a shared parent issue (default: false)
```
When `group-reports` is enabled:
* A parent “\[aw] Failed runs” issue is automatically created and managed.
* Each failure run report is linked as a sub-issue under the parent.
* Up to 64 sub-issues are tracked per parent issue.
See the full reference: [/reference/safe-outputs/#group-reports-group-reports](/gh-aw/reference/safe-outputs/#group-reports-group-reports)
## No-op run reports
[Section titled “No-op run reports”](#no-op-run-reports)
When an agent determines that no action is needed (for example, no issues were found), it outputs a no-op message. By default, this message is posted as a comment on the triggering issue or pull request, keeping a visible record of runs that intentionally did nothing.
To disable posting no-op messages as issue comments:
```yaml
safe-outputs:
create-issue:
noop:
report-as-issue: false # Disable posting noop messages as issue comments
```
No-op messages still appear in the workflow step summary even when `report-as-issue` is `false`.
To disable the no-op output entirely:
```yaml
safe-outputs:
create-issue:
noop: false # Disable noop output completely
```
See the full reference: [/reference/safe-outputs/#no-op-logging-noop](/gh-aw/reference/safe-outputs/#no-op-logging-noop)
## Operational monitoring
[Section titled “Operational monitoring”](#operational-monitoring)
Use `gh aw status` to see which workflows are enabled and their latest run state.
For deeper investigation, the audit commands are the primary monitoring tool for agentic workflows:
* `gh aw audit ` — single-run report with tool usage, MCP failures, firewall activity, and cost metrics
* `gh aw audit ` — compare two runs to detect behavioral regressions or new network accesses (pass additional IDs to compare base against multiple runs)
* `gh aw logs --format markdown [workflow]` — cross-run security and performance report for trend monitoring
```bash
# Audit the most recent run
gh aw audit 12345678
# Compare two runs for regressions
gh aw audit 12345678 12345679
# Compare base against multiple runs at once
gh aw audit 12345678 12345679 12345680
# Trend report across the last 10 runs of a workflow
gh aw logs my-workflow --format markdown --count 10
```
Tip
Use `gh aw logs --format markdown` inside a scheduled workflow agent to automate trend monitoring and surface cost or security regressions without manual intervention.
See [Audit Commands](/gh-aw/reference/audit/) for full flag documentation, and [CLI Reference](/gh-aw/setup/cli/) for all available commands.
# OpenTelemetry
> Use OpenTelemetry in GitHub Agentic Workflows for enterprise-scale observability: export workflow traces to OTLP backends and inspect telemetry without leaning on GitHub API-heavy workflows.
Experimental
This guide is experimental. The recommended backend integrations, shared imports, and setup flow on this page may change as the observability story is tested across more real-world workflows.
Observability in GitHub Agentic Workflows often begins with inspecting a single run. That is usually enough at first, but it breaks down once you need retained telemetry, visibility across runs, and a way to investigate without repeatedly spending GitHub API quota or running into rate limits.
OpenTelemetry is the standard way to do that. It lets you export workflow traces through the [OpenTelemetry Protocol (OTLP)](https://opentelemetry.io/docs/reference/specification/protocol/otlp/) to backends such as Datadog, Grafana, or Sentry, and read telemetry back through MCP when agents need to investigate. Most workflows should use either write-side OTLP or read-side MCP. Use both only when you need to correlate newly emitted spans with traces already in the backend.
## Choose a backend
[Section titled “Choose a backend”](#choose-a-backend)
OpenTelemetry gives you a standard way to export workflow traces. The main choice is which backend should receive and surface that telemetry.
* **Datadog** is a strong fit when workflow telemetry needs to plug into broader operational monitoring and service health.
* **Grafana** is a strong fit for teams that want an OpenTelemetry-first stack for traces, dashboards, and investigation.
* **Sentry** is a strong fit when workflow telemetry should live next to application errors and performance debugging.
## Write telemetry through OTLP
[Section titled “Write telemetry through OTLP”](#write-telemetry-through-otlp)
This is configured with [`observability.otlp`](/gh-aw/reference/frontmatter/#observability-observability) in workflow frontmatter:
* Datadog
.github/workflows/daily-report.md
```aw
---
network:
allowed:
- "*.datadoghq.com"
- "*.datadoghq.eu"
- "*.ddog-gov.com"
observability:
otlp:
endpoint:
- url: ${{ secrets.GH_AW_OTEL_DATADOG_ENDPOINT }}
headers:
DD-API-KEY: ${{ secrets.DD_API_KEY }}
---
```
* Grafana
.github/workflows/daily-report.md
```aw
---
network:
allowed:
- "*.grafana.net"
observability:
otlp:
endpoint:
- url: ${{ secrets.GH_AW_OTEL_GRAFANA_ENDPOINT }}
headers:
Authorization: ${{ secrets.GH_AW_OTEL_GRAFANA_AUTHORIZATION }}
---
```
* Sentry
.github/workflows/daily-report.md
```aw
---
network:
allowed:
- "*.sentry.io"
observability:
otlp:
endpoint:
- url: ${{ secrets.GH_AW_OTEL_SENTRY_ENDPOINT }}
headers:
Authorization: ${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }}
---
```
We also support sending to multiple OTLP endpoints in the same workflow. Use the array form when the workflow should fan out to more than one collector, for example Datadog and Grafana or Datadog and Sentry.
Once configured, GitHub Agentic Workflows exports built-in workflow spans such as setup and conclusion events to the configured OTLP backend or backends, such as Datadog, Grafana, Sentry, or another OTLP-compatible system. You can also emit custom spans from your workflow code using the OpenTelemetry API and an OTLP-compatible client library in your workflow language. In the backend, those spans are available as traces for querying and drilldown.
## Read telemetry through MCP
[Section titled “Read telemetry through MCP”](#read-telemetry-through-mcp)
This is configured with [`mcp-servers`](/gh-aw/reference/frontmatter-full/) in workflow frontmatter. Use the read path when the agent needs to inspect telemetry that already exists in a backend such as Datadog, Grafana, Sentry, Tempo, or another OpenTelemetry-compatible system.
* Datadog
.github/workflows/telemetry-investigation.md
```aw
---
mcp-servers:
datadog:
url: "https://mcp.datadoghq.com/api/unstable/mcp-server/mcp"
headers:
DD_API_KEY: "${{ secrets.DD_API_KEY }}"
DD_APPLICATION_KEY: "${{ secrets.DD_APPLICATION_KEY }}"
DD_SITE: "${{ secrets.DD_SITE || 'datadoghq.com' }}"
allowed:
- search_datadog_dashboards
- search_datadog_slos
- search_datadog_metrics
- get_datadog_metric
---
```
* Grafana
.github/workflows/telemetry-investigation.md
```aw
---
mcp-servers:
grafana:
container: "grafana/mcp-grafana"
entrypointArgs:
- "-t"
- "stdio"
- "--disable-write"
allowed:
- list_datasources
- tempo_traceql-search
- tempo_get-trace
- tempo_get-attribute-names
- tempo_get-attribute-values
env:
GRAFANA_URL: "${{ secrets.GRAFANA_URL }}"
GRAFANA_SERVICE_ACCOUNT_TOKEN: "${{ secrets.GRAFANA_SERVICE_ACCOUNT_TOKEN }}"
---
```
* Sentry
.github/workflows/telemetry-investigation.md
```aw
---
mcp-servers:
sentry:
command: "npx"
args: ["@sentry/mcp-server@0.33.0"]
allowed:
- whoami
- find_organizations
- find_projects
- get_trace_details
- search_events
- search_issues
env:
SENTRY_ACCESS_TOKEN: ${{ secrets.SENTRY_ACCESS_TOKEN }}
SENTRY_HOST: ${{ env.SENTRY_HOST || 'sentry.io' }}
---
```
In this model, the workflow does not emit new spans by itself. It gives the agent a tool that can query existing traces and spans from an external backend.
> Keep both write-side OTLP configuration and read-side MCP configuration in [shared workflow files](/gh-aw/reference/imports/) and import them where needed. In this repository, that usually means [shared/otlp.md](https://github.com/github/gh-aw/blob/main/.github/workflows/shared/otlp.md) for the combined Sentry and Grafana OTLP pattern, [shared/mcp/grafana.md](https://github.com/github/gh-aw/blob/main/.github/workflows/shared/mcp/grafana.md) or [shared/mcp/datadog.md](https://github.com/github/gh-aw/blob/main/.github/workflows/shared/mcp/datadog.md) for read access, and [shared/otel-queries.md](https://github.com/github/gh-aw/blob/main/.github/workflows/shared/otel-queries.md) for the query playbook.
## Setup
[Section titled “Setup”](#setup)
Choose your backend here and follow the matching setup path. When many workflows need the same observability wiring, shared imports are usually the right default.
* Datadog
To set up Datadog as an OTLP write backend and an MCP read source, follow these steps.
Datadog uses one OTLP endpoint plus `DD_API_KEY` for write, and `DD_API_KEY`, `DD_APPLICATION_KEY`, and `DD_SITE` for read.
1. **Choose the Datadog site you will use.** Keep write-side OTLP export and read-side MCP access pointed at the same Datadog site. Store that site in `DD_SITE`. Common values include `datadoghq.com`, `datadoghq.eu`, `ddog-gov.com`, `us5.datadoghq.com`, and `ap1.datadoghq.com`.
2. **Create a Datadog API key.** In Datadog, open `Organization Settings`, then `API Keys`, and create a key for workflow telemetry. Store it as `DD_API_KEY`. This key is used for OTLP write and is also sent to the Datadog MCP server.
3. **Create a Datadog application key.** In Datadog, open `Organization Settings`, then `Application Keys`, and create a key for read-side investigation workflows. Store it as `DD_APPLICATION_KEY`. The MCP configuration needs this key in addition to `DD_API_KEY`.
4. **Store the OTLP endpoint for the same site.** Get the Datadog OTLP traces endpoint for the site selected in step 1 and store it as `GH_AW_OTEL_DATADOG_ENDPOINT`. Keep `GH_AW_OTEL_DATADOG_ENDPOINT`, `DD_API_KEY`, `DD_APPLICATION_KEY`, and `DD_SITE` aligned to the same Datadog account and site.
5. **Run one workflow to write and one workflow to read.** Trigger a workflow that exports spans through OTLP, then run a Datadog-backed investigation workflow that imports [shared/mcp/datadog.md](https://github.com/github/gh-aw/blob/main/.github/workflows/shared/mcp/datadog.md). If write-side OTLP and read-side MCP point at different Datadog sites, export may succeed while the investigation workflow reads the wrong account or environment.
Note
The Datadog MCP configuration documented here is read-side only. It gives the workflow access to dashboards, metrics, and SLOs that already exist in Datadog. OTLP export is a separate write-side configuration.
* Grafana
To set up Grafana as both an OTLP backend and an MCP source, follow these steps:
1. **Choose a Grafana stack.** To start, create or choose a dedicated non-production stack such as a dev, staging, or sandbox stack. If multiple stacks exist, choose one that both exposes OTLP connection details for trace ingestion and is the Grafana instance whose Tempo datasource should be queried by workflows.
2. **Get the OTLP write credentials.** Open the selected stack and get to the OpenTelemetry setup page. Depending on where Grafana sends you, this is usually either the Grafana Cloud Portal OpenTelemetry tile with `Configure`, or the Grafana app UI under `Connections`, `Add new connection`, and `OpenTelemetry (OTLP)`. Copy the generated OTLP endpoint and authorization header values. Map those values to gh-aw secrets as follows: `GH_AW_OTEL_GRAFANA_ENDPOINT` should be the value shown for `OTEL_EXPORTER_OTLP_ENDPOINT`, and `GH_AW_OTEL_GRAFANA_AUTHORIZATION` should be the value used in the `Authorization` header, typically `Basic ...`. Use only the header value for `GH_AW_OTEL_GRAFANA_AUTHORIZATION`. gh-aw adds the `Authorization:` header name when compiling the workflow.
3. **Get the Grafana read credentials.** In Grafana for the same stack, open `Administration`, go to `Users and access`, then `Service accounts`, create a service account for workflow telemetry queries, and add a token for it. Map those values to gh-aw secrets as follows: `GRAFANA_URL` should be the base URL of the selected stack, typically `https://.grafana.net`, and `GRAFANA_SERVICE_ACCOUNT_TOKEN` should be the generated service account token. Start with a read-oriented role such as `Viewer`. If the token can connect to Grafana but cannot query traces, the next thing to check is datasource or Tempo permissions for that service account.
4. **Store all four values for the same stack.** For a Grafana-backed workflow, `GH_AW_OTEL_GRAFANA_ENDPOINT`, `GH_AW_OTEL_GRAFANA_AUTHORIZATION`, `GRAFANA_URL`, and `GRAFANA_SERVICE_ACCOUNT_TOKEN` must all point to the same stack. It is normal for the write-side and read-side credentials to be different values. Grafana OTLP write uses `GH_AW_OTEL_GRAFANA_AUTHORIZATION`, while Grafana MCP read uses `GRAFANA_SERVICE_ACCOUNT_TOKEN`.
#### Import a starter dashboard template
[Section titled “Import a starter dashboard template”](#import-a-starter-dashboard-template)
If you want a dashboard people can upload instead of building from scratch, start with [gh-aw-observability-starter.json](https://github.com/github/gh-aw/blob/main/grafana/gh-aw-observability-starter.json).
Import it in Grafana with `Dashboards`, then `New`, then `Import dashboard`, upload the JSON file, and select your Tempo datasource when Grafana prompts for it.
This starter focuses on the telemetry GitHub Agentic Workflows already emits today:
* workflow run volume
* P95 agent span duration
* input and output tokens over time
* cache-read token reuse
* engine mix and model mix
* recent matching traces for drilldown
It is intentionally a first-pass operations and token-economics dashboard. Cost-versus-value, agentic-versus-deterministic, and portfolio-management views need extra outcome instrumentation in your spans, such as execution mode, accepted outcomes, escalations, or review-required markers.
Caution
Different credentials for write and read are expected. What must stay aligned is the backend you expect to query. If traces are written to one Grafana stack but the workflow reads from another Grafana stack, export may succeed while Grafana queries still return no current-run spans. It is also valid to write to Sentry and Grafana at the same time. In that setup, Sentry uses its own OTLP endpoint and auth, while Grafana still uses a separate OTLP auth value for write and a service account token for read.
* Sentry
To set up Sentry as an OTLP write backend, follow these steps.
This section is write-side only. Sentry uses one OTLP endpoint plus one Sentry OTLP auth value, and there is no separate Sentry read token in this guide. If your workflow reads telemetry from another backend, that backend has its own read credentials and they are independent from the Sentry write secrets.
1. **Choose or create a Sentry project.** Start with a dedicated non-production project such as `gh-aw-dev` or `gh-aw-sandbox`. If your organization already has several Sentry projects, choose the one that should receive workflow traces from GitHub Agentic Workflows. Keep the first setup isolated from production alerting until export is working the way you expect.
2. **Open the project keys page.** In Sentry for that project, do not use the project overview or details page. Open `Project Settings`, then `Client Keys (DSN)` instead. This is the page Sentry uses for first-time setup and it is also where Sentry exposes the values needed for direct OTLP trace export.
3. **Get the OTLP write credentials.** On the same project, copy the direct OTLP traces endpoint and the authentication header Sentry provides for OpenTelemetry export. The header Sentry expects for direct OTLP traces is `x-sentry-auth`, and the value is commonly in the form `sentry sentry_key=...`. In other words, the wire format Sentry expects is `x-sentry-auth: sentry sentry_key=...`. Map those values to gh-aw secrets as follows: `GH_AW_OTEL_SENTRY_ENDPOINT` should be the direct OTLP traces endpoint for that project, and `GH_AW_OTEL_SENTRY_AUTHORIZATION` should hold only the Sentry header value, typically `sentry sentry_key=...`. Do not use the DSN itself as the OTLP endpoint. Use the direct OTLP traces URL for the project. For direct Sentry OTLP, the value is typically not `Bearer ...` or `Basic ...`. If Sentry shows you a full header expression, store only the header value in `GH_AW_OTEL_SENTRY_AUTHORIZATION`, not the header name.
4. **Store both values for the same Sentry project.** `GH_AW_OTEL_SENTRY_ENDPOINT` and `GH_AW_OTEL_SENTRY_AUTHORIZATION` must refer to the same Sentry project. Mixing an endpoint from one project with auth from another is an easy first-time setup mistake and usually results in export failures or data arriving in the wrong place. If you are splitting write and read across different systems, that does not create a second Sentry credential here. It only means Sentry keeps its write-side endpoint and auth, while the separate read backend uses its own read-side credentials.
5. **Run a workflow once and verify traces arrive in Sentry.** Trigger a workflow that imports the shared OTLP configuration, then open the same Sentry project and check the trace or performance views for a new run. If nothing arrives, the first things to check are that the secrets were copied from the correct project, the OTLP endpoint is the direct traces endpoint rather than the DSN, and the auth value was stored without the header name.
## Related documentation
[Section titled “Related documentation”](#related-documentation)
* [Imports](/gh-aw/reference/imports/) for bundling shared observability configuration
* [Architecture](/gh-aw/introduction/architecture/#observability) for the broader runtime observability model
# TrialOps
> Test and validate agentic workflows in isolated trial repositories before deploying to production
Experimental
TrialOps features are experimental.
TrialOps uses temporary trial repositories for safely validating and iterating on workflows before deployment to target repositories. The `trial` command creates isolated private repos where workflows execute and capture safe outputs (issues, PRs, comments) without affecting your actual codebase.
## How Trial Mode Works
[Section titled “How Trial Mode Works”](#how-trial-mode-works)
```bash
gh aw trial githubnext/agentics/weekly-research
```
The CLI creates a temporary private repository (default: `gh-aw-trial`), installs and executes the workflow via `workflow_dispatch`. Results are saved locally in `trials/weekly-research.DATETIME-ID.json`, in the trial repository on GitHub, and summarized in the console.
## Repository Modes
[Section titled “Repository Modes”](#repository-modes)
| Mode | Flag | Description |
| ------- | ---------------------------------- | ----------------------------------------------------------------- |
| Default | (none) | `github.repository` points to your repo; outputs go to trial repo |
| Direct | `--repo myorg/test-repo` | Runs in specified repo; creates real issues/PRs there |
| Logical | `--logical-repo myorg/target-repo` | Simulates running against specified repo; outputs in trial repo |
| Clone | `--clone-repo myorg/real-repo` | Clones repo contents so workflows can analyze actual code |
## Basic Usage
[Section titled “Basic Usage”](#basic-usage)
### Dry-Run Mode
[Section titled “Dry-Run Mode”](#dry-run-mode)
Preview what would happen without executing workflows or creating repositories:
```bash
gh aw trial ./my-workflow.md --dry-run
```
### Single Workflow
[Section titled “Single Workflow”](#single-workflow)
```bash
gh aw trial githubnext/agentics/weekly-research # From GitHub
gh aw trial ./my-workflow.md # Local file
```
### Multiple Workflows
[Section titled “Multiple Workflows”](#multiple-workflows)
Compare workflows side-by-side with combined results:
```bash
gh aw trial githubnext/agentics/daily-plan githubnext/agentics/weekly-research
```
Outputs: individual result files plus `trials/combined-results.DATETIME.json`.
### Repeated Trials
[Section titled “Repeated Trials”](#repeated-trials)
Test consistency by running multiple times:
```bash
gh aw trial githubnext/agentics/my-workflow --repeat 3
```
### Custom Trial Repository
[Section titled “Custom Trial Repository”](#custom-trial-repository)
```bash
gh aw trial githubnext/agentics/my-workflow --host-repo my-custom-trial
gh aw trial ./my-workflow.md --host-repo . # Use current repo
```
## Advanced Patterns
[Section titled “Advanced Patterns”](#advanced-patterns)
### Issue Context
[Section titled “Issue Context”](#issue-context)
Provide issue context for issue-triggered workflows:
```bash
gh aw trial githubnext/agentics/triage-workflow \
--trigger-context "https://github.com/myorg/repo/issues/123"
```
### Append Instructions
[Section titled “Append Instructions”](#append-instructions)
Test workflow responses to additional constraints without modifying the source:
```bash
gh aw trial githubnext/agentics/my-workflow \
--append "Focus on security issues and create detailed reports."
```
### Cleanup Options
[Section titled “Cleanup Options”](#cleanup-options)
```bash
gh aw trial ./my-workflow.md --delete-host-repo-after # Delete after completion
gh aw trial ./my-workflow.md --force-delete-host-repo-before # Clean slate before running
```
## Understanding Trial Results
[Section titled “Understanding Trial Results”](#understanding-trial-results)
Results are saved in `trials/*.json` with workflow runs, issues, PRs, and comments viewable in the trial repository’s Actions and Issues tabs.
**Result file structure:**
```json
{
"workflow_name": "weekly-research",
"run_id": "12345678",
"safe_outputs": {
"issues_created": [{
"number": 5,
"title": "Research quantum computing trends",
"url": "https://github.com/user/gh-aw-trial/issues/5"
}]
},
"agentic_run_info": {
"duration_seconds": 45,
"token_usage": 2500
}
}
```
**Success indicators:** Green checkmark, expected outputs created, no errors in logs.
**Common issues:**
* **Workflow dispatch failed** - Add `workflow_dispatch` trigger
* **No safe outputs** - Configure safe outputs in workflow
* **Permission errors** - Verify API keys
* **Timeout** - Use `--timeout 60` (minutes)
## Comparing Multiple Workflows
[Section titled “Comparing Multiple Workflows”](#comparing-multiple-workflows)
Run multiple workflows to compare quality, quantity, performance, and consistency:
```bash
gh aw trial v1.md v2.md v3.md --repeat 2
cat trials/combined-results.*.json | jq '.results[] | {workflow: .workflow_name, issues: .safe_outputs.issues_created | length}'
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Run workflows from separate repositories
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) - Coordinate across multiple repositories
* [OrchestratorOps](/gh-aw/patterns/orchestrator-ops/) — Orchestrate multi-step initiatives
* [CLI Commands](/gh-aw/setup/cli/) - Complete CLI reference
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - Configuration options
* [Workflow Triggers](/gh-aw/reference/triggers/) - Including workflow\_dispatch
* [Security Best Practices](/gh-aw/introduction/architecture/) - Authentication and security
# Agentic Authoring
> More advanced techniques to author agentic workflows using agents.
Using our authoring agent is an effective way to create, debug, optimize your agentic workflows. This is a continuation of the [Create Agentic Workflows](/gh-aw/setup/creating-workflows/) page.
## Configuring Your Repository
[Section titled “Configuring Your Repository”](#configuring-your-repository)
In order to enable the agentic authoring experience, you will need to configure your repository with a few files. Run this prompt or the `init` command.
```text
Initialize this repository for GitHub Agentic Workflows using https://raw.githubusercontent.com/github/gh-aw/main/install.md
```
or
```plaintext
gh aw init
```
Make sure to commit and push the files to your repository.
## Using the GitHub Web Interface
[Section titled “Using the GitHub Web Interface”](#using-the-github-web-interface)
**If you have access to GitHub Copilot**, you can create and edit Agentic Workflows directly from the web interface. While non-interactive, it’s useful for quickly turning an idea into a working workflow. For a more interactive experience, use a coding agent (see below).
Your browser doesn't support HTML5 video. [Download Create an agentic workflow from the GitHub web interface](/gh-aw/videos/create-workflow-on-github.mp4).
Create an agentic workflow from the GitHub web interface
Tip
On the first run in a new repository, the workflow will surely fail because the secrets are not configured. The agentic workflow should detect the missing tokens and create an issue with instructions on how to configure them.
## Remixing Workflows Between Repositories
[Section titled “Remixing Workflows Between Repositories”](#remixing-workflows-between-repositories)
When you need to adapt an existing workflow from another repository, use the `create-agentic-agent` to perform AI-assisted migration. The agent analyzes the source workflow, identifies dependencies, adapts configuration for your repository, and validates the result. This is useful for forking workflows as starting points or one-time migrations requiring substantial changes. For synchronized updates across repositories, use [Reusing Workflows](/gh-aw/guides/packaging-imports/) with `gh aw add` instead.
Example prompt for migration:
```text
Migrate the release.md workflow from github/gh-aw to this repository.
Adapt permissions and repository-specific references for our structure.
```
## Debugging Workflows
[Section titled “Debugging Workflows”](#debugging-workflows)
Use the agentic workflows agent to diagnose and fix failing workflow runs.
### Through Copilot
[Section titled “Through Copilot”](#through-copilot)
If your repository is [configured for agentic authoring](#configuring-your-repository), use the `agentic-workflows` agent in Copilot Chat:
```text
/agent agentic-workflows debug https://github.com/OWNER/REPO/actions/runs/RUN_ID
```
The agent audits the run, identifies the root cause (missing tools, permission errors, network blocks), and suggests targeted fixes.
Tip
Copy this prompt, replace `OWNER`, `REPO`, and `RUN_ID` with your values, and paste it into Copilot Chat. You can find the run URL on the GitHub Actions run page.
### Self-Contained (with URL)
[Section titled “Self-Contained (with URL)”](#self-contained-with-url)
For any AI assistant or coding agent, share the URL to the standalone debugging prompt:
```text
Debug this workflow run using https://raw.githubusercontent.com/github/gh-aw/main/debug.md
The failed workflow run is at https://github.com/OWNER/REPO/actions/runs/RUN_ID
```
Copy debug instructions
The `debug.md` file is a self-contained prompt. The agent fetches it and follows the instructions to install the `gh aw` CLI, analyze logs, apply fixes, and open a pull request with the changes.
## Advanced Techniques
[Section titled “Advanced Techniques”](#advanced-techniques)
### Planner
[Section titled “Planner”](#planner)
If you prefer to use an AI chatbot to author agentic workflows, use the [agentic-chat instructions](https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/agentic-chat.md) with any conversational AI to create clear, actionable task descriptions. Copy agentic-chat instructions
Copy the instructions, paste into your AI chat, then describe your workflow goal. The assistant asks clarifying questions and generates a structured task description (wrapped in 5 backticks) ready to use in your workflow. It focuses on what needs to be done rather than how, making it ideal for creating specifications that coding agents can execute.
### Dictation
[Section titled “Dictation”](#dictation)
When creating agentic workflows using speech-to-text, use the [dictation instructions prompt](https://raw.githubusercontent.com/github/gh-aw/main/DICTATION.md) to correct terminology mismatches and formatting issues. Copy dictation instructions
This prompt corrects terminology (e.g., “ghaw” → “gh-aw”, “work flow” → “workflow”), transforms casual speech into imperative task descriptions, removes filler words, and adds implicit context. Load it into your AI assistant before or after dictating.
# Editing Workflows
> Learn when you can edit workflows directly on GitHub.com versus when recompilation is required, and best practices for iterating on agentic workflows.
Agentic workflows consist of two parts: the **YAML frontmatter** (compiled into the lock file; changes require recompilation) and the **markdown body** (loaded at runtime; changes take effect immediately). This lets you iterate on AI instructions without recompilation while maintaining strict control over security-sensitive configuration.
See [Creating Agentic Workflows](/gh-aw/setup/creating-workflows/) for guidance on creating workflows with AI assistance.
## Editing Without Recompilation
[Section titled “Editing Without Recompilation”](#editing-without-recompilation)
You can edit the **markdown body** directly on GitHub.com or in any editor without recompiling. Changes take effect on the next workflow run.
### What You Can Edit
[Section titled “What You Can Edit”](#what-you-can-edit)
The markdown body is loaded at runtime from the original `.md` file. You can freely edit task instructions, output templates, conditional logic (“If X, then do Y”), context explanations, and examples.
### Example: Adding Instructions
[Section titled “Example: Adding Instructions”](#example-adding-instructions)
**Before** (in `.github/workflows/issue-triage.md`):
```markdown
---
on:
issues:
types: [opened]
---
# Issue Triage
Read issue #${{ github.event.issue.number }} and add appropriate labels.
```
**After** (edited on GitHub.com):
```markdown
---
on:
issues:
types: [opened]
---
# Issue Triage
Read issue #${{ github.event.issue.number }} and add appropriate labels.
## Labeling Criteria
Apply these labels based on content:
- `bug`: Issues describing incorrect behavior with reproduction steps
- `enhancement`: Feature requests or improvements
- `question`: Help requests or clarifications needed
- `documentation`: Documentation updates or corrections
For priority, consider:
- `high-priority`: Security issues, critical bugs, blocking issues
- `medium-priority`: Important features, non-critical bugs
- `low-priority`: Nice-to-have improvements, minor enhancements
```
✓ This change takes effect immediately without recompilation.
## Editing With Recompilation Required
[Section titled “Editing With Recompilation Required”](#editing-with-recompilation-required)
Caution
Changes to the **YAML frontmatter** always require recompilation. These are security-sensitive configuration options.
### What Requires Recompilation
[Section titled “What Requires Recompilation”](#what-requires-recompilation)
Any changes to the frontmatter configuration between `---` markers:
* **Triggers** (`on:`): Event types, filters, schedules
* **Permissions** (`permissions:`): Repository access levels
* **Tools** (`tools:`): Tool configurations, MCP servers, allowed tools
* **Network** (`network:`): Allowed domains, firewall rules
* **Safe outputs** (`safe-outputs:`): Output types, threat detection
* **MCP Scripts** (`mcp-scripts:`): Custom MCP tools defined inline
* **Runtimes** (`runtimes:`): Node, Python, Go version overrides
* **Imports** (`imports:`): Shared configuration files
* **Custom jobs** (`jobs:`): Additional workflow jobs
* **Engine** (`engine:`): AI engine selection (copilot, claude, codex)
* **Timeout** (`timeout-minutes:`): Maximum execution time
* **Roles** (`roles:`): Permission requirements for actors
### Example: Adding a Tool (Requires Recompilation)
[Section titled “Example: Adding a Tool (Requires Recompilation)”](#example-adding-a-tool-requires-recompilation)
**Before**:
```yaml
---
on:
issues:
types: [opened]
---
```
**After** (must recompile):
```yaml
---
on:
issues:
types: [opened]
tools:
github:
toolsets: [issues]
---
```
! Run `gh aw compile my-workflow` before committing this change.
## Expressions and Environment Variables
[Section titled “Expressions and Environment Variables”](#expressions-and-environment-variables)
### Allowed Expressions
[Section titled “Allowed Expressions”](#allowed-expressions)
You can safely use these expressions in markdown without recompilation:
```markdown
# Process Issue
Read issue #${{ github.event.issue.number }} in repository ${{ github.repository }}.
Issue title: "${{ github.event.issue.title }}"
Use sanitized content: "${{ steps.sanitized.outputs.text }}"
Actor: ${{ github.actor }}
Repository: ${{ github.repository }}
```
These expressions are evaluated at runtime and validated for security. See [Templating](/gh-aw/reference/templating/) for the complete list of allowed expressions.
### Prohibited Expressions
[Section titled “Prohibited Expressions”](#prohibited-expressions)
Arbitrary expressions are blocked for security. This will fail at runtime:
```markdown
# ✗ WRONG - Will be rejected
Run this command: ${{ github.event.comment.body }}
```
Use `steps.sanitized.outputs.text` for sanitized user input instead.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Workflow Structure](/gh-aw/reference/workflow-structure/) - Overall file organization
* [Frontmatter Reference](/gh-aw/reference/frontmatter/) - All configuration options
* [Markdown Reference](/gh-aw/reference/markdown/) - Writing effective instructions
* [Compilation Process](/gh-aw/reference/compilation-process/) - How compilation works
* [Templating](/gh-aw/reference/templating/) - Expression syntax and substitution
# GitHub Actions Primer
> A comprehensive guide to understanding GitHub Actions, from its history and core concepts to testing workflows and comparing with agentic workflows
**GitHub Actions** is GitHub’s integrated automation platform for building, testing, and deploying code from your repository. It enables automated workflows triggered by repository events, schedules, or manual triggers — all defined in YAML files in your repository. Agentic workflows compile from markdown files into secure GitHub Actions YAML, inheriting these core concepts while adding AI-driven decision-making and enhanced security.
## Core Concepts
[Section titled “Core Concepts”](#core-concepts)
### YAML Workflows
[Section titled “YAML Workflows”](#yaml-workflows)
A **YAML workflow** is an automated process defined in `.github/workflows/`. Each workflow consists of jobs that execute when triggered by events. Workflows must be stored on the **main** or default branch to be active and are versioned alongside your code.
**Example** (`.github/workflows/ci.yml`):
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Run tests
run: npm test
```
### Jobs
[Section titled “Jobs”](#jobs)
A **job** is a set of steps that execute on the same runner (virtual machine). Jobs run in parallel by default but can depend on each other with `needs:`. Each job runs in a fresh VM, and results are shared between jobs using artifacts. Default timeout is 360 minutes for standard GitHub Actions jobs; the agent execution step in agentic workflows defaults to 20 minutes.
```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: npm run build
test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: npm test
```
### Steps
[Section titled “Steps”](#steps)
**Steps** are individual tasks within a job, running sequentially. They can execute shell commands or use pre-built actions from the GitHub Marketplace. Steps share the same filesystem and environment; a failed step stops the job by default.
```yaml
steps:
# Action step - uses a pre-built action
- uses: actions/checkout@v6
# Run step - executes a shell command
- name: Install dependencies
run: npm install
# Action with inputs
- uses: actions/setup-node@v4
with:
node-version: '20'
```
## Security Model
[Section titled “Security Model”](#security-model)
### Workflow Storage and Execution
[Section titled “Workflow Storage and Execution”](#workflow-storage-and-execution)
Workflows must be stored in `.github/workflows/` on the **default branch** to be active and trusted. This ensures changes undergo code review, maintains an audit trail, prevents privilege escalation from feature branches, and treats the default branch as a trust boundary.
```yaml
# Workflows on main branch can access secrets
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- run: echo "Has access to production secrets"
```
### Permission Model
[Section titled “Permission Model”](#permission-model)
GitHub Actions uses the **principle of least privilege** with explicit permission declarations. Fork pull requests are read-only by default; all required permissions should be explicitly declared.
```yaml
permissions:
contents: read # Read repository contents
issues: write # Create/modify issues
pull-requests: write # Create/modify PRs
jobs:
example:
runs-on: ubuntu-latest
steps:
- run: echo "Job has specified permissions only"
```
With GItHub Agentic Workflows, **write permissions are not used explicitly**. Instead much more restricted capabilities to write to GitHub are declared through **safe outputs**, which validate, constrain and sanitize all GitHub API interactions.
### Secret Management
[Section titled “Secret Management”](#secret-management)
**Secrets** are encrypted environment variables stored at the repository, organization, or environment level. They are never exposed in logs, only accessible to workflows on default/protected branches, and scoped by environment for additional protection.
```yaml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to production
env:
API_KEY: ${{ secrets.API_KEY }}
run: ./deploy.sh
```
## Testing and Debugging Workflows
[Section titled “Testing and Debugging Workflows”](#testing-and-debugging-workflows)
### Testing from Branches with workflow\_dispatch
[Section titled “Testing from Branches with workflow\_dispatch”](#testing-from-branches-with-workflow_dispatch)
The **`workflow_dispatch`** trigger allows manual workflow execution from any branch, invaluable for development and testing:
```yaml
name: Test Workflow
on:
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
debug:
description: 'Enable debug logging'
required: false
type: boolean
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo "Testing in ${{ inputs.environment }}"
- run: echo "Debug mode: ${{ inputs.debug }}"
```
To run: navigate to the **Actions** tab → select your workflow → click **Run workflow** → choose your branch and provide inputs.
Tip
Enable debug logging by setting repository secrets `ACTIONS_STEP_DEBUG: true` and `ACTIONS_RUNNER_DEBUG: true`.
**Note:** The workflow definition must be merged to the main branch before it can be executed. Only `workflow_dispatch` works on non-default branches — event triggers do not.
### Debugging Workflow Runs
[Section titled “Debugging Workflow Runs”](#debugging-workflow-runs)
View logs in the **Actions** tab by clicking a run, then a job, then individual steps. Use workflow commands for structured output:
```yaml
steps:
- name: Debug context
run: |
echo "::debug::Debugging workflow context"
echo "::notice::This is a notice"
echo "::warning::This is a warning"
echo "::error::This is an error"
- name: Debug environment
run: |
echo "GitHub event: ${{ github.event_name }}"
echo "Actor: ${{ github.actor }}"
printenv | sort
```
## Agentic Workflows vs Traditional GitHub Actions
[Section titled “Agentic Workflows vs Traditional GitHub Actions”](#agentic-workflows-vs-traditional-github-actions)
While agentic workflows compile to GitHub Actions YAML and run on the same infrastructure, they introduce significant enhancements in security, simplicity, and AI-powered decision-making.
| Feature | Traditional GitHub Actions | Agentic Workflows |
| ------------------------- | -------------------------------------- | ---------------------------------------- |
| **Definition Language** | YAML with explicit steps | Natural language markdown |
| **Complexity** | Requires YAML expertise, API knowledge | Describe intent in plain English |
| **Decision Making** | Fixed if-then logic | AI-powered contextual decisions |
| **Security Model** | Token-based with broad permissions | Sandboxed with safe-outputs |
| **Write Operations** | Direct API access with `GITHUB_TOKEN` | Sanitized through safe-output validation |
| **Network Access** | Unrestricted by default | Allowlisted domains only |
| **Execution Environment** | Standard runner VM | Enhanced sandbox with MCP isolation |
| **Tool Integration** | Manual action selection | MCP server automatic tool discovery |
| **Testing** | `workflow_dispatch` on branches | Same, plus local compilation |
| **Auditability** | Standard workflow logs | Enhanced with agent reasoning logs |
## Next Steps and Resources
[Section titled “Next Steps and Resources”](#next-steps-and-resources)
* **[Quick Start](/gh-aw/setup/quick-start/)** - Create your first agentic workflow
* **[Security Best Practices](/gh-aw/introduction/architecture/)** - Deep dive into agentic security model
* **[Safe Outputs](/gh-aw/reference/safe-outputs/)** - Learn about validated GitHub operations
* **[Workflow Structure](/gh-aw/reference/workflow-structure/)** - Understand markdown workflow syntax
* **[Design Patterns](/gh-aw/patterns/issue-ops/)** - Real-world agentic workflow patterns
* **[Glossary](/gh-aw/reference/glossary/)** - Key terms and concepts
* **[GitHub Actions Documentation](https://docs.github.com/en/actions)** - Official reference
* **[Workflow Syntax](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions)** - Complete YAML reference
* **[Security Hardening](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions)** - Security best practices
# Using MCPs
> How to use Model Context Protocol (MCP) servers with GitHub Agentic Workflows to connect AI agents to GitHub, databases, and external services.
[Model Context Protocol](/gh-aw/reference/glossary/#mcp-model-context-protocol) (MCP) is a standard for AI tool integration, allowing agents to securely connect to external tools, databases, and services. GitHub Agentic Workflows includes built-in GitHub MCP integration and supports custom MCP servers for external services.
## Quick Start
[Section titled “Quick Start”](#quick-start)
Get your first MCP integration running in under 5 minutes.
### Step 1: Add GitHub Tools
[Section titled “Step 1: Add GitHub Tools”](#step-1-add-github-tools)
Create a workflow file at `.github/workflows/my-workflow.md`:
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
issues: read
tools:
github:
toolsets: [default]
---
# Issue Analysis Agent
Analyze the issue and provide a summary of similar existing issues.
```
The `toolsets: [default]` configuration gives your agentic workflow access to repository, issue, and pull request tools.
### Step 2: Compile and Test
[Section titled “Step 2: Compile and Test”](#step-2-compile-and-test)
```bash
gh aw compile my-workflow
gh aw mcp inspect my-workflow
```
## GitHub MCP Server
[Section titled “GitHub MCP Server”](#github-mcp-server)
The GitHub MCP server is built into agentic workflows and provides comprehensive access to GitHub’s API.
### Available Toolsets
[Section titled “Available Toolsets”](#available-toolsets)
| Toolset | Description | Tools |
| --------------- | --------------------------- | ----------------------------------------------------- |
| `context` | User and team information | `get_teams`, `get_team_members` |
| `repos` | Repository operations | `get_repository`, `get_file_contents`, `list_commits` |
| `issues` | Issue management | `list_issues`, `create_issue`, `update_issue` |
| `pull_requests` | PR operations | `list_pull_requests`, `create_pull_request` |
| `actions` | Workflow runs and artifacts | `list_workflows`, `list_workflow_runs` |
| `discussions` | GitHub Discussions | `list_discussions`, `create_discussion` |
| `code_security` | Security alerts | `list_code_scanning_alerts` |
| `users` | User profiles | `get_me`, `get_user`, `list_users` |
The `default` toolset includes: `context`, `repos`, `issues`, `pull_requests`. When used in workflows, `[default]` expands to action-friendly toolsets that work with GitHub Actions tokens. Note: The `users` toolset is not included by default as GitHub Actions tokens do not support user operations.
### Operating Modes
[Section titled “Operating Modes”](#operating-modes)
Remote mode (`mode: remote`) connects to a hosted server with no Docker required. Local mode (`mode: local`) runs in Docker, enabling version pinning for offline or restricted environments. See [Remote vs Local Mode](/gh-aw/reference/github-tools/#github-tools-access-modes).
The GitHub MCP server always operates read-only. Write operations are handled through [safe outputs](/gh-aw/reference/safe-outputs/), which run in a separate permission-controlled job.
## Manually Configuring a Custom MCP Server
[Section titled “Manually Configuring a Custom MCP Server”](#manually-configuring-a-custom-mcp-server)
Caution
Custom MCP servers should be **read-only**. Write operations must go through [safe outputs](/gh-aw/reference/safe-outputs/) or [Custom Safe Outputs](/gh-aw/reference/custom-safe-outputs/). Ensure your MCP server implements authentication and authorization to prevent unauthorized write access.
Add MCP servers to your workflow’s frontmatter using the `mcp-servers:` section:
```aw
---
on: issues
permissions:
contents: read
mcp-servers:
microsoftdocs:
url: "https://learn.microsoft.com/api/mcp"
allowed: ["*"]
notion:
container: "mcp/notion"
env:
NOTION_TOKEN: "${{ secrets.NOTION_TOKEN }}"
allowed:
- "search_pages"
- "get_page"
- "get_database"
- "query_database"
---
# Your workflow content here
```
## Custom MCP Server Types
[Section titled “Custom MCP Server Types”](#custom-mcp-server-types)
### Stdio MCP Servers
[Section titled “Stdio MCP Servers”](#stdio-mcp-servers)
Execute commands with stdin/stdout communication for Python modules, Node.js scripts, and local executables:
```yaml
mcp-servers:
serena:
command: "uvx"
args: ["--from", "git+https://github.com/oraios/serena", "serena"]
allowed: ["*"]
```
### Docker Container MCP Servers
[Section titled “Docker Container MCP Servers”](#docker-container-mcp-servers)
Run containerized MCP servers with environment variables, volume mounts, and network restrictions:
```yaml
mcp-servers:
custom-tool:
container: "mcp/custom-tool:v1.0"
args: ["-v", "/host/data:/app/data"] # Volume mounts before image
entrypointArgs: ["serve", "--port", "8080"] # App args after image
env:
API_KEY: "${{ secrets.API_KEY }}"
allowed: ["tool1", "tool2"]
network:
allowed:
- defaults
- api.example.com
```
The `container` field generates `docker run --rm -i `.
### HTTP MCP Servers
[Section titled “HTTP MCP Servers”](#http-mcp-servers)
Remote MCP servers accessible via HTTP. Configure authentication using the `headers` field for static API keys, or the `auth` field for dynamic token acquisition:
```yaml
mcp-servers:
deepwiki:
url: "https://mcp.deepwiki.com/sse"
allowed:
- read_wiki_structure
- read_wiki_contents
- ask_question
authenticated-api:
url: "https://api.example.com/mcp"
headers:
Authorization: "Bearer ${{ secrets.API_TOKEN }}"
allowed: ["*"]
```
#### GitHub Actions OIDC Authentication
[Section titled “GitHub Actions OIDC Authentication”](#github-actions-oidc-authentication)
For MCP servers that accept GitHub Actions OIDC tokens, use the `auth` field instead of a static `headers` value. The gateway acquires a short-lived JWT from the GitHub Actions OIDC endpoint and injects it as an `Authorization: Bearer` header on every outgoing request.
```yaml
permissions:
id-token: write # required for OIDC token acquisition
mcp-servers:
my-secure-server:
url: "https://my-server.example.com/mcp"
auth:
type: github-oidc
audience: "https://my-server.example.com" # optional; defaults to the server URL
allowed: ["*"]
```
The `auth.type: github-oidc` field is only valid on HTTP servers. The MCP server is responsible for validating the token; the gateway acts as a token forwarder. See [MCP Gateway — Upstream Authentication](/gh-aw/reference/mcp-gateway/#76-upstream-authentication-oidc) for full specification details.
### Registry-based MCP Servers
[Section titled “Registry-based MCP Servers”](#registry-based-mcp-servers)
Reference MCP servers from the GitHub MCP registry (the `registry` field provides metadata for tooling and is not enforced by gh-aw):
```yaml
mcp-servers:
markitdown:
registry: https://api.mcp.github.com/v0/servers/microsoft/markitdown
container: "ghcr.io/microsoft/markitdown"
allowed: ["*"]
```
## MCP Tool Filtering
[Section titled “MCP Tool Filtering”](#mcp-tool-filtering)
Use `allowed:` to specify which tools are available, or `["*"]` to allow all:
```yaml
mcp-servers:
notion:
container: "mcp/notion"
allowed: ["search_pages", "get_page"] # or ["*"] to allow all
```
The `allowed:` filter is enforced at the **MCP gateway level** — the gateway only exposes the listed tools to the agent. This enforcement applies regardless of which AI engine or permission mode is in use.
## Shared MCP Configurations
[Section titled “Shared MCP Configurations”](#shared-mcp-configurations)
Pre-configured MCP server specifications are available in [`.github/workflows/shared/mcp/`](https://github.com/github/gh-aw/tree/main/.github/workflows/shared/mcp) and can be copied or imported directly. Examples include:
| MCP Server | Import Path | Key Capabilities |
| ----------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Jupyter** | `shared/mcp/jupyter.md` | Execute code, manage notebooks, visualize data |
| **Drain3** | `shared/mcp/drain3.md` | Log pattern mining with 8 tools including `index_file`, `list_clusters`, `find_anomalies` |
| **AgentDB** | `shared/mcp/agentdb.md` | Semantic and hybrid retrieval over agent-collected corpora (e.g. discussions, issues), backed by a runtime store at `AGENTDB_PATH` |
| **Others** | `shared/mcp/*.md` | AST-Grep, Azure, Brave Search, Context7, DataDog, DeepWiki, Fabric RTI, MarkItDown, Microsoft Docs, Notion, Sentry, Serena, Server Memory, Slack, Tavily |
## Adding MCP Servers from the Registry
[Section titled “Adding MCP Servers from the Registry”](#adding-mcp-servers-from-the-registry)
Use `gh aw mcp add` to browse and add servers from the GitHub MCP registry (default: `https://api.mcp.github.com/v0`):
```bash
gh aw mcp add # List available servers
gh aw mcp add my-workflow makenotion/notion-mcp-server # Add server
gh aw mcp add my-workflow makenotion/notion-mcp-server --transport stdio # Specify transport
gh aw mcp add my-workflow makenotion/notion-mcp-server --tool-id my-notion # Custom tool ID
gh aw mcp add my-workflow server-name --registry https://custom.registry.com/v1 # Custom registry
```
## Practical Examples
[Section titled “Practical Examples”](#practical-examples)
### Example 1: Basic Issue Triage
[Section titled “Example 1: Basic Issue Triage”](#example-1-basic-issue-triage)
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
issues: read
tools:
github:
toolsets: [default]
safe-outputs:
add-comment:
---
# Issue Triage Agent
Analyze issue #${{ github.event.issue.number }} and add a comment with category, related issues, and suggested labels.
```
### Example 2: Security Audit with Discussions
[Section titled “Example 2: Security Audit with Discussions”](#example-2-security-audit-with-discussions)
```aw
---
on: weekly on sunday
permissions:
contents: read
security-events: read
discussions: write
tools:
github:
toolsets: [default, code_security, discussions]
safe-outputs:
create-discussion:
category: "Security"
title-prefix: "[security-scan] "
---
# Security Audit Agent
Review code scanning alerts and create weekly security discussions with findings.
```
## Debugging and Troubleshooting
[Section titled “Debugging and Troubleshooting”](#debugging-and-troubleshooting)
Inspect MCP configurations with CLI commands: `gh aw mcp inspect my-workflow` (add `--server --verbose` for details) or `gh aw mcp list-tools my-workflow`.
For advanced debugging, import `shared/mcp-debug.md` to access diagnostic tools and the `report_diagnostics_to_pull_request` custom safe-output.
**Common issues**: Connection failures (verify syntax, env vars, network) or tool not found (check toolsets configuration or `allowed` list with `gh aw mcp inspect`).
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MCP Scripts](/gh-aw/reference/mcp-scripts/) - Define custom inline tools without external MCP servers
* [Tools](/gh-aw/reference/tools/) - Complete tools reference
* [CLI Commands](/gh-aw/setup/cli/) - CLI commands including `mcp inspect`
* [Imports](/gh-aw/reference/imports/) - Modularizing workflows with includes
* [Frontmatter](/gh-aw/reference/frontmatter/) - All configuration options
* [Workflow Structure](/gh-aw/reference/workflow-structure/) - Directory organization
* [Model Context Protocol Specification](https://github.com/modelcontextprotocol/specification)
* [GitHub MCP Server](https://github.com/github/github-mcp-server)
# Network Configuration Guide
> Common network configurations for package registries, CDNs, and development tools
This guide provides practical examples for configuring network access in GitHub Agentic Workflows while maintaining security.
## Quick Start
[Section titled “Quick Start”](#quick-start)
Configure network access by adding ecosystem identifiers to the `network.allowed` list. Always include `defaults` for basic infrastructure:
```yaml
network:
allowed:
- defaults # Required: Basic infrastructure
- python # PyPI, conda (for Python projects)
- node # npm, yarn, pnpm (for Node.js projects)
- go # Go module proxy (for Go projects)
- containers # Docker Hub, GHCR (for container projects)
```
## Available Ecosystems
[Section titled “Available Ecosystems”](#available-ecosystems)
For the full list of ecosystem identifiers and the domains they include, see the [Ecosystem Identifiers reference](/gh-aw/reference/network/#ecosystem-identifiers).
## Common Configuration Patterns
[Section titled “Common Configuration Patterns”](#common-configuration-patterns)
```yaml
# Python project with containers
network:
allowed:
- defaults
- python
- containers
# Full-stack web development
network:
allowed:
- defaults
- node
- playwright
- github
# DevOps automation
network:
allowed:
- defaults
- terraform
- containers
- github
```
## Custom Domains
[Section titled “Custom Domains”](#custom-domains)
Add specific domains for your services. Both base domains and wildcard patterns are supported:
```yaml
network:
allowed:
- defaults
- python
- "api.example.com" # Matches api.example.com and subdomains
- "*.cdn.example.com" # Wildcard: matches any subdomain of cdn.example.com
```
**Wildcard pattern behavior:**
* `*.example.com` matches `sub.example.com`, `deep.nested.example.com`, and `example.com`
* Only single wildcards at the start are supported (e.g., `*.*.example.com` is invalid)
Tip
Both `example.com` and `*.example.com` match subdomains. Use wildcards when you want to explicitly document that subdomain access is expected.
## Protocol-Specific Filtering
[Section titled “Protocol-Specific Filtering”](#protocol-specific-filtering)
Restrict domains to specific protocols for enhanced security (Copilot engine with AWF firewall):
```yaml
engine: copilot
network:
allowed:
- defaults
- "https://secure.api.example.com" # HTTPS-only
- "http://legacy.internal.com" # HTTP-only
- "example.org" # Both protocols (default)
sandbox:
agent: awf # Firewall enabled
```
**Validation:** Invalid protocols (e.g., `ftp://`) are rejected at compile time.
See [Network Permissions - Protocol-Specific Filtering](/gh-aw/reference/network/#protocol-specific-domain-filtering) for complete details.
## Strict Mode and Ecosystem Identifiers
[Section titled “Strict Mode and Ecosystem Identifiers”](#strict-mode-and-ecosystem-identifiers)
Workflows use [strict mode](/gh-aw/reference/frontmatter/#strict-mode-strict) by default, which enforces ecosystem identifiers instead of individual domains for security. This applies to all engines.
```yaml
# ✗ Rejected in strict mode
network:
allowed:
- "pypi.org" # Error: use 'python' ecosystem instead
- "npmjs.org" # Error: use 'node' ecosystem instead
# ✓ Accepted in strict mode
network:
allowed:
- python # Ecosystem identifier
- node # Ecosystem identifier
```
### Error Messages
[Section titled “Error Messages”](#error-messages)
When strict mode rejects a domain that belongs to a known ecosystem, the error message suggests the ecosystem identifier:
```text
error: strict mode: network domains must be from known ecosystems (e.g., 'defaults',
'python', 'node') for all engines in strict mode. Custom domains are not allowed for
security. Did you mean: 'pypi.org' belongs to ecosystem 'python'?
```
When strict mode rejects a custom domain:
```text
error: strict mode: network domains must be from known ecosystems (e.g., 'defaults',
'python', 'node') for all engines in strict mode. Custom domains are not allowed for
security. Set 'strict: false' to use custom domains.
```
### Using Custom Domains
[Section titled “Using Custom Domains”](#using-custom-domains)
To use custom domains (domains not in known ecosystems), disable strict mode:
```yaml
---
strict: false # Required for custom domains
network:
allowed:
- python # Ecosystem identifier
- "api.example.com" # Custom domain (only allowed with strict: false)
---
```
**Security Note**: Custom domains bypass ecosystem validation. Only disable strict mode when necessary and ensure you trust the custom domains you allow.
## Security Best Practices
[Section titled “Security Best Practices”](#security-best-practices)
1. **Start minimal** - Only add ecosystems you actually use
2. **Use ecosystem identifiers** - Don’t list individual domains (use `python` instead of `pypi.org`, `files.pythonhosted.org`, etc.)
3. **Keep strict mode enabled** - Provides enhanced security validation (enabled by default)
4. **Add incrementally** - Start with `defaults`, add ecosystems as needed based on firewall denials
## Troubleshooting Firewall Blocking
[Section titled “Troubleshooting Firewall Blocking”](#troubleshooting-firewall-blocking)
View firewall activity with `gh aw logs --run-id ` to identify blocked domains:
```text
Firewall Log Analysis
Blocked Domains:
✗ registry.npmjs.org:443 (3 requests) → Add `node` ecosystem
✗ pypi.org:443 (2 requests) → Add `python` ecosystem
```
Common mappings: npm/Node.js → `node`, PyPI/Python → `python`, Docker → `containers`, Go modules → `go`.
## Advanced Options
[Section titled “Advanced Options”](#advanced-options)
Disable all external network access (engine communication still allowed):
```yaml
network: {}
```
View complete ecosystem domain lists in the [ecosystem domains source](https://github.com/github/gh-aw/blob/main/pkg/workflow/data/ecosystem_domains.json).
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Network Permissions Reference](/gh-aw/reference/network/) - Complete network configuration reference
* [Playwright Reference](/gh-aw/reference/playwright/) - Browser automation and network requirements
* [Security Guide](/gh-aw/introduction/architecture/) - Security best practices
* [Troubleshooting](/gh-aw/troubleshooting/common-issues/) - Common issues and solutions
# Reusing Workflows
> How to reuse, add, share, update, and distribute workflows.
## Adding Workflows
[Section titled “Adding Workflows”](#adding-workflows)
You can add any existing workflow you have access to from external repositories.
Use the `gh aw add-wizard` command to add a workflow with interactive guidance:
```bash
gh aw add-wizard
```
For example, to add the `daily-repo-status` workflow from the `githubnext/agentics` repository:
```bash
# Full GitHub URL
gh aw add-wizard https://github.com/githubnext/agentics/blob/main/workflows/daily-repo-status.md
# Short form (for workflows in top-level workflows/ directory)
gh aw add-wizard githubnext/agentics/daily-repo-status
# Skip the API key prompt when a secret is already configured
gh aw add-wizard githubnext/agentics/daily-repo-status --skip-secret
```
This checks requirements, adds the workflow markdown file to your repository, and generates the corresponding YAML workflow. After adding, commit and push the changes to your repository.
The `--skip-secret` flag bypasses the interactive API key prompt. Use it when the required secret (e.g., `COPILOT_GITHUB_TOKEN`) is already configured at the organization or repository level.
For non-interactive installation, use `gh aw add` with optional versioning. By default this looks in the `workflows/` directory, but you can specify an explicit path if needed:
```bash
gh aw add githubnext/agentics/ci-doctor # short form
gh aw add githubnext/agentics/ci-doctor@v1.0.0 # with version
gh aw add githubnext/agentics/workflows/ci-doctor.md # explicit path
```
Use `--name`, `--pr`, `--force`, `--engine`, or `--verbose` flags to customize installation. The `source` field is automatically added to workflow frontmatter for tracking origin and enabling updates.
When installing a workflow, `gh aw add` also automatically fetches:
* Workflows referenced in the workflow’s [`dispatch-workflow`](/gh-aw/reference/safe-outputs/#workflow-dispatch-dispatch-workflow) safe output.
* Files declared in the workflow’s [`resources:`](/gh-aw/reference/frontmatter/#resources-resources) frontmatter field (companion workflows, custom actions).
Note
Check carefully that the workflow comes from a trusted source and is appropriate for your use in your repository. Review the workflow’s content and understand what it does before adding it to your repository.
Note
Workflows marked with `private: true` in their frontmatter cannot be added to other repositories. Attempting to do so will fail with an error. See [Private Workflows](/gh-aw/reference/frontmatter/#private-workflows-private) for details.
## Using an Agent to Import and Adapt a Workflow
[Section titled “Using an Agent to Import and Adapt a Workflow”](#using-an-agent-to-import-and-adapt-a-workflow)
You can use a coding agent to import a workflow from another repository and adapt it for your own. The agent reads the source workflow, customizes repository-specific configuration (labels, assignees, branch names, permissions), and sets up the repository — including initialization if needed.
Tip
Use this approach when you want to significantly customize a workflow before using it. For straightforward imports without modification, use [`gh aw add`](#adding-workflows) or [`gh aw add-wizard`](#adding-workflows) instead.
### GitHub Web Interface
[Section titled “GitHub Web Interface”](#github-web-interface)
**If you have access to GitHub Copilot**, use one of these prompts in your repository to import and adapt a workflow from another repo. Each prompt also initializes the repository for GitHub Agentic Workflows if it has not been set up yet.
* Daily Status Report
```markdown
Initialize this repository for GitHub Agentic Workflows using https://raw.githubusercontent.com/github/gh-aw/main/install.md
Then import and adapt the daily-repo-status workflow from githubnext/agentics. The source is at https://github.com/githubnext/agentics/blob/main/workflows/daily-repo-status.md. Adapt any labels, team references, and output format to suit this repository.
```
* Issue Triage
```markdown
Initialize this repository for GitHub Agentic Workflows using https://raw.githubusercontent.com/github/gh-aw/main/install.md
Then import and adapt an issue triage workflow from github/gh-aw. Find a suitable issue triage workflow in that repository and adapt it: update the labels, assignee logic, and any repository-specific rules to match this project's conventions.
```
* CI Doctor
```markdown
Initialize this repository for GitHub Agentic Workflows using https://raw.githubusercontent.com/github/gh-aw/main/install.md
Then import and adapt the CI Doctor workflow from githubnext/agentics. The source is at https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md. Adapt the workflow to match this repository's CI setup, branch naming, and issue labeling conventions.
```
Tip
On the first run in a new repository, the workflow may fail because secrets are not yet configured. The agentic workflow should detect missing tokens and open an issue with setup instructions.
### Coding Agent
[Section titled “Coding Agent”](#coding-agent)
Follow these steps to import and adapt a workflow using VSCode, Claude, Codex, or Copilot in your terminal.
1. **Start your coding agent** in the context of your repository.
2. **Enter the following prompt**, replacing `SOURCE_WORKFLOW`, `OWNER`, and `REPO` with the workflow you want to import:
```text
Initialize this repository for GitHub Agentic Workflows using https://raw.githubusercontent.com/github/gh-aw/main/install.md
Then import and adapt the SOURCE_WORKFLOW workflow from OWNER/REPO. The source is at https://github.com/OWNER/REPO/blob/main/workflows/SOURCE_WORKFLOW.md.
Adapt the workflow for this repository: update any labels, assignees, branch names, and permissions to match this project's structure. Keep the overall purpose and logic of the workflow intact.
```
You can add as much extra context, constraints, or customization goals after the last line as you need.
3. **Set up required secrets** if you haven’t done so already. See [Engines](/gh-aw/reference/engines/) for the secrets your chosen engine requires.
After the agent finishes, review the adapted workflow, merge the pull request, and trigger a run from the Actions tab or with `gh aw run`.
## Updating Workflows
[Section titled “Updating Workflows”](#updating-workflows)
When you add a workflow, a tracking `source:` entry remembers where it came from. You can keep workflows synchronized with their source repositories:
```bash
gh aw update # update all workflows
gh aw update ci-doctor # update specific workflow
gh aw update ci-doctor issue-triage # update multiple
```
Use `--major`, `--force`, `--no-merge`, `--engine`, or `--verbose` flags to control update behavior. Semantic versions (e.g., `v1.2.3`) update to latest compatible release within same major version. Branch references update to latest commit. SHA references update to the latest commit on the default branch. Updates use 3-way merge by default to preserve local changes; use `--no-merge` to replace with the upstream version. When merge conflicts occur, manually resolve conflict markers and run `gh aw compile`.
## Imports
[Section titled “Imports”](#imports)
Import reusable components using the `imports:` field in frontmatter. File paths are relative to the workflow location:
```yaml
---
on: issues
engine: copilot
imports:
- shared/common-tools.md
- shared/security-setup.md
- shared/mcp/tavily.md
---
```
During `gh aw add`, imports are expanded to track source repository (e.g., `shared/common-tools.md` becomes `githubnext/agentics/shared/common-tools.md@abc123def`).
Remote imports are automatically cached in `.github/aw/imports/` by commit SHA. This enables offline workflow compilation once imports have been downloaded. The cache is shared across different refs pointing to the same commit, reducing redundant downloads.
See [Imports Reference](/gh-aw/reference/imports/) for path formats, merge semantics, and field-specific behavior.
## Importing Agent Files
[Section titled “Importing Agent Files”](#importing-agent-files)
Agent files provide specialized AI instructions and behavior. See [Importing Copilot Copilot Agent Files](/gh-aw/reference/copilot-custom-agents/) for details on creating and importing agent files from external repositories.
## Example: Modular Workflow with Imports
[Section titled “Example: Modular Workflow with Imports”](#example-modular-workflow-with-imports)
Create a shared Model Context Protocol (MCP) server configuration in `.github/workflows/shared/mcp/tavily.md`:
```yaml
---
mcp-servers:
tavily:
url: "https://mcp.tavily.com/mcp/?tavilyApiKey=${{ secrets.TAVILY_API_KEY }}"
allowed: ["*"]
network:
allowed:
- mcp.tavily.com
---
```
Reference it in your workflow to include the Tavily MCP server alongside other tools:
```yaml
---
on:
issues:
types: [opened]
imports:
- shared/mcp/tavily.md
tools:
github:
toolsets: [issues]
permissions:
contents: read
---
# Research Agent
Perform web research using Tavily and respond to issues.
```
**Result**: The compiled workflow includes both the Tavily MCP server from the import and the GitHub tools from the main workflow, with network permissions automatically merged to allow access to both `mcp.tavily.com` and GitHub API endpoints.
## Best Practices
[Section titled “Best Practices”](#best-practices)
Use semantic versioning for stable workflows and agent files, branches for development, and commit SHAs for immutability.
**Related:** [CLI Commands](/gh-aw/setup/cli/) | [Workflow Structure](/gh-aw/reference/workflow-structure/) | [Frontmatter](/gh-aw/reference/frontmatter/) | [Imports](/gh-aw/reference/imports/) | [Copilot Agent Files](/gh-aw/reference/copilot-custom-agents/)
# Self-Hosted Runners
> How to configure and run agentic workflows on self-hosted runners, ARC/Kubernetes, and GHES environments.
Use the `runs-on` frontmatter field to target a self-hosted runner instead of the default `ubuntu-latest`.
Runners must be Linux with Docker support. macOS and Windows are not supported.
Self-hosted runners must allow `sudo` for agentic workflows. This is a requirement to allow all GH-AW security features to be enabled. Specific technical needs are:
* AWF (Agentic Workflow Firewall) applies host-level `iptables` rules to the Linux kernel `DOCKER-USER` chain to enforce network egress filtering for all agent containers on the AWF bridge network. This outer security boundary requires root UID.
* Container-level `iptables`, Squid proxy ACLs, and capability drops add additional defense in depth, but they do not replace host-level filtering.
For these reasons, a non-sudo mode is not supported, including ARC configurations with `allowPrivilegeEscalation: false`.
## ARC with Docker-in-Docker (DinD)
[Section titled “ARC with Docker-in-Docker (DinD)”](#arc-with-docker-in-docker-dind)
Actions Runner Controller (ARC) deployments that use a Docker-in-Docker sidecar split the runner container and the Docker daemon container across separate filesystems, so bind mounts constructed from the runner’s perspective fail inside the daemon.
`gh aw compile` emits a runtime probe in generated workflows that inspects `DOCKER_HOST` and appends `--docker-host-path-prefix /tmp/gh-aw` to the AWF invocation when the value matches `tcp://localhost:` or `tcp://127.0.0.1:`. No workflow-level configuration is required.
The probe is gated on AWF `v0.25.43` or newer. Workflows pinned to an older AWF version, or running on GitHub-hosted runners (where `DOCKER_HOST` is unset or points at a Unix socket), are unaffected.
## runs-on formats
[Section titled “runs-on formats”](#runs-on-formats)
**String** — single runner label:
```aw
---
on: issues
runs-on: self-hosted
---
```
**Array** — runner must have *all* listed labels (logical AND):
```aw
---
on: issues
runs-on: [self-hosted, linux, x64]
---
```
**Object** — named runner group, optionally filtered by labels:
```aw
---
on: issues
runs-on:
group: my-runner-group
labels: [linux, x64]
---
```
## Sharing configuration via imports
[Section titled “Sharing configuration via imports”](#sharing-configuration-via-imports)
`runs-on` must be set in each workflow — it is not merged from imports. Other settings like `network` and `tools` can be shared:
.github/workflows/shared/runner-config.md
```aw
---
network:
allowed:
- defaults
- private-registry.example.com
tools:
bash: {}
---
```
```aw
---
on: issues
imports:
- shared/runner-config.md
runs-on: [self-hosted, linux, x64]
---
Triage this issue.
```
## Configuring the detection job runner
[Section titled “Configuring the detection job runner”](#configuring-the-detection-job-runner)
When [threat detection](/gh-aw/reference/threat-detection/) is enabled, the detection job runs on the agent job’s runner by default. Override it with `safe-outputs.threat-detection.runs-on`:
```aw
---
on: issues
runs-on: [self-hosted, linux, x64]
safe-outputs:
create-issue: {}
threat-detection:
runs-on: ubuntu-latest
---
```
This is useful when your self-hosted runner lacks outbound internet access for AI detection, or when you want to run the detection job on a cheaper runner.
## Configuring the framework job runner
[Section titled “Configuring the framework job runner”](#configuring-the-framework-job-runner)
Framework jobs — activation, pre-activation, safe-outputs, unlock, APM, update\_cache\_memory, and push\_repo\_memory — default to `ubuntu-slim`. Use `runs-on-slim:` to override all of them at once:
```aw
---
on: issues
runs-on: [self-hosted, linux, x64]
runs-on-slim: self-hosted
safe-outputs:
create-issue: {}
---
```
Note
`runs-on` controls only the main agent job. `runs-on-slim` controls all framework/generated jobs. `safe-outputs.runs-on` still takes precedence over `runs-on-slim` for safe-output jobs specifically.
## Configuring the maintenance workflow runner
[Section titled “Configuring the maintenance workflow runner”](#configuring-the-maintenance-workflow-runner)
The generated `agentics-maintenance.yml` workflow defaults to `ubuntu-slim` for all its jobs. To use a self-hosted runner for maintenance jobs, set `runs_on` in `.github/workflows/aw.json`:
**Single label:**
```json
{
"maintenance": {
"runs_on": "self-hosted"
}
}
```
**Multiple labels** (runner must match all):
```json
{
"maintenance": {
"runs_on": ["self-hosted", "linux", "x64"]
}
}
```
This setting applies to every job in `agentics-maintenance.yml` (close-expired-entities, cleanup-cache-memory, run\_operation, apply\_safe\_outputs, create\_labels, validate\_workflows, and activity\_report). Re-run `gh aw compile` after changing `aw.json` to regenerate the workflow.
Note
`aw.json` is separate from individual workflow frontmatter. It provides repository-level settings for generated infrastructure workflows.
## Related documentation
[Section titled “Related documentation”](#related-documentation)
* [Frontmatter](/gh-aw/reference/frontmatter/#run-configuration-run-name-runs-on-runs-on-slim-timeout-minutes) — `runs-on` and `runs-on-slim` syntax reference
* [Imports](/gh-aw/reference/imports/) — importable fields and merge semantics
* [Threat Detection](/gh-aw/reference/threat-detection/) — detection job configuration
* [Network Access](/gh-aw/reference/network/) — configuring outbound network permissions
* [Sandbox](/gh-aw/reference/sandbox/) — container and Docker requirements
* [Ephemerals](/gh-aw/reference/ephemerals/#maintenance-configuration) — full `aw.json` maintenance configuration reference
* [Enterprise Configuration](/gh-aw/reference/enterprise-configuration/) — custom API endpoints for GHEC/GHES
## Runner environment requirements
[Section titled “Runner environment requirements”](#runner-environment-requirements)
Self-hosted runners must meet these requirements for agentic workflows to run reliably.
### Docker
[Section titled “Docker”](#docker)
A working Docker daemon is required. The MCP gateway and sandbox run as containers.
* **Unix socket**: Docker must be accessible via a Unix socket (typically `/var/run/docker.sock`). If `DOCKER_HOST` is unset, the gateway mounts `/var/run/docker.sock`. If `DOCKER_HOST` is `unix://...` or a bare absolute path, the gateway mounts that socket path. Other schemes (for example `tcp://...`) are ignored for mounts and default back to `/var/run/docker.sock`.
* **Docker group**: The runner user must be in the `docker` group, or the socket must be world-readable.
* **ARC/Kubernetes**: If using [actions-runner-controller](https://github.com/actions/actions-runner-controller) with Docker-in-Docker (dind), the dind sidecar must share the Docker socket via an `emptyDir` volume. The gateway will retry the socket check for up to 10 seconds to handle startup race conditions.
### Filesystem
[Section titled “Filesystem”](#filesystem)
* **Use `RUNNER_TEMP` for transient state.** Put sandbox state, tool downloads, and intermediate outputs in `$RUNNER_TEMP`, which is cleaned between jobs. On shared runners, avoid writing arbitrary workflow data to `/tmp` because it can persist across jobs. The `/tmp/gh-aw` prefix is reserved for gh-aw/AWF ARC DinD path rewriting. `actions/setup` resets `/tmp/gh-aw` at job start, and your normal runner `/tmp` cleanup policy should handle stale data from interrupted jobs.
* **No root or sudo assumption.** The runner user may not have root or `sudo` access (except for the initial iptables setup, which requires `sudo`). Tool installs, file operations, and sandbox setup should work as the unprivileged runner user.
* **No global installs.** Do not install packages to `/usr/local/`, `/opt/hostedtoolcache/`, or other system-wide paths. These may be read-only, shared across runners, or bind-mounted read-only inside the sandbox. Use job-scoped writable locations instead.
* **No hardcoded `HOME` paths.** The runner’s home directory may not be `/home/runner`. Use `$HOME` or `$RUNNER_TEMP` instead of hardcoded paths.
### Post-job cleanup
[Section titled “Post-job cleanup”](#post-job-cleanup)
Self-hosted runners persist between jobs. Agentic workflows should clean up after themselves:
* Files written to `$RUNNER_TEMP` are automatically cleaned.
* Docker containers on the `awf-net` bridge are stopped and removed by the sandbox teardown.
* If your workflow creates files outside `$RUNNER_TEMP` (e.g. in `$GITHUB_WORKSPACE`), the runner’s built-in workspace cleanup handles this.
### Network
[Section titled “Network”](#network)
Self-hosted runners need outbound HTTPS access to:
* `api.githubcopilot.com` (or your enterprise Copilot endpoint)
* `github.com` (or your GHES instance)
* `ghcr.io` (to pull the MCP gateway container image)
* Any domains listed in your workflow’s `network.allowed` configuration
## GHES (GitHub Enterprise Server)
[Section titled “GHES (GitHub Enterprise Server)”](#ghes-github-enterprise-server)
Agentic workflows can run on GHES with some additional configuration.
### Artifact compatibility
[Section titled “Artifact compatibility”](#artifact-compatibility)
GHES does not support the `@actions/artifact` v2.0.0+ backend used by `upload-artifact@v4+` and `download-artifact@v4+`. Compiled workflows use the latest artifact action versions by default, which fail on GHES with `GHESNotSupportedError`.
Enable GHES compatibility mode in `.github/workflows/aw.json` to use compatible v3.x artifact actions:
```json
{
"ghes": true
}
```
Or compile with `--ghes` for one-off workflow generation:
```bash
gh aw compile --ghes my-workflow.md
```
This makes the compiler emit `upload-artifact@v3.2.2` and `download-artifact@v3.1.0` instead of the latest versions, which are compatible with all GHES versions.
### API endpoint
[Section titled “API endpoint”](#api-endpoint)
GHES instances need the `api-target` engine configuration. See [Enterprise Configuration](/gh-aw/reference/enterprise-configuration/) for full setup instructions.
```aw
---
engine:
id: copilot
api-target: api.enterprise.githubcopilot.com
network:
allowed:
- defaults
- github.company.com
- api.enterprise.githubcopilot.com
---
```
## ARC (Actions Runner Controller)
[Section titled “ARC (Actions Runner Controller)”](#arc-actions-runner-controller)
When running on [ARC](https://github.com/actions/actions-runner-controller) with Kubernetes:
### Docker-in-Docker (dind) sidecar
[Section titled “Docker-in-Docker (dind) sidecar”](#docker-in-docker-dind-sidecar)
The standard ARC dind pattern with a shared `emptyDir` for the Docker socket is supported. The MCP gateway:
1. Resolves the Docker socket path from `DOCKER_HOST` (supports `unix://` paths and bare absolute paths)
2. Auto-detects the socket’s group ID for correct permissions
3. Retries the socket check for up to 10 seconds to handle the race condition where the gateway starts before `dockerd`
### Pod security
[Section titled “Pod security”](#pod-security)
The runner pod requires `privileged: true` on both the dind sidecar and the runner container. This is needed for:
* `dockerd` in the dind sidecar
* `iptables` rules for the agentic workflow firewall
* Chroot/sandbox setup in the runner container
# Upgrading Agentic Workflows
> Step-by-step guide to upgrade your repository to the latest version of agentic workflows, including updating extensions, applying codemods, compiling workflows, and validating changes.
This guide walks you through upgrading agentic workflows. `gh aw upgrade` handles the full process: updating the dispatcher agent file, migrating deprecated workflow syntax, and recompiling all workflows.
Tip
Quick Upgrade
For most users, upgrading is a single command:
```bash
gh aw upgrade
```
This updates agent files, applies codemods, and compiles all workflows.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
Before upgrading, ensure you have GitHub CLI (`gh`) v2.0.0+, the latest gh-aw extension, and a clean working directory in your Git repository. Verify with `gh --version`, `gh extension list | grep gh-aw`, and `git status`.
Create a backup branch before upgrading so you can recover if something goes wrong:
```bash
git checkout -b backup-before-upgrade
git checkout - # return to your previous branch
```
## Step 1: Upgrade the Extension
[Section titled “Step 1: Upgrade the Extension”](#step-1-upgrade-the-extension)
Upgrade the `gh aw` extension to get the latest features and codemods:
```bash
gh extension upgrade gh-aw
```
Check your version with `gh aw version` and compare against the [latest release](https://github.com/github/gh-aw/releases). If you encounter issues, try a clean reinstall with `gh extension remove gh-aw` followed by `gh extension install github/gh-aw`.
## Step 2: Run the Upgrade Command
[Section titled “Step 2: Run the Upgrade Command”](#step-2-run-the-upgrade-command)
Run the upgrade command from your repository root:
```bash
gh aw upgrade
```
This command performs three main operations:
### 2.1 Updates Dispatcher Agent File
[Section titled “2.1 Updates Dispatcher Agent File”](#21-updates-dispatcher-agent-file)
Updates `.github/agents/agentic-workflows.agent.md` to the latest template. Workflow prompt files (`.github/aw/*.md`) are resolved directly from GitHub by the agent — they’re no longer managed by the CLI.
### 2.2 Applies Codemods to All Workflows
[Section titled “2.2 Applies Codemods to All Workflows”](#22-applies-codemods-to-all-workflows)
The upgrade automatically applies codemods to fix deprecated fields in all workflow files (`.github/workflows/*.md`).
### 2.3 Compiles All Workflows
[Section titled “2.3 Compiles All Workflows”](#23-compiles-all-workflows)
The upgrade automatically compiles all workflows to generate or update `.lock.yml` files, ensuring they’re ready to run in GitHub Actions.
### Command Options
[Section titled “Command Options”](#command-options)
```bash
gh aw upgrade # updates agent files + codemods + compiles
gh aw upgrade -v # verbose output
gh aw upgrade --no-fix # skip codemods and compilation
gh aw upgrade --dir custom/workflows
```
## Step 3: Review the Changes
[Section titled “Step 3: Review the Changes”](#step-3-review-the-changes)
Run `git diff .github/workflows/` to verify the changes. Typical migrations include `sandbox: false` → `sandbox.agent: false`, `app:` → `github-app:`, `safe-inputs:` → `mcp-scripts:`, `daily at` → `daily around`, and removal of deprecated `network.firewall` and `mcp-scripts.mode` fields.
## Step 4: Commit and Push
[Section titled “Step 4: Commit and Push”](#step-4-commit-and-push)
Stage and commit your changes:
```bash
git add .github/workflows/ .github/agents/
git commit -m "Upgrade agentic workflows to latest version"
git push origin main
```
Always commit both `.md` and `.lock.yml` files together.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
**Extension upgrade fails:** Try a clean reinstall with `gh extension remove gh-aw && gh extension install github/gh-aw`.
**Codemods not applied:** Manually apply with `gh aw fix --write -v`.
**Compilation errors:** Review errors with `gh aw compile my-workflow --validate` and fix YAML syntax in source files.
**Workflows not running:** Verify `.lock.yml` files are committed, check status with `gh aw status`, and confirm secrets are valid with `gh aw secrets bootstrap`.
**Breaking changes:** Revert with `git checkout backup-before-upgrade` and review [release notes](https://github.com/github/gh-aw/releases).
## Advanced Topics
[Section titled “Advanced Topics”](#advanced-topics)
**Upgrading across versions:** Review the [changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md) for cumulative changes when upgrading across multiple releases.
See the [troubleshooting guide](/gh-aw/troubleshooting/common-issues/) if you run into issues.
# Security Architecture
> Comprehensive security architecture overview for GitHub Agentic Workflows, including defense-in-depth mechanisms against rogue MCP servers and malicious agents.
GitHub Agentic Workflows implements a defense-in-depth security architecture that protects against untrusted Model Context Protocol (MCP) servers and compromised agents. This document provides an overview of our security model and visual diagrams of the key components.
## Security Model
[Section titled “Security Model”](#security-model)
Agentic Workflows (AW) adopts a layered approach that combines substrate-enforced isolation, declarative specification, and staged execution. Each layer enforces distinct security properties under different assumptions and constrains the impact of failures above it.
### Threat Model
[Section titled “Threat Model”](#threat-model)
We consider an adversary that may compromise untrusted user-level components, e.g., containers, and may cause them to behave arbitrarily within the privileges granted to them. The adversary may attempt to:
* Access or corrupt the memory or state of other components
* Communicate over unintended channels
* Abuse legitimate channels to perform unintended actions
* Confuse higher-level control logic by deviating from expected workflows
We assume the adversary does not compromise the underlying hardware or cryptographic primitives. Attacks exploiting side channels and covert channels are also out of scope.
***
### Layer 1: Substrate-Level Trust
[Section titled “Layer 1: Substrate-Level Trust”](#layer-1-substrate-level-trust)
AWs run on a GitHub Actions runner virtual machine (VM) and trust Actions’ hardware and kernel-level enforcement mechanisms, including the CPU, MMU, kernel, and container runtime. AWs also rely on three privileged containers: (1) a network firewall that is trusted to configure connectivity for other components via `iptables` and launch the agent container, (2) an API proxy that routes model traffic and may hold endpoint-specific credentials or routing configuration for supported engines, and (3) an MCP Gateway that is trusted to configure and spawn isolated MCP-server containers. Collectively, the substrate level ensures memory isolation between components, CPU and resource isolation, mediation of privileged operations and system calls, and explicit, kernel-enforced communication boundaries. These guarantees hold even if an untrusted user-level component is fully compromised and executes arbitrary code. Trust violations at the substrate level require vulnerabilities in the firewall, MCP Gateway, container runtime, kernel, hypervisor, or hardware. If this layer fails, higher-level security guarantees may not hold.
***
### Layer 2: Configuration-Level Trust
[Section titled “Layer 2: Configuration-Level Trust”](#layer-2-configuration-level-trust)
AW trusts declarative configuration artifacts, e.g., Action steps, network-firewall policies, MCP server configurations, and the toolchains that interpret them to correctly instantiate system structure and connectivity. The configuration level constrains which components are loaded, how components are connected, which communication channels are permitted, and what component privileges are assigned. Externally minted authentication tokens, e.g., agent API keys and GitHub access tokens, are a critical configuration input and are treated as imported capabilities that bound components’ external effects; declarative configuration controls their distribution, e.g., which tokens are loaded into which containers. Security violations arise due to misconfigurations, overly permissive specifications, and limitations of the declarative model. This layer defines what components exist and how they communicate, but it does not constrain how components use those channels over time.
***
### Layer 3: Plan-Level Trust
[Section titled “Layer 3: Plan-Level Trust”](#layer-3-plan-level-trust)
AW additionally relies on plan-level trust to constrain component behavior over time. At this layer, the trusted compiler decomposes a workflow into stages. For each stage, the plan specifies (1) which components are active and their permissions, (2) the data produced by the stage, and (3) how that data may be consumed by subsequent stages. In particular, plan-level trust ensures that important external side effects are explicit and undergo thorough vetting.
A primary instantiation of plan-level trust is the **SafeOutputs** subsystem. SafeOutputs is a set of trusted components that operate on external state. An agent can interact with read-only MCP servers, e.g., the GitHub MCP server, but externalized writes, such as creating GitHub pull requests, are buffered as artifacts by SafeOutputs rather than applied immediately. When the agent finishes, SafeOutputs’ buffered artifacts can be processed by a deterministic sequence of filters and analyses defined by configuration. These checks can include structural constraints, e.g., limiting the number of pull requests, policy enforcement, and automated sanitization to ensure that sensitive information such as authentication tokens are not exported. These filtered and transformed artifacts are passed to a subsequent stage in which they are externalized.
Security violations at the planning layer arise from incorrect plan construction, incomplete or overly permissive stage definitions, or errors in the enforcement of plan transitions. This layer does not protect against failures of substrate-level isolation or mis-allocation of permissions at credential-minting or configuration time. However, it limits the blast radius of a compromised component to the stage in which it is active and its influence on the artifacts passed to the next stage.
## Component Overview
[Section titled “Component Overview”](#component-overview)
The security architecture operates across multiple layers: compilation-time validation, runtime isolation, permission separation, network controls, and output sanitization. The following diagram illustrates the relationships between these components and the flow of data through the system.
```
flowchart TB
subgraph Input[" Input Layer"]
WF[/"Workflow (.md)"/]
IMPORTS[/"Imports & Includes"/]
EVENT[/"GitHub Event (Issue, PR, Comment)"/]
end
subgraph Compile[" Compilation-Time Security"]
SCHEMA["Schema Validation"]
EXPR["Expression Safety Check"]
PIN["Action SHA Pinning"]
SCAN["Security Scanners (actionlint, zizmor, poutine)"]
end
subgraph Runtime[" Runtime Security"]
PRE["Pre-Activation Role & Permission Checks"]
ACT["Activation Content Sanitization"]
AGENT["Agent Execution Read-Only Permissions"]
REDACT_MAIN["Secret Redaction Credential Protection"]
end
subgraph Isolation[" Isolation Layer"]
AWF["Agent Workflow Firewall Network Egress Control"]
PROXY["API Proxy Agent auth-token isolation"]
MCP["MCP Server Sandboxing Container Isolation"]
TOOL["Tool Allowlisting Explicit Permissions"]
end
subgraph Output[" Output Security"]
DETECT["Threat Detection AI-Powered Analysis"]
SAFE["Safe Outputs Permission Separation"]
SANITIZE["Output Sanitization Content Validation"]
end
subgraph Result["✓ Controlled Actions"]
ISSUE["Create Issue"]
PR["Create PR"]
COMMENT["Add Comment"]
end
WF --> SCHEMA
IMPORTS --> SCHEMA
SCHEMA --> EXPR
EXPR --> PIN
PIN --> SCAN
SCAN -->|".lock.yml"| PRE
EVENT --> ACT
PRE --> ACT
ACT --> AGENT
AGENT <--> AWF
AGENT <--> PROXY
AGENT <--> MCP
AGENT <--> TOOL
AGENT --> REDACT_MAIN
REDACT_MAIN --> DETECT
DETECT --> SAFE
SAFE --> SANITIZE
SANITIZE --> ISSUE
SANITIZE --> PR
SANITIZE --> COMMENT
```
## Safe Outputs: Permission Isolation
[Section titled “Safe Outputs: Permission Isolation”](#safe-outputs-permission-isolation)
The SafeOutputs subsystem enforces permission isolation by ensuring that agent execution never has direct write access to external state. The agent job runs with minimal read-only permissions, while write operations are deferred to separate jobs that execute only after the agent completes. This separation ensures that even a fully compromised agent cannot directly modify repository state.
```
flowchart LR
subgraph AgentJob["Agent Job Read-Only Permissions"]
AGENT["AI Agent Execution"]
OUTPUT[/"agent_output.json (Artifact)"/]
AGENT --> OUTPUT
end
subgraph Detection["Threat Detection Job"]
ANALYZE["Analyze for: • Secret Leaks • Malicious Patches"]
end
subgraph SafeJobs["Safe Output Jobs Write Permissions (Scoped)"]
direction TB
ISSUE["create_issue issues: write"]
COMMENT["add_comment issues: write"]
PR["create_pull_request contents: write pull-requests: write"]
LABEL["add_labels issues: write"]
end
subgraph GitHub["GitHub API"]
API["GitHub REST/GraphQL API"]
end
OUTPUT -->|"Download Artifact"| ANALYZE
ANALYZE -->|"✓ Approved"| SafeJobs
ANALYZE -->|"✗ Blocked"| BLOCKED["Workflow Fails"]
ISSUE --> API
COMMENT --> API
PR --> API
LABEL --> API
```
Tip
The SafeOutputs subsystem provides security by design: the agent never requires write permissions because all write operations are performed by separate, validated jobs with minimal scoped permissions.
## Agent Workflow Firewall (AWF)
[Section titled “Agent Workflow Firewall (AWF)”](#agent-workflow-firewall-awf)
The Agent Workflow Firewall (AWF) containerizes the agent, binds it to a Docker network, and uses iptables to redirect HTTP/HTTPS traffic through a Squid proxy container. The Squid proxy controls the agent’s egress traffic via a configurable domain allowlist to prevent data exfiltration and restrict compromised agents to permitted domains. The AWF setup process drops its iptables capabilities before launching the agent.
Containerizing an agent improves security by limiting its access to the host, but this may come at a cost. In particular, many coding agents expect full access to the host and break if containerized naively. To support agents that need more access to the host, AWF provides a more permissive ‘chroot mode’ that mounts a subset of host system directories read-only under ‘/host’, mounts the host’s HOME and ‘/tmp’ directories read-write, imports a subset of host environment variables like USER and PATH, and then launches the agent in a ‘/host’ chroot jail. This allows the agent to safely use host-installed binaries (Python, Node.js, Go, etc.) from their normal paths, while controlling access to the host network, environment variables, and other sensitive resources.
Thus, AWF separates two concerns:
* **Filesystem**: Controlled access to host binaries and runtimes via chroot
* **Network**: All traffic routed through proxy enforcing the domain allowlist
```
flowchart TB
subgraph Agent["AI Agent Process"]
COPILOT["Agent CLI"]
WEB["WebFetch Tool"]
SEARCH["WebSearch Tool"]
end
subgraph Firewall["Agent Workflow Firewall (AWF)"]
WRAP["Process Wrapper"]
ALLOW["Domain Allowlist"]
LOG["Activity Logging"]
WRAP --> ALLOW
ALLOW --> LOG
end
subgraph Network["Network Layer"]
direction TB
ALLOWED_OUT["✓ Allowed Domains"]
BLOCKED_OUT["✗ Blocked Domains"]
end
subgraph Ecosystems["Ecosystem Bundles"]
direction TB
DEFAULTS["defaults certificates, JSON schema"]
PYTHON["python PyPI, Conda"]
NODE["node npm, npmjs.com"]
CUSTOM["Custom Domains api.example.com"]
end
COPILOT --> WRAP
WEB --> WRAP
SEARCH --> WRAP
ALLOW --> ALLOWED_OUT
ALLOW --> BLOCKED_OUT
DEFAULTS --> ALLOW
PYTHON --> ALLOW
NODE --> ALLOW
CUSTOM --> ALLOW
ALLOWED_OUT --> INTERNET[" Internet"]
BLOCKED_OUT --> DROP[" Dropped"]
```
**Configuration Example:**
```yaml
engine: copilot
network:
firewall: true
allowed:
- defaults # Basic infrastructure
- python # PyPI ecosystem
- node # npm ecosystem
- "api.example.com" # Custom domain
```
## MCP Gateway and Firewall Integration
[Section titled “MCP Gateway and Firewall Integration”](#mcp-gateway-and-firewall-integration)
When the MCP gateway is enabled, it operates in conjunction with AWF to ensure that MCP traffic remains contained within trusted boundaries. The gateway spawns isolated containers for MCP servers while AWF mediates all network egress, ensuring that agent-to-server communication traverses only approved channels.
```
flowchart LR
subgraph Host["Host machine"]
GATEWAY["gh-aw-mcpg\nDocker container\nHost port 80 maps to container port 8000"]
GH_MCP["GitHub MCP Server\nspawned via Docker socket"]
GATEWAY -->|"spawns"| GH_MCP
end
subgraph AWFNet["AWF network namespace"]
AGENT["Agent container\nAgent CLI + MCP client\n172.30.0.20"]
PROXY["Squid proxy\n172.30.0.10"]
end
AGENT -->|"CONNECT host.docker.internal:80"| PROXY
PROXY -->|"allowed domain\n(host.docker.internal)"| GATEWAY
GATEWAY -->|"forwards to"| GH_MCP
```
**Architecture Summary**
1. AWF establishes an isolated network with a Squid proxy that enforces the workflow `network.allowed` list.
2. The agent container can only egress through Squid. To reach the gateway, it uses `host.docker.internal:80` (Docker’s host alias). This hostname must be included in the firewall’s allowed list.
3. The `gh-aw-mcpg` container publishes host port 80 mapped to container port 8000. It uses the Docker socket to spawn MCP server containers.
4. All MCP traffic remains within the host boundary: AWF restricts egress, and the gateway routes requests to sandboxed MCP servers.
5. When supported by an agent, AWF creates a trusted `api-proxy` that routes model traffic on the agent’s behalf while keeping that traffic behind AWF’s network controls. This proxy should not be treated as a separate caller-authentication boundary for arbitrary code already running inside the agent container.
Caution
The MCP gateway API key that is mounted into the agent container is not a strong security boundary against a compromised or malicious agent. An agent running arbitrary code may extract the key from process memory, runtime state, or other in-container channels. Treat this key as leaked by design and rely on substrate isolation, network policy, and staged permission separation for security.
## MCP Server Sandboxing
[Section titled “MCP Server Sandboxing”](#mcp-server-sandboxing)
MCP servers execute within isolated containers, enforcing substrate-level separation between the agent and each server instance. Tool filtering at the configuration level restricts which operations each server may expose, limiting the attack surface available to a compromised agent. This isolation ensures that even if an MCP server is compromised, it cannot access the memory or state of other components.
```
flowchart TB
subgraph Agent["AI Agent"]
ENGINE["AI Engine (Copilot, Claude, Codex)"]
end
subgraph MCPLayer["MCP Server Layer"]
direction TB
subgraph GitHub["GitHub MCP"]
GH_TOOLS["Enabled Tools: • issue_read • list_commits • search_code"]
GH_BLOCKED["Blocked Tools: • delete_repository • update_branch_protection"]
end
subgraph Custom["Custom MCP (Docker)"]
CONTAINER[" Isolated Container"]
NET["Network Allowlist"]
ENV["Env Var Injection"]
end
subgraph HTTP["HTTP MCP"]
ENDPOINT["HTTPS Endpoint"]
HEADERS["Secure Headers"]
end
end
subgraph Toolfilter["Tool Filtering"]
ALLOWED["allowed: [tool1, tool2]"]
DENIED["✗ Unlisted tools blocked"]
end
ENGINE <-->|"stdio/HTTP"| GitHub
ENGINE <-->|"stdio"| CONTAINER
ENGINE <-->|"HTTP"| ENDPOINT
ALLOWED --> GH_TOOLS
ALLOWED --> GH_BLOCKED
CONTAINER --> NET
CONTAINER --> ENV
ENDPOINT --> HEADERS
```
**Isolation Properties:**
* **Container Isolation**: Custom MCP servers run in Docker containers with no shared state
* **Network Controls**: Per-container domain allowlists enforced via Squid proxy
* **Tool Allowlisting**: Explicit `allowed:` lists restrict available operations
* **Secret Injection**: Secrets are passed via environment variables, never in configuration files
## Threat Detection Pipeline
[Section titled “Threat Detection Pipeline”](#threat-detection-pipeline)
The threat detection job is a sub-stage within the SafeOutputs subsystem. After the agent job completes and its outputs are buffered as artifacts, a separate detection job downloads these artifacts and invokes a prompted AI agent to analyze them for suspicious content. This detection agent operates with a security-focused system prompt and examines the agent’s outputs, patches, and execution context. The detection job runs in isolation from the original agent and has no access to write permissions; its sole responsibility is to emit a pass/fail verdict that gates the subsequent safe output jobs.
Detection checks include identification of secret leakage, malicious code patterns, and policy violations. If the detection agent identifies threats, the workflow terminates before any writes are externalized. Workflow authors can customize detection behavior by providing additional detection prompts or integrating external security scanners.
```
flowchart TB
subgraph Input["SafeOutputs Artifacts"]
JSON[/"agent_output.json (Buffered actions)"/]
PATCH[/"aw.patch (Git diff from agent)"/]
PROMPT[/"prompt.txt (Original workflow context)"/]
end
subgraph DetectionJob["Threat Detection Job"]
direction TB
DOWNLOAD["Download artifacts"]
AGENT["Detection Agent (Security-focused prompt)"]
subgraph Checks["Analysis Targets"]
SECRETS["Secret Leaks API keys, tokens Credentials in outputs"]
MALICIOUS["Malicious Patches Backdoors, vulnerabilities Suspicious modifications"]
POLICY["Policy Violations Scope violations Unauthorized operations"]
end
CUSTOM["Custom Detection Steps"]
end
subgraph Verdict["Verdict"]
SAFE_CHECK{{"Threats Detected?"}}
end
subgraph Outcome["Outcome"]
PROCEED["✓ Safe output jobs proceed"]
BLOCK["✗ Workflow fails No writes externalized"]
end
JSON --> DOWNLOAD
PATCH --> DOWNLOAD
PROMPT --> DOWNLOAD
DOWNLOAD --> AGENT
AGENT --> Checks
Checks --> CUSTOM
CUSTOM --> SAFE_CHECK
SAFE_CHECK -->|"No"| PROCEED
SAFE_CHECK -->|"Yes"| BLOCK
```
**Detection Job Properties:**
* **Isolated Execution**: The detection agent runs in a separate job with no write permissions and no access to the original agent’s runtime state
* **Prompted Analysis**: Detection uses the same AI engine as the workflow, but with a security-focused system prompt that instructs the agent to identify threats
* **Artifact-Based**: The detection agent only sees the buffered artifacts (outputs, patches, context), not live repository state
* **Blocking Verdict**: The detection job must complete successfully and emit a “safe” verdict before any safe output jobs execute
**Detection Mechanisms:**
* **AI Detection**: Default AI-powered analysis using the workflow engine with a security-focused detection prompt
* **Custom Steps**: Integration with security scanners (Semgrep, TruffleHog, LlamaGuard) via `threat-detection.steps` configuration
* **Custom Prompts**: Domain-specific detection instructions for specialized threat models via `threat-detection.prompt` configuration
**Configuration Example:**
```yaml
threat-detection:
prompt: |
Additionally check for:
- References to internal infrastructure URLs
- Attempts to modify CI/CD configuration files
- Changes to security-sensitive files (.github/workflows, package.json scripts)
steps:
- name: Run TruffleHog
run: trufflehog filesystem /tmp/gh-aw --only-verified
- name: Run Semgrep
run: semgrep scan /tmp/gh-aw/aw.patch --config=auto
```
## Compilation-Time Security
[Section titled “Compilation-Time Security”](#compilation-time-security)
AW enforces security constraints at compilation time through schema validation, expression allowlisting, and action pinning. The trusted compiler validates declarative configuration artifacts before they are deployed, rejecting misconfigurations and overly permissive specifications. This layer constrains what components may be loaded and how they may be connected, but it does not constrain runtime behavior.
```
flowchart TB
subgraph Source["Source Files"]
MD[/"workflow.md"/]
IMPORTS[/"imports/*.md"/]
end
subgraph Validation["Schema & Expression Validation"]
SCHEMA["JSON Schema Validation • Valid frontmatter fields • Correct types & formats"]
EXPR["Expression Safety • Allowlisted expressions only • No secrets in expressions"]
end
subgraph Pinning["Action Pinning"]
SHA["SHA Resolution actions/checkout@sha # v4"]
CACHE[/"actions-lock.json (Cached SHAs)"/]
end
subgraph Scanners["Security Scanners"]
ACTIONLINT["actionlint Workflow linting (includes shellcheck & pyflakes)"]
ZIZMOR["zizmor Security vulnerabilities Privilege escalation"]
POUTINE["poutine Supply chain risks Third-party actions"]
end
subgraph Strict["Strict Mode Enforcement"]
PERMS["✗ No write permissions"]
NETWORK["✓ Explicit network config"]
WILDCARD["✗ No wildcard domains"]
DEPRECATED["✗ No deprecated fields"]
end
subgraph Output["Compilation Output"]
LOCK[/".lock.yml (Validated Workflow)"/]
ERROR["✗ Compilation Error"]
end
MD --> SCHEMA
IMPORTS --> SCHEMA
SCHEMA --> EXPR
EXPR --> SHA
SHA <--> CACHE
SHA --> ACTIONLINT
ACTIONLINT --> ZIZMOR
ZIZMOR --> POUTINE
POUTINE --> Strict
Strict -->|"All Checks Pass"| LOCK
Strict -->|"Violation Found"| ERROR
```
**Compilation Commands:**
```bash
# Generate the lock file from the workflow frontmatter, which includes schema validation,
# expression safety checks, action pinning, and security scanning
gh aw compile
# Enable added security scanners for additional validation
gh aw compile --actionlint --zizmor --poutine
```
## Content Sanitization
[Section titled “Content Sanitization”](#content-sanitization)
User-generated content is sanitized before being passed to the agent. The sanitization pipeline applies a series of transformations to normalize potentially problematic content. This mechanism operates at the activation stage boundary, ensuring that untrusted input is processed before it is passed to the agent.
```
flowchart LR
subgraph Raw["Raw Event Content"]
TITLE["Issue Title"]
BODY["Issue/PR Body"]
COMMENT["Comment Text"]
end
subgraph Sanitization["Content Sanitization Pipeline"]
direction TB
MENTIONS["@mention Neutralization @user → `@user`"]
BOTS["Bot Trigger Protection fixes #123 → `fixes #123`"]
XML["XML/HTML Tag Conversion <script> → (script)"]
URI["URI Filtering Only HTTPS from trusted domains"]
SPECIAL["Special Character Handling Unicode normalization"]
LIMIT["Content Limits 0.5MB max, 65k lines"]
CONTROL["Control Character Removal ANSI escapes stripped"]
end
subgraph Safe["Sanitized Output"]
SAFE_TEXT["needs.activation.outputs.text ✓ Safe for AI consumption"]
end
TITLE --> MENTIONS
BODY --> MENTIONS
COMMENT --> MENTIONS
MENTIONS --> BOTS
BOTS --> XML
XML --> URI
URI --> SPECIAL
SPECIAL --> LIMIT
LIMIT --> CONTROL
CONTROL --> SAFE_TEXT
```
**Sanitization Properties:**
| Mechanism | Input | Output | Protection |
| --------------------------- | ------------------ | ------------------ | --------------------------------------- |
| **@mention Neutralization** | `@user` | `` `@user` `` | Prevents unintended user notifications |
| **Bot Trigger Protection** | `fixes #123` | `` `fixes #123` `` | Prevents automatic issue linking |
| **XML/HTML Tag Conversion** | ` → (script)alert('xss')(/script)
→ (img src=x onerror=...)
→ (!-- hidden comment --)
```
## Integrity Filtering
[Section titled “Integrity Filtering”](#integrity-filtering)
Integrity filtering controls which GitHub content an agent can access during a workflow run, based on **author trust** and **merge status** rather than push access alone. The MCP gateway intercepts tool calls and filters content below the configured `min-integrity` threshold before the AI engine sees it — items from blocked users or below the minimum trust level are removed transparently.
For public repositories, `min-integrity: approved` is applied automatically — restricting content to owners, members, and collaborators — even without additional authentication. The four configurable levels (`merged`, `approved`, `unapproved`, `none`) are cumulative from most to least restrictive. Individual users can be blocked unconditionally, and trusted reviewers can promote specific items via approval labels.
See [Integrity Filtering Reference](/gh-aw/reference/integrity/) for configuration options, integrity levels, and examples.
## Secret Redaction
[Section titled “Secret Redaction”](#secret-redaction)
Before workflow artifacts are uploaded, all files in the `/tmp/gh-aw` directory are scanned for secret values and redacted. This mechanism prevents accidental credential leakage through logs, outputs, or artifacts. Secret redaction executes unconditionally (with `if: always()`), ensuring that secrets are protected even if the workflow fails at an earlier stage.
```
flowchart LR
subgraph Sources["Secret Sources"]
YAML["Workflow YAML"]
ENV["Environment Variables"]
MCP_CONF["MCP Server Config"]
end
subgraph Collection["Secret Collection"]
SCAN["Scan for secrets.* patterns"]
EXTRACT["Extract secret names: SECRET_NAME_1 SECRET_NAME_2"]
end
subgraph Redaction["Secret Redaction Step"]
direction TB
FIND["Find files in /tmp/gh-aw (.txt, .json, .log, .md, .yml)"]
MATCH["Match exact secret values"]
REPLACE["Replace with masked value: abc***** (first 3 chars + asterisks)"]
end
subgraph Output["Safe Artifacts"]
LOGS["Redacted Logs"]
JSON_OUT["Sanitized JSON"]
PROMPT["Clean Prompt Files"]
end
YAML --> SCAN
ENV --> SCAN
MCP_CONF --> SCAN
SCAN --> EXTRACT
EXTRACT --> FIND
FIND --> MATCH
MATCH --> REPLACE
REPLACE --> LOGS
REPLACE --> JSON_OUT
REPLACE --> PROMPT
```
**Redaction Properties:**
* **Automatic Detection**: Scans workflow YAML for `secrets.*` patterns and collects all secret references
* **Exact String Matching**: Uses safe string matching (not regex) to prevent injection attacks
* **Partial Visibility**: Displays first 3 characters followed by asterisks for debugging without exposing full secrets
* **Custom Masking**: Supports additional custom secret masking steps via `secret-masking:` configuration
**Configuration Example:**
```yaml
secret-masking:
steps:
- name: Redact custom patterns
run: |
find /tmp/gh-aw -type f -exec sed -i 's/password123/REDACTED/g' {} +
```
Secret redaction executes with `if: always()` to ensure secrets are never leaked, even if the workflow fails at an earlier stage.
## Job Execution Flow
[Section titled “Job Execution Flow”](#job-execution-flow)
Workflow execution follows a strict dependency order that enforces security checks at each stage boundary. The plan-level decomposition ensures that each stage has explicit inputs and outputs, and that transitions between stages are mediated by validation steps.
```
flowchart TB
subgraph PreActivation["Pre-Activation Job"]
ROLE["Role Permission Check"]
DEADLINE["Stop-After Deadline"]
SKIP["Skip-If-Match Check"]
COMMAND["Command Position Validation"]
end
subgraph Activation["Activation Job"]
CONTEXT["Prepare Workflow Context"]
SANITIZE["Sanitize Event Text"]
LOCK_CHECK["Validate Lock File"]
end
subgraph Agent["Agent Job"]
CHECKOUT["Repository Checkout"]
RUNTIME["Runtime Setup (Node.js, Python)"]
CACHE_RESTORE["Cache Restore"]
MCP_START["Start MCP Containers"]
PROMPT["Generate Prompt"]
EXECUTE["Execute AI Engine"]
REDACT[" Secret Redaction"]
UPLOAD["Upload Output Artifact"]
CACHE_SAVE["Save Cache"]
end
subgraph Detection["Detection Job"]
DOWNLOAD_DETECT["Download Artifact"]
ANALYZE["AI + Custom Analysis"]
VERDICT["Security Verdict"]
end
subgraph SafeOutputs["Safe Output Jobs"]
CREATE_ISSUE["create_issue"]
ADD_COMMENT["add_comment"]
CREATE_PR["create_pull_request"]
end
subgraph Conclusion["Conclusion Job"]
AGGREGATE["Aggregate Results"]
SUMMARY["Generate Summary"]
end
ROLE --> DEADLINE
DEADLINE --> SKIP
SKIP --> COMMAND
COMMAND -->|"✓ Pass"| CONTEXT
COMMAND -->|"✗ Fail"| SKIP_ALL["Skip All Jobs"]
CONTEXT --> SANITIZE
SANITIZE --> LOCK_CHECK
LOCK_CHECK --> CHECKOUT
CHECKOUT --> RUNTIME
RUNTIME --> CACHE_RESTORE
CACHE_RESTORE --> MCP_START
MCP_START --> PROMPT
PROMPT --> EXECUTE
EXECUTE --> REDACT
REDACT --> UPLOAD
UPLOAD --> CACHE_SAVE
CACHE_SAVE --> DOWNLOAD_DETECT
DOWNLOAD_DETECT --> ANALYZE
ANALYZE --> VERDICT
VERDICT -->|"✓ Safe"| CREATE_ISSUE
VERDICT -->|"✓ Safe"| ADD_COMMENT
VERDICT -->|"✓ Safe"| CREATE_PR
VERDICT -->|"✗ Threat"| BLOCK_ALL["Block All Safe Outputs"]
CREATE_ISSUE --> AGGREGATE
ADD_COMMENT --> AGGREGATE
CREATE_PR --> AGGREGATE
AGGREGATE --> SUMMARY
```
## Observability
[Section titled “Observability”](#observability)
AW provides comprehensive observability through GitHub Actions runs and artifacts. Workflow artifacts preserve prompts, outputs, patches, and logs for post-hoc analysis. This observability layer supports debugging, security auditing, and cost monitoring without compromising runtime isolation.
```
flowchart TB
subgraph Workflow["Workflow Execution"]
RUN["GitHub Actions Run"]
JOBS["Job Logs"]
STEPS["Step Outputs"]
end
subgraph Artifacts["Workflow Artifacts"]
AGENT_OUT[/"agent_output.json AI decisions & actions"/]
PROMPT[/"prompt.txt Generated prompts"/]
PATCH[/"aw.patch Code changes"/]
LOGS[/"engine logs Token usage & timing"/]
FIREWALL[/"firewall logs Network requests"/]
end
subgraph CLI["CLI Tools"]
AW_LOGS["gh aw logs Download & analyze runs"]
AW_AUDIT["gh aw audit Investigate failures"]
AW_STATUS["gh aw status Workflow health"]
end
subgraph Insights["Observability Insights"]
COST[" Cost Tracking Token usage per run"]
DEBUG[" Debugging Step-by-step trace"]
SECURITY[" Security Audit Network & tool access"]
PERF[" Performance Duration & bottlenecks"]
end
RUN --> JOBS
JOBS --> STEPS
STEPS --> Artifacts
AGENT_OUT --> AW_LOGS
PROMPT --> AW_LOGS
PATCH --> AW_AUDIT
LOGS --> AW_LOGS
FIREWALL --> AW_AUDIT
AW_LOGS --> COST
AW_LOGS --> PERF
AW_AUDIT --> DEBUG
AW_AUDIT --> SECURITY
AW_STATUS --> DEBUG
```
**Observability Properties:**
* **Artifact Preservation**: All workflow outputs (prompts, patches, logs) are saved as downloadable artifacts
* **Cost Monitoring**: Token usage and costs across workflow runs are tracked via `gh aw logs`
* **Failure Analysis**: Failed runs can be investigated with `gh aw audit` to examine prompts, errors, and network activity
* **Firewall Logs**: All network requests made by the agent are logged for security auditing
* **Step Summaries**: Rich markdown summaries in GitHub Actions display agent decisions and outputs
**CLI Commands for Observability:**
```bash
# Download and analyze workflow run logs
gh aw logs
# Investigate a specific workflow run
gh aw audit
# Check workflow health and status
gh aw status
```
## Security Layers Summary
[Section titled “Security Layers Summary”](#security-layers-summary)
| Layer | Mechanism | Protection Against |
| ----------------- | ----------------------------------------------- | ----------------------------------------------------------- |
| **Substrate** | GitHub Actions runner (VM, kernel, hypervisor) | Memory corruption, privilege escalation, host escape |
| **Substrate** | Docker container runtime | Process isolation bypass, shared state access |
| **Substrate** | AWF network controls (iptables) | Data exfiltration, unauthorized API calls |
| **Substrate** | MCP sandboxing (container isolation) | Container escape, unauthorized tool access |
| **Configuration** | Schema validation, expression allowlist | Invalid configurations, unauthorized expressions |
| **Configuration** | Action SHA pinning | Supply chain attacks, tag hijacking |
| **Configuration** | Security scanners (actionlint, zizmor, poutine) | Privilege escalation, misconfigurations, supply chain risks |
| **Configuration** | Pre-activation checks (role/permission) | Unauthorized users, expired workflows |
| **Plan** | Integrity filtering (`min-integrity`) | Untrusted user input, context poisoning, social engineering |
| **Plan** | Content sanitization | @mention abuse, bot triggers |
| **Plan** | Secret redaction | Credential leakage in logs/artifacts |
| **Plan** | Threat detection | Malicious patches, secret leaks |
| **Plan** | Permission separation (SafeOutputs) | Direct write access abuse |
| **Plan** | Output sanitization | Content injection, XSS |
| **Plan** | Artifact preservation, CLI tools | Debugging failures, auditing security, cost tracking |
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Integrity Filtering](/gh-aw/reference/integrity/) - Author-trust and merge-status content filtering
* [Threat Detection Guide](/gh-aw/reference/threat-detection/) - Configuring threat analysis
* [Network Permissions](/gh-aw/reference/network/) - Network access control
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - Output processing configuration
* [AI Engines](/gh-aw/reference/engines/) - Engine-specific security features
* [Compilation Process](/gh-aw/reference/compilation-process/) - Build-time security validation
* [CLI Commands](/gh-aw/setup/cli/) - Workflow management and observability tools
# How They Work
> Understanding the core concepts and architecture of GitHub Agentic Workflows, from compilation to execution
GitHub Agentic Workflows hosts coding agents in [GitHub Actions](https://docs.github.com/en/actions), to perform complex, multi-step tasks automatically. This enables [Continuous AI](https://githubnext.com/projects/continuous-ai) - systematic, automated application of AI to software collaboration.
## Workflow Structure
[Section titled “Workflow Structure”](#workflow-structure)
Each workflow contains [frontmatter](/gh-aw/reference/glossary/#frontmatter) (the YAML configuration section between `---` markers) and markdown instructions. The frontmatter defines [triggers](/gh-aw/reference/triggers/) (when the workflow runs), [permissions](/gh-aw/reference/permissions/) (what it can access), and [tools](/gh-aw/reference/tools/) (what capabilities the AI has), while the markdown contains natural language task descriptions. This declarative structure enables reliable, secure agentic programming by sandboxing AI capabilities and triggering at the right moments.
```aw
---
on: ...
permissions: ...
tools: ...
---
# Natural Language Instructions
Analyze this issue and provide helpful triage comments...
```
## AI Engines
[Section titled “AI Engines”](#ai-engines)
Workflows support **GitHub Copilot** (default), **Claude by Anthropic**, **Codex**, and **Gemini by Google**. Each [engine](/gh-aw/reference/engines/) (AI model/provider) interprets natural language instructions and executes them using configured tools and permissions.
## Tools and Model Context Protocol (MCP)
[Section titled “Tools and Model Context Protocol (MCP)”](#tools-and-model-context-protocol-mcp)
Workflows use [tools](/gh-aw/reference/tools/) through the **[Model Context Protocol](/gh-aw/reference/glossary/#mcp-model-context-protocol)** (MCP, a standardized protocol for connecting AI agents to external tools and services) for GitHub operations, external APIs, file operations, and custom integrations.
## Agentic vs. Traditional Workflows
[Section titled “Agentic vs. Traditional Workflows”](#agentic-vs-traditional-workflows)
**Traditional workflows** execute pre-programmed steps with fixed if/then logic. They do exactly what you tell them, every time, in the same way.
**[Agentic workflows](/gh-aw/reference/glossary/#agentic)** (workflows that have agency-the ability to make autonomous decisions) use AI to understand context, make decisions, and generate content by interpreting natural language instructions flexibly. They combine deterministic GitHub Actions infrastructure with AI-driven decision-making, adapting their behavior based on the specific situation they encounter.
## Security Design
[Section titled “Security Design”](#security-design)
Agentic workflows implement a defense-in-depth security architecture that protects against prompt injection, rogue MCP servers, and malicious agents. The architecture operates across multiple layers: compilation-time validation, runtime isolation, permission separation, network controls, and output sanitization.
```
flowchart LR
INPUT[" Input"] --> COMPILE[" Compile"]
COMPILE --> RUNTIME[" Runtime"]
RUNTIME --> ISOLATION[" Isolation"]
ISOLATION --> OUTPUT[" Output"]
OUTPUT --> ACTIONS["✓ Actions"]
```
Workflows run with minimal permissions (no write access by default), use tool allowlists, and process outputs through a [safety layer](/gh-aw/introduction/architecture/) before applying changes. Critical actions can require human approval. For detailed security documentation, see the [Security Architecture](/gh-aw/introduction/architecture/) page.
## MCP Scripts and Safe Outputs
[Section titled “MCP Scripts and Safe Outputs”](#mcp-scripts-and-safe-outputs)
* **[MCP Scripts](/gh-aw/reference/mcp-scripts/)** (custom inline tools) - Custom MCP tools defined inline in workflow frontmatter
* **[Safe outputs](/gh-aw/reference/safe-outputs/)** (validated GitHub operations) - Pre-approved actions the AI can request without write permissions
## Regenerating the Lock File
[Section titled “Regenerating the Lock File”](#regenerating-the-lock-file)
Use `gh aw compile` to generate [`.lock.yml` files](/gh-aw/reference/glossary/#workflow-lock-file-lockyml) from the frontmatter of the workflow `.md` files. The `.md` file is the editable source of truth, while `.lock.yml` is the compiled GitHub Actions workflow with security hardening. Commit both files.
## Continuous AI Patterns
[Section titled “Continuous AI Patterns”](#continuous-ai-patterns)
Enable [Continuous AI](https://githubnext.com/projects/continuous-ai) patterns like keeping documentation current, improving code quality incrementally, intelligently triaging issues and PRs, and automating code review.
## Best Practices
[Section titled “Best Practices”](#best-practices)
Start simple and iterate with clear, specific instructions. Test workflows using `gh aw compile --watch` and `gh aw run`, monitor costs with `gh aw logs`, and review AI-generated content before merging. Use [`safe outputs`](/gh-aw/reference/safe-outputs/) (pre-approved GitHub operations) for controlled creation of issues, comments, and PRs.
# About Workflows
> Understanding how GitHub Agentic Workflows transforms natural language into automated AI-powered workflows
## What are Agentic Workflows?
[Section titled “What are Agentic Workflows?”](#what-are-agentic-workflows)
**[Agentic workflows](/gh-aw/reference/glossary/#agentic-workflow)** are AI-powered automation that can understand context, make decisions, and take meaningful actions-all from natural language instructions you write in markdown.
Unlike traditional automation with fixed if-then rules, agentic workflows use coding agents (like Copilot CLI, Claude by Anthropic, or Codex) to:
* **Understand context**: Read your repository, issues, and pull requests to grasp the current situation
* **Make decisions**: Choose appropriate actions based on the context, not just predefined conditions
* **Adapt behavior**: Respond flexibly to different scenarios without requiring explicit programming for each case
## Coding agents, running with tools, in GitHub Actions
[Section titled “Coding agents, running with tools, in GitHub Actions”](#coding-agents-running-with-tools-in-github-actions)
With coding agents, you describe your automation needs in plain language. GitHub Agentic Workflows makes this possible by running natural language markdown files as agents in [GitHub Actions](https://github.com/features/actions) that are executed by AI coding agents (AI systems that execute your instructions).
Instead of writing intricate scripts to handle issue triage, code reviews, or release management, you simply describe what you want to happen. The AI agent understands your repository context, interprets the situation, and takes appropriate actions-all from a few lines of markdown.
Here’s a simple example:
```markdown
---
on: # Trigger: when to run
issues:
types: [opened]
permissions: read-all # Security: read-only by default
safe-outputs: # Allowed write operations
add-comment:
---
# Issue Clarifier
Analyze the current issue and ask for additional details if the issue is unclear.
```
The YAML section at the top is called [**frontmatter**](/gh-aw/reference/frontmatter/)-it configures when the workflow runs and what it can do. The markdown body contains your natural language instructions. See [Workflow Structure](/gh-aw/reference/workflow-structure/) for details.
The `gh aw compile` command this markdown file into a hardened [GitHub Actions Workflow](https://docs.github.com/en/actions/concepts/workflows-and-actions/workflows#about-workflows) [`.lock.yml` file](/gh-aw/reference/glossary/#workflow-lock-file-lockyml) (the compiled workflow that GitHub Actions runs) that embeds the frontmatter and loads the markdown body at runtime. This runs an AI agent in a containerized environment whenever a new issue is opened.
[Compilation](/gh-aw/reference/glossary/#compilation) (converting markdown to GitHub Actions YAML) validates your configuration, applies security hardening, and generates the final workflow file that GitHub Actions can execute. Think of it like compiling code-you write human-friendly markdown, the compiler produces machine-ready YAML.
The AI agent reads your repository context, understands the issue content, and takes appropriate actions - all defined in natural language rather than complex code.
Workflows use read-only permissions by default, with write operations only allowed through sanitized [`safe-outputs`](/gh-aw/reference/safe-outputs/) (validated GitHub operations) that enable creating issues, comments, and PRs without giving the AI direct write access. Access can be gated to team members only, ensuring AI agents operate within controlled boundaries.
More sample workflows can be found in the [Agentics collection](https://github.com/githubnext/agentics).
# Presentation Slides
> View the GitHub Agentic Workflows presentation slides
View the GitHub Agentic Workflows presentation slides to learn about the evolution from CI/CD to Continuous AI, how agentic workflows enable AI automation in natural language, security features, and real-world examples.
[April 7, 2026 PDF ](/gh-aw/slides/20260407-github-agentic-workflows.pdf)[February 24, 2026 PDF ](/gh-aw/slides/20260224-github-agentic-workflows.pdf)[Interactive HTML ](/gh-aw/slides/)
## Walkthroughs
[Section titled “Walkthroughs”](#walkthroughs)
### DeepResearch
[Section titled “DeepResearch”](#deepresearch)
* DeepResearch
* Discussion Miner
*
* Issue Monster
* Copilot
### Version Updater
[Section titled “Version Updater”](#version-updater)
*
* Issue Monster
* Copilot
### Workflow Skill Extractor
[Section titled “Workflow Skill Extractor”](#workflow-skill-extractor)
* Report
* Plan (Human)
* Copilot
### Agent Persona Exploration
[Section titled “Agent Persona Exploration”](#agent-persona-exploration)
* Persona Simulator
* Plan
* Issue Monster
* Copilot
### Q - the Agent optimizer
[Section titled “Q - the Agent optimizer”](#q---the-agent-optimizer)
*
*
### Multi Resolution Web Tester
[Section titled “Multi Resolution Web Tester”](#multi-resolution-web-tester)
*
*
### Release notes
[Section titled “Release notes”](#release-notes)
*
### Issues as Spec
[Section titled “Issues as Spec”](#issues-as-spec)
* Community
* Copilot
### Report Assign Fix
[Section titled “Report Assign Fix”](#report-assign-fix)
* AW self reported failure
* Copilot
# BatchOps
> Process large volumes of work in parallel or chunked batches using matrix jobs, rate-limit-aware throttling, and result aggregation
BatchOps is a pattern for processing large volumes of work items efficiently. Instead of iterating sequentially through hundreds of items in a single workflow run, BatchOps splits work into chunks, parallelizes where possible, handles partial failures gracefully, and aggregates results into a consolidated report.
```
flowchart LR
trigger([Trigger]) --> workers[Parallel batch workers]
workers --> agg[Aggregate results]
```
## When to Use BatchOps
[Section titled “When to Use BatchOps”](#when-to-use-batchops)
| Scenario | Recommendation |
| ------------------------------------- | ----------------------------------------------------------- |
| < 50 items, order matters | Sequential ([WorkQueueOps](/gh-aw/patterns/workqueue-ops/)) |
| 50–500 items, order doesn’t matter | BatchOps with chunked processing |
| > 500 items, high parallelism safe | BatchOps with matrix fan-out |
| Items have dependencies on each other | Sequential (WorkQueueOps) |
| Items are fully independent | BatchOps (any strategy) |
| Strict rate limits or quotas | Rate-limit-aware batching |
## Batch Strategy 1: Chunked Processing
[Section titled “Batch Strategy 1: Chunked Processing”](#batch-strategy-1-chunked-processing)
Split work into fixed-size pages using `GITHUB_RUN_NUMBER`. Each run processes one page, picking up the next slice on the next scheduled run. Items must have a stable sort key (creation date, issue number) so pagination is deterministic.
```
flowchart LR
run([Run N]) --> fetch[Fetch page N]
fetch --> agent[AI processes batch]
agent --> next([Run N+1, next page])
```
Example workflow:
.github/workflows/stale-processor.md
```aw
---
on:
schedule: daily on weekdays
workflow_dispatch:
tools:
github:
toolsets: [issues]
bash:
- "jq"
- "date"
safe-outputs:
add-labels:
allowed: [stale, needs-triage, archived]
max: 30
add-comment:
max: 30
steps:
- name: compute-page
id: compute-page
run: |
PAGE_SIZE=25
# Use run number mod to cycle through pages; reset every 1000 runs
PAGE=$(( (GITHUB_RUN_NUMBER % 1000) * PAGE_SIZE ))
echo "page_offset=$PAGE" >> "$GITHUB_OUTPUT"
echo "page_size=$PAGE_SIZE" >> "$GITHUB_OUTPUT"
---
# Chunked Issue Processor
This run covers offset ${{ steps.compute-page.outputs.page_offset }} with page size ${{ steps.compute-page.outputs.page_size }}.
1. List issues sorted by creation date (oldest first), skipping the first ${{ steps.compute-page.outputs.page_offset }} and taking ${{ steps.compute-page.outputs.page_size }}.
2. For each issue: add `stale` if last updated > 90 days ago with no recent comments; add `needs-triage` if it has no labels; post a stale warning comment if applicable.
3. Summarize: issues labeled, comments posted, any errors.
```
## Batch Strategy 2: Fan-Out with Matrix
[Section titled “Batch Strategy 2: Fan-Out with Matrix”](#batch-strategy-2-fan-out-with-matrix)
Use GitHub Actions matrix to run multiple batch workers in parallel, each responsible for a non-overlapping shard. Use `fail-fast: false` so one shard failure doesn’t cancel the others. Each shard gets its own token and API rate limit quota.
```
flowchart LR
trigger([Trigger]) --> s0[Shard 0]
trigger --> s1[Shard 1]
trigger --> s2[Shard 2]
```
Example workflow:
.github/workflows/batch-worker.md
```aw
---
on:
workflow_dispatch:
inputs:
total_shards:
description: "Number of parallel workers"
default: "4"
required: false
jobs:
batch:
strategy:
matrix:
shard: [0, 1, 2, 3]
fail-fast: false # Continue other shards even if one fails
tools:
github:
toolsets: [issues, pull_requests]
safe-outputs:
add-labels:
allowed: [reviewed, duplicate, wontfix]
max: 50
---
# Matrix Batch Worker — Shard ${{ matrix.shard }} of ${{ inputs.total_shards }}
Process only issues where `(issue_number % ${{ inputs.total_shards }}) == ${{ matrix.shard }}` — this ensures no two shards process the same issue.
1. List all open issues (up to 500) and keep only those assigned to this shard.
2. For each issue: check for duplicates (similar title/content); add label `reviewed`; if a duplicate is found, add `duplicate` and reference the original.
3. Report: issues in this shard, how many labeled, any failures.
```
## Batch Strategy 3: Rate-Limit-Aware Batching
[Section titled “Batch Strategy 3: Rate-Limit-Aware Batching”](#batch-strategy-3-rate-limit-aware-batching)
Throttle API calls by processing items in small sub-batches with explicit pauses. Slower than unbounded processing but dramatically reduces rate-limit errors. Use [Rate Limiting Controls](/gh-aw/reference/rate-limiting-controls/) for built-in throttling.
```
flowchart LR
trigger([Trigger]) --> batch[Process sub-batch]
batch --> pause[Pause between batches]
pause --> report[Report totals]
```
Example workflow:
.github/workflows/rate-limited-batch.md
```aw
---
on:
workflow_dispatch:
inputs:
batch_size:
description: "Items per sub-batch"
default: "10"
pause_seconds:
description: "Seconds to pause between sub-batches"
default: "30"
tools:
github:
toolsets: [repos, issues]
bash:
- "sleep"
- "jq"
safe-outputs:
add-comment:
max: 100
add-labels:
allowed: [labeled-by-bot]
max: 100
---
# Rate-Limited Batch Processor
Process all open issues in sub-batches of ${{ inputs.batch_size }}, pausing ${{ inputs.pause_seconds }} seconds between batches.
1. Fetch all open issue numbers (paginate if needed).
2. For each sub-batch: read each issue body, determine the correct label, add the label, then pause before the next sub-batch.
3. On HTTP 429: pause 60 seconds and retry once before marking the item as failed.
4. Report: total processed, failed, skipped.
```
## Batch Strategy 4: Result Aggregation
[Section titled “Batch Strategy 4: Result Aggregation”](#batch-strategy-4-result-aggregation)
Collect results from multiple batch workers or runs and aggregate them into a single summary issue. Use [cache-memory](/gh-aw/reference/cache-memory/) to store intermediate results when runs span multiple days.
```
flowchart LR
runs[Past batch runs] --> cache[cache-memory]
cache --> agent[AI agent]
agent --> issue[Update tracking issue]
```
Example workflow:
.github/workflows/batch-aggregator.md
```aw
---
on:
workflow_dispatch:
inputs:
report_issue:
description: "Issue number to aggregate results into"
required: true
tools:
cache-memory: true
github:
toolsets: [issues, repos]
bash:
- "jq"
safe-outputs:
add-comment:
max: 1
update-issue:
body: true
steps:
- name: collect-results
run: |
# Aggregate results from all result files written by previous batch runs
RESULTS_DIR="/tmp/gh-aw/cache-memory/batch-results"
if [ -d "$RESULTS_DIR" ]; then
jq -s '
{
total_processed: (map(.processed) | add // 0),
total_failed: (map(.failed) | add // 0),
total_skipped: (map(.skipped) | add // 0),
runs: length,
errors: (map(.errors // []) | add // [])
}
' "$RESULTS_DIR"/*.json > /tmp/gh-aw/cache-memory/aggregate.json
cat /tmp/gh-aw/cache-memory/aggregate.json
else
echo '{"total_processed":0,"total_failed":0,"total_skipped":0,"runs":0,"errors":[]}' \
> /tmp/gh-aw/cache-memory/aggregate.json
fi
---
# Batch Result Aggregator
Aggregate results from previous batch runs stored in `/tmp/gh-aw/cache-memory/batch-results/` into issue #${{ inputs.report_issue }}.
1. Read `/tmp/gh-aw/cache-memory/aggregate.json` for totals and each individual result file for per-run breakdowns.
2. Update issue #${{ inputs.report_issue }} body with a Markdown table: summary row (processed/failed/skipped) plus per-run breakdown. List any errors requiring manual intervention.
3. Add a comment: "Batch complete ✓" if no failures, or "Batch complete with failures !" with a list of failed items.
4. For each failed item, create a sub-issue so it can be retried.
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [WorkQueueOps](/gh-aw/patterns/workqueue-ops/) — Sequential queue processing with issue checklists, sub-issues, cache-memory, and Discussions
* [ResearchPlanAssignOps](/gh-aw/patterns/research-plan-assign-ops/) — Research → Plan → Assign for developer-supervised work
* [Cache Memory](/gh-aw/reference/cache-memory/) — Persistent state storage across workflow runs
* [Repo Memory](/gh-aw/reference/repo-memory/) — Git-committed persistent state
* [Rate Limiting Controls](/gh-aw/reference/rate-limiting-controls/) — Built-in throttling for API-heavy workflows
* [Concurrency](/gh-aw/reference/concurrency/) — Prevent overlapping batch runs
# ChatOps
> Interactive automation triggered by slash commands (/review, /deploy) in issues and PRs - human-in-the-loop workflows
ChatOps brings automation into GitHub conversations through [command triggers](/gh-aw/reference/command-triggers/) that respond to slash commands in issues, pull requests, and comments. Team members can trigger workflows by typing commands like `/review` or `/deploy` directly in discussions.
```
flowchart LR
user(["/command"]) --> auth[Auth check]
auth --> agent[AI agent]
agent --> output[Safe outputs]
```
By default, only users with write permissions can trigger ChatOps commands. Narrow or widen that with `on.roles:` — see [Repository Access Roles](/gh-aw/reference/triggers/#filtering-by-repository-access-roles-onroles-onskip-roles).
## Example: Code Reviewer
[Section titled “Example: Code Reviewer”](#example-code-reviewer)
In the following example, when someone types `/review`, the AI analyzes code changes and posts review comments. The agent runs with read-only permissions while [safe-outputs](/gh-aw/reference/safe-outputs/) (validated GitHub operations) handle write operations securely.
The example uses `events:` to restrict which comment contexts activate a command — in this case `[pull_request_comment]` to respond only in PR threads. See [Filtering Command Events](/gh-aw/reference/command-triggers/#filtering-command-events).
The example also references the triggering content via `steps.sanitized.outputs.text`, which strips injection attempts, excessive content, and untrusted mentions — see [Context Text](/gh-aw/reference/command-triggers/#context-text).
```aw
---
on:
slash_command:
name: review
events: [pull_request_comment] # Only respond to /review in PR comments
permissions:
contents: read
pull-requests: read
safe-outputs:
create-pull-request-review-comment:
max: 5
add-comment:
---
# Code Review Assistant
When someone types /review in a pull request comment, perform a thorough analysis of the changes.
Examine the diff for potential bugs, security vulnerabilities, performance implications, code style issues, and missing tests or documentation.
Create specific review comments on relevant lines of code and add a summary comment with overall observations and recommendations.
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [IssueOps](/gh-aw/patterns/issue-ops/) — Event-driven issue automation
* [DispatchOps](/gh-aw/patterns/dispatch-ops/) — Manual workflow triggers
* [LabelOps](/gh-aw/patterns/label-ops/) — Label-triggered automation
* [MultiRepoOps — Side Repository](/gh-aw/patterns/multi-repo-ops/#the-side-repository-pattern-isolated-automation) — Isolated workflow execution
* [Command Triggers](/gh-aw/reference/command-triggers/) — Slash command configuration
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Secure write operations
* [Authentication](/gh-aw/reference/auth/) — PAT and GitHub App setup
# DeterministicOps
> Combine deterministic computation and data extraction with agentic reasoning in GitHub Agentic Workflows for powerful hybrid automation.
GitHub Agentic Workflows can combine deterministic computation ([`steps:`](/gh-aw/reference/steps-jobs/#custom-steps-steps) and [`jobs:`](/gh-aw/reference/steps-jobs/#custom-jobs-jobs)) with AI reasoning, enabling hybrid agentic data preprocessing. This pattern can reliably collect and prepare data, then the AI agent reads the results and generates insights. Use this for data aggregation, report generation, trend analysis, auditing, and any hybrid pipeline.
## When to Use
[Section titled “When to Use”](#when-to-use)
Combine deterministic steps with AI agents to precompute data, filter triggers, preprocess inputs, post-process outputs, or build multi-stage computation and reasoning pipelines.
## Example: Release Highlights Generator
[Section titled “Example: Release Highlights Generator”](#example-release-highlights-generator)
This workflow generates release highlights for new tags. It uses deterministic steps to fetch structured data about the release and recent PRs, then the AI agent synthesizes this into a release summary.
When using `steps:` or `jobs:`, files placed in `/tmp/gh-aw/agent/` are automatically uploaded as artifacts and available to the AI agent.
```
flowchart TD
det[Deterministic steps] -- artifacts --> agent[AI agent]
agent -- safe-outputs --> so[Safe output jobs]
```
Example workflow:
.github/workflows/release-highlights.md
```aw
---
on:
push:
tags: ['v*.*.*']
safe-outputs:
update-release:
steps:
- run: |
gh release view "${GITHUB_REF#refs/tags/}" --json name,tagName,body > /tmp/gh-aw/agent/release.json
gh pr list --state merged --limit 100 --json number,title,labels > /tmp/gh-aw/agent/prs.json
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
---
# Release Highlights Generator
Generate release highlights for `${GITHUB_REF#refs/tags/}`. Analyze PRs in `/tmp/gh-aw/agent/prs.json`, categorize changes, and use update-release to prepend highlights to the release notes.
```
## Data Caching
[Section titled “Data Caching”](#data-caching)
For workflows that run frequently or process large datasets, use GitHub Actions caching to avoid redundant API calls:
```aw
---
cache:
- key: pr-data-${{ github.run_id }}
path: /tmp/gh-aw/pr-data
restore-keys: |
pr-data-
steps:
- name: Check cache and fetch only new data
run: |
if [ -f /tmp/gh-aw/pr-data/recent-prs.json ]; then
echo "Using cached data"
else
gh pr list --limit 100 --json ... > /tmp/gh-aw/pr-data/recent-prs.json
fi
---
```
## Deterministic Trigger Filtering
[Section titled “Deterministic Trigger Filtering”](#deterministic-trigger-filtering)
Deterministic steps can also be used for [Custom Trigger Filtering](/gh-aw/reference/triggers/#filtering-by-custom-steps-onsteps), to control whether the agentic workflow should run based on complex conditions that are easier to express in code than in workflow expressions.
## Deterministic Post-Processing
[Section titled “Deterministic Post-Processing”](#deterministic-post-processing)
[Custom Safe Outputs](/gh-aw/reference/custom-safe-outputs/) can also be used for deterministic post-processing of AI outputs.
.github/workflows/code-review\.md
```yaml
---
on:
pull_request:
types: [opened]
safe-outputs:
jobs:
format-and-notify:
description: "Format and post review"
runs-on: ubuntu-latest
inputs:
summary: {required: true, type: string}
steps:
- ...
---
# Code Review Agent
Review the pull request and use format-and-notify to post your summary.
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Pre-Activation Steps](/gh-aw/reference/triggers/#pre-activation-steps-onsteps) — Inline step injection into the pre-activation job
* [Pre-Activation Permissions](/gh-aw/reference/triggers/#pre-activation-permissions-onpermissions) — Grant additional scopes for `on.steps:` API calls
* [Custom Safe Outputs](/gh-aw/reference/custom-safe-outputs/) — Custom post-processing jobs
* [Frontmatter Reference](/gh-aw/reference/frontmatter/) — Configuration options
* [Compilation Process](/gh-aw/reference/compilation-process/) — How jobs are orchestrated
* [Imports](/gh-aw/reference/imports/) — Sharing configurations across workflows
* [Templating](/gh-aw/reference/templating/) — Using GitHub Actions expressions
# DispatchOps
> Manually trigger and test agentic workflows with custom inputs using workflow_dispatch
DispatchOps is the design pattern where workflows are designed primarily for manual execution via the GitHub Actions UI or CLI. This is used for on-demand tasks, testing, and other workflows that need human judgment about timing. The [`workflow_dispatch` trigger](/gh-aw/reference/triggers/) lets you run workflows with custom inputs whenever needed, with [safe outputs](/gh-aw/reference/safe-outputs/) handling write operations securely.
Use manual dispatch for research tasks, operational commands, testing workflows during development, debugging production issues, or any task that doesn’t fit a schedule or event trigger.
```
flowchart LR
user([Developer]) --> trigger[workflow_dispatch
with inputs]
trigger --> agent[AI agent]
agent --> outputs[Safe outputs]
```
## Example: Research Assistant
[Section titled “Example: Research Assistant”](#example-research-assistant)
This example shows a workflow with a string input and a choice input, using conditional logic to adjust behavior at runtime:
```aw
---
on:
workflow_dispatch:
inputs:
topic:
description: 'Research topic'
required: true
type: string
depth:
description: 'Analysis depth'
type: choice
options:
- brief
- detailed
default: brief
permissions:
contents: read
safe-outputs:
create-discussion:
---
# Research Assistant
Research the following topic: "${{ github.event.inputs.topic }}"
{{#if (eq github.event.inputs.depth "detailed")}}
Provide an in-depth analysis: background, key findings, trade-offs, and concrete recommendations with supporting evidence.
{{else}}
Provide a concise summary: top 3 findings and a single recommendation.
{{/if}}
```
Reference inputs with `${{ github.event.inputs.INPUT_NAME }}`. Supported types: `string`, `boolean`, `choice`, `environment`. See [Triggers Reference](/gh-aw/reference/triggers/) for full input syntax and [Templating](/gh-aw/reference/templating/) for conditionals.
## Manually Running Workflows
[Section titled “Manually Running Workflows”](#manually-running-workflows)
**From GitHub.com**: Go to the **Actions** tab, select the workflow, click **Run workflow**, fill in inputs, and confirm.
**Via CLI**:
```bash
gh aw run research --raw-field topic="quantum computing" --raw-field depth=detailed
```
```bash
gh aw run research --wait # Wait for completion
gh aw run research --ref branch # Run from a specific branch
```
See [CLI Commands](/gh-aw/setup/cli/) for the full `gh aw run` reference.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Triggers Reference](/gh-aw/reference/triggers/) — Complete `workflow_dispatch` syntax including all input types
* [Templating](/gh-aw/reference/templating/) — Expressions and conditionals in workflow prompts
* [TrialOps](/gh-aw/experimental/trial-ops/) — Testing workflows in isolation
* [CLI Commands](/gh-aw/setup/cli/) — Complete `gh aw run` reference
# IssueOps
> Automate issue triage, categorization, and responses when issues are opened - fully automated issue management
IssueOps transforms GitHub issues into automation triggers that analyze, categorize, and respond to issues automatically. Use it for auto-triage, smart routing, initial responses, and quality checks. GitHub Agentic Workflows makes this natural through [issue triggers](/gh-aw/reference/triggers/) and [safe-outputs](/gh-aw/reference/safe-outputs/) that handle automated responses securely without write permissions for the main AI job.
When issues are created, workflows activate automatically. The AI analyzes content and provides intelligent responses through automated comments.
## Example: Issue Triage Assistant
[Section titled “Example: Issue Triage Assistant”](#example-issue-triage-assistant)
This workflow responds to new issues with contextual guidance. It analyzes the title and description for bug reports needing information, feature requests to categorize, questions to answer, or potential duplicates. The AI then comments with helpful next steps or immediate assistance.
```
flowchart LR
event([Issue opened]) --> agent[AI triage]
agent --> label[Labels]
agent --> comment[Comment]
```
Example workflow:
.github/workflows/issue-triage.md
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
actions: read
safe-outputs:
add-comment:
max: 2
---
# Issue Triage Assistant
Analyze new issue content and provide helpful guidance. Examine the title and description for bug reports needing information, feature requests to categorize, questions to answer, or potential duplicates. Respond with a comment guiding next steps or providing immediate assistance.
```
This creates an intelligent triage system that responds to new issues with contextual guidance.
## Organizing Work with Sub-Issues
[Section titled “Organizing Work with Sub-Issues”](#organizing-work-with-sub-issues)
Break large work into agent-ready tasks using parent-child issue hierarchies. Create hierarchies with the `parent` field and temporary IDs, or link existing issues with `link-sub-issue`:
```aw
---
on:
command:
name: plan
safe-outputs:
create-issue:
title-prefix: "[task] "
max: 6
---
# Planning Assistant
Create a parent tracking issue, then sub-issues linked via parent field:
{"type": "create_issue", "temporary_id": "aw_abc123", "title": "Feature X", "body": "Tracking issue"}
{"type": "create_issue", "parent": "aw_abc123", "title": "Task 1", "body": "First task"}
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [ChatOps](/gh-aw/patterns/chat-ops/) — Interactive slash command automation
* [LabelOps](/gh-aw/patterns/label-ops/) — Label-triggered automation
* [WorkQueueOps](/gh-aw/patterns/workqueue-ops/) — Sequential queue processing
* [ResearchPlanAssignOps](/gh-aw/patterns/research-plan-assign-ops/) — Research → Plan → Assign
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Secure write operations
* [GitHub Tools](/gh-aw/reference/github-tools/) — GitHub API toolsets
* [Concurrency](/gh-aw/reference/concurrency/) — Prevent race conditions
* [Cache Memory](/gh-aw/reference/cache-memory/) — Persistent state across runs
# LabelOps
> Workflows triggered by label changes - automate actions when specific labels are added or removed
LabelOps uses GitHub labels as workflow triggers, metadata, and state markers. GitHub Agentic Workflows supports two distinct approaches to label-based triggers: [`label_command`](/gh-aw/reference/command-triggers/) for command-style one-shot activation, and [`names:` filtering](/gh-aw/reference/triggers/#filtering-with-labels-names) for persistent label-state awareness.
```
flowchart LR
label([Label applied]) --> cmd{label_command?}
cmd -- yes --> remove[Auto-removed]
cmd -- no --> keep[Stays on item]
```
The `label_command` trigger treats a label as a one-shot command: applying the label fires the workflow, and the label is **automatically removed** so it can be re-applied to re-trigger. This is the right choice when you want a label to mean “do this now” rather than “this item has this property.”
## Example: Deploy Preview
[Section titled “Example: Deploy Preview”](#example-deploy-preview)
This workflow triggers when a `deploy` label is applied to a pull request. It builds and deploys a preview environment, then posts the URL as a comment. The workflow runs with read-only permissions while [safe-outputs](/gh-aw/reference/safe-outputs/) handle the comment creation securely.
```
flowchart LR
apply([Apply deploy label]) --> fire[Trigger fires, label removed]
fire --> agent[AI agent]
agent --> comment[Comment with URL]
```
Example workflow:
.github/workflows/deploy-preview\.md
```aw
---
on:
label_command: deploy
permissions:
contents: read
safe-outputs:
add-comment:
max: 1
---
# Deploy Preview
A `deploy` label was applied to this pull request. Build and deploy a preview environment and post the URL as a comment.
The matched label name is available as `${{ needs.activation.outputs.label_command }}` if needed to distinguish between multiple label commands.
```
After activation the `deploy` label is removed from the pull request, so a reviewer can apply it again to trigger another deployment without any cleanup step.
The label that triggered the workflow is exposed as an output of the activation job:
```plaintext
${{ needs.activation.outputs.label_command }}
```
This is useful when a workflow handles multiple label commands and needs to branch on which one was applied.
### Combining with slash commands
[Section titled “Combining with slash commands”](#combining-with-slash-commands)
`label_command` can be combined with [`slash_command:`](/gh-aw/patterns/chat-ops/) in the same workflow. The two triggers are OR’d — the workflow activates when either condition is met:
```yaml
on:
slash_command: deploy
label_command:
name: deploy
events: [pull_request]
```
This lets a workflow be triggered both by a `/deploy` comment and by applying a `deploy` label, sharing the same agent logic.
## Label Filtering
[Section titled “Label Filtering”](#label-filtering)
Another way to relate workflows to labels is to use [Label Trigger Filtering](/gh-aw/reference/triggers/#filtering-with-labels-names) to ensure that a workflow only runs when a particular label is present on an item.
For example:
```aw
---
on:
issues:
types: [labeled]
names: [bug, critical, security]
permissions:
contents: read
actions: read
safe-outputs:
add-comment:
max: 1
---
# Critical Issue Handler
When a critical label is added to an issue, analyze the severity and provide immediate triage guidance.
Check the issue for:
- Impact scope and affected users
- Reproduction steps
- Related dependencies or systems
- Recommended priority level
Respond with a comment outlining next steps and recommended actions.
```
In this example, the workflow activates only when the `bug`, `critical`, or `security` labels are added to an issue, not for other label changes. The labels remain on the issue after the workflow runs.
## Applying and Removing Labels
[Section titled “Applying and Removing Labels”](#applying-and-removing-labels)
To let an agent apply or remove labels, use the [`add-labels`](/gh-aw/reference/safe-outputs/#add-labels-add-labels) and [`remove-labels`](/gh-aw/reference/safe-outputs/#remove-labels-remove-labels) safe outputs. Use `allowed` to restrict which labels the agent can touch:
```yaml
safe-outputs:
add-labels:
allowed: [bug, team-*, area/*] # restrict to specific labels or glob patterns
remove-labels:
allowed: [needs-triage] # agents can remove triage label after processing
```
Both operations accept glob patterns in `allowed` and `blocked`, and support cross-repository targets via `target-repo`. See the [Add Labels](/gh-aw/reference/safe-outputs/#add-labels-add-labels) and [Remove Labels](/gh-aw/reference/safe-outputs/#remove-labels-remove-labels) reference for the full set of options.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [IssueOps](/gh-aw/patterns/issue-ops/) — Issue-triggered workflows
* [ChatOps](/gh-aw/patterns/chat-ops/) — Slash command automation
* [Trigger Events](/gh-aw/reference/triggers/) — Complete trigger configuration including label filtering
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Secure write operations
* [Frontmatter Reference](/gh-aw/reference/frontmatter/) — Complete workflow configuration options
# MemoryOps
> Design patterns for using memory to build stateful workflows that track progress, share data, and compute trends
MemoryOps is a set of design patterns using [Cache Memory](/gh-aw/reference/cache-memory/) and [Repo Memory](/gh-aw/reference/repo-memory/) to persist state across workflow runs. Use memory to build workflows that record their progress, resume after interruptions, share data between workflows, incremental processing, trend analysis, multi-step tasks, and workflow coordination.
```
flowchart LR
r1([Run 1]) --> s1[memory\nstate 1]
s1 --> r2([Run 2])
r2 --> s2[memory\nstate 2]
s2 --> r3([Run 3])
```
When writing prompting for using memory, you can usually use surprisingly high level descriptions of the information to be stored. You often don’t even need to write a schema for the memory. The first run will write data with an appropriate schema, later runs will read it and see the implicit schema. The agent can adapt to changes in the schema over time as long as the data is still there. This makes memory a very flexible tool for stateful workflows without needing to define rigid schemas upfront.
## Memory Types
[Section titled “Memory Types”](#memory-types)
Two types of memory stores are available. Each has different use cases and access patterns.
[Cache Memory](/gh-aw/reference/cache-memory/) gives fast, ephemeral storage using GitHub Actions cache (7 days retention):
```yaml
tools:
cache-memory:
key: my-workflow-state
```
Use for temporary state, session data, short-term caching. The memory is available at `/tmp/gh-aw/cache-memory/`.
[Repo Memory](/gh-aw/reference/repo-memory/) gives persistent, version-controlled storage in a dedicated Git branch:
```yaml
tools:
repo-memory:
branch-name: memory/my-workflow
file-glob: ["*.json", "*.jsonl"]
```
Use for historical data, trend tracking, permanent state. By default the memory is available at `/tmp/gh-aw/repo-memory/default/`.
## Pattern 1: Exhaustive Processing
[Section titled “Pattern 1: Exhaustive Processing”](#pattern-1-exhaustive-processing)
Track progress through large datasets with todo/done lists to ensure complete coverage across multiple runs.
```markdown
Analyze all open issues in the repository. Track your progress in cache-memory
so you can resume if the workflow times out. Mark each issue as done after
processing it. Generate a final report with statistics.
```
The agent maintains a state file with items to process and completed items, updating it after each item so the workflow can resume if interrupted:
```json
{
"todo": [123, 456, 789],
"done": [101, 102],
"errors": [],
"last_run": 1705334400
}
```
Real examples: `.github/workflows/repository-quality-improver.md`, `.github/workflows/copilot-agent-analysis.md`
## Pattern 2: State Persistence
[Section titled “Pattern 2: State Persistence”](#pattern-2-state-persistence)
Save workflow checkpoints to resume long-running tasks that may timeout.
```markdown
Migrate 10,000 records from the old format to the new format. Process 500
records per run and save a checkpoint. Each run should resume from the last
checkpoint until all records are migrated.
```
The agent stores a checkpoint with the last processed position and resumes from it each run:
```json
{
"last_processed_id": 1250,
"batch_number": 13,
"total_migrated": 1250,
"status": "in_progress"
}
```
Real examples: `.github/workflows/daily-news.md`, `.github/workflows/cli-consistency-checker.md`
## Pattern 3: Shared Information
[Section titled “Pattern 3: Shared Information”](#pattern-3-shared-information)
Share data between workflows using [repo-memory](/gh-aw/reference/repo-memory/) branches. A producer workflow stores data; consumers read it using the same branch name.
*Producer workflow:*
```markdown
Every 6 hours, collect repository metrics (issues, PRs, stars) and store them
in repo-memory so other workflows can analyze the data later.
```
*Consumer workflow:*
```markdown
Load the historical metrics from repo-memory and compute weekly trends.
Generate a trend report with visualizations.
```
Both workflows reference the same branch:
```yaml
tools:
repo-memory:
branch-name: memory/shared-data
```
Real examples: `.github/workflows/metrics-collector.md` (producer), trend analysis workflows (consumers)
## Pattern 4: Data Caching
[Section titled “Pattern 4: Data Caching”](#pattern-4-data-caching)
Cache API responses to avoid rate limits and reduce workflow time. The agent checks for fresh cached data before making API calls, using suggested TTLs: repository metadata (24h), contributor lists (12h), issues/PRs (1h), workflow runs (30m).
```markdown
Fetch repository metadata and contributor lists. Cache the data for 24 hours
to avoid repeated API calls. If the cache is fresh, use it. Otherwise, fetch
new data and update the cache.
```
Real examples: `.github/workflows/daily-news.md`
## Pattern 5: Trend Computation
[Section titled “Pattern 5: Trend Computation”](#pattern-5-trend-computation)
Store time-series data and compute trends, moving averages, and statistics. The agent appends new data points to a JSON Lines history file and computes trends using Python.
```markdown
Collect daily build times and test times. Store them in repo-memory as
time-series data. Compute 7-day and 30-day moving averages. Generate trend
charts showing whether performance is improving or declining over time.
```
Real examples: `.github/workflows/daily-code-metrics.md`, `.github/workflows/shared/charts-with-trending.md`
## Pattern 6: Multiple Memory Stores
[Section titled “Pattern 6: Multiple Memory Stores”](#pattern-6-multiple-memory-stores)
Use multiple memory instances for different lifecycles — cache-memory for temporary session data, separate repo-memory branches for metrics, configuration, and archives.
```markdown
Use cache-memory for temporary API responses during this run. Store daily
metrics in one repo-memory branch for trend analysis. Keep data schemas in
another branch. Archive full snapshots in a third branch with compression.
```
```yaml
tools:
cache-memory:
key: session-data # Fast, temporary
repo-memory:
- id: metrics
branch-name: memory/metrics # Time-series data
- id: config
branch-name: memory/config # Schema and metadata
- id: archive
branch-name: memory/archive # Compressed backups
```
## Best Practices
[Section titled “Best Practices”](#best-practices)
### Use JSON Lines for Time-Series Data
[Section titled “Use JSON Lines for Time-Series Data”](#use-json-lines-for-time-series-data)
Append-only format ideal for logs and metrics:
```bash
# Append without reading entire file
echo '{"date": "2024-01-15", "value": 42}' >> data.jsonl
```
### Include Metadata
[Section titled “Include Metadata”](#include-metadata)
Document your data structure:
```json
{
"dataset": "performance-metrics",
"schema": {
"date": "YYYY-MM-DD",
"value": "integer"
},
"retention": "90 days"
}
```
### Implement Data Rotation
[Section titled “Implement Data Rotation”](#implement-data-rotation)
Prevent unbounded growth:
```bash
# Keep only last 90 entries
tail -n 90 history.jsonl > history-trimmed.jsonl
mv history-trimmed.jsonl history.jsonl
```
## Security Considerations
[Section titled “Security Considerations”](#security-considerations)
Memory stores are visible to anyone with repository access. Never store credentials, API tokens, PII, or secrets — only aggregate statistics and anonymized data.
```bash
# ✓ GOOD - Aggregate statistics
echo '{"open_issues": 42}' > metrics.json
# ✗ BAD - Individual user data
echo '{"user": "alice", "email": "alice@example.com"}' > users.json
```
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
**Cache not persisting**: Verify cache key is consistent across runs
**Repo memory not updating**: Check `file-glob` patterns match your files and files are within `max-file-size` limit
**Out of memory errors**: Process data in chunks instead of loading entirely, implement data rotation
**Merge conflicts**: Use JSON Lines format (append-only), separate branches per workflow, or add run ID to filenames
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Cache Memory](/gh-aw/reference/cache-memory/) — Full cache-memory reference
* [Repository Memory](/gh-aw/reference/repo-memory/) — Full repo-memory reference
* [MCP Servers](/gh-aw/guides/mcps/) - Memory MCP server configuration
* [DeterministicOps](/gh-aw/patterns/deterministic-ops/) - Data preprocessing and extraction
* [Safe Outputs](/gh-aw/reference/custom-safe-outputs/) - Storing workflow outputs
* [Frontmatter Reference](/gh-aw/reference/frontmatter/) - Configuration options
# MonitorOps
> Monitor agentic workflows across a repository, publish observability reports, and escalate recurring failures or waste.
Use this pattern when you want a scheduled workflow to inspect other agentic workflows using [workflow logs and auditing](/gh-aw/reference/audit/), summarize what happened, and escalate unusual cost or failure patterns.
The [agentic-ops repository](https://github.com/githubnext/agentic-ops) provides the reference implementation for this approach.
```
flowchart LR
schedule([Schedule]) --> analyze[Analyze workflow logs]
analyze --> report[Report to Discussion]
analyze --> escalate[Escalate failures to issue]
```
## What this pattern does
[Section titled “What this pattern does”](#what-this-pattern-does)
This pattern reviews workflow logs across a repository, classifies notable behavior, and publishes a structured report. When it detects repeated failures, abnormal token consumption, or other unhealthy patterns, it can escalate those findings into issues for follow-up.
This pattern is useful for repository-wide monitoring because it creates a durable operational record instead of relying on ad hoc inspection of individual workflow runs.
## Typical workflow
[Section titled “Typical workflow”](#typical-workflow)
1. Run on a schedule to collect recent workflow activity.
2. Analyze logs, costs, and failure signals across runs.
3. Post a summary report to a GitHub Discussion or another durable destination.
4. Open or update issues when the same problem crosses a threshold.
## When to use it
[Section titled “When to use it”](#when-to-use-it)
Use this pattern when a repository has enough workflow activity that maintainers need a regular summary instead of checking each run manually. It also helps when workflows span multiple teams and failures or waste need to be surfaced in a shared location.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [BatchOps](/gh-aw/patterns/batch-ops/) — Process large volumes in parallel chunks
* [Audit Commands](/gh-aw/reference/audit/) — Investigate individual runs and regressions
* [OpenTelemetry](/gh-aw/reference/open-telemetry/) — Workflow telemetry and spans
* [Cache Memory](/gh-aw/reference/cache-memory/) — Persistent state across runs
* [Concurrency](/gh-aw/reference/concurrency/) — Prevent overlapping workflow runs
* [Monitoring with Projects](/gh-aw/experimental/monitoring-with-projects/) — Durable tracking with Projects
# MultiRepoOps
> Coordinate agentic workflows across multiple GitHub repositories with automated issue tracking, feature synchronization, and organization-wide enforcement
MultiRepoOps extends operational automation patterns (IssueOps, ChatOps, etc.) across multiple GitHub repositories. Using [cross-repository safe outputs](/gh-aw/reference/cross-repository/) and [secure authentication](/gh-aw/reference/auth/), MultiRepoOps enables coordinating work between related projects — creating tracking issues in central repos, synchronizing features to sub-repositories, and enforcing organization-wide policies — all through AI-powered workflows.
```
flowchart LR
subgraph source["Source repo"]
event([Event]) --> agent[AI agent]
end
agent --> targetA[Target repo A]
agent --> targetB[Target repo B]
```
## Common MultiRepoOps Patterns
[Section titled “Common MultiRepoOps Patterns”](#common-multirepoops-patterns)
Four topologies cover most use cases:
| Pattern | Description | Examples |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Side repository** | Workflows live in a dedicated automation repo and target one or more main repos — keeps AI-generated content isolated from your main codebase | [Triage from Side Repo](/gh-aw/examples/multi-repo/triage-from-side-repo/), [Code Quality Monitoring](/gh-aw/examples/multi-repo/code-quality-monitoring/) |
| **Central control plane** | A private control repo runs a scheduled orchestrator that filters, prioritizes, and dispatches per-repo worker workflows | [Dependabot Rollout](/gh-aw/examples/multi-repo/dependabot-rollout/) |
| **Hub-and-spoke** | Component repos each push events to a central tracker via `target-repo` — aggregates signals from many sources into one place | [Cross-Repo Issue Tracking](/gh-aw/examples/multi-repo/issue-tracking/) |
| **Upstream-to-downstream** | Source repo propagates changes outward to one or more downstream repos via PRs; `max` controls fan-out breadth | [Feature Synchronization](/gh-aw/examples/multi-repo/feature-sync/) |
## The Side Repository Pattern (Isolated Automation)
[Section titled “The Side Repository Pattern (Isolated Automation)”](#the-side-repository-pattern-isolated-automation)
A **side repository** is a dedicated automation repo that runs workflows targeting one or more main codebases. This keeps AI-generated issues, comments, and workflow runs isolated from your main repository — no changes needed to existing projects and no mixing of automation infrastructure with production code.
```
flowchart LR
subgraph side["Side repo (workflows)"]
event([Schedule / dispatch]) --> agent[AI agent]
end
agent -->|target-repo| main[Main repo]
```
Teams new to agentic workflows can adopt this pattern: create a private repository, add a PAT as a secret, and point `target-repo` at your main codebase. No changes required to the main repo.
```aw
---
on: weekly on monday
safe-outputs:
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
create-issue:
target-repo: "my-org/main-repo"
labels: [automation, weekly-check]
max: 5
tools:
github:
github-token: ${{ secrets.GH_AW_MAIN_REPO_TOKEN }}
toolsets: [repos, issues, pull_requests]
---
# Weekly Repository Health Check
Analyze my-org/main-repo and create issues for stale PRs (>30 days), failed CI runs on main, and open security advisories.
```
Using [Slash commands](/gh-aw/reference/command-triggers/) from a side repo require a bridge: a thin relay workflow in the main repo listens for the command and forwards it via `workflow_dispatch` to the side repo. See [Triage from Side Repo](/gh-aw/examples/multi-repo/triage-from-side-repo/) for a complete walkthrough.
Authentication details and step-by-step setup are covered in the [Triage from Side Repo](/gh-aw/examples/multi-repo/triage-from-side-repo/) and [Code Quality Monitoring](/gh-aw/examples/multi-repo/code-quality-monitoring/) examples, and in the [Authentication reference](/gh-aw/reference/auth/).
## The Central Control Plane Pattern (Org-Wide Rollouts)
[Section titled “The Central Control Plane Pattern (Org-Wide Rollouts)”](#the-central-control-plane-pattern-org-wide-rollouts)
For large-scale operations — security patches, policy rollouts, configuration standardization — use a **single private repository as a control plane**. An orchestrator workflow filters and prioritizes targets, then dispatches per-repo worker workflows.
```
flowchart LR
subgraph central["Central control repo"]
schedule([Schedule]) --> orch[Orchestrator\nfilter & prioritize]
end
orch --> w1[Repo A]
orch --> w2[Repo B]
orch --> w3[Repo N]
```
This pattern supports phased adoption (pilot waves first), central governance, security-aware prioritization, and a complete decision trail — without pushing `main` changes to individual target repositories.
**Orchestrator** (`dispatch-workflow` safe output + `max` limit):
```aw
---
on:
schedule: weekly on monday
tools:
github:
github-token: ${{ secrets.GH_AW_READ_ORG_TOKEN }}
toolsets: [repos]
safe-outputs:
dispatch-workflow:
workflows: [worker-workflow]
max: 5
---
# Rollout Orchestrator
Filter repositories, categorize by complexity, prioritize the rollout order, and dispatch the worker workflow for each selected repository. Summarize candidates, breakdown, and rationale.
```
**Worker** (`checkout` + `target-repo` safe outputs per dispatched repo):
```aw
---
on:
workflow_dispatch:
inputs:
target_repo:
description: 'Target repository (owner/repo format)'
required: true
type: string
checkout:
repository: ${{ github.event.inputs.target_repo }}
github-token: ${{ secrets.ORG_REPO_CHECKOUT_TOKEN }}
current: true
safe-outputs:
github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: ${{ github.event.inputs.target_repo }}
max: 1
---
# Worker: Apply Changes to Target Repository
Analyze ${{ github.event.inputs.target_repo }}, apply the required changes, and create a pull request explaining what was changed and why.
```
Keep orchestrator permissions narrow; delegate repo-specific writes to workers. Add correlation IDs to dispatch inputs for tracking. See the [Dependabot Rollout example](/gh-aw/examples/multi-repo/dependabot-rollout/) for a complete end-to-end walkthrough.
## The Hub-and-Spoke Pattern
[Section titled “The Hub-and-Spoke Pattern”](#the-hub-and-spoke-pattern)
Each component repository runs its own workflow that forwards events to a central tracker via `target-repo`. The central repository accumulates a unified view without needing direct access to individual component repos.
```
flowchart LR
compA[Component repo A] -->|create-issue| hub[Central tracker]
compB[Component repo B] -->|create-issue| hub
compC[Component repo C] -->|create-issue| hub
```
Useful for component-based architectures where multiple teams need a shared visibility layer, cross-project initiatives, or aggregating metrics from distributed repositories. See [Cross-Repo Issue Tracking](/gh-aw/examples/multi-repo/issue-tracking/) for a complete example.
## The Upstream-to-Downstream Pattern
[Section titled “The Upstream-to-Downstream Pattern”](#the-upstream-to-downstream-pattern)
The source repository propagates changes outward to downstream repos whenever relevant paths change. The agent adapts the changes for each target’s structure and opens a pull request for review.
```
flowchart LR
src[Source repo] -->|create-pull-request| d1[Downstream A]
src -->|create-pull-request| d2[Downstream B]
src -->|create-pull-request| d3[Downstream N]
```
Use `max` to control fan-out breadth, and `title-prefix` plus labels to make the automated PRs easy to filter. See [Feature Synchronization](/gh-aw/examples/multi-repo/feature-sync/) for a complete example.
## Cross-Repository Safe Outputs
[Section titled “Cross-Repository Safe Outputs”](#cross-repository-safe-outputs)
Most safe output types support `target-repo` to write to external repositories, and `allowed-repos` for dynamic multi-target workflows. See [Cross-Repository Safe Outputs](/gh-aw/reference/cross-repository/#cross-repository-safe-outputs) for the complete list and configuration options, including `target-repo: "*"` for runtime-determined targets and the [GitHub Tools reference](/gh-aw/reference/cross-repository/#cross-repository-reading) for reading from private repositories.
## Deterministic Multi-Repo Workflows
[Section titled “Deterministic Multi-Repo Workflows”](#deterministic-multi-repo-workflows)
For direct repository access without agent involvement, check out multiple repositories using `checkout:` frontmatter or `actions/checkout` steps. See the [Deterministic Multi-Repo example](/gh-aw/reference/cross-repository/#example-deterministic-multi-repo-workflows) in the cross-repository reference.
## Example Workflows
[Section titled “Example Workflows”](#example-workflows)
Explore detailed MultiRepoOps examples:
* **[Feature Synchronization](/gh-aw/examples/multi-repo/feature-sync/)** — Sync code changes from main repo to sub-repositories
* **[Cross-Repo Issue Tracking](/gh-aw/examples/multi-repo/issue-tracking/)** — Hub-and-spoke tracking architecture
* **[Dependabot Rollout](/gh-aw/examples/multi-repo/dependabot-rollout/)** — Org-wide orchestrator + worker rollout from a central control repo
* **[Triage from Side Repo](/gh-aw/examples/multi-repo/triage-from-side-repo/)** — Automated issue triage running from an isolated automation repository
* **[Code Quality Monitoring](/gh-aw/examples/multi-repo/code-quality-monitoring/)** — Scheduled quality checks from a side repository with checkout
## Best Practices
[Section titled “Best Practices”](#best-practices)
Use GitHub Apps over PATs for automatic token revocation; scope tokens minimally to target repositories. Set appropriate `max` limits and consistent label/prefix conventions. Test against public repositories first before rolling out to private or org-wide targets.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [IssueOps](/gh-aw/patterns/issue-ops/) — Single-repo issue automation
* [ChatOps](/gh-aw/patterns/chat-ops/) — Command-driven workflows
* [Cross-Repository Operations](/gh-aw/reference/cross-repository/) — Checkout and `target-repo` configuration
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Complete safe output configuration
* [GitHub Tools](/gh-aw/reference/github-tools/) — GitHub API toolsets
* [Authentication](/gh-aw/reference/auth/) — PAT and GitHub App setup
* [Reusing Workflows](/gh-aw/guides/packaging-imports/) — Sharing workflows across repos
# OrchestratorOps
> Coordinate multiple agentic workflows using an orchestrator/worker pattern — one workflow decides what to do, dispatches workers to do the concrete work.
OrchestratorOps is a pattern where one workflow (the **orchestrator**) fans out work to one or more **worker** workflows. The orchestrator decides what to do and in what order; workers execute concrete tasks with scoped permissions and tools. This keeps complex multi-step operations manageable, observable, and independently resumable.
```
flowchart LR
trigger([Trigger]) --> orch[Orchestrator\ndecide & dispatch]
orch --> w1[Worker A]
orch --> w2[Worker B]
orch --> w3[Worker N]
```
## When to Use OrchestratorOps
[Section titled “When to Use OrchestratorOps”](#when-to-use-orchestratorops)
Use OrchestratorOps when a single workflow run is too coarse — the work spans multiple repositories, requires different tools or permissions per step, benefits from parallel execution, or needs intermediate human review between phases. Common cases include multi-repo rollouts, phased dependency upgrades, and initiative-level automation that touches many issues or PRs.
## The Orchestrator/Worker Pattern
[Section titled “The Orchestrator/Worker Pattern”](#the-orchestratorworker-pattern)
* **Orchestrator**: decides what to do next, splits work into units, dispatches workers.
* **Worker(s)**: do the concrete work (triage, code changes, analysis) with scoped permissions and tools.
* **Optional monitoring**: both orchestrator and workers can update a GitHub Project board for visibility.
## Dispatch Workers with `dispatch-workflow`
[Section titled “Dispatch Workers with dispatch-workflow”](#dispatch-workers-with-dispatch-workflow)
Allow dispatching specific workflows via GitHub’s `workflow_dispatch` API:
```yaml
safe-outputs:
dispatch-workflow:
workflows: [repo-triage-worker, dependency-audit-worker]
max: 10
```
During compilation, gh-aw validates the target workflows exist and support `workflow_dispatch`. Workers receive a JSON payload and run asynchronously as independent workflow runs.
See [`dispatch-workflow` safe output](/gh-aw/reference/safe-outputs/#workflow-dispatch-dispatch-workflow).
## Call Workers with `call-workflow`
[Section titled “Call Workers with call-workflow”](#call-workers-with-call-workflow)
Call reusable workflows (`workflow_call`) via compile-time fan-out — no API call at runtime:
```yaml
safe-outputs:
call-workflow:
workflows: [spring-boot-bugfix, frontend-dep-upgrade]
max: 1
```
The compiler validates that each worker declares `workflow_call`, generates a typed MCP tool per worker from its inputs, and emits a conditional `uses:` job. At runtime the worker whose name the agent selected executes as part of the same workflow run — preserving `github.actor` and billing attribution.
See [`call-workflow` safe output](/gh-aw/reference/safe-outputs/#workflow-call-call-workflow).
Use `call-workflow` when actor attribution matters, workers must finish before the orchestrator concludes, or you want zero API overhead. Use `dispatch-workflow` when workers should run asynchronously, outlive the parent run, or need `workflow_dispatch` inputs.
## Passing Correlation IDs
[Section titled “Passing Correlation IDs”](#passing-correlation-ids)
If your workers need shared context, pass an explicit input such as `tracker_id` (string) and include it in worker outputs (e.g., writing it into a Project custom field).
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [BatchOps](/gh-aw/patterns/batch-ops/) — Parallel processing of large item volumes
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Central control plane pattern (orchestrator + worker across repos)
* [WorkQueueOps](/gh-aw/patterns/workqueue-ops/) — Sequential processing with ordering guarantees
* [Safe Outputs (`dispatch-workflow`)](/gh-aw/reference/safe-outputs/#workflow-dispatch-dispatch-workflow) — Dispatching workers
* [Safe Outputs (`call-workflow`)](/gh-aw/reference/safe-outputs/#workflow-call-call-workflow) — Calling reusable workflows
* [Monitoring with Projects](/gh-aw/experimental/monitoring-with-projects/) — Tracking orchestrator/worker progress
# OrchestratorOps
> Coordinate multiple agentic workflows using an orchestrator/worker pattern — one workflow decides what to do, dispatches workers to do the concrete work.
OrchestratorOps is a pattern where one workflow (the **orchestrator**) fans out work to one or more **worker** workflows. The orchestrator decides what to do and in what order; workers execute concrete tasks with scoped permissions and tools. This keeps complex multi-step operations manageable, observable, and independently resumable.
```
flowchart LR
trigger([Trigger]) --> orch[Orchestrator\ndecide & dispatch]
orch --> w1[Worker A]
orch --> w2[Worker B]
orch --> w3[Worker N]
```
## When to Use OrchestratorOps
[Section titled “When to Use OrchestratorOps”](#when-to-use-orchestratorops)
Use OrchestratorOps when a single workflow run is too coarse — the work spans multiple repositories, requires different tools or permissions per step, benefits from parallel execution, or needs intermediate human review between phases. Common cases include multi-repo rollouts, phased dependency upgrades, and initiative-level automation that touches many issues or PRs.
## The Orchestrator/Worker Pattern
[Section titled “The Orchestrator/Worker Pattern”](#the-orchestratorworker-pattern)
* **Orchestrator**: decides what to do next, splits work into units, dispatches workers.
* **Worker(s)**: do the concrete work (triage, code changes, analysis) with scoped permissions and tools.
* **Optional monitoring**: both orchestrator and workers can update a GitHub Project board for visibility.
## Dispatch Workers with `dispatch-workflow`
[Section titled “Dispatch Workers with dispatch-workflow”](#dispatch-workers-with-dispatch-workflow)
Allow dispatching specific workflows via GitHub’s `workflow_dispatch` API:
```yaml
safe-outputs:
dispatch-workflow:
workflows: [repo-triage-worker, dependency-audit-worker]
max: 10
```
During compilation, gh-aw validates the target workflows exist and support `workflow_dispatch`. Workers receive a JSON payload and run asynchronously as independent workflow runs.
See [`dispatch-workflow` safe output](/gh-aw/reference/safe-outputs/#workflow-dispatch-dispatch-workflow).
## Call Workers with `call-workflow`
[Section titled “Call Workers with call-workflow”](#call-workers-with-call-workflow)
Call reusable workflows (`workflow_call`) via compile-time fan-out — no API call at runtime:
```yaml
safe-outputs:
call-workflow:
workflows: [spring-boot-bugfix, frontend-dep-upgrade]
max: 1
```
The compiler validates that each worker declares `workflow_call`, generates a typed MCP tool per worker from its inputs, and emits a conditional `uses:` job. At runtime the worker whose name the agent selected executes as part of the same workflow run — preserving `github.actor` and billing attribution.
See [`call-workflow` safe output](/gh-aw/reference/safe-outputs/#workflow-call-call-workflow).
Use `call-workflow` when actor attribution matters, workers must finish before the orchestrator concludes, or you want zero API overhead. Use `dispatch-workflow` when workers should run asynchronously, outlive the parent run, or need `workflow_dispatch` inputs.
## Passing Correlation IDs
[Section titled “Passing Correlation IDs”](#passing-correlation-ids)
If your workers need shared context, pass an explicit input such as `tracker_id` (string) and include it in worker outputs (e.g., writing it into a Project custom field).
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [BatchOps](/gh-aw/patterns/batch-ops/) — Parallel processing of large item volumes
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Central control plane pattern (orchestrator + worker across repos)
* [WorkQueueOps](/gh-aw/patterns/workqueue-ops/) — Sequential processing with ordering guarantees
* [Safe Outputs (`dispatch-workflow`)](/gh-aw/reference/safe-outputs/#workflow-dispatch-dispatch-workflow) — Dispatching workers
* [Safe Outputs (`call-workflow`)](/gh-aw/reference/safe-outputs/#workflow-call-call-workflow) — Calling reusable workflows
* [Monitoring with Projects](/gh-aw/experimental/monitoring-with-projects/) — Tracking orchestrator/worker progress
# ProjectOps
> Automate GitHub Projects with agentic routing, field updates, and controlled write operations
ProjectOps helps teams run project operations with less manual upkeep.
It builds on [GitHub Projects](https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects), which provides the core planning and tracking layer for issues and pull requests, and adds support for judgment-heavy decisions.
ProjectOps reads project state with GitHub tools and applies changes through [safe-outputs](/gh-aw/reference/safe-outputs/). It is most useful when you need context-aware routing and field updates. For simple, rule-based transitions, [built-in automations](https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/using-the-built-in-automations) are usually enough.
In practice, this gives teams faster triage decisions, cleaner board state, stronger planning signals across related issues and pull requests, and more decision-ready status updates.
## How it works
[Section titled “How it works”](#how-it-works)
```
flowchart LR
ev([Issue / PR event\nor schedule]) --> agent[ProjectOps agent]
subgraph gh["GitHub Projects"]
board[(board\nfields & items)]
end
agent -->|read — projects toolset| board
board -->|project state| agent
agent -->|update-project\nadd-comment| board
```
A practical way to adopt ProjectOps is to start with read-only MCP/GitHub analysis, then gradually add targeted write operations as workflow confidence and policy maturity increase.
ProjectOps combines two capability layers:
* **GitHub tools (`tools.github` + `projects` toolset)** for reading and analyzing project state.
* **Safe outputs** for controlled write operations, including:
* **[`update-project`](/gh-aw/reference/safe-outputs/#project-board-updates-update-project)** — use when you want to add issues/PRs to a project or update fields (status, priority, owner, dates, custom values).
* **[`create-project-status-update`](/gh-aw/reference/safe-outputs/#project-status-updates-create-project-status-update)** — use when you want a stakeholder-facing summary in the project Updates tab (weekly health, blockers, risks, next decisions).
* **[`create-project`](/gh-aw/reference/safe-outputs/#project-creation-create-project)** — use when automation needs to bootstrap a new board for an initiative or team.
* **[`add-comment`](/gh-aw/reference/safe-outputs/#comment-creation-add-comment)** — use when you want to explain routing decisions or request missing info on the triggering issue/PR.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
1. **A Project board** and copy the project URL. See [Creating a project](https://docs.github.com/en/issues/planning-and-tracking-with-projects/creating-projects/creating-a-project#creating-a-project).
2. **A Project token** (PAT or GitHub App token). See [Authentication (Projects)](/gh-aw/reference/auth-projects/).
3. **A field contract** (for example: Status, Priority, Team, Iteration, Target Date). See [Understanding fields](https://docs.github.com/en/issues/planning-and-tracking-with-projects/understanding-fields).
## Project Token Authentication
[Section titled “Project Token Authentication”](#project-token-authentication)
The default `GITHUB_TOKEN` is repository-scoped and cannot access the Projects API. See [Authentication (Projects)](/gh-aw/reference/auth-projects/) for PAT, GitHub App, and secret layout instructions.
## Examples
[Section titled “Examples”](#examples)
Let’s look at examples of these in action, starting with the [Project Board Summarizer](#project-board-summarizer) (read-only analysis), then moving to controlled write operations with the [Project Board Maintainer](#project-board-maintainer) example.
### Project Board Summarizer
[Section titled “Project Board Summarizer”](#project-board-summarizer)
Let’s start with a simple agentic workflow that reviews project board state and generates a summary without applying any changes.
```aw
---
on:
schedule: weekly on monday
permissions:
contents: read
actions: read
tools:
github:
github-token: ${{ secrets.GH_AW_READ_PROJECT_TOKEN }}
toolsets: [default, projects]
---
# Project Board Summarizer
Review [project 1](https://github.com/orgs/my-mona-org/projects/1).
Return only:
- New this week
- Blocked + why
- Stale/inconsistent fields
- Top 3 human actions
Read-only. Do not update the project.
```
Our project board might look like this:

Running the agentic workflow generates a concise summary of project status. We can find this in the GitHub Actions agent run output:

### Project Board Maintainer
[Section titled “Project Board Maintainer”](#project-board-maintainer)
Let’s write an agentic workflow that applies changes to a project board based on issue content and context. This workflow will run on new issues, analyze the issue and project state, and decide whether to add the issue to the project board and how to set key fields.
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
actions: read
tools:
github:
github-token: ${{ secrets.GH_AW_READ_PROJECT_TOKEN }}
toolsets: [default, projects]
safe-outputs:
update-project:
github-token: ${{ secrets.GH_AW_WRITE_PROJECT_TOKEN }}
project: https://github.com/orgs/my-mona-org/projects/1
max: 1
add-comment:
max: 1
---
# Intelligent Issue Triage
Analyze each new issue in this repository and decide whether it belongs on the project board.
Set structured fields only from allowed values:
- Status: Needs Triage | Proposed | In Progress | Blocked
- Priority: Low | Medium | High
- Team: Platform | Docs | Product
Post a short comment on the issue explaining your routing decision and any uncertainty.
```
Once this workflow is compiled and running, it will automatically triage new issues with controlled write operations to the project board and issue comments.
Let’s create a new issue to see this in action:

The Project Board Maintainer analyzes the issue content and context, then decides to add it to the project board with specific field values (for example, Status: Proposed, Priority: Medium, Team: Docs). It also posts a comment on the issue explaining the decision and any uncertainty.

## Best practices
[Section titled “Best practices”](#best-practices)
In production, keep the loop simple: issue arrives, agent classifies and proposes/sets fields, safe outputs apply allowed writes, and humans review high-impact changes and exceptions.
* **Auto-apply** low-risk hygiene (add item, set initial status/team).
* **Suggest-only** commitments (priority/date/iteration changes).
* **Always gate** cross-team or cross-repo impact.
* Use `max` caps, allowlists, and explicit approvals to control writes.
* Keep single-select values exact to avoid field drift.
* If you only need simple event-based transitions, prefer [built-in GitHub Project workflows](https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/using-the-built-in-automations).
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [IssueOps](/gh-aw/patterns/issue-ops/) — Event-driven issue automation
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Secure write operations
* [GitHub Tools](/gh-aw/reference/github-tools/) — GitHub API toolsets for reading project state
* [Monitoring with Projects](/gh-aw/experimental/monitoring-with-projects/) — Durable tracking with Projects
* [Authentication (Projects)](/gh-aw/reference/auth-projects/) — PAT and GitHub App setup for Projects
# ResearchPlanAssignOps
> Orchestrate deep research, structured planning, and automated assignment to drive AI-powered development cycles from insight to merged PR
ResearchPlanAssignOps is a four-phase development pattern that moves from automated discovery to merged code with human control at every decision point. A research agent surfaces insights, a planning agent converts them into actionable issues, a coding agent implements the work by [assigning issues to GitHub Copilot](/gh-aw/reference/assign-to-copilot/), and a human reviews and merges.
## The Four Phases
[Section titled “The Four Phases”](#the-four-phases)
```
flowchart LR
research([Research]) --> plan[Plan issues]
plan --> assign[Assign to Copilot]
assign --> merge[Review & merge]
```
Each phase produces a concrete artifact consumed by the next, and every transition is a human checkpoint.
### Phase 1: Research
[Section titled “Phase 1: Research”](#phase-1-research)
A scheduled workflow investigates the codebase from a specific angle and publishes its findings as a GitHub discussion. The discussion is the contract between the research phase and everything that follows—it contains the analysis, recommendations, and context a planner needs.
The [`go-fan`](https://github.com/github/gh-aw/blob/main/.github/workflows/go-fan.md) workflow is a live example: it runs each weekday, picks one Go dependency, compares current usage against upstream best practices, and creates a `[go-fan]` discussion under the `audits` category.
```aw
---
name: Go Fan
on:
schedule: daily on weekdays
workflow_dispatch:
engine: claude
safe-outputs:
create-discussion:
title-prefix: "[go-fan] "
category: "audits"
max: 1
close-older-discussions: true
tools:
cache-memory: true
github:
toolsets: [default]
---
Analyze today's Go dependency. Compare current usage in this
repository against upstream best practices and recent releases.
Save a summary to scratchpad/mods/ and create a discussion
with findings and improvement recommendations.
```
The research agent uses `cache-memory` to track which modules have been reviewed so it rotates through them systematically across runs.
### Phase 2: Plan
[Section titled “Phase 2: Plan”](#phase-2-plan)
After reading the research discussion, a developer triggers the `/plan` command on it. The [`plan`](https://github.com/github/gh-aw/blob/main/.github/workflows/plan.md) workflow reads the discussion, extracts concrete work items, and creates up to five sub-issues grouped under a parent tracking issue.
```plaintext
/plan focus on the quick wins and API simplifications
```
The planner formats each sub-issue for a coding agent: a clear objective, the files to touch, step-by-step implementation guidance, and acceptance criteria. Issues are tagged `[plan]` and `ai-generated`.
Tip
The `/plan` command accepts inline guidance. Steer it toward high-priority findings or away from lower-priority ones before it generates issues.
### Phase 3: Assign
[Section titled “Phase 3: Assign”](#phase-3-assign)
With well-scoped issues in hand, the developer [assigns them to Copilot](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/create-a-pr#assigning-an-issue-to-copilot) for automated implementation. Copilot opens a pull request and posts progress updates as it works.
Issues can be assigned individually through the GitHub UI, or pre-assigned in bulk via an orchestrator workflow:
```aw
---
name: Auto-assign plan issues to Copilot
on:
issues:
types: [labeled]
engine: copilot
safe-outputs:
assign-to-user:
target: "*"
add-comment:
target: "*"
---
When an issue is labeled `plan` and has no assignee,
assign it to Copilot and add a comment indicating
automated assignment.
```
For multi-issue plans, assignments can run in parallel—Copilot works independently on each issue and opens separate PRs.
### Phase 4: Merge
[Section titled “Phase 4: Merge”](#phase-4-merge)
Copilot’s pull request is reviewed by a human maintainer. The maintainer checks correctness, runs tests, and merges. The tracking issue created in Phase 2 closes automatically when all sub-issues are resolved.
## End-to-End Example
[Section titled “End-to-End Example”](#end-to-end-example)
The following trace shows the full cycle using `go-fan` as the research driver.
**Monday 7 AM** — `go-fan` runs and creates a discussion:
> **\[go-fan] Go Module Review: spf13/cobra**
>
> Current usage creates a new `Command` per invocation. cobra v1.8 introduced `SetContext` for propagating cancellation. Quick wins: pass context through subcommands, use `PersistentPreRunE` for shared setup.
**Monday afternoon** — Developer reads the discussion and types:
```plaintext
/plan
```
The planner creates a parent tracking issue `[plan] cobra improvements` with three sub-issues:
* `[plan] Pass context through subcommands using cobra SetContext`
* `[plan] Refactor shared setup into PersistentPreRunE`
* `[plan] Add context cancellation tests`
**Monday afternoon** — Developer assigns the first two issues to Copilot. Both open PRs within minutes.
**Tuesday** — Developer reviews PRs, requests a minor change on one, approves the other. Both merge by end of day. The tracking issue closes.
## Workflow Configuration Patterns
[Section titled “Workflow Configuration Patterns”](#workflow-configuration-patterns)
### Research: produce one discussion per run
[Section titled “Research: produce one discussion per run”](#research-produce-one-discussion-per-run)
```aw
safe-outputs:
create-discussion:
expires: 1d
category: "research"
max: 1
close-older-discussions: true
```
`close-older-discussions: true` prevents discussion accumulation—only the latest finding stays open for the planner.
### Research: maintain memory across runs
[Section titled “Research: maintain memory across runs”](#research-maintain-memory-across-runs)
```aw
tools:
cache-memory: true
```
Use `cache-memory` to track state between scheduled runs—which items have been reviewed, trend data, or historical baselines.
### Plan: issue grouping
[Section titled “Plan: issue grouping”](#plan-issue-grouping)
```aw
safe-outputs:
create-issue:
expires: 2d
title-prefix: "[plan] "
labels: [plan, ai-generated]
max: 5
group: true
```
`group: true` creates a parent tracking issue automatically. Do not create the parent manually—the workflow handles it.
### Assign: pre-assign via `assignees`
[Section titled “Assign: pre-assign via assignees”](#assign-pre-assign-via-assignees)
For research workflows that produce self-contained, well-scoped issues, skip the manual plan phase and assign directly:
```aw
safe-outputs:
create-issue:
title-prefix: "[fix] "
labels: [ai-generated]
assignees: copilot
```
The `duplicate-code-detector` workflow uses this approach—duplication fixes are narrow enough that a planning phase adds no value.
## Customization
[Section titled “Customization”](#customization)
Adapt this pattern by varying:
* **Research focus**: static analysis, performance metrics, documentation quality, security, code duplication, test coverage
* **Frequency**: daily, weekly, on-demand
* **Report format**: discussions (for open-ended findings), issues (for self-contained tasks)
* **Planning approach**: automatic (well-scoped research goes straight to Copilot via `assignees: copilot`) vs. manual (developer reviews before assigning)
* **Assignment method**: pre-assign in the research workflow, bulk-assign via an orchestrator workflow, or assign individually through the GitHub UI
## Limitations
[Section titled “Limitations”](#limitations)
The multi-phase approach takes longer than direct execution and requires developers to review research reports and generated issues. Research agents may surface findings that don’t require action (false positives), and each phase transition needs clear handoffs. Research agents often require specialized MCPs (Serena, Tavily, etc.) for deeper analysis.
## When to Use ResearchPlanAssignOps
[Section titled “When to Use ResearchPlanAssignOps”](#when-to-use-researchplanassignops)
This pattern fits when:
* The scope of work is unknown until analysis runs
* Issues need human prioritization before implementation
* Research findings vary in quality (some runs find nothing actionable)
* Multiple work items can be executed in parallel
Prefer a simpler pattern when:
* The work is already well-defined (use [IssueOps](/gh-aw/patterns/issue-ops/))
* Issues can go directly to Copilot without review (use the `assignees: copilot` shortcut in your research workflow)
* Work spans multiple repositories (use [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/))
## Existing Workflows
[Section titled “Existing Workflows”](#existing-workflows)
| Phase | Workflow | Description |
| -------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Research | [`go-fan`](https://github.com/github/gh-aw/blob/main/.github/workflows/go-fan.md) | Daily Go dependency analysis with best-practice comparison |
| Research | [`copilot-cli-deep-research`](https://github.com/github/gh-aw/blob/main/.github/workflows/copilot-cli-deep-research.md) | Weekly analysis of Copilot CLI feature usage |
| Research | [`static-analysis-report`](https://github.com/github/gh-aw/blob/main/.github/workflows/static-analysis-report.md) | Daily security scan with clustered findings |
| Research | [`duplicate-code-detector`](https://github.com/github/gh-aw/blob/main/.github/workflows/duplicate-code-detector.md) | Daily semantic duplication analysis (auto-assigns) |
| Plan | [`plan`](https://github.com/github/gh-aw/blob/main/.github/workflows/plan.md) | `/plan` slash command—converts issues or discussions into sub-issues |
| Assign | GitHub UI / workflow | [Assign issues to Copilot](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/create-a-pr#assigning-an-issue-to-copilot) for automated PR creation |
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [DispatchOps](/gh-aw/patterns/dispatch-ops/) — Manually triggered research and one-off investigations
* [WorkQueueOps](/gh-aw/patterns/workqueue-ops/) — Sequential queue processing for large backlogs
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Secure write operations
* [Assign to Copilot](/gh-aw/reference/assign-to-copilot/) — Assigning issues to GitHub Copilot
# SpecOps
> Maintain and propagate W3C-style specifications using agentic workflows
SpecOps is a pattern for maintaining formal specifications using agentic workflows. It leverages the [`w3c-specification-writer` agent](https://github.com/github/gh-aw/blob/main/.github/agents/w3c-specification-writer.agent.md) to create W3C-style specifications with RFC 2119 keywords (MUST, SHALL, SHOULD, MAY) and automatically propagates changes to consuming implementations via [cross-repository workflows](/gh-aw/reference/cross-repository/).
```
flowchart LR
update([Update spec]) --> review[Review & merge spec PR]
review --> propagate[Propagate to consumer repos]
```
## How SpecOps Works
[Section titled “How SpecOps Works”](#how-specops-works)
1. **Update specification** — Trigger a workflow with the `w3c-specification-writer` agent to edit the spec document (RFC 2119 keywords, version bump, change log).
2. **Review changes** — Approve the specification pull request.
3. **Propagate automatically** — On merge, workflows detect updates and create PRs in consuming repositories (like [gh-aw-mcpg](https://github.com/github/gh-aw-mcpg)) to maintain compliance.
4. **Verify compliance** — Test generation workflows update compliance test suites against the new requirements.
## Update Specifications
[Section titled “Update Specifications”](#update-specifications)
Create a workflow to update specifications using the [`w3c-specification-writer` agent](https://github.com/github/gh-aw/blob/main/.github/agents/w3c-specification-writer.agent.md):
```yaml
---
name: Update MCP Gateway Spec
on:
workflow_dispatch:
inputs:
change_description:
description: 'What needs to change in the spec?'
required: true
type: string
engine: copilot
strict: true
safe-outputs:
create-pull-request:
title-prefix: "[spec] "
labels: [documentation, specification]
tools:
edit:
bash:
---
# Specification Update Workflow
Update the MCP Gateway specification using the w3c-specification-writer agent.
**Change Request**: ${{ inputs.change_description }}
## Your Task
1. Review the current specification at `docs/src/content/docs/reference/mcp-gateway.md`
2. Apply the requested changes following W3C conventions:
- Use RFC 2119 keywords (MUST, SHALL, SHOULD, MAY)
- Update version number (major/minor/patch)
- Add entry to Change Log section
- Update Status of This Document if needed
3. Ensure changes maintain clear conformance requirements, testable specifications, and complete examples
4. Create a pull request with the updated specification
```
## Propagate Changes
[Section titled “Propagate Changes”](#propagate-changes)
After specification updates merge, automatically propagate changes to consuming repositories:
```yaml
---
name: Propagate Spec Changes
on:
push:
branches:
- main
paths:
- 'docs/src/content/docs/reference/mcp-gateway.md'
engine: copilot
strict: true
safe-outputs:
create-pull-request:
title-prefix: "[spec-update] "
labels: [dependencies, specification]
tools:
github:
toolsets: [repos, pull_requests]
edit:
bash:
---
# Specification Propagation Workflow
The MCP Gateway specification has been updated. Propagate changes to consuming repositories.
## Consuming Repositories
- **gh-aw-mcpg**: Update implementation compliance, schemas, and tests
- **gh-aw**: Update MCP gateway validation and documentation
## Your Task
1. Read the latest specification version and change log
2. Identify breaking changes and new requirements
3. For each consuming repository:
- Update implementation to match spec
- Run tests to verify compliance
- Create pull request with changes
4. Create tracking issue linking all PRs
```
## Specification Structure
[Section titled “Specification Structure”](#specification-structure)
W3C-style specifications require: Abstract, Status, Introduction, Conformance, numbered technical sections with RFC 2119 keywords, Compliance testing, References, and a Change log.
**Example RFC 2119 usage**:
```markdown
## 3. Gateway Configuration
The gateway MUST validate all configuration fields before startup.
The gateway SHOULD log validation errors with field names.
The gateway MAY cache validated configurations.
```
See the [`w3c-specification-writer` agent](https://github.com/github/gh-aw/blob/main/.github/agents/w3c-specification-writer.agent.md) for a complete template and guidelines.
## Semantic Versioning
[Section titled “Semantic Versioning”](#semantic-versioning)
| Bump | When |
| ----------------- | --------------------------------- |
| **Major (X.0.0)** | Breaking changes |
| **Minor (0.Y.0)** | New features, backward-compatible |
| **Patch (0.0.Z)** | Bug fixes, clarifications |
The [MCP Gateway Specification](/gh-aw/reference/mcp-gateway/) is a live example — maintained by the `layout-spec-maintainer` workflow and implemented in [gh-aw-mcpg](https://github.com/github/gh-aw-mcpg).
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Cross-repository coordination
* [Cross-Repository Operations](/gh-aw/reference/cross-repository/) — Checkout and target-repo configuration
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — Secure write operations
# WorkQueueOps
> Process a queue of work items using GitHub issues, sub-issues, cache-memory, or Discussions as durable queue backends
WorkQueueOps is a pattern for systematically processing a large backlog of work items. Instead of processing everything at once, work is queued using issue checklists, [cache-memory](/gh-aw/reference/cache-memory/), or Discussions as durable backends, tracked, and consumed incrementally — surviving interruptions, rate limits, and multi-day horizons. Use it when operations are idempotent and progress visibility matters.
```
flowchart LR
queue[(Queue)] --> process[Process next N items]
process --> mark[Mark complete]
mark --> queue
```
## Queue Strategy 1: Issue Checklist as Queue
[Section titled “Queue Strategy 1: Issue Checklist as Queue”](#queue-strategy-1-issue-checklist-as-queue)
Use GitHub issue checkboxes as a lightweight, human-readable queue. The agent reads the issue body, finds unchecked items, processes each one, and checks it off. Best for small-to-medium batches (< 100 items). Use [Concurrency](/gh-aw/reference/concurrency/) controls to prevent race conditions between parallel runs.
```aw
---
on:
workflow_dispatch:
inputs:
queue_issue:
description: "Issue number containing the checklist queue"
required: true
tools:
github:
toolsets: [issues]
safe-outputs:
update-issue:
body: true
add-comment:
max: 1
concurrency:
group: workqueue-${{ inputs.queue_issue }}
cancel-in-progress: false
---
# Checklist Queue Processor
You are processing a work queue stored as checkboxes in issue #${{ inputs.queue_issue }}.
1. Read issue #${{ inputs.queue_issue }} and find all unchecked items (`- [ ]`).
2. For each unchecked item (at most 10 per run): perform the required work, then edit the issue body to change `- [ ]` to `- [x]`.
3. Add a comment summarizing what was completed and what remains.
4. If all items are checked, close the issue with a summary comment.
```
```
flowchart LR
issue[Issue checklist] --> process[Process unchecked items]
process --> check[Check off completed]
```
## Queue Strategy 2: Sub-Issues as Queue
[Section titled “Queue Strategy 2: Sub-Issues as Queue”](#queue-strategy-2-sub-issues-as-queue)
Create one sub-issue per work item. The agent queries open sub-issues of a parent tracking issue, processes each one, and closes it when done. Scales to hundreds of items with individual discussion threads per item. Use `max:` limits on `close-issue` to avoid notification storms.
```aw
---
on:
schedule: hourly
workflow_dispatch:
tools:
github:
toolsets: [issues]
safe-outputs:
add-comment:
max: 5
close-issue:
max: 5
concurrency:
group: sub-issue-queue
cancel-in-progress: false
---
# Sub-Issue Queue Processor
You are processing a queue of open sub-issues. The parent tracking issue is labeled `queue-tracking`.
1. Find the open issue labeled `queue-tracking` — this is the queue parent.
2. List its open sub-issues and process at most 5 per run.
3. For each sub-issue: read the body, perform the work, add a result comment, then close the issue.
4. Add a progress comment on the parent issue showing how many items remain.
If no sub-issues are open, post a comment on the parent issue saying the queue is empty.
```
```
flowchart LR
parent[Parent tracking issue] --> subissues[Open sub-issues]
subissues --> process[Process & close per item]
```
## Queue Strategy 3: Cache-Memory Queue
[Section titled “Queue Strategy 3: Cache-Memory Queue”](#queue-strategy-3-cache-memory-queue)
Store queue state as a JSON file in [cache-memory](/gh-aw/reference/cache-memory/). Each run loads the file, picks up where the last run left off, and saves the updated state. Best for large queues and multi-day processing horizons where items are generated programmatically. Cache-memory is scoped to a single branch; use filesystem-safe timestamps in filenames (no colons — e.g., `YYYY-MM-DD-HH-MM-SS-sss`).
````aw
---
on:
schedule: daily on weekdays
workflow_dispatch:
tools:
cache-memory: true
github:
toolsets: [repos, issues]
bash:
- "jq"
safe-outputs:
add-comment:
max: 10
add-labels:
allowed: [processed, needs-review]
max: 10
---
# Cache-Memory Queue Processor
You process items from a persistent JSON queue at `/tmp/gh-aw/cache-memory/workqueue.json`:
```json
{
"pending": ["item-1", "item-2"],
"in_progress": [],
"completed": ["item-0"],
"failed": [],
"last_run": "2026-04-07-06-00-00"
}
````
1. Load the queue file. If it doesn’t exist, initialize it by listing all open issues without the label `processed` and populating `pending` with their numbers.
2. Move up to 10 items from `pending` to `in_progress`.
3. For each item: perform the required operation, then move it to `completed` on success or `failed` (with an error note) on failure.
4. Save the updated queue JSON and report: X completed, Y failed, Z remaining.
If `pending` is empty, announce that the queue is exhausted.
````plaintext
```mermaid
flowchart LR
json[workqueue.json] --> process[Process pending items]
process --> save[Save updated queue]
````
## Queue Strategy 4: Discussion-Based Queue
[Section titled “Queue Strategy 4: Discussion-Based Queue”](#queue-strategy-4-discussion-based-queue)
Use a GitHub Discussion to track pending work items. Unresolved replies represent pending work; processing an item means resolving its reply. Best for community-sourced queues and async collaboration where humans need to inspect items before or after processing. Requires `discussions` in the GitHub toolset.
```aw
---
on:
schedule: daily
workflow_dispatch:
tools:
github:
toolsets: [discussions]
safe-outputs:
add-comment:
max: 5
create-discussion:
title-prefix: "[queue-log] "
category: "General"
concurrency:
group: discussion-queue
cancel-in-progress: false
---
# Discussion Queue Processor
A GitHub Discussion titled "Work Queue" (category "General") tracks pending items.
Each unresolved top-level reply is a work item.
1. Find the "Work Queue" discussion and list all unresolved replies (`isAnswered: false`).
2. For each unresolved reply (at most 5 per run): parse the work description, perform the work, then reply with the result.
3. Create a summary discussion post documenting what was processed today.
```
```
flowchart LR
discussion[Work Queue discussion] --> replies[Unresolved replies]
replies --> process[Process & resolve]
```
## Idempotency and Concurrency
[Section titled “Idempotency and Concurrency”](#idempotency-and-concurrency)
All WorkQueueOps patterns should be **idempotent**: running the same item twice should not cause double processing.
| Technique | How |
| -------------------- | --------------------------------------------------------------------------------- |
| Check before acting | Query current state (label present? comment exists?) before making changes |
| Atomic state updates | Write queue state in a single step; avoid partial updates |
| Concurrency groups | Use `concurrency.group` with `cancel-in-progress: false` to prevent parallel runs |
| Retry budgets | Track failed items separately; set a retry limit before giving up |
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [BatchOps](/gh-aw/patterns/batch-ops/) — Process large volumes in parallel chunks rather than sequentially
* [ResearchPlanAssignOps](/gh-aw/patterns/research-plan-assign-ops/) — Research → Plan → Assign pattern for developer-supervised work
* [Cache Memory](/gh-aw/reference/cache-memory/) — Persistent state storage across workflow runs
* [Repo Memory](/gh-aw/reference/repo-memory/) — Git-committed persistent state for cross-branch sharing
* [Concurrency](/gh-aw/reference/concurrency/) — Prevent race conditions in queue-based workflows
# A/B Experiments
> Run A/B experiments in GitHub Agentic Workflows to test prompt variants and measure the effect of different instructions across runs.
The `experiments` section of the workflow frontmatter enables statistical A/B testing by defining named experiments, each with a set of variant values. At runtime the activation job selects one variant per experiment using a balanced round-robin counter and exposes the selection to the workflow prompt.
## Declaring experiments
[Section titled “Declaring experiments”](#declaring-experiments)
Add an `experiments` map to the workflow frontmatter. Each key names an experiment; the value is either a simple array of variants (bare-array form) or a rich object with additional metadata fields.
### Bare-array form
[Section titled “Bare-array form”](#bare-array-form)
```aw
---
on:
issues:
types: [opened]
engine: copilot
experiments:
style: [concise, detailed]
---
Summarize this issue in a **${{ experiments.style }}** way.
```
### Rich object form
[Section titled “Rich object form”](#rich-object-form)
Use the object form to attach metadata that drives automated reporting, guardrail enforcement, and lifecycle tracking:
```aw
---
on:
schedule: daily on weekdays
engine: copilot
experiments:
prompt_style:
variants: [concise, detailed]
description: "Test whether a concise prompt reduces token cost without quality loss"
hypothesis: "H0: no change in effective_tokens. H1: concise reduces tokens by >=15%"
metric: effective_tokens
secondary_metrics: [duration_ms, discussion_word_count]
guardrail_metrics:
- name: success_rate
threshold: ">=0.95"
- name: empty_output_rate
threshold: "==0"
weight: [50, 50]
min_samples: 25
start_date: "2026-05-05"
end_date: "2026-07-25"
issue: 1234
---
Summarize the findings in a **${{ experiments.prompt_style }}** way.
```
Note
Experiment names must be valid identifiers: start with a letter or underscore, followed by letters, digits, or underscores (e.g. `style`, `feature_1`). Names that do not match this pattern are ignored.
## Using variants in the prompt
[Section titled “Using variants in the prompt”](#using-variants-in-the-prompt)
Reference a variant with `${{ experiments. }}`. At runtime this is substituted with the selected variant string (e.g. `concise`).
Use the `{{#if experiments. }}` block syntax for conditional prompt sections. A variant value of `no` is treated as falsy, enabling yes/no flag experiments:
```aw
---
experiments:
caveman: [yes, no]
---
{{#if experiments.caveman }}
Talk like a caveman in all your responses. Me test. You run.
{{/if}}
Address the issue described above.
```
## Statistical balancing
[Section titled “Statistical balancing”](#statistical-balancing)
The activation job maintains a per-variant invocation counter that is persisted according to the `storage` setting in the `experiments:` block (see [Storage Configuration](#storage-configuration) below). The variant with the lowest cumulative count is selected on each run; when multiple variants share the lowest count (including the very first run when state is empty), one is chosen at random so no variant is systematically favoured. Over N runs every variant is used approximately N/K times (K = variant count), providing basic A/B balance with no configuration.
When a `weight` array is provided, weighted-random selection is used instead of round-robin. Each variant is chosen with probability proportional to its weight (e.g. `[70, 30]` gives the first variant a 70% probability). When `start_date` or `end_date` is set and today falls outside the window, the control variant (first entry) is returned without incrementing any counter.
## Storage Configuration
[Section titled “Storage Configuration”](#storage-configuration)
The `storage` key inside the `experiments:` map controls how experiment state is persisted:
```yaml
experiments:
storage: repo # or: cache (default: repo)
prompt_style: [concise, detailed]
```
| Value | Behavior |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `repo` (**default**) | Commits state to a git branch named `experiments/{sanitizedWorkflowID}` (workflow ID lowercased with hyphens removed, e.g. `my-workflow` → `experiments/myworkflow`). Durable — survives cache evictions. Requires `contents: write` permission (added automatically by the compiler). |
| `cache` | Uses GitHub Actions cache (legacy). State may be evicted after 7 days of inactivity. |
When `storage: repo`, the compiler adds a `push_experiments_state` job that runs after the activation job and commits the updated `state.json` to the experiments branch.
## Accessing assignments downstream
[Section titled “Accessing assignments downstream”](#accessing-assignments-downstream)
Each experiment exposes its selected variant as an activation job output:
| Expression | Description |
| -------------------------------------- | ---------------------------------------- |
| `needs.activation.outputs.` | Selected variant for experiment `` |
| `needs.activation.outputs.experiments` | All assignments as a JSON object |
Use these expressions in downstream jobs defined in the `jobs:` frontmatter section.
## Analyzing results
[Section titled “Analyzing results”](#analyzing-results)
The activation job uploads the counter state as an `experiment` artifact. Download and inspect it with the `gh aw` CLI:
```bash
# Download the experiment artifact for a specific run
gh aw audit --artifacts experiment
# Display experiment assignments in the audit report
gh aw audit
```
The `A/B Experiments` section of the audit report shows the variant chosen on the most recent run and the cumulative counts across all runs:
```plaintext
A/B Experiments
• caveman = yes (cumulative: no:4, yes:5)
• style = concise (cumulative: concise:5, detailed:4)
```
### Filtering audit results by variant
[Section titled “Filtering audit results by variant”](#filtering-audit-results-by-variant)
Use `--experiment` and `--variant` to filter audit runs to a specific variant:
```bash
gh aw audit --experiment prompt_style --variant concise
```
### Step summary
[Section titled “Step summary”](#step-summary)
Each activation job writes a Markdown step summary that shows variant assignments, cumulative counts, and — when the rich object form is used — progress toward `min_samples`:
```plaintext
## A/B Experiment Assignments
| Experiment | Selected Variant | All Variants | Cumulative Counts |
| --- | --- | --- | --- |
| prompt_style | concise | concise, detailed | concise: 8, detailed: 7|
### Sampling Progress
prompt_style (target: 25 per variant)
concise: ████████░░░░░░░░░░░░ 8/25 (32%)
detailed: ███████░░░░░░░░░░░░░ 7/25 (28%)
### Experiment Details
**prompt_style**
> Test whether a concise prompt reduces token cost without quality loss
**Hypothesis:** H0: no change in effective_tokens. H1: concise reduces tokens by >=15%
**Guardrail metrics:**
- `success_rate` >=0.95
- `empty_output_rate` ==0
Tracking issue: [#1234](https://github.com/owner/repo/issues/1234)
```
## Frontmatter reference
[Section titled “Frontmatter reference”](#frontmatter-reference)
### Bare-array form
[Section titled “Bare-array form”](#bare-array-form-1)
| Field | Type | Description |
| -------------------- | ---------- | ------------------------------------------------------- |
| `experiments` | `object` | Map of experiment name → variant array or config object |
| `experiments.` | `string[]` | Array of two or more variant strings for one experiment |
### Object form fields
[Section titled “Object form fields”](#object-form-fields)
| Field | Type | Required | Description |
| ------------------- | ----------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `variants` | `string[]` | ✓ | Array of two or more variant strings |
| `description` | `string` | | Human-readable explanation of what the experiment tests |
| `hypothesis` | `string` | | Null and alternative hypothesis (e.g. `"H0: no change. H1: concise reduces tokens by >=15%"`) |
| `metric` | `string` | | Primary metric to observe (e.g. `effective_tokens`, `duration_ms`) |
| `secondary_metrics` | `string[]` | | Additional metrics to track alongside the primary metric |
| `guardrail_metrics` | `object[]` | | List of `{name, threshold}` pairs that must not degrade. Threshold is a comparison expression like `>=0.95` or `==0` |
| `min_samples` | `integer` | | Minimum runs per variant required before statistical analysis is considered reliable. The step summary shows a progress bar toward this target. |
| `weight` | `integer[]` | | Per-variant probability weights (same length as `variants`). Enables weighted-random selection; values are relative and need not sum to 100. |
| `issue` | `integer` | | GitHub issue number that tracks this experiment’s lifecycle |
| `start_date` | `string` | | ISO-8601 date (`YYYY-MM-DD`) before which the experiment is inactive. The control variant is returned before this date without incrementing any counter. |
| `end_date` | `string` | | ISO-8601 date (`YYYY-MM-DD`) after which the experiment is inactive. The control variant is returned after this date without incrementing any counter. |
# A/B Experiments Specification
> Formal W3C-style specification for the GitHub Agentic Workflows A/B experiment system — frontmatter schema, variant selection, state persistence, expression integration, audit CLI, and statistical reporting.
# A/B Experiments Specification
[Section titled “A/B Experiments Specification”](#ab-experiments-specification)
**Version**: 1.0.0\
**Status**: Draft\
**Latest Version**: [experiments-specification](/gh-aw/practices/experiments-specification/)\
**Editors**: gh-aw maintainers
***
## Abstract
[Section titled “Abstract”](#abstract)
This specification defines the A/B experiment system for GitHub Agentic Workflows (gh-aw). It covers the `experiments:` frontmatter schema, variant selection algorithms, state persistence backends, expression and template integration, activation job structure, audit CLI integration, and statistical analysis requirements. Conforming implementations provide operators with a zero-infrastructure mechanism to conduct controlled experiments on agentic workflow behavior using only workflow frontmatter declarations, without any external service dependency.
This document consolidates and supersedes the normative sections of ADR-29534, ADR-29618, ADR-29628, ADR-29985, and ADR-29996. It also incorporates corrective requirements identified during an expert review of the implementation in May 2026.
***
## Status of This Document
[Section titled “Status of This Document”](#status-of-this-document)
This is a **Draft** specification. It may be updated, replaced, or made obsolete at any time. A future revision will promote this document to Candidate Recommendation once the reference implementation (gh-aw v1.x) satisfies all conformance requirements below.
Promotion from **Draft** to **Candidate Recommendation** requires all of the following:
1. **Reference implementation completeness**: 100% of normative requirements in §§4–12 are implemented in `gh-aw` and mapped to concrete implementation files (**tracking issue**: [#31983](https://github.com/github/gh-aw/issues/31983)).
2. **Compliance coverage**: At least 95% of normative requirements have automated tests, and all MUST/MUST NOT requirements have at least one passing automated test (**tracking issue**: [#31983](https://github.com/github/gh-aw/issues/31983)).
3. **CI stability window**: The experiments-related test suite passes on the default branch for 30 consecutive days with no unresolved regression in variant selection, persistence, or reporting behavior (**tracking issue**: [#31983](https://github.com/github/gh-aw/issues/31983)).
4. **Interoperability evidence**: At least two production workflows using `experiments:` run for a minimum of 500 total assignments each with valid assignment artifacts and reproducible audit output (**tracking issue**: [#31983](https://github.com/github/gh-aw/issues/31983)).
5. **Review sign-off**: Written approval from at least two gh-aw maintainers that Sections 10–14 are complete, internally consistent, and suitable for Candidate Recommendation publication (**tracking issue**: [#31983](https://github.com/github/gh-aw/issues/31983)).
### Sync
[Section titled “Sync”](#sync)
* **Who reviews**: The experiments specification editors (`gh-aw maintainers`) perform the primary review; one release owner for the current minor version performs final sign-off.
* **When**: Review occurs on the first business day of each month and during every minor-release cut.
* **What triggers an immediate sync update**:
1. Any change to `experiments:` schema fields or validation behavior (§4)
2. Any change to variant selection, gating, or persistence logic (§§5–7)
3. Any change to audit/reporting output contracts (§§10–11)
4. Any incident postmortem that identifies spec/implementation drift
When a trigger occurs, spec updates **SHOULD** be merged in the same PR as the implementation change or in a linked follow-up PR within 3 business days.
Feedback should be filed as GitHub issues against the `github/gh-aw` repository with the `experiments` label.
***
## Table of Contents
[Section titled “Table of Contents”](#table-of-contents)
1. [Introduction](#1-introduction)
2. [Conformance](#2-conformance)
3. [Definitions](#3-definitions)
4. [Frontmatter Schema](#4-frontmatter-schema)
5. [Variant Selection Algorithms](#5-variant-selection-algorithms)
6. [Date-Range Gating](#6-date-range-gating)
7. [State Persistence](#7-state-persistence)
8. [Expression and Template Integration](#8-expression-and-template-integration)
9. [Activation Job Structure](#9-activation-job-structure)
10. [Audit CLI Integration](#10-audit-cli-integration)
11. [Statistical Analysis and Reporting](#11-statistical-analysis-and-reporting)
12. [Simultaneous Experiments and Interaction Effects](#12-simultaneous-experiments-and-interaction-effects)
13. [Security Considerations](#13-security-considerations)
14. [Compliance Testing](#14-compliance-testing)
15. [References](#15-references)
16. [Appendices](#appendices)
17. [Change Log](#change-log)
***
## 1. Introduction
[Section titled “1. Introduction”](#1-introduction)
### 1.1 Purpose
[Section titled “1.1 Purpose”](#11-purpose)
Agentic workflows compiled by gh-aw use a frontmatter-driven configuration model. Teams running these workflows need a first-class mechanism to test different prompt variants (tone, verbosity, persona, feature flags embedded in the prompt) across successive workflow runs. Without such a mechanism, variant testing is ad-hoc, untracked, and statistically unbalanced.
This specification defines a self-contained A/B experiment system that requires no external service, no manual coordination, and no changes outside the workflow frontmatter.
### 1.2 Scope
[Section titled “1.2 Scope”](#12-scope)
This specification covers:
* The `experiments:` frontmatter schema and its two syntactic forms (bare-array, rich-object).
* Variant selection algorithms: balanced round-robin (least-used), weighted random, and date-gated fallback.
* State persistence backends: git-branch (`repo`) and GitHub Actions cache (`cache`).
* Expression and Handlebars template integration in the compiled workflow prompt.
* The activation job structure generated by the compiler.
* The `gh aw audit` CLI filtering interface for experiment-annotated runs.
* Requirements for statistical analysis and reporting workflows that consume experiment artifacts.
This specification does **not** cover:
* The internal compiler architecture beyond what is observable at the compiled YAML boundary.
* External analytics dashboards or third-party experiment platforms.
* Multi-armed bandit or adaptive allocation algorithms (considered future work).
### 1.3 Design Goals
[Section titled “1.3 Design Goals”](#13-design-goals)
1. **Zero external dependencies** — all state is stored within the repository or GitHub Actions infrastructure.
2. **Declarative** — the complete experiment configuration lives in the workflow frontmatter.
3. **Backward compatible** — adding `experiments:` to an existing workflow MUST NOT break any existing compiled output; removing it MUST restore the original output exactly.
4. **Statistically sound** — the default selection algorithm guarantees approximate variant balance in the minimum number of runs.
5. **Observable** — every run produces a durable artifact recording the variant assignment, and OTEL attributes propagate assignments to distributed-tracing backends automatically.
***
## 2. Conformance
[Section titled “2. Conformance”](#2-conformance)
### 2.1 Requirements Notation
[Section titled “2.1 Requirements Notation”](#21-requirements-notation)
The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **NOT RECOMMENDED**, **MAY**, and **OPTIONAL** in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119).
### 2.2 Conformance Classes
[Section titled “2.2 Conformance Classes”](#22-conformance-classes)
This specification defines three conformance classes:
| Class | Requirements |
| ---------------------- | ---------------------------------------------------------------------------------------- |
| **Level 1 — Basic** | Satisfies all MUST/MUST NOT requirements in §4, §5, §8, and §9 |
| **Level 2 — Standard** | Level 1 plus §6 (date gating), §7 (state persistence), §10 (audit CLI) |
| **Level 3 — Complete** | Level 2 plus §11 (statistical analysis and reporting) and §12 (simultaneous experiments) |
An implementation is considered **non-conformant** if it fails any MUST or MUST NOT requirement at the level it claims to implement.
### 2.3 Normative vs. Informative Content
[Section titled “2.3 Normative vs. Informative Content”](#23-normative-vs-informative-content)
Sections containing numbered requirements (e.g., “R-SCHEMA-001”) are **normative**. Notes, rationale blocks, and appendices are **informative** and carry no conformance weight.
***
## 3. Definitions
[Section titled “3. Definitions”](#3-definitions)
| Term | Definition |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **Experiment** | A named A/B test declared in workflow frontmatter, associating an identifier with two or more variant strings. |
| **Variant** | A named string value representing one treatment arm in an experiment. |
| **Control variant** | The first variant in the declared `variants` array; used as baseline and as fallback during date gating. |
| **Invocation counter** | A per-experiment, per-variant integer stored in `state.json` that records the cumulative number of times a variant has been selected. |
| **state.json** | The JSON file that stores invocation counters and per-run assignment history for all experiments in a single workflow. |
| **Run record** | An entry in the `state.json` `runs` array recording the run ID, timestamp, and variant assignments for one workflow run. |
| **Sanitized workflow ID** | The workflow basename (without `.md`) with hyphens removed and lowercased, used as a cache/branch key component. |
| **Activation job** | The `activation` GitHub Actions job generated by the compiler that picks variants and exposes them to downstream jobs. |
| **Experiment artifact** | A GitHub Actions artifact named `{sanitizedID}-experiment` uploaded by the activation job and containing `state.json` and `assignments.json`. |
| **assignments.json** | A file in the experiment artifact containing only the current run’s variant assignments as a flat JSON object. |
***
## 4. Frontmatter Schema
[Section titled “4. Frontmatter Schema”](#4-frontmatter-schema)
### 4.1 Field Declaration
[Section titled “4.1 Field Declaration”](#41-field-declaration)
**R-SCHEMA-001**: Workflow frontmatter **MAY** include an `experiments` field. Its absence **MUST** produce no change in compiled output.
**R-SCHEMA-002**: The value of `experiments` **MUST** be a YAML map. Non-map values **MUST** be rejected at compile time with a descriptive error.
**R-SCHEMA-003**: Every key in the `experiments` map, except the reserved `storage` key (§7.1), **MUST** be an experiment name that matches the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. Keys that do not match **MUST** be silently skipped with a compile-time warning emitted to stderr.
> **Note (informative)**: The identifier pattern ensures experiment names can be used as GitHub Actions step output names and embedded in `${{ experiments. }}` expressions without bracket notation.
### 4.2 Bare-Array Form
[Section titled “4.2 Bare-Array Form”](#42-bare-array-form)
**R-SCHEMA-004**: Each experiment value **MAY** be declared as a YAML sequence of two or more strings:
```yaml
experiments:
prompt_style: [concise, detailed]
```
**R-SCHEMA-005**: A bare-array value with fewer than two entries **MUST NOT** be accepted; the compiler **MUST** emit a compile-time error.
### 4.3 Rich Object Form
[Section titled “4.3 Rich Object Form”](#43-rich-object-form)
**R-SCHEMA-006**: Each experiment value **MAY** alternatively be declared as a YAML object with a required `variants` field and optional metadata fields. The two forms **MUST** be accepted in the same `experiments` map without conflict.
**R-SCHEMA-007**: The `variants` field **MUST** be an array of two or more non-empty strings. The same minimum-two-variants constraint from R-SCHEMA-005 applies.
**R-SCHEMA-008**: The following optional fields are defined for the object form:
| Field | Type | Description |
| ------------------- | ------------------- | --------------------------------------------------------------------- |
| `description` | string | Human-readable explanation of what the experiment tests. |
| `hypothesis` | string | Null and alternative hypothesis statements. |
| `metric` | string | Primary metric name to observe (e.g., `effective_tokens`). |
| `secondary_metrics` | string\[] | Additional metrics to collect. |
| `guardrail_metrics` | object\[] | Thresholds that must not degrade (see §4.4). |
| `min_samples` | integer ≥ 1 | Minimum runs per variant before analysis is reliable. Defaults to 20. |
| `weight` | integer\[] | Per-variant probability weights (see §5.2). |
| `issue` | integer ≥ 1 | GitHub issue number tracking this experiment. |
| `start_date` | string (YYYY-MM-DD) | Experiment is inactive before this date (see §6). |
| `end_date` | string (YYYY-MM-DD) | Experiment is inactive after this date (see §6). |
| `analysis_type` | string enum | Statistical test for automated reporting (see §11.2). |
| `tags` | string\[] | Free-form labels for dashboard filtering. |
| `notify` | object | Significance-alert destination (see §4.5). |
**R-SCHEMA-009**: The `weight`, `issue`, `min_samples`, `start_date`, `end_date`, `analysis_type`, `tags`, and `notify` fields carry no effect on variant assignment outside their documented subsections. `description`, `hypothesis`, `metric`, `secondary_metrics`, and `tags` are purely informative at runtime.
**R-SCHEMA-010**: Implementations **MUST NOT** introduce additional properties in the object form without a corresponding schema update; the compiler **MUST** reject unknown keys under strict mode.
### 4.4 Guardrail Metrics
[Section titled “4.4 Guardrail Metrics”](#44-guardrail-metrics)
**R-SCHEMA-011**: Each entry in `guardrail_metrics` **MUST** be an object with exactly two string fields: `name` and `threshold`. The `threshold` **MUST** match the pattern `^(>=|<=|==|>|<)-?\d+(\.\d+)?$` (e.g., `>=0.95`, `==0`, `<=0.05`).
**R-SCHEMA-012**: Guardrail evaluation is **INFORMATIVE** at the schema level — the compiler does not enforce guardrails at compile time. Reporting tooling (§11) **MUST** evaluate each guardrail and include pass/fail status in its output.
### 4.5 Notify Object
[Section titled “4.5 Notify Object”](#45-notify-object)
**R-SCHEMA-013**: The `notify` object **MUST** contain only the keys `discussion` and/or `issue`, each of which **MUST** be a positive integer (minimum 1). Unknown keys in `notify` **MUST** be rejected by schema validation.
**R-SCHEMA-014**: When `notify.issue` is set and the reporting workflow posts a comment to that issue, the compiled workflow **MUST** declare `permissions: issues: write`. Implementations that generate reporting workflows **MUST** automatically add this permission when `notify.issue` is present in any experiment configuration within the scope of that workflow.
> **Note (informative)**: Failure to include `issues: write` causes comment posting to silently fail with a 403 response. This was identified as a defect in the `daily-experiment-report` workflow (May 2026 review).
***
## 5. Variant Selection Algorithms
[Section titled “5. Variant Selection Algorithms”](#5-variant-selection-algorithms)
### 5.1 Balanced Round-Robin (Least-Used)
[Section titled “5.1 Balanced Round-Robin (Least-Used)”](#51-balanced-round-robin-least-used)
**R-SELECT-001**: When `weight` is absent or invalid (§5.2), implementations **MUST** select the variant with the lowest cumulative invocation count stored in `state.json`.
**R-SELECT-002**: When two or more variants share the lowest count — including the initial state where all counts are zero — implementations **MUST** break ties by selecting uniformly at random from the tied variants. No variant **MUST** be systematically favoured by position.
**R-SELECT-003**: After selecting a variant via round-robin, implementations **MUST** increment the invocation counter for that variant in `state.json` before persisting state.
> **Note (informative)**: Round-robin guarantees that over K×N runs each variant appears approximately N times, achieving balance in far fewer runs than random selection. The random tie-breaking on the first run ensures no variant is systematically advantaged.
### 5.2 Weighted Random Selection
[Section titled “5.2 Weighted Random Selection”](#52-weighted-random-selection)
**R-SELECT-004**: When `weight` is provided and its length equals the length of `variants`, implementations **MUST** use weighted random selection: each variant is chosen with probability proportional to its weight value.
**R-SELECT-005**: When all weight values are zero, implementations **MUST** return the control variant (first entry in `variants`) without erroring.
**R-SELECT-006**: Weighted random selection **MUST** increment the invocation counter for the selected variant before persisting state.
> **Note (normative correction)**: ADR-29618 Rule 9 incorrectly stated that weighted selection “MUST NOT increment any variant counter.” This rule is hereby superseded. Counter increments for weighted selection are required to enable `min_samples` progress tracking and accurate per-run history. The reference implementation (`pick_experiment.cjs`) already implements this correct behavior by calling `recordVariant` unconditionally after both selection paths.
**R-SELECT-007**: When `weight` is provided but its length does not equal the length of `variants`, implementations **MUST** treat `weight` as absent and fall back to round-robin selection (R-SELECT-001).
> **Note (statistical, informative)**: Standard power calculations assume balanced allocations. When weights are non-uniform (e.g., `[70, 30]`), the effective sample size is reduced. The `min_samples` target should be interpreted as the minimum required for the **smaller group**. For a 70/30 split, experimenters should set `min_samples` to the desired count for the 30% arm and expect the 70% arm to accumulate proportionally more observations.
### 5.3 Variant Exposure
[Section titled “5.3 Variant Exposure”](#53-variant-exposure)
**R-SELECT-008**: Implementations **MUST** expose each selected variant as a named step output `steps.pick-experiment.outputs.` and **MUST** also set a combined JSON step output `steps.pick-experiment.outputs.experiments` containing all variant assignments as a serialized JSON object.
**R-SELECT-009**: Experiment names **MUST** be sorted alphabetically when building the `experiments` JSON output to produce deterministic, reproducible output across runs with identical state.
***
## 6. Date-Range Gating
[Section titled “6. Date-Range Gating”](#6-date-range-gating)
**R-DATE-001**: When `start_date` is provided and the current date (UTC, `YYYY-MM-DD` format) is strictly before `start_date`, implementations **MUST** return the control variant without incrementing any counter.
**R-DATE-002**: When `end_date` is provided and the current date (UTC, `YYYY-MM-DD` format) is strictly after `end_date`, implementations **MUST** return the control variant without incrementing any counter.
**R-DATE-003**: Date comparison **MUST** use UTC date. Local timezone offsets **MUST NOT** affect the result.
**R-DATE-004**: When both `start_date` and `end_date` are provided and the current UTC date is within the inclusive range `[start_date, end_date]`, the experiment is active and normal variant selection (§5) applies.
**R-DATE-005**: If `start_date` or `end_date` do not match the `YYYY-MM-DD` pattern, implementations **SHOULD** treat them as absent (ignore silently) rather than hard-failing, to preserve forward compatibility.
***
## 7. State Persistence
[Section titled “7. State Persistence”](#7-state-persistence)
### 7.1 Storage Configuration
[Section titled “7.1 Storage Configuration”](#71-storage-configuration)
**R-STORE-001**: The `experiments:` map **MUST** support a reserved `storage` key whose value is one of `"repo"` (default) or `"cache"`. Any other value **MUST** produce a compile-time warning and fall back to `"repo"`.
**R-STORE-002**: When `storage` is absent, implementations **MUST** behave as if `storage: repo` was specified.
**R-STORE-003**: The `storage` key **MUST NOT** be treated as an experiment name; it **MUST** be excluded from experiment configuration extraction.
### 7.2 `state.json` Format
[Section titled “7.2 state.json Format”](#72-statejson-format)
**R-STORE-004**: The `state.json` file **MUST** be a valid JSON object with the following top-level structure:
```json
{
"counts": {
"": {
"":
}
},
"runs": [
{
"run_id": "",
"timestamp": "",
"assignments": {
"": ""
}
}
]
}
```
**R-STORE-005**: The `runs` array **MUST** be pruned to at most 512 entries (keeping the most recent) to prevent unbounded growth.
**R-STORE-006**: When loading a `state.json` that has no `runs` field (legacy format), implementations **MUST** initialize `runs` to an empty array and continue normally.
**R-STORE-007**: When at least one experiment is assigned on a run, implementations **MUST** append one run record to `state.runs` before persisting. Each record **MUST** contain:
* `run_id`: the value of `GITHUB_RUN_ID`, or `""` when absent.
* `timestamp`: an ISO-8601 UTC timestamp of the selection moment.
* `assignments`: an object mapping each assigned experiment name to its selected variant.
**R-STORE-008**: When no experiments are assigned (e.g., all experiments are outside their date window), implementations **MUST NOT** append a run record or rewrite `state.json`.
### 7.3 `repo` Storage Mode
[Section titled “7.3 repo Storage Mode”](#73-repo-storage-mode)
**R-STORE-REPO-001**: When `storage: repo` is active, the activation job **MUST** load experiment state by fetching `state.json` from the git branch named `experiments/{sanitizedWorkflowID}` via the GitHub REST API (GET /repos/{owner}/{repo}/contents/{path}).
**R-STORE-REPO-002**: A 404 response (branch or file does not exist) **MUST** be treated as an empty initial state; the activation job **MUST NOT** fail.
**R-STORE-REPO-003**: After the activation job completes, a dedicated `push_experiments_state` job **MUST** be generated. This job **MUST**:
* Download the experiment artifact from the current run.
* Commit the updated `state.json` and `assignments.json` to the experiments git branch.
* Declare `permissions: contents: write`.
* Be listed as a dependency of the conclusion job to ensure state is persisted before the workflow terminates.
**R-STORE-REPO-004**: The commit **SHOULD** be made via the GitHub GraphQL `createCommitOnBranch` mutation (producing a verified, signed commit). A plain `git push` **MAY** be used as a fallback when the GraphQL mutation is unavailable.
**R-STORE-REPO-005**: The push step **SHOULD** implement retry logic with exponential backoff (minimum 3 attempts, base delay ≥ 1 second) to handle transient API failures and concurrent push conflicts.
> **Note (race condition, informative)**: When two workflow runs start concurrently, both will read the same `state.json` from the branch before either has committed its update. Both runs will therefore select the same least-used variant. The retry logic in R-STORE-REPO-005 handles write conflicts at push time but does not prevent duplicate variant selections at read time. On low-frequency workflows (daily cron) this is effectively never a problem. On high-frequency workflows (hourly or per-commit), experimenters should account for a small probability of temporarily imbalanced runs. A future revision of this specification **MAY** address this with an optimistic-concurrency guard at the fetch step.
### 7.4 `cache` Storage Mode
[Section titled “7.4 cache Storage Mode”](#74-cache-storage-mode)
**R-STORE-CACHE-001**: When `storage: cache` is explicitly set, the activation job **MUST** restore experiment state from GitHub Actions cache using a key of the form `experiments-{sanitizedWorkflowID}-{GITHUB_RUN_ID}` and a restore-key prefix `experiments-{sanitizedWorkflowID}-`.
**R-STORE-CACHE-002**: The activation job **MUST** save experiment state back to cache after variant selection using `if: always()`.
**R-STORE-CACHE-003**: When `storage: cache` is active, no `push_experiments_state` job **SHALL** be generated.
**R-STORE-CACHE-004**: Implementations **MUST NOT** require `contents: write` permission when `storage: cache` is configured.
> **Note (informative)**: GitHub Actions cache has a 7-day inactivity eviction policy. State accumulated during an experiment may be silently lost over holidays or between infrequent runs. For this reason `repo` is the default storage mode. Use `cache` only when `contents: write` cannot be granted to the workflow.
### 7.5 Experiment Artifact
[Section titled “7.5 Experiment Artifact”](#75-experiment-artifact)
**R-STORE-ARTIFACT-001**: The activation job **MUST** upload the experiment state directory as a GitHub Actions artifact named `{sanitizedWorkflowID}-experiment` (or `experiment` for `workflow_call` triggers) with `if: always()` and a retention period of at least 30 days.
**R-STORE-ARTIFACT-002**: When `assignments.json` exists in the state directory, it **MUST** be included in the artifact alongside `state.json`.
***
## 8. Expression and Template Integration
[Section titled “8. Expression and Template Integration”](#8-expression-and-template-integration)
### 8.1 Compiler Expression Rewriting
[Section titled “8.1 Compiler Expression Rewriting”](#81-compiler-expression-rewriting)
**R-EXPR-001**: The compiler **MUST** rewrite every `${{ experiments. }}` expression in the frontmatter or prompt source to `steps.pick-experiment.outputs.` during the expression extraction phase, so the runtime value is injected by the GitHub Actions expression engine.
**R-EXPR-002**: Each experiment **MUST** be mapped to an environment variable named `GH_AW_EXPERIMENTS_` (uppercased) that resolves to `steps.pick-experiment.outputs.`. This environment variable **MUST** be set in every workflow step that performs prompt interpolation or template substitution.
### 8.2 Handlebars Template Integration
[Section titled “8.2 Handlebars Template Integration”](#82-handlebars-template-integration)
**R-EXPR-003**: Implementations **MUST** substitute `__GH_AW_EXPERIMENTS___` placeholders in the raw prompt text **before** Handlebars template rendering, so that `{{#if experiments. == "value" }}` conditionals evaluate the actual runtime variant.
**R-EXPR-004**: Implementations **MUST NOT** pass raw `__GH_AW_EXPERIMENTS_*__` placeholders to the Handlebars rendering engine; all substitutions **MUST** occur in a prior step.
**R-EXPR-005**: The `isTruthy` helper used in Handlebars conditionals **MUST** treat the string `"no"` as falsy, in addition to the standard falsy values `""`, `"false"`, `"0"`, `undefined`, and `null`. This enables yes/no flag experiments where `{{#if experiments.feature }}` evaluates to false when the `no` variant is active.
> **Note (informative)**: The `"no"` falsy behavior is a deliberate design choice that enables simple boolean-flag experiments (`feature: [yes, no]`). It differs from standard JavaScript truthiness and should be clearly documented for contributors.
***
## 9. Activation Job Structure
[Section titled “9. Activation Job Structure”](#9-activation-job-structure)
**R-JOB-001**: When the `experiments` field is present in the frontmatter, the compiled activation job **MUST** include the experiment steps defined in §9.1 or §9.2 as appropriate.
**R-JOB-002**: Implementations **MUST NOT** inject experiment steps into workflows that do not declare the `experiments` frontmatter field.
**R-JOB-003**: The activation job **MUST** expose a `needs.activation.outputs.experiments` output containing the full JSON variant assignment object so that downstream jobs can reference it via `needs.activation.outputs.experiments`.
### 9.1 `cache` Storage Step Order
[Section titled “9.1 cache Storage Step Order”](#91-cache-storage-step-order)
When `storage: cache`, the activation job **MUST** include the following steps in order:
1. **Restore experiment state** — `actions/cache/restore` with the workflow-specific key.
2. **Pick experiment variants** — `pick_experiment.cjs` via `actions/github-script`.
3. **Save experiment state** — `actions/cache/save` with `if: always()`.
4. **Upload experiment artifact** — `actions/upload-artifact` with `if: always()`.
### 9.2 `repo` Storage Step Order
[Section titled “9.2 repo Storage Step Order”](#92-repo-storage-step-order)
When `storage: repo` (default), the activation job **MUST** include the following steps in order:
1. **Restore experiment state from git** — `load_experiment_state_from_repo.cjs` via `actions/github-script`.
2. **Pick experiment variants** — `pick_experiment.cjs` via `actions/github-script`.
3. **Upload experiment artifact** — `actions/upload-artifact` with `if: always()`.
A separate `push_experiments_state` job (R-STORE-REPO-003) commits the updated state after the activation job completes.
### 9.3 OTEL Resource Attributes
[Section titled “9.3 OTEL Resource Attributes”](#93-otel-resource-attributes)
**R-JOB-004**: After variant selection, when at least one experiment is assigned, `pick_experiment.cjs` **MUST** call `core.exportVariable("OTEL_RESOURCE_ATTRIBUTES", …)` with key-value pairs of the form `experiment.=`, comma-separated when multiple experiments are active.
**R-JOB-005**: When `OTEL_RESOURCE_ATTRIBUTES` is already set, implementations **MUST** append the experiment attributes to the existing value with a comma separator rather than overwriting it.
**R-JOB-006**: When no experiments are assigned, implementations **MUST NOT** modify `OTEL_RESOURCE_ATTRIBUTES`.
***
## 10. Audit CLI Integration
[Section titled “10. Audit CLI Integration”](#10-audit-cli-integration)
### 10.1 Filter Flags
[Section titled “10.1 Filter Flags”](#101-filter-flags)
**R-AUDIT-001**: The `gh aw audit` command **MUST** accept an `--experiment ` flag that filters runs to those with a variant assignment for the named experiment.
**R-AUDIT-002**: The `gh aw audit` command **MUST** accept a `--variant ` flag that, when combined with `--experiment`, further restricts results to runs assigned that exact variant value.
**R-AUDIT-003**: `--variant` used without `--experiment` **MUST** cause a non-zero exit code with an error message that includes a suggestion to add `--experiment`.
**R-AUDIT-004**: When a run is skipped by the filter, an informational message **MUST** be emitted to stderr identifying the run ID, the experiment name, and (when applicable) the required variant.
### 10.2 Run Overview Display
[Section titled “10.2 Run Overview Display”](#102-run-overview-display)
**R-AUDIT-005**: The run Overview section **MUST** include an `Experiment` field when the run’s experiment artifact contains one or more assignments.
**R-AUDIT-006**: The experiment label **MUST** be formatted as a comma-separated, alphabetically sorted list of `name=variant` pairs (e.g., `caveman=yes, style=concise`).
**R-AUDIT-007**: The `Experiment` field **MUST** be omitted from console and JSON output when no experiment assignments are present (`omitempty` semantics).
### 10.3 Per-Run Assignment Lookup
[Section titled “10.3 Per-Run Assignment Lookup”](#103-per-run-assignment-lookup)
**R-AUDIT-008**: When `state.runs` is non-empty and the last record’s `assignments` map is non-empty, the audit reporter **MUST** use that record’s assignments directly as the current-run experiment data.
**R-AUDIT-009**: When `state.runs` is empty, absent, or the last record’s `assignments` map is empty, the audit reporter **MUST** fall back to the max-count heuristic: the variant with the highest cumulative count is assumed to have been selected on the most recent run; ties are broken by sorted variant order.
### 10.4 Filter Application
[Section titled “10.4 Filter Application”](#104-filter-application)
**R-AUDIT-010**: Implementations **MUST** apply the experiment/variant filter before calling any report-rendering code. A filtered-out run **MUST** return `nil`, not an error.
**R-AUDIT-011**: Implementations **MUST** apply the filter in both the cached-summary path and the fresh-processing path for consistent behavior.
**R-AUDIT-012**: Implementations **SHOULD** extract experiment data at most once per `AuditWorkflowRun` invocation to avoid redundant artifact reads.
**R-AUDIT-013**: When neither `--experiment` nor `--variant` is set, implementations **MUST NOT** read the experiment artifact solely for filtering purposes.
***
## 11. Statistical Analysis and Reporting
[Section titled “11. Statistical Analysis and Reporting”](#11-statistical-analysis-and-reporting)
This section applies to the **Level 3 — Complete** conformance class (§2.2) and to any automated workflow that reports on experiment outcomes.
### 11.1 Per-Run Assignment Source
[Section titled “11.1 Per-Run Assignment Source”](#111-per-run-assignment-source)
**R-STAT-001**: Reporting tools that consume `state.json` files **MUST** derive per-run variant assignments from the `state.runs` array when it is present and non-empty.
**R-STAT-002**: Reporting tools **MUST NOT** use the cumulative-count delta inference method (comparing consecutive snapshots) as the primary assignment source when `state.runs` is available. The delta method **MAY** be used as a fallback for legacy state files with no `runs` array.
> **Note (informative)**: The delta method is fragile — it fails when multiple runs complete between downloaded snapshots, when runs are cancelled before the experiment step, or when `state.json` is fetched from different points in the artifact history. The `runs` array, introduced in v1.1.0 (ADR-29985), provides exact, auditable per-run assignment records.
### 11.2 Statistical Tests
[Section titled “11.2 Statistical Tests”](#112-statistical-tests)
**R-STAT-003**: When `analysis_type` is declared for an experiment, reporting tools **SHOULD** use the specified test for significance analysis:
| `analysis_type` value | Test to apply |
| --------------------- | ------------------------------------------------------------ |
| `t_test` | Welch’s two-sample t-test (does not assume equal variance) |
| `mann_whitney` | Mann-Whitney U non-parametric rank test |
| `proportion_test` | Two-proportion z-test |
| `bayesian_ab` | Bayesian A/B analysis (posterior probability of superiority) |
**R-STAT-004**: When `analysis_type` is absent, reporting tools **SHOULD** default to the two-proportion z-test for binary outcomes (success/failure) and Welch’s t-test for continuous metrics (e.g., duration).
### 11.3 Multiple Comparison Correction
[Section titled “11.3 Multiple Comparison Correction”](#113-multiple-comparison-correction)
**R-STAT-005**: When an experiment declares K ≥ 3 variants and reporting tools perform pairwise comparisons against the control, the significance threshold **SHOULD** be adjusted using the Bonferroni correction: `α_adjusted = 0.05 / (K − 1)`.
> **Note (informative)**: Without correction, the probability of at least one false positive across K−1 pairwise tests at α = 0.05 is approximately 1 − (1 − 0.05)^(K−1). For K = 3 this is \~9.75%; for K = 5 it exceeds 18%. The Bonferroni correction is conservative but simple. The Holm-Bonferroni step-down procedure is a less conservative alternative.
**R-STAT-006**: When a multiple-comparison correction is applied, reporting tools **MUST** state the correction method and the adjusted α threshold in the report output.
### 11.4 Minimum Sample Size Gate
[Section titled “11.4 Minimum Sample Size Gate”](#114-minimum-sample-size-gate)
**R-STAT-007**: Reporting tools **MUST NOT** issue a PROMOTE recommendation for any variant until all variants in the experiment have accumulated at least `min_samples` runs (or 20 if `min_samples` is not declared). When any variant is below threshold, the recommendation **MUST** be EXTEND.
**R-STAT-008**: When weights are non-uniform (§5.2), the `min_samples` target applies to the **smallest expected group**. For a `weight: [70, 30]` experiment with `min_samples: 30`, the control arm is not eligible for analysis until the 30% arm has at least 30 observations, even if the 70% arm has accumulated many more.
### 11.5 Guardrail Evaluation
[Section titled “11.5 Guardrail Evaluation”](#115-guardrail-evaluation)
**R-STAT-009**: Reporting tools that evaluate `guardrail_metrics` **MUST** emit a `GUARDRAIL_FAILED` status for any variant that violates a declared threshold, and **MUST** override the recommendation to ABANDON regardless of the primary-metric p-value.
**R-STAT-010**: Multi-variant experiments **MUST** show guardrail pass/fail status per variant, not aggregated across the experiment.
### 11.6 Reporting Workflow Permissions
[Section titled “11.6 Reporting Workflow Permissions”](#116-reporting-workflow-permissions)
**R-STAT-011**: Any automated workflow that posts comments to issues (e.g., via `notify.issue` or step-based issue comment creation) **MUST** declare `permissions: issues: write` in its frontmatter.
**R-STAT-012**: Any automated workflow that posts discussions **MUST** declare `permissions: discussions: write`.
***
## 12. Simultaneous Experiments and Interaction Effects
[Section titled “12. Simultaneous Experiments and Interaction Effects”](#12-simultaneous-experiments-and-interaction-effects)
**R-MULTI-001**: Each experiment in the `experiments` map **MUST** be assigned independently. The selection algorithm for one experiment **MUST NOT** depend on the selected variant of any other experiment.
**R-MULTI-002**: Implementations **SHOULD NOT** run more than three experiments simultaneously in a single workflow. When more than three experiments are active, a compile-time warning **SHOULD** be emitted.
> **Note (statistical, informative)**: When two or more experiments are active simultaneously, observed differences in outcome metrics can be caused by either experiment individually or by their interaction (i.e., a specific combination of variant values). This violation of the Stable Unit Treatment Value Assumption (SUTVA) inflates the risk of misattribution. For example, if `prompt_style=concise` and `emoji_density=heavy` are both active, it is impossible to determine from pairwise analysis alone whether a change in output quality was caused by verbosity, emoji use, or the combination. Experimenters who need to measure interactions **MUST** use a full factorial design and ensure sufficient sample size for all K₁ × K₂ × … cell combinations.
**R-MULTI-003**: Reporting tools **MUST** note in their output when multiple experiments were simultaneously active on runs included in the analysis window, to alert reviewers to potential confounding.
**R-MULTI-004**: Experiments that change the `engine:` frontmatter key **MUST NOT** be implemented within a single workflow file. Engine-switching experiments **MUST** use separate compiled workflow files (one per variant), which can then be compared via their respective GitHub Actions run metrics.
**R-MULTI-005**: When two or more experiments are simultaneously active in the same analysis window, reporting tools **MUST** detect and bound interaction risk by preserving the full assignment vector per run and evaluating whether each observed combination cell has sufficient sample coverage. If interaction effects cannot be bounded (for example, sparse cells below `min_samples`), the report **MUST** emit an explicit interaction-risk status and **MUST NOT** recommend PROMOTE for affected variants.
### 12.1 Conflict Resolution Norms
[Section titled “12.1 Conflict Resolution Norms”](#121-conflict-resolution-norms)
A **conflict** occurs when two or more simultaneously active experiments would assign incompatible configurations to the same workflow run. This subsection defines normative behavior for each storage mode.
**R-CONFLICT-001 (general)**: When two experiments assign variants that together produce a logically invalid workflow configuration (e.g., two `engine:` variants via separate experiment keys), the compiler **MUST** reject the workflow at compile time with a descriptive error. Runtime conflict detection is **NOT** a substitute for compile-time validation.
#### 12.1.1 Conflict Resolution for `repo` Storage Mode
[Section titled “12.1.1 Conflict Resolution for repo Storage Mode”](#1211-conflict-resolution-for-repo-storage-mode)
**R-CONFLICT-REPO-001**: Under `repo` storage, each experiment’s variant selection reads and writes an independent key in `state.json`. There is no shared mutable state between experiments at the selection layer. Variant assignments for experiment A **MUST NOT** block or override variant assignments for experiment B, even when both experiments are active on the same run.
**R-CONFLICT-REPO-002**: When a concurrent write conflict is detected at push time (e.g., a non-fast-forward rejection from the GitHub API), the push step **MUST** retry with the merged state from both runs. The retry **MUST NOT** discard either run’s assignment record.
**R-CONFLICT-REPO-003**: If two concurrent runs select the same least-used variant for the same experiment (a read-time race), both selections are considered valid. The run records **MUST** reflect each run’s independently selected variant. No conflict error is raised for this condition.
#### 12.1.2 Conflict Resolution for `cache` Storage Mode
[Section titled “12.1.2 Conflict Resolution for cache Storage Mode”](#1212-conflict-resolution-for-cache-storage-mode)
**R-CONFLICT-CACHE-001**: Under `cache` storage, GitHub Actions cache is eventually consistent across concurrent runs. When two runs attempt to save conflicting cache entries under the same key, GitHub Actions will store one entry and silently drop the other. Implementations **MUST** treat this as an acceptable data loss (see §7.4 informative note on cache eviction) and **MUST NOT** treat a missing cache restore as an error condition.
**R-CONFLICT-CACHE-002**: Because `cache` storage does not provide atomic read-modify-write semantics, implementations using `cache` mode **MUST** document to users that high-concurrency workflows may experience elevated variant imbalance compared to `repo` mode.
#### 12.1.3 Conflict Resolution for Mixed Storage Mode
[Section titled “12.1.3 Conflict Resolution for Mixed Storage Mode”](#1213-conflict-resolution-for-mixed-storage-mode)
**R-CONFLICT-MIX-001**: All experiments within a single workflow **MUST** share the same `storage` mode. Mixed-mode configurations (some experiments in `repo`, others in `cache`) are **NOT SUPPORTED** and **MUST** produce a compile-time error.
**R-CONFLICT-MIX-002**: This restriction exists because the `storage` key is a single top-level field in the `experiments` map that applies uniformly to all experiments in that map. Workflow authors who require different storage modes for different experiments **MUST** split them into separate workflow files.
***
## 13. Security Considerations
[Section titled “13. Security Considerations”](#13-security-considerations)
### 13.1 State File Integrity
[Section titled “13.1 State File Integrity”](#131-state-file-integrity)
The experiment state is stored in a git branch (`repo` mode) or GitHub Actions cache (`cache` mode). Both backends are protected by repository access controls. However:
* Any user with write access to the repository can modify `state.json` on the experiments branch, potentially manipulating variant counters or forging run records.
* Implementers that require tamper-evident state **SHOULD** use signed commits via the GitHub GraphQL `createCommitOnBranch` mutation (R-STORE-REPO-004).
### 13.2 Prompt Injection via Variant Values
[Section titled “13.2 Prompt Injection via Variant Values”](#132-prompt-injection-via-variant-values)
Variant strings declared in frontmatter are static strings set by the workflow author. They are not derived from user-supplied input and therefore do not introduce prompt injection risk at the frontmatter level. Workflow authors **MUST NOT** use runtime user input (e.g., issue titles, PR bodies) as variant values.
### 13.3 OTEL Attribute Leakage
[Section titled “13.3 OTEL Attribute Leakage”](#133-otel-attribute-leakage)
Experiment assignments exported as OTEL resource attributes (§9.3) may be visible in distributed-tracing backends. Variant names and experiment names **SHOULD NOT** embed sensitive information.
### 13.4 Permission Minimization
[Section titled “13.4 Permission Minimization”](#134-permission-minimization)
* The `repo` storage mode requires `contents: write`. Workflows **SHOULD** limit all other permissions to `read` to minimize the blast radius of a compromised token.
* Reporting workflows that post comments require `issues: write` or `discussions: write` (§11.6). These permissions **SHOULD** be granted only to the specific reporting workflow, not to the experiment-running workflow itself.
***
## 14. Compliance Testing
[Section titled “14. Compliance Testing”](#14-compliance-testing)
### 14.1 Test Suite Requirements
[Section titled “14.1 Test Suite Requirements”](#141-test-suite-requirements)
Conformance at each level is verified by the following test categories.
#### 14.1.1 Schema Tests (Level 1)
[Section titled “14.1.1 Schema Tests (Level 1)”](#1411-schema-tests-level-1)
| Test ID | Requirement | Description |
| ------------ | ------------ | ---------------------------------------------------------------- |
| T-SCHEMA-001 | R-SCHEMA-005 | Reject bare-array with fewer than 2 variants |
| T-SCHEMA-002 | R-SCHEMA-003 | Skip and warn on invalid experiment name |
| T-SCHEMA-003 | R-SCHEMA-007 | Reject object form with `variants` containing < 2 entries |
| T-SCHEMA-004 | R-SCHEMA-011 | Reject guardrail with invalid threshold pattern |
| T-SCHEMA-005 | R-SCHEMA-013 | Reject `notify` object with unknown keys |
| T-SCHEMA-006 | R-SCHEMA-001 | Compile workflow without `experiments:` field — output unchanged |
#### 14.1.2 Variant Selection Tests (Level 1)
[Section titled “14.1.2 Variant Selection Tests (Level 1)”](#1412-variant-selection-tests-level-1)
| Test ID | Requirement | Description |
| ------------ | ------------ | ------------------------------------------------------- |
| T-SELECT-001 | R-SELECT-001 | Round-robin: select variant with lowest count |
| T-SELECT-002 | R-SELECT-002 | Round-robin: random tie-breaking on first run |
| T-SELECT-003 | R-SELECT-003 | Round-robin: counter incremented after selection |
| T-SELECT-004 | R-SELECT-004 | Weighted: selection probability proportional to weights |
| T-SELECT-005 | R-SELECT-006 | Weighted: counter incremented after selection |
| T-SELECT-006 | R-SELECT-005 | Weighted: all-zero weights return control variant |
| T-SELECT-007 | R-SELECT-007 | Weighted: mismatched length falls back to round-robin |
#### 14.1.3 Expression Integration Tests (Level 1)
[Section titled “14.1.3 Expression Integration Tests (Level 1)”](#1413-expression-integration-tests-level-1)
| Test ID | Requirement | Description |
| ---------- | ----------- | --------------------------------------------------------- |
| T-EXPR-001 | R-EXPR-001 | `${{ experiments.x }}` rewritten to step output reference |
| T-EXPR-002 | R-EXPR-003 | Placeholder substituted before Handlebars rendering |
| T-EXPR-003 | R-EXPR-005 | `"no"` treated as falsy in `isTruthy` |
| T-EXPR-004 | R-EXPR-004 | Raw placeholder not passed to Handlebars engine |
#### 14.1.4 State Persistence Tests (Level 2)
[Section titled “14.1.4 State Persistence Tests (Level 2)”](#1414-state-persistence-tests-level-2)
| Test ID | Requirement | Description |
| ----------- | ----------------- | --------------------------------------------------------- |
| T-STORE-001 | R-STORE-REPO-002 | Empty state on first run (404 branch) |
| T-STORE-002 | R-STORE-004 | Valid `state.json` structure written after run |
| T-STORE-003 | R-STORE-007 | Run record appended with correct fields |
| T-STORE-004 | R-STORE-005 | `runs` pruned to ≤ 512 entries |
| T-STORE-005 | R-STORE-006 | Legacy state (no `runs` field) initialized to empty array |
| T-STORE-006 | R-STORE-CACHE-004 | No `contents: write` required for cache mode |
#### 14.1.5 Audit CLI Tests (Level 2)
[Section titled “14.1.5 Audit CLI Tests (Level 2)”](#1415-audit-cli-tests-level-2)
| Test ID | Requirement | Description |
| ----------- | ----------- | ---------------------------------------------------------- |
| T-AUDIT-001 | R-AUDIT-003 | `--variant` without `--experiment` returns non-zero exit |
| T-AUDIT-002 | R-AUDIT-008 | Assignment read from `state.runs` when available |
| T-AUDIT-003 | R-AUDIT-009 | Fallback to max-count heuristic for legacy state |
| T-AUDIT-004 | R-AUDIT-005 | Overview `Experiment` field present when assignments exist |
| T-AUDIT-005 | R-AUDIT-007 | `Experiment` field omitted when no assignments |
#### 14.1.6 Statistical Reporting Tests (Level 3)
[Section titled “14.1.6 Statistical Reporting Tests (Level 3)”](#1416-statistical-reporting-tests-level-3)
| Test ID | Requirement | Description |
| ---------- | ----------- | ---------------------------------------------------------- |
| T-STAT-001 | R-STAT-001 | Assignments derived from `state.runs`, not delta inference |
| T-STAT-002 | R-STAT-005 | Bonferroni correction applied for K ≥ 3 variants |
| T-STAT-003 | R-STAT-007 | PROMOTE withheld until all variants reach `min_samples` |
| T-STAT-004 | R-STAT-009 | GUARDRAIL\_FAILED forces ABANDON recommendation |
| T-STAT-005 | R-STAT-011 | Reporting workflow declares `issues: write` |
### 14.2 Compliance Checklist
[Section titled “14.2 Compliance Checklist”](#142-compliance-checklist)
| Requirement | Test ID | Level | Status |
| ---------------- | ------------ | ----- | ----------- |
| R-SCHEMA-001 | T-SCHEMA-006 | 1 | Required |
| R-SCHEMA-005 | T-SCHEMA-001 | 1 | Required |
| R-SELECT-001 | T-SELECT-001 | 1 | Required |
| R-SELECT-002 | T-SELECT-002 | 1 | Required |
| R-SELECT-003 | T-SELECT-003 | 1 | Required |
| R-SELECT-006 | T-SELECT-005 | 1 | Required |
| R-EXPR-001 | T-EXPR-001 | 1 | Required |
| R-EXPR-005 | T-EXPR-003 | 1 | Required |
| R-STORE-002 | — | 2 | Required |
| R-STORE-REPO-002 | T-STORE-001 | 2 | Required |
| R-STORE-007 | T-STORE-003 | 2 | Required |
| R-AUDIT-003 | T-AUDIT-001 | 2 | Required |
| R-AUDIT-008 | T-AUDIT-002 | 2 | Required |
| R-STAT-001 | T-STAT-001 | 3 | Required |
| R-STAT-005 | T-STAT-002 | 3 | Recommended |
| R-STAT-007 | T-STAT-003 | 3 | Required |
| R-STAT-011 | T-STAT-005 | 3 | Required |
***
## 15. References
[Section titled “15. References”](#15-references)
### Normative References
[Section titled “Normative References”](#normative-references)
* **\[RFC 2119]** Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, RFC 2119, March 1997.
* **\[ADR-29534]** gh-aw maintainers, “Frontmatter A/B Experiments with Balanced Variant Selection”, 2026-05-01. `docs/adr/29534-frontmatter-ab-experiments-variant-selection.md`
* **\[ADR-29618]** gh-aw maintainers, “Rich Experiment Metadata Schema Extension with Weighted Selection and Date Gating”, 2026-05-01. `docs/adr/29618-rich-experiment-metadata-schema-extension.md` *(normative sections superseded by §5.2 of this document)*
* **\[ADR-29628]** gh-aw maintainers, “Add `--experiment` and `--variant` Filter Flags to `gh aw audit`”, 2026-05-01. `docs/adr/29628-experiment-variant-filter-flags-for-audit.md`
* **\[ADR-29985]** gh-aw maintainers, “Experiment Per-Run State, OTEL Integration, and Schema Extensions”, 2026-05-03. `docs/adr/29985-experiment-per-run-state-otel-integration-and-schema-extensions.md`
* **\[ADR-29996]** gh-aw maintainers, “Experiment State Storage — Git Branch as Default, Cache as Fallback”, 2026-05-03. `docs/adr/29996-experiment-state-git-branch-storage.md`
### Informative References
[Section titled “Informative References”](#informative-references)
* **\[SUTVA]** Rubin, D. B., “Estimating Causal Effects of Treatments in Randomized and Nonrandomized Studies”, *Journal of Educational Psychology*, 66(5):688–701, 1974. (Stable Unit Treatment Value Assumption)
* **\[BONFERRONI]** Dunn, O. J., “Multiple Comparisons Among Means”, *Journal of the American Statistical Association*, 56(293):52–64, 1961.
* **\[WELCH-TTEST]** Welch, B. L., “The Generalization of Student’s Problem When Several Different Population Variances are Involved”, *Biometrika*, 34(1/2):28–35, 1947.
* **\[GitHub Actions Cache]** GitHub Docs, “Caching dependencies to speed up workflows”.
***
## Appendices
[Section titled “Appendices”](#appendices)
### Appendix A: Full Object-Form Example
[Section titled “Appendix A: Full Object-Form Example”](#appendix-a-full-object-form-example)
```yaml
---
on:
schedule: daily on weekdays
engine: copilot
permissions:
contents: read
pull-requests: read
experiments:
storage: repo
prompt_style:
variants: [concise, detailed, step_by_step]
description: "Test whether verbosity level affects output quality"
hypothesis: "H0: no change in effective_tokens. H1: concise reduces by >=15%"
metric: effective_tokens
secondary_metrics: [duration_ms, discussion_word_count]
guardrail_metrics:
- name: success_rate
threshold: ">=0.95"
- name: empty_output_rate
threshold: "==0"
weight: [40, 40, 20]
min_samples: 30
start_date: "2026-05-01"
end_date: "2026-08-01"
issue: 1234
analysis_type: t_test
tags: [cost, prompting, verbosity]
notify:
issue: 1234
---
Summarize the pull requests merged today.
{{#if experiments.prompt_style == "concise" }}
Write a maximum of 5 bullet points.
{{#else if experiments.prompt_style == "detailed" }}
Write a structured report with sections for new features, bug fixes, refactors, and docs.
{{#else}}
Write a numbered step-by-step walkthrough of each change with rationale.
{{#endif}}
```
### Appendix A2: Weighted Variant Selection — Worked Example
[Section titled “Appendix A2: Weighted Variant Selection — Worked Example”](#appendix-a2-weighted-variant-selection--worked-example)
This appendix walks through the probability math for a three-variant `weighted` experiment to illustrate how the `weight` array maps to selection probability, how counters are updated, and how balance is maintained over many runs.
#### A2.1 Scenario Setup
[Section titled “A2.1 Scenario Setup”](#a21-scenario-setup)
An experiment named `response_tone` has three variants with non-uniform weights:
```yaml
experiments:
storage: repo
response_tone:
variants: [formal, casual, neutral]
weight: [20, 50, 30]
```
The weight values are **relative proportions**, not absolute percentages. The implementation normalizes them to compute probabilities:
```plaintext
total_weight = 20 + 50 + 30 = 100
P(formal) = 20 / 100 = 0.20 (20%)
P(casual) = 50 / 100 = 0.50 (50%)
P(neutral) = 30 / 100 = 0.30 (30%)
```
For a 10-run experiment sequence, the **expected** variant distribution is:
| Variant | Weight | Expected runs (of 10) |
| ------- | ------ | --------------------- |
| formal | 20 | 2 |
| casual | 50 | 5 |
| neutral | 30 | 3 |
#### A2.2 Selection Algorithm (Weighted Random)
[Section titled “A2.2 Selection Algorithm (Weighted Random)”](#a22-selection-algorithm-weighted-random)
The `weighted` algorithm draws a uniform random number `r ∈ [0, 1)` and maps it to a variant via cumulative weight:
```plaintext
Cumulative ranges:
[0.00, 0.20) → formal
[0.20, 0.70) → casual
[0.70, 1.00) → neutral
```
Example draws:
| r | Selected variant |
| ---- | ---------------- |
| 0.11 | formal |
| 0.45 | casual |
| 0.72 | neutral |
| 0.19 | formal |
| 0.68 | casual |
#### A2.3 Counter Updates
[Section titled “A2.3 Counter Updates”](#a23-counter-updates)
After each run, the counter for the selected variant is incremented in `state.json`. After 10 runs with the distribution above, a typical `counts` object is:
```json
{
"counts": {
"response_tone": {
"formal": 2,
"casual": 5,
"neutral": 3
}
}
}
```
Per R-SELECT-006, the `weighted` algorithm **MUST** increment invocation counters after every selection. This allows the audit CLI and reporting workflows to verify that observed variant frequencies approximate the declared weights over time.
#### A2.4 Long-Run Balance Verification
[Section titled “A2.4 Long-Run Balance Verification”](#a24-long-run-balance-verification)
Over N runs, the observed frequency for variant v should converge to `weight[v] / total_weight` by the Law of Large Numbers. Reporting workflows SHOULD flag experiments where any variant’s observed frequency deviates from its target weight by more than ±10 percentage points over at least 30 runs, as this may indicate a misconfigured `weight` array or a bug in the selection implementation.
For the example above, after 100 runs:
| Variant | Expected runs | Acceptable range (±10 pp) |
| ------- | ------------- | ------------------------- |
| formal | 20 | 10 – 30 |
| casual | 50 | 40 – 60 |
| neutral | 30 | 20 – 40 |
#### A2.5 Contrast with Balanced Round-Robin
[Section titled “A2.5 Contrast with Balanced Round-Robin”](#a25-contrast-with-balanced-round-robin)
The `balanced` (least-used) algorithm ignores weights and selects the least-run variant deterministically. Use `weighted` when you intentionally want unequal traffic allocation (e.g., to expose fewer users to an experimental variant while still gathering comparative data). Use `balanced` when you want equal allocation and maximum statistical efficiency per total run count.
### Appendix B: `state.json` Schema
[Section titled “Appendix B: state.json Schema”](#appendix-b-statejson-schema)
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["counts"],
"properties": {
"counts": {
"type": "object",
"additionalProperties": {
"type": "object",
"additionalProperties": { "type": "integer", "minimum": 0 }
}
},
"runs": {
"type": "array",
"maxItems": 512,
"items": {
"type": "object",
"required": ["run_id", "timestamp", "assignments"],
"properties": {
"run_id": { "type": "string" },
"timestamp": { "type": "string", "format": "date-time" },
"assignments": {
"type": "object",
"additionalProperties": { "type": "string" }
}
}
}
}
}
}
```
### Appendix C: Sample Size Reference
[Section titled “Appendix C: Sample Size Reference”](#appendix-c-sample-size-reference)
For a two-proportion test with 80% statistical power and α = 0.05 (two-tailed), the approximate minimum runs per variant are:
| Minimum Detectable Effect (pp) | Runs per variant |
| ------------------------------ | ---------------- |
| 5 | \~620 |
| 10 | \~160 |
| 15 | \~70 |
| 20 | \~40 |
| 30 | \~20 |
> **Note for weighted experiments**: When `weight` is non-uniform, apply these figures to the **smaller group**. For a 70/30 split aiming to detect a 10 pp effect, you need \~160 runs in the 30% arm (≈ 533 total runs).
### Appendix D: Known Limitations
[Section titled “Appendix D: Known Limitations”](#appendix-d-known-limitations)
1. **Read-time race condition**: Concurrent runs with `repo` storage may read stale state and select the same variant. See R-STORE-REPO-005 and the informative note in §7.3.
2. **Interaction effects**: Running multiple experiments simultaneously can produce unattributable results. See §12 and R-MULTI-002.
3. **Engine-switching experiments**: Changing the `engine:` key requires separate workflow files; see R-MULTI-004.
4. **`analysis_type` advisory only**: Reporting workflows that do not implement all four statistical tests will fall back to defaults. The field documents intent; it does not enforce a specific computation path.
5. **State branch growth**: The experiments git branch grows monotonically. Operators **MAY** prune old commits from the experiments branch without affecting the current state.
### Sync Follow-ups (May 2026 Expert Review)
[Section titled “Sync Follow-ups (May 2026 Expert Review)”](#sync-follow-ups-may-2026-expert-review)
This appendix itemizes corrective follow-ups referenced in the abstract.
* **FR-001 (implemented via R-SELECT-006)**: Weighted selection increments invocation counters after each selection.
* **FR-002 (implemented via R-STAT-001/R-STAT-002)**: Reporting uses `state.runs` assignment records instead of count-delta inference.
* **FR-003 (implemented via R-STAT-011/R-STAT-012)**: Reporting workflows that write issues/discussions declare explicit write permissions.
* **FR-004 (implemented via R-MULTI-005)**: Concurrent-experiment interaction effects are explicitly detected and bounded before promotion decisions.
* **FR-005 (implemented via daily-experiment-report workflow update)**: Reporting guidance now includes a factorial-interaction helper for K₁×K₂ cell-level significance output and sparse-cell risk surfacing.
* **FR-006 (implemented via compiler diagnostics)**: The compiler now emits a warning when more than one experiment is active and weighted traffic is configured, indicating potential sparse interaction cells.
***
## Change Log
[Section titled “Change Log”](#change-log)
### Version 1.1.0 (Draft) — 2026-05-12
[Section titled “Version 1.1.0 (Draft) — 2026-05-12”](#version-110-draft--2026-05-12)
* **Added**: Daily reporting helper guidance for factorial K₁×K₂ interaction cell significance output.
* **Added**: Compiler warning requirement for sparse interaction-cell risk when multiple experiments and weighted traffic are configured.
* **Updated**: Sync Follow-ups appendix to replace v1.1.0 TODOs with implemented corrective items.
### Version 1.0.1 (Draft) — 2026-05-07
[Section titled “Version 1.0.1 (Draft) — 2026-05-07”](#version-101-draft--2026-05-07)
* **Added**: R-MULTI-005 requiring interaction-risk detection/bounding for simultaneous experiments.
* **Added**: Sync Follow-ups appendix with itemized May 2026 expert-review corrective items and owned TODOs.
### Version 1.0.0 (Draft) — 2026-05-03
[Section titled “Version 1.0.0 (Draft) — 2026-05-03”](#version-100-draft--2026-05-03)
* **Initial publication** consolidating ADR-29534, ADR-29618, ADR-29628, ADR-29985, and ADR-29996.
* **Correction**: R-SELECT-006 supersedes ADR-29618 Rule 9 — weighted selection MUST increment invocation counters (was incorrectly stated as MUST NOT; the reference implementation already implements the correct behavior).
* **Added**: R-STAT-001/R-STAT-002 — reporting tools MUST use `state.runs` for per-run assignment lookup, not the fragile delta-count inference method.
* **Added**: R-STAT-005/R-STAT-006 — Bonferroni correction SHOULD be applied for K ≥ 3 variants to control family-wise error rate.
* **Added**: R-STAT-008 — `min_samples` applies to the smallest expected group when weights are non-uniform.
* **Added**: R-STAT-011/R-STAT-012 — reporting workflows MUST declare `issues: write` / `discussions: write` when posting comments.
* **Added**: R-MULTI-002/R-MULTI-003 — warning for > 3 simultaneous experiments; interaction effects must be noted in reports.
* **Added**: §13 Security Considerations — state integrity, prompt injection, OTEL leakage, permission minimization.
* **Added**: Appendix C (sample size reference) and Appendix D (known limitations).
* **Informative note**: `storage: cache` default changed to `storage: repo` in ADR-29996; any documentation or issue templates that still refer to “cache-based” assignment should be updated.
***
*Copyright 2026 GitHub, Inc. All rights reserved. This specification is maintained by the gh-aw project team.*
# Maintaining Repos with Agentic Workflows
> How to use repo-assist, safe-outputs, and integrity filtering to manage an open-source repository at scale — controlling what agents can do, filtering untrusted input, and debugging failures.
Open-source maintainers face a unique challenge when running agentic workflows: anyone can open an issue or PR, triggering agent runs that consume compute and tokens — but not every contributor is equally trustworthy. gh-aw addresses this with two complementary safety mechanisms:
* **Safe-outputs** — The primary mechanism for controlling *what an agent can do*. Every GitHub mutation (opening issues, commenting, creating PRs) must be explicitly declared; anything not listed is blocked.
* **Integrity filtering** — The primary mechanism for controlling *what content the agent sees*. Content from untrusted authors is filtered from the agent’s context before the run starts.
Together they form a defense-in-depth model: integrity filtering keeps untrusted content out of the agent’s context, and safe-outputs ensure the agent can only produce authorized side-effects. This guide shows how to use [Repo Assist](https://github.com/githubnext/agentics/blob/main/docs/repo-assist.md) as the primary entry point for managing incoming work, and how to configure both mechanisms so your repository scales safely.
## Repo Assist as Your Triage Layer
[Section titled “Repo Assist as Your Triage Layer”](#repo-assist-as-your-triage-layer)
[Repo Assist](https://github.com/githubnext/agentics/blob/main/docs/repo-assist.md) is a workflow that runs on every new issue or PR, classifies the content, and routes work to the right place. It is the recommended starting point for any public repository because it:
* Sees all incoming content (including from untrusted users), so nothing is silently ignored.
* Applies lightweight, low-cost classification (labels, comments) rather than heavy agent actions.
* Acts as a gate that downstream code-modifying agents depend on before they run.
## Controlling Workflow Outputs with Safe-Outputs
[Section titled “Controlling Workflow Outputs with Safe-Outputs”](#controlling-workflow-outputs-with-safe-outputs)
Safe-outputs is the primary mechanism for controlling what a workflow can do. Every action that produces a side-effect on GitHub — labeling an issue, posting a comment, opening a pull request, merging — must be explicitly declared in the `safe-outputs:` block. If an action isn’t listed, the runtime blocks it before it reaches the API.
This is what makes it safe to run repo-assist with `min-integrity: unapproved`: even if the agent were to generate an instruction to open a PR or close an issue, the runtime would reject it because those outputs weren’t declared.
The available safe-outputs map directly to GitHub actions:
| Safe-output | What it allows |
| ---------------------- | ----------------------------------- |
| `label-issue` | Apply or remove labels on an issue |
| `comment-issue` | Post a comment on an issue |
| `comment-pull-request` | Post a comment on a pull request |
| `create-pull-request` | Open a new pull request |
| `merge-pull-request` | Merge a pull request (experimental) |
| `close-issue` | Close an issue |
| `create-issue` | Open a new issue |
| `assign-issue` | Assign an issue to a user or team |
## Controlling Workflow Inputs with Integrity Filtering
[Section titled “Controlling Workflow Inputs with Integrity Filtering”](#controlling-workflow-inputs-with-integrity-filtering)
Integrity filtering is the primary mechanism for controlling what content the agent sees. It evaluates the author of each issue, PR, or comment and removes items that don’t meet the configured trust threshold — before the agent’s context is assembled. Every public repository automatically applies `min-integrity: approved` as a baseline — repo-assist overrides this to `unapproved` so it can see issues from contributors and first-time contributors, not just trusted members.
The four configurable levels, from most to least restrictive:
| Level | Who qualifies |
| ------------ | -------------------------------------------------------------------------------------------------------------- |
| `merged` | PRs merged into the default branch; commits reachable from main |
| `approved` | Owners, members, collaborators; non-fork PRs on public repos; recognized bots (`dependabot`, `github-actions`) |
| `unapproved` | Contributors who have had a PR merged before; first-time contributors |
| `none` | All content including users with no prior relationship |
Choose based on what the workflow does:
* **Repo-assist / triage workflows**: `unapproved` — classify content from contributors and first-time contributors without acting on it.
* **Code-modifying workflows** (open PRs, apply patches, close issues): `approved` or `merged` — only act on trusted input.
* **Spam detection or analytics**: `none` — see everything, but produce no direct GitHub mutations.
### Reactions as Trust Signals
[Section titled “Reactions as Trust Signals”](#reactions-as-trust-signals)
Maintainers can use GitHub reactions (, ) to promote content past the integrity filter without modifying labels. This is useful in repo-assist workflows where a maintainer wants to fast-track an external contribution.
To enable reactions, add the `integrity-reactions` feature flag:
```aw
features:
integrity-reactions: true
tools:
github:
min-integrity: approved
```
The compiler handles the rest — when `integrity-reactions: true` is set, it automatically:
* Enables the CLI proxy (`cli-proxy: true`), which is required for reaction-based integrity decisions
* Injects default endorsement reactions: `THUMBS_UP`, `HEART`
* Injects default disapproval reactions: `THUMBS_DOWN`, `CONFUSED`
* Uses `endorser-min-integrity: approved` (only reactions from owners, members, and collaborators count)
* Uses `disapproval-integrity: none` (a disapproval reaction demotes content to `none`)
These defaults mean that when a trusted member (owner, member, or collaborator) adds a or reaction to an issue or comment, the item’s integrity is promoted to `approved` — making it visible to agents using `min-integrity: approved`. Conversely, a or reaction from a trusted member demotes the item to `none`.
See the [Integrity Filtering Reference](/gh-aw/reference/integrity/) for complete configuration details.
## Scaling Strategies
[Section titled “Scaling Strategies”](#scaling-strategies)
### Token Budget Awareness
[Section titled “Token Budget Awareness”](#token-budget-awareness)
Integrity filtering directly reduces token consumption: items filtered by the gateway never appear in the agent’s context window. On a busy public repository, `min-integrity: approved` on downstream agents can reduce context size dramatically compared to seeing all activity.
Use `gh aw logs --format markdown --count 20` to track token trends over time. The cross-run report surfaces cost spikes, anomalous token usage, and per-run breakdowns so you can detect regressions before they accumulate.
### Rate Limiting
[Section titled “Rate Limiting”](#rate-limiting)
The `user-rate-limit` frontmatter key caps how many times a workflow can run in a sliding window, preventing a flood of incoming issues from exhausting compute or inference budget:
```aw
user-rate-limit:
max-runs-per-window: 5
window: 60
```
See [Rate Limiting Controls](/gh-aw/reference/rate-limiting-controls/) for full options.
### Pre-Activation Association Skips
[Section titled “Pre-Activation Association Skips”](#pre-activation-association-skips)
For maintainer-operated moderation and triage workflows, you can skip runs early for specific event/author-association combinations using `on.skip-author-associations`:
```aw
on:
issue_comment:
types: [created]
skip-author-associations:
issue_comment: [owner, member, collaborator]
```
This compiles into a pre-activation job-level `if` guard (using event-specific payload fields such as `github.event.comment.author_association`, `github.event.issue.author_association`, and `github.event.pull_request.author_association`), so matching runs are skipped before agent execution starts.
### Concurrency Controls
[Section titled “Concurrency Controls”](#concurrency-controls)
Workflows automatically use dual concurrency control (per-workflow and per-engine). For repo-assist, you may want higher concurrency so multiple issues are triaged in parallel rather than queued:
```aw
concurrency:
max-parallel: 3
```
### Scoping Repository Access
[Section titled “Scoping Repository Access”](#scoping-repository-access)
`allowed-repos` prevents cross-repository reads that aren’t necessary for the workflow’s task:
```aw
tools:
github:
allowed-repos: "myorg/*"
min-integrity: approved
```
This is useful in monorepo or multi-repo setups where the agent should only read from the organization’s own repos.
## Debugging Failed Workflows
[Section titled “Debugging Failed Workflows”](#debugging-failed-workflows)
### Quick Start: AI-Assisted Debugging
[Section titled “Quick Start: AI-Assisted Debugging”](#quick-start-ai-assisted-debugging)
The fastest path to a root cause is to hand the failing run URL to the Copilot CLI:
```bash
copilot
```
Inside the CLI:
```text
/agent agentic-workflows
Debug this run: https://github.com/OWNER/REPO/actions/runs/RUN_ID
```
The agent loads the `debug-agentic-workflow` prompt, audits the run, and explains what went wrong. Follow up with specific questions about blocked domains, missing tools, or safe-output failures.
On GitHub.com with [agentic authoring configured](/gh-aw/guides/agentic-authoring/):
```text
/agent agentic-workflows debug https://github.com/OWNER/REPO/actions/runs/RUN_ID
```
### Manual Debugging with CLI Commands
[Section titled “Manual Debugging with CLI Commands”](#manual-debugging-with-cli-commands)
**Audit a specific run:**
```bash
gh aw audit RUN_ID
gh aw audit RUN_ID --json # machine-readable output
gh aw audit RUN_ID --parse # writes log.md and firewall.md
```
The audit report covers: failure summary, tool usage, MCP server health, firewall analysis, token metrics, and missing tools.
**Analyze logs across multiple runs:**
```bash
gh aw logs my-workflow
gh aw logs my-workflow --format markdown --count 10
gh aw logs --filtered-integrity # only runs with DIFC-filtered events
```
**Compare two runs for regressions:**
```bash
gh aw audit BASELINE_ID CURRENT_ID
```
### Common Failure Patterns
[Section titled “Common Failure Patterns”](#common-failure-patterns)
| Failure | Symptom / Cause | Fixes |
| ---------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Missing tool calls** | Tool not configured or wrong name. Check `missing_tools` in audit. | Add to `tools:` in frontmatter; fix any `safeoutputs-` prefix; check MCP connectivity. |
| **Authentication failures** | Token permissions too narrow or API key missing. | Review `permissions:` block; ensure secrets are set; see [Auth Reference](/gh-aw/reference/auth/). |
| **Integrity filtering blocking content** | Author’s association below `min-integrity`. `DIFC_FILTERED` events in audit show details. | Adjust `min-integrity`; add author to `trusted-users`; use `approval-labels`; check `gh aw logs --filtered-integrity`. |
| **Safe-output validation failures** | Agent attempted undeclared GitHub action. Safe-outputs blocks anything not listed. | Review `safe-outputs:`; check `safe_outputs.jsonl` in audit artifacts; see [Safe Outputs Reference](/gh-aw/reference/safe-outputs/). |
| **Token budget exhaustion** | Run hit token limit before completing. | Raise `min-integrity` to reduce context; add `cache-memory:`; simplify prompt; tighten `user-rate-limit`. |
| **Network blocks** | Required domain blocked by firewall. | Check firewall section of audit; add domain to `network.allowed`; see [Network Configuration Guide](/gh-aw/guides/network-configuration/). |
### Iterative Debug Workflow
[Section titled “Iterative Debug Workflow”](#iterative-debug-workflow)
1. Check the workflow run summary in the GitHub Actions UI.
2. Run `gh aw audit RUN_ID` for a structured breakdown.
3. For complex issues, use `/agent agentic-workflows` in Copilot Chat.
4. Edit the `.md` file → run `gh aw compile` to validate → trigger a new run.
5. Compare the new run against the baseline with `gh aw audit BASELINE_ID NEW_ID`.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) — Complete output type documentation and format requirements
* [Integrity Filtering Reference](/gh-aw/reference/integrity/) — Complete `min-integrity` and policy configuration
* [Rate Limiting Controls](/gh-aw/reference/rate-limiting-controls/) — Preventing runaway workflows
* [Cost Management](/gh-aw/reference/cost-management/) — Token budget tracking and optimization
* [Audit Commands](/gh-aw/reference/audit/) — `gh aw audit` and `gh aw logs` reference
* [Debugging Workflows](/gh-aw/troubleshooting/debugging/) — Detailed debugging procedures
* [Network Configuration Guide](/gh-aw/guides/network-configuration/) — Firewall and domain setup
* [GitHub Tools Reference](/gh-aw/reference/github-tools/) — Full `tools.github` options
# Organization Practices
> Guidance for adopting, sharing, and governing GitHub Agentic Workflows across teams and repositories.
Organization Practices collects guidance that matters at team and enterprise scale such as:
* Safe rollout strategies before production writes are enabled
* Workflow sharing across repositories and organizations
* Centralized ownership models for workflow infrastructure
* Platform conventions for versioning, review, and promotion
## Safe Rollout
[Section titled “Safe Rollout”](#safe-rollout)
[Safe Rollout](/gh-aw/practices/safe-rollout/) describes how to move from report-only or staged behavior to production writes with evidence and control. One technique inside that progression is shadow evaluation, where the workflow writes to a safe non-production target before promotion.
## Sharing Workflows
[Section titled “Sharing Workflows”](#sharing-workflows)
[Sharing Workflows](/gh-aw/practices/sharing-workflows/) describes how workflows can be reused across repositories and organizations. It covers imports, reusable components, and central workflow repositories.
# Safe Rollout
> Move from report-only or staged behavior to direct production writes with evidence and control.
Safe rollout is the practice of increasing workflow autonomy in steps instead of enabling direct production writes immediately.
The main question is not whether a workflow is useful, but whether it is trusted enough to act on the live system. In practice, teams usually move through a ladder: report-only first, then staged behavior, then a more realistic safe-write technique if needed, and finally direct production writes.
## Rollout Ladder
[Section titled “Rollout Ladder”](#rollout-ladder)
The usual progression is:
1. Start in report-only mode.
2. Enable `staged` behavior when proposed writes need to be previewed.
3. Use shadow evaluation when preview mode is not enough and the real write path needs to be exercised safely.
4. Promote the same workflow to direct production writes.
`staged` and shadow evaluation are not interchangeable. Staged mode is sufficient when the question is what the workflow would do. Shadow evaluation is needed when the question is whether the real write path behaves correctly on a safe non-production target.
## When Staged Is Enough
[Section titled “When Staged Is Enough”](#when-staged-is-enough)
Use staged mode when the main risk is decision quality rather than operational behavior.
It is usually enough when maintainers only need to review proposed actions, compare alternatives, or inspect whether the workflow’s judgment is reasonable before any write is allowed.
## When Shadow Evaluation Is Needed
[Section titled “When Shadow Evaluation Is Needed”](#when-shadow-evaluation-is-needed)
Use shadow evaluation when staged mode is too weak because the real write path itself needs validation.
This is a good fit when:
* the workflow must update real target objects to prove the behavior is correct
* concurrency, deduplication, or serialization needs to be tested on a live-like surface
* maintainers need to inspect the actual produced state, not only proposed intent
* cross-repository writes, permissions, or dispatch boundaries need to be exercised safely
Shadow evaluation is one technique inside safe rollout, not a separate top-level pattern.
## Design Rules
[Section titled “Design Rules”](#design-rules)
### Production truth stays authoritative
[Section titled “Production truth stays authoritative”](#production-truth-stays-authoritative)
Do not let the evaluation surface become the new source of truth. Production events and later trusted human actions should remain authoritative.
### Prediction snapshots should be explicit
[Section titled “Prediction snapshots should be explicit”](#prediction-snapshots-should-be-explicit)
If later comparison matters, persist what the workflow predicted at decision time. Do not reconstruct predictions from logs.
### Correction evidence needs provenance
[Section titled “Correction evidence needs provenance”](#correction-evidence-needs-provenance)
Not every later edit should count as trustworthy truth. Record provenance such as actor type, manual versus automated source, trust status, and origin repository role.
### Evaluation surfaces should remain disposable
[Section titled “Evaluation surfaces should remain disposable”](#evaluation-surfaces-should-remain-disposable)
Keep the shadow target thin. It should support measurement and rollout, not become a second long-lived control plane.
## Example Shape
[Section titled “Example Shape”](#example-shape)
The common repository split is:
* production repository: emits live events and contains authoritative later human truth
* ops repository: persists predictions, collects corrections, publishes reports, and updates instructions
* shadow repository: temporary non-production write target during rollout
That shape is often useful, but it is still rollout guidance rather than a primary pattern.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/)
* [Staged Mode](/gh-aw/reference/staged-mode/)
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/)
# Sharing Workflows
> Share, reuse, and govern workflows across repositories and organizations.
Sharing workflows across an organization involves several independent layers. Each layer can be adopted independently; teams do not need all of them at once.
The recommended enterprise pattern is to maintain one central `agentic-workflows` repository with versioned workflow templates and shared components. Consuming repositories then use `gh aw add` to install full workflows and `imports:` to pull in common modules.
## Sharing Layers
[Section titled “Sharing Layers”](#sharing-layers)
### 1. Copy and install whole workflows
[Section titled “1. Copy and install whole workflows”](#1-copy-and-install-whole-workflows)
A repository can pull in a complete workflow from another repository:
```bash
gh aw add acme-org/agentic-workflows/ci-doctor@v1.2.0
```
The `source:` field is automatically added to the installed workflow’s frontmatter so the origin and version are tracked. Use `gh aw add-wizard` for interactive installation with guided prompts. Use `gh aw add` for scripted or CI-driven installation.
See [Reusing Workflows](/gh-aw/guides/packaging-imports/) for the full command reference and options.
### 2. Reusable workflow components
[Section titled “2. Reusable workflow components”](#2-reusable-workflow-components)
Shared building blocks — tool configurations, MCP server definitions, safety policies, and prompt snippets — can be imported into any workflow:
```yaml
imports:
- acme-org/shared-workflows/shared/security-setup.md@v2.1.0
- acme-org/shared-workflows/shared/mcp/tavily.md@v1.0.0
```
Remote imports are cached under `.github/aw/imports/` by commit SHA after the first fetch. This enables reproducible offline compilation and avoids redundant downloads when multiple refs point to the same commit.
See [Imports Reference](/gh-aw/reference/imports/) for path formats, merge semantics, and field-specific behavior.
### 3. Parameterized templates
[Section titled “3. Parameterized templates”](#3-parameterized-templates)
Shared workflows that declare an `import-schema` accept runtime parameters via `uses`/`with`:
```yaml
imports:
- uses: acme-org/shared-workflows/shared/reviewer.md@v1
with:
languages: ["go", "typescript"]
severity: "high"
```
This lets a single shared component serve multiple consuming workflows with different configurations without requiring separate copies.
See [Imports Reference](/gh-aw/reference/imports/#calling-a-parameterized-shared-workflow) for schema declaration and validation details.
### 4. Versioning and update flow
[Section titled “4. Versioning and update flow”](#4-versioning-and-update-flow)
Enterprise workflow sharing needs a clear versioning model:
* **Exact release tags** (`@v1.2.0`) pin to a specific immutable release. They do not move on their own, so `gh aw update` will keep fetching that same tagged version unless you change the `source:` ref explicitly.
* **Moving release refs** (`@v1`) follow the latest compatible release within that stream. These are the typical refs to use when you want `gh aw update` to pick up newer upstream releases automatically.
* **Branch refs** (`@develop`) track the latest commit on a branch — useful for development integration.
* **SHA pins** (`@abc123def`) provide strict reproducibility and never move without an explicit change.
To pull upstream changes into an already-installed workflow:
```bash
gh aw update ci-doctor # update one workflow
gh aw update # update all tracked workflows
```
Updates use a 3-way merge by default to preserve local edits. Use `--no-merge` to replace the local copy with the upstream version without merging. When the recorded `source:` uses a moving major ref such as `@v1`, `gh aw update` stays within that major line unless `--major` is passed.
### 5. Private and internal sharing controls
[Section titled “5. Private and internal sharing controls”](#5-private-and-internal-sharing-controls)
Not all workflows are safe to share across organizations. GitHub Agentic Workflows provides controls at multiple levels:
* **`private: true`** in frontmatter blocks a workflow from being installed into other repositories via `gh aw add`. Attempting to add a private workflow from another repository fails with an error.
* **Repository visibility** controls which workflows are discoverable. Private repositories require access before any workflow can be fetched.
* **Org-internal catalogs** can be implemented by placing workflows in a private or internal organization repository, ensuring only organization members can install them.
See [Private Workflows](/gh-aw/reference/frontmatter/#private-workflows-private) for configuration details.
### 6. Import caching and lock behavior
[Section titled “6. Import caching and lock behavior”](#6-import-caching-and-lock-behavior)
When a workflow is compiled, remote imports are resolved and locked. The compiled `.lock.yml` file records the exact commit SHA for every remote import, making runs reproducible regardless of upstream branch movement.
Imports are cached locally under `.github/aw/imports/` by commit SHA. Cached imports are used for all subsequent compilations until you explicitly update them. This means the lock file and the import cache together form the reproducibility guarantee for shared workflows.
### 7. Cross-repository execution model
[Section titled “7. Cross-repository execution model”](#7-cross-repository-execution-model)
Separate from sharing workflow definitions, workflows can operate across repositories at runtime:
* Read files and metadata from other repositories during execution.
* Check out code from target repositories for analysis or modification.
* Write safe outputs to target repositories with explicit authentication and allowlists.
```yaml
safe-outputs:
create-issue:
target-repo: "acme-org/target-repo"
allowed-repos: ["acme-org/repo1", "acme-org/repo2"]
```
Cross-repository operations require appropriate GitHub token permissions and explicit `allowed-repos` declarations. See [Cross-Repository Operations](/gh-aw/reference/cross-repository/) for authentication, permissions, and safe output configuration.
## Recommended Enterprise Pattern
[Section titled “Recommended Enterprise Pattern”](#recommended-enterprise-pattern)
The recommended pattern for organizations sharing workflows at scale:
1. **One central `agentic-workflows` repository** holds versioned workflow templates and shared components under `workflows/` and `shared/`.
2. **Consuming repositories** use `gh aw add acme-org/agentic-workflows/@` to install complete workflows.
3. **Common modules** (MCP configurations, safety policies, shared prompts) live in `shared/` and are imported via `imports:` in consuming workflows.
4. **Version tags** on the central repository provide stable anchors for production consumers while branches support development integration.
5. **`private: true`** marks internal-only workflows that should not be exported outside the organization.
This model gives platform teams centralized ownership and update control while giving consuming teams reproducibility through version pins and the ability to preserve local customizations through 3-way merge.
## Governance Questions
[Section titled “Governance Questions”](#governance-questions)
When workflows are shared across an organization, the important decisions are usually operational rather than technical:
* Who owns the source workflow and reviews proposed changes.
* How updates are tested, tagged, and promoted to consuming repositories.
* Which repositories may consume or dispatch to shared workflows.
* How secrets, permissions, and safe outputs are standardized across consumers.
* When a consuming team may fork a workflow rather than stay on the shared version.
Those decisions affect reliability more than the file format does.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Reusing Workflows](/gh-aw/guides/packaging-imports/)
* [Imports Reference](/gh-aw/reference/imports/)
* [Cross-Repository Operations](/gh-aw/reference/cross-repository/)
* [Private Workflows](/gh-aw/reference/frontmatter/#private-workflows-private)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/)
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/)
# Artifacts
> Complete reference for artifact names, directory structures, and download patterns used by GitHub Agentic Workflows.
GitHub Agentic Workflows upload several artifacts during workflow execution. This reference documents every artifact name, its contents, and how to access the data — especially for downstream workflows that use `gh run download` directly instead of `gh aw logs`.
## Quick Reference
[Section titled “Quick Reference”](#quick-reference)
| Artifact Name | Constant | Type | Description |
| --------------------- | --------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent` | `constants.AgentArtifactName` | Multi-file | Unified agent job outputs (logs, safe outputs, token usage summary) |
| `activation` | `constants.ActivationArtifactName` | Multi-file | Activation job output (`aw_info.json`, `prompt.txt`, rate limits) |
| `firewall-audit-logs` | `constants.FirewallAuditArtifactName` | Multi-file | AWF firewall audit/observability logs (token usage, network policy, audit trail) |
| `detection` | `constants.DetectionArtifactName` | Single-file | Threat detection log (`detection.log`) |
| `safe-output` | `constants.SafeOutputArtifactName` | Legacy/back-compat | Historical standalone safe output artifact (`safe_output.jsonl`); in current compiled workflows this content is included in the unified `agent` artifact instead |
| `agent-output` | `constants.AgentOutputArtifactName` | Legacy/back-compat | Historical standalone agent output artifact (`agent_output.json`); in current compiled workflows this content is included in the unified `agent` artifact instead |
| `aw-info` | — | Single-file | Engine configuration (`aw_info.json`) |
| `prompt` | — | Single-file | Generated prompt (`prompt.txt`) |
| `experiment` | `constants.ExperimentArtifactName` | Multi-file | A/B experiment state (`state.json`) uploaded by the activation job when experiments are declared in the frontmatter |
| `safe-outputs-items` | `constants.SafeOutputItemsArtifactName` | Single-file | Safe output items manifest |
| `code-scanning-sarif` | `constants.SarifArtifactName` | Single-file | SARIF file for code scanning results |
## Artifact Sets
[Section titled “Artifact Sets”](#artifact-sets)
The `gh aw logs` and `gh aw audit` commands support `--artifacts` to download only specific artifact groups:
| Set Name | Artifacts Downloaded | Use Case |
| ------------ | --------------------- | ----------------------------------------------------------------- |
| `all` | Everything | Full analysis (default) |
| `agent` | `agent` | Agent logs and outputs |
| `activation` | `activation` | Activation data (`aw_info.json`, `prompt.txt`) |
| `firewall` | `firewall-audit-logs` | Network policy and firewall audit data |
| `mcp` | `firewall-audit-logs` | MCP gateway traffic logs |
| `detection` | `detection` | Threat detection output |
| `experiment` | `experiment` | A/B experiment state (only present when experiments are declared) |
| `github-api` | `activation`, `agent` | GitHub API rate limit logs |
```bash
# Download only firewall artifacts
gh aw logs --artifacts firewall
# Download agent and firewall artifacts
gh aw logs --artifacts agent --artifacts firewall
# Download everything (default)
gh aw logs
```
## `firewall-audit-logs`
[Section titled “firewall-audit-logs”](#firewall-audit-logs)
The `firewall-audit-logs` artifact is uploaded by **all firewall-enabled workflows**. It contains AWF (Agent Workflow Firewall) structured audit and observability logs.
> **! Important:** This artifact is **separate** from the `agent` artifact. Token usage data (`token-usage.jsonl`) lives here, not in the `agent` artifact.
### Directory Structure
[Section titled “Directory Structure”](#directory-structure)
```plaintext
firewall-audit-logs/
├── api-proxy-logs/
│ └── token-usage.jsonl ← Token usage data (input/output/cache tokens per API request)
├── squid-logs/
│ └── access.log ← Network policy log (domain allow/deny decisions)
├── audit.jsonl ← Firewall audit trail (policy matches, rule evaluations)
└── policy-manifest.json ← Policy configuration snapshot
```
### Accessing Token Usage Data
[Section titled “Accessing Token Usage Data”](#accessing-token-usage-data)
**Recommended: Use `gh aw logs`**
```bash
# Download and analyze firewall data
gh aw logs --artifacts firewall
# Output as JSON for scripting
gh aw logs --artifacts firewall --json
```
**Direct download with `gh run download`:**
```bash
# Download the firewall-audit-logs artifact
gh run download -n firewall-audit-logs
# Token usage data is at:
cat firewall-audit-logs/api-proxy-logs/token-usage.jsonl
# Network access log is at:
cat firewall-audit-logs/squid-logs/access.log
# Audit trail is at:
cat firewall-audit-logs/audit.jsonl
# Policy manifest is at:
cat firewall-audit-logs/policy-manifest.json
```
### Common Mistake
[Section titled “Common Mistake”](#common-mistake)
Downstream workflows sometimes download `agent-artifacts` or `agent` expecting to find `token-usage.jsonl`. This will silently return no data — the token usage file is only in the `firewall-audit-logs` artifact.
```bash
# ✗ WRONG — token-usage.jsonl is NOT in the agent artifact
gh run download -n agent
cat agent/token-usage.jsonl # File not found!
# ✓ CORRECT — download from firewall-audit-logs
gh run download -n firewall-audit-logs
cat firewall-audit-logs/api-proxy-logs/token-usage.jsonl
```
### JSON Schemas
[Section titled “JSON Schemas”](#json-schemas)
The JSONL files in this artifact are described by versioned JSON Schemas published by [github/gh-aw-firewall](https://github.com/github/gh-aw-firewall). Each record includes a `_schema` field (for example `"audit/v0.26.0"`) so consumers can identify the record type and AWF version.
| File | Schema asset | Pinned URL |
| ---------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------ |
| `audit.jsonl` | `audit.schema.json` | `https://github.com/github/gh-aw-firewall/releases/download//audit.schema.json` |
| `api-proxy-logs/token-usage.jsonl` | `token-usage.schema.json` | `https://github.com/github/gh-aw-firewall/releases/download//token-usage.schema.json` |
Use `releases/latest/download/` in place of a specific tag to track the most recent published release. Schemas are versioned by AWF release tag; consumers should match `_schema` by prefix (for example `_schema.startsWith("audit/")`) so additive changes remain non-breaking.
## `agent`
[Section titled “agent”](#agent)
The unified `agent` artifact contains all agent job outputs.
### Contents
[Section titled “Contents”](#contents)
* Agent execution logs
* Safe output data (`agent_output.json`)
* GitHub API rate limit logs (`github_rate_limits.jsonl`)
* Token usage summary (`agent_usage.json`) — aggregated totals only; per-request data is in `firewall-audit-logs`
* `otel.jsonl` — OTLP span mirror written by gh-aw’s JavaScript span exporters (only present when `observability.otlp` is configured)
* `copilot-otel.jsonl` — OTLP spans emitted by Copilot CLI (only present when `observability.otlp` is configured)
For OTLP configuration, runtime environment variables, and span semantics, see [OpenTelemetry](/gh-aw/reference/open-telemetry/).
## `activation`
[Section titled “activation”](#activation)
The `activation` artifact contains activation job outputs.
### Contents
[Section titled “Contents”](#contents-1)
* `aw_info.json` — Engine configuration and workflow metadata
* `prompt.txt` — The generated prompt sent to the AI agent
* `github_rate_limits.jsonl` — Rate limit data from the activation job
## `detection`
[Section titled “detection”](#detection)
The `detection` artifact contains threat detection output.
### Contents
[Section titled “Contents”](#contents-2)
* `detection.log` — Threat detection analysis results
Legacy name: `threat-detection.log` (still supported for backward compatibility).
## `experiment`
[Section titled “experiment”](#experiment)
The `experiment` artifact is uploaded by the **activation job** only when the workflow frontmatter declares one or more `experiments` entries. It is not present on runs without experiments.
### Contents
[Section titled “Contents”](#contents-3)
* `state.json` — Cumulative per-variant invocation counters used to balance A/B assignments across runs
### Accessing experiment data
[Section titled “Accessing experiment data”](#accessing-experiment-data)
```bash
# Download the experiment artifact for a specific run
gh aw audit --artifacts experiment
# Display the A/B experiment section in the audit report
gh aw audit
```
The `A/B Experiments` section of the audit report shows the variant chosen for the run and the cumulative counts:
```plaintext
A/B Experiments
• style = concise (cumulative: concise:5, detailed:4)
```
See [A/B Experiments](/gh-aw/practices/experiments/) for how to declare experiments in workflow frontmatter.
## Naming Compatibility
[Section titled “Naming Compatibility”](#naming-compatibility)
Artifact names changed between upload-artifact v4 and v5. The `gh aw logs` and `gh aw audit` commands handle both naming schemes transparently:
| Old Name (pre-v5) | New Name (v5+) | File Inside |
| ---------------------- | -------------- | ------------------- |
| `aw_info.json` | `aw-info` | `aw_info.json` |
| `safe_output.jsonl` | `safe-output` | `safe_output.jsonl` |
| `agent_output.json` | `agent-output` | `agent_output.json` |
| `prompt.txt` | `prompt` | `prompt.txt` |
| `threat-detection.log` | `detection` | `detection.log` |
Single-file artifacts are automatically flattened to root level regardless of their artifact directory name. Multi-file artifacts (`firewall-audit-logs`, `agent`, `activation`, `experiment`) retain their directory structure.
## Workflow Call Prefixes
[Section titled “Workflow Call Prefixes”](#workflow-call-prefixes)
When workflows are invoked via `workflow_call`, GitHub Actions prepends a short hash to artifact names (e.g., `abc123-firewall-audit-logs`). The CLI handles this automatically by matching artifact names that end with `-{base-name}`.
```bash
# Both of these are recognized as the firewall artifact:
# - firewall-audit-logs (direct invocation)
# - abc123-firewall-audit-logs (workflow_call invocation)
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Audit Commands](/gh-aw/reference/audit/) — Download and analyze workflow run artifacts
* [Cost Management](/gh-aw/reference/cost-management/) — Track token usage and inference spend
* [Network](/gh-aw/reference/network/) — Firewall and domain allow/deny configuration
* [Compilation Process](/gh-aw/reference/compilation-process/) — How workflows are compiled including artifact upload steps
# Assign to Copilot
> Programmatically assign GitHub Copilot coding agent to issues and pull requests
This page describes how to programmatically assign the [GitHub Copilot coding agent](https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent) to issues or pull requests using the `assign-to-agent` safe output. This automates the [standard GitHub workflow for assigning issues to Copilot](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/create-a-pr#assigning-an-issue-to-copilot).
## When to Use
[Section titled “When to Use”](#when-to-use)
Use `assign-to-agent` when you need to programmatically assign Copilot coding agent to **existing** issues or PRs through workflow automation.
If you’re creating new issues and want to assign Copilot coding agent immediately, use `assignees: copilot` in your [`create-issue`](/gh-aw/reference/safe-outputs/#issue-creation-create-issue) configuration instead.
## Configuration
[Section titled “Configuration”](#configuration)
```yaml
safe-outputs:
assign-to-agent:
name: "copilot" # default agent (default: "copilot")
model: "claude-opus-4.6" # default AI model (default: "auto")
custom-agent: "agent-id" # default custom agent ID (optional)
custom-instructions: "..." # default custom instructions (optional)
allowed: [copilot] # restrict to specific agents (optional)
max: 1 # max assignments (default: 1)
target: "triggering" # "triggering" (default), "*", or number
target-repo: "owner/repo" # where the issue lives (cross-repository)
pull-request-repo: "owner/repo" # where the PR should be created (may differ from issue repo)
allowed-pull-request-repos: [owner/repo1, owner/repo2] # additional allowed PR repositories
base-branch: "develop" # target branch for PR (default: target repo's default branch)
github-token: ${{ secrets.GH_AW_AGENT_TOKEN }} # token for permissions
```
**Supported agents:** `copilot` (`copilot-swe-agent`)
## Target Issue or Pull Request
[Section titled “Target Issue or Pull Request”](#target-issue-or-pull-request)
The `target` parameter determines which issue or PR to assign the agent to:
* `target: "triggering"` (default) - Auto-resolves from `github.event.issue.number` or `github.event.pull_request.number`
* `target: "*"` - Requires explicit `issue_number` or `pull_number` in agent output
* `target: "123"` - Always uses issue/PR #123
## Cross-Repository PR Creation
[Section titled “Cross-Repository PR Creation”](#cross-repository-pr-creation)
Use `pull-request-repo` to create pull requests in a different repository than where the issue lives — useful when issues are tracked centrally but code lives elsewhere. The issue repository is determined by `target-repo` or defaults to the workflow’s repository.
`pull-request-repo` is automatically included in the allowed list; use `allowed-pull-request-repos` for additional repositories. Use `base-branch` to target a specific branch (defaults to the target repo’s default branch).
## Assignee Filtering
[Section titled “Assignee Filtering”](#assignee-filtering)
When an `allowed` list is configured, existing agent assignees not in the list are removed while regular user assignees are preserved.
## Authentication
[Section titled “Authentication”](#authentication)
This safe output requires a fine-grained PAT to authenticate the agent assignment operation. The default `GITHUB_TOKEN` lacks the necessary permissions.
### Using a Personal Access Token (PAT)
[Section titled “Using a Personal Access Token (PAT)”](#using-a-personal-access-token-pat)
The required token type and permissions depend on whether you own the repository or an organization owns it.
1. **Create the PAT** with **Repository permissions**: Actions, Contents, Issues, Pull requests (all Write).
* [User-owned repositories](https://github.com/settings/personal-access-tokens/new?name=GH_AW_AGENT_TOKEN\&description=GitHub+Agentic+Workflows+-+Agent+assignment\&actions=write\&contents=write\&issues=write\&pull_requests=write): Resource owner = your user account; Repository access = “Public repositories” or specific repos
* [Organization-owned repositories](https://github.com/settings/personal-access-tokens/new?name=GH_AW_AGENT_TOKEN\&description=GitHub+Agentic+Workflows+-+Agent+assignment\&actions=write\&contents=write\&issues=write\&pull_requests=write): Resource owner = the organization; Repository access = specific repositories that will use the workflow
2. Add to repository secrets:
```bash
gh aw secrets set GH_AW_AGENT_TOKEN --value "YOUR_AGENT_PAT"
```
### Using a GitHub App
[Section titled “Using a GitHub App”](#using-a-github-app)
GitHub App tokens are not supported for Copilot assignment
The Copilot assignment API only accepts fine-grained PATs — GitHub App installation tokens are rejected regardless of permissions. When `github-app:` is configured in `safe-outputs`, `assign-to-agent` falls back to: explicit `github-token:` in `assign-to-agent`, then `github-token:` at the `safe-outputs` level, then the magic secret chain (`GH_AW_AGENT_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN`).
### Using a magic secret
[Section titled “Using a magic secret”](#using-a-magic-secret)
Alternatively, you can set the magic secret `GH_AW_AGENT_TOKEN` to a suitable PAT (see the above guide for creating one). This secret name is known to GitHub Agentic Workflows and does not need to be explicitly referenced in your workflow.
```bash
gh aw secrets set GH_AW_AGENT_TOKEN --value ""
```
Your browser doesn't support HTML5 video. [Download Creating a fine-grained PAT for organization-owned repositories with permissions for agent assignment](/gh-aw/videos/create-pat-org-agent.mp4).
Creating a fine-grained PAT for organization-owned repositories with permissions for agent assignment
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - All safe output configurations
* [Authentication Reference](/gh-aw/reference/auth/) - All tokens and secrets
* [IssueOps](/gh-aw/patterns/issue-ops/) - Issue-triggered workflow patterns
# Auditing Workflows
> Reference for the gh aw audit commands — single-run analysis, behavioral diff, and cross-run security reports.
The `gh aw audit` commands download workflow run artifacts and logs, analyze MCP tool usage and network behavior, and produce structured reports suited for security reviews, debugging, and feeding to AI agents.
## `gh aw audit [...]`
[Section titled “gh aw audit \ \[\...\]”](#gh-aw-audit-run-id-or-url-run-id-or-url)
Audit one or more workflow runs. When a single run is provided, a detailed Markdown report is generated. When two or more runs are provided, the first is used as the base (reference) run and the remaining runs are compared against it, producing a diff report.
**Arguments:**
| Argument | Description |
| ---------------------- | ------------------------------------------------------------------------------ |
| `` | A numeric run ID, GitHub Actions run URL, job URL, or job URL with step anchor |
| `[...]` | Additional run IDs or URLs to compare against the first (diff mode) |
**Accepted input formats (per argument):**
* Numeric run ID: `1234567890`
* Run URL: `https://github.com/owner/repo/actions/runs/1234567890`
* Job URL: `https://github.com/owner/repo/actions/runs/1234567890/job/9876543210`
* Job URL with step: `https://github.com/owner/repo/actions/runs/1234567890/job/9876543210#step:7:1`
* Short run URL: `https://github.com/owner/repo/runs/1234567890`
* GitHub Enterprise URLs using the same formats above
When a job URL is provided without a step anchor (single-run mode), the command extracts the output of the first failing step. When a step anchor is included, it extracts that specific step.
In diff mode, job URLs and step-anchored URLs are accepted for any argument — the job/step specificity is silently normalized to the parent run ID, so it is always a run-level diff.
Self-comparisons and duplicate run IDs are rejected when using diff mode.
**Flags:**
| Flag | Default | Description |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------- |
| `-o, --output ` | `./logs` | Directory to write downloaded artifacts and report files |
| `--json` | off | Output report as JSON to stdout |
| `--parse` | off | Run JavaScript parsers on agent and firewall logs, writing `log.md` and `firewall.md` (single-run only) |
| `--repo ` | auto | Specify repository when the run ID is not from a URL |
| `--stdin` | off | Read run IDs or URLs from stdin (one per line) instead of positional arguments |
| `--verbose` | off | Print detailed progress information |
| `--format ` | `pretty` | Diff output format: `pretty` or `markdown` (multi-run only) |
**Single-run examples:**
```bash
gh aw audit 1234567890
gh aw audit https://github.com/owner/repo/actions/runs/1234567890
gh aw audit 1234567890 --parse
gh aw audit 1234567890 --json
gh aw audit 1234567890 -o ./audit-reports
gh aw audit 1234567890 --repo owner/repo
```
**Stdin mode:**
Use `--stdin` to pass run IDs or URLs from a file or pipeline. This is mutually exclusive with positional arguments. Blank lines and lines starting with `#` are ignored. When passing bare numeric IDs (without embedded repo context), `--repo owner/repo` is required.
```bash
echo "1234567890" | gh aw audit --stdin
echo -e "1234567890\n9876543210" | gh aw audit --stdin # diff mode: first is base
cat run-ids.txt | gh aw audit --stdin
cat run-ids.txt | gh aw audit --stdin --repo owner/repo # required for bare numeric IDs
```
**Multi-run diff examples:**
```bash
gh aw audit 12345 12346 # Compare two runs
gh aw audit 12345 12346 12347 12348 # Compare base against 3 runs
gh aw audit 12345 12346 --format markdown # Markdown output for PR comments
gh aw audit 12345 12346 --json # JSON for CI integration
gh aw audit 12345 12346 --repo owner/repo # Specify repository
```
**Single-run report sections** (rendered in Markdown or JSON): Overview, Comparison, Task/Domain, Behavior Fingerprint, Agentic Assessments, Metrics, Key Findings, Recommendations, Observability Insights, Performance Metrics, Engine Config, Prompt Analysis, Session Analysis, Safe Output Summary, MCP Server Health, Jobs, Downloaded Files, Missing Tools, Missing Data, Noops, MCP Failures, Firewall Analysis, Policy Analysis, Redacted Domains, Errors, Warnings, Tool Usage, MCP Tool Usage, Created Items.
The Metrics section includes an `ambient_context` object when available. Ambient context captures the first LLM inference footprint for the run:
* `ambient_context.input_tokens` — input tokens for the first invocation
* `ambient_context.cached_tokens` — cache-read tokens reused by the first invocation
* `ambient_context.effective_tokens` — `input_tokens + cached_tokens`
**Diff output** includes:
* New and removed network domains
* Domain status changes (allowed denied)
* Volume changes (request count changes above a 100% threshold)
* Anomaly flags (new denied domains, previously-denied domains now allowed)
* MCP tool invocation changes (new/removed tools, call count and error count diffs)
* Run metrics comparison (token usage, duration, turns)
* Token usage breakdown: input tokens, output tokens, cache read/write tokens, effective tokens, total API requests, and cache efficiency per run
* Tokens per turn: effective tokens divided by turn count for each run, with the change between runs
* Tool call breakdown: per-tool call counts (new, removed, and changed tools) with max input/output sizes
* Bash command breakdown: aggregated call counts and max input/output sizes for each distinct bash command invoked
**Diff output behavior with multiple comparisons:**
* `--json` outputs a single object for one comparison, or an array for multiple
* `--format pretty` and `--format markdown` separate multiple diffs with dividers
## `gh aw logs --format `
[Section titled “gh aw logs --format \”](#gh-aw-logs---format-fmt)
Generate a cross-run security and performance audit report across multiple recent workflow runs. This feature is built into the `gh aw logs` command via the `--format` flag.
**Flags:**
| Flag | Default | Description |
| --------------------- | ------------- | ---------------------------------------------------------------------------------------------------- |
| `[workflow]` | all workflows | Filter by workflow name or filename (positional argument) |
| `-c, --count ` | 10 | Number of recent runs to analyze |
| `--last ` | — | Alias for `--count` |
| `--format ` | — | Output format: `markdown` or `pretty` (generates cross-run audit report) |
| `--json` | off | Output cross-run report as JSON (when combined with `--format`) |
| `--repo ` | auto | Specify repository |
| `-o, --output ` | `./logs` | Directory for downloaded artifacts |
| `--stdin` | off | Read run IDs or URLs from stdin (one per line) instead of run-discovery; content filters still apply |
| `--verbose` | off | Print detailed progress |
The report output includes an executive summary, domain inventory, metrics trends, MCP server health, and per-run breakdown. It detects cross-run anomalies such as domain access spikes, elevated MCP error rates, and connection rate changes.
For each run in detailed logs JSON output, an `ambient_context` object is included when token usage data is available. It reflects only the first LLM invocation in the run (`input_tokens`, `cached_tokens`, `effective_tokens`).
**`--stdin` mode:** Pass `--stdin` to supply an explicit list of run IDs or URLs instead of letting the command discover runs from the GitHub API. Date, count, and workflow-name filters are ignored; `--engine`, `--firewall`, `--safe-output`, and other content filters still apply. Blank lines and `#`-prefixed lines are ignored. Bare numeric IDs require `--repo owner/repo`.
```bash
cat run-ids.txt | gh aw logs --stdin
echo "1234567890" | gh aw logs --stdin --engine claude
cat run-ids.txt | gh aw logs --stdin --repo owner/repo # required for bare numeric IDs
```
**Examples:**
```bash
gh aw logs --format markdown
gh aw logs daily-repo-status --format markdown --count 10
gh aw logs agent-task --format markdown --last 5 --json
gh aw logs --format pretty
gh aw logs --format markdown --repo owner/repo --count 10
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Cost Management](/gh-aw/reference/cost-management/) — Track token usage and inference spend
* [Artifacts](/gh-aw/reference/artifacts/) — Artifact names, directory structures, and token usage file locations (`token-usage.jsonl` in `firewall-audit-logs`)
* [Effective Tokens Specification](/gh-aw/reference/effective-tokens-specification/) — How effective tokens are computed
* [Network](/gh-aw/reference/network/) — Firewall and domain allow/deny configuration
* [MCP Gateway](/gh-aw/reference/mcp-gateway/) — MCP server health and debugging
* [CLI Commands](/gh-aw/setup/cli/) — Full CLI reference
## Consuming Audit Reports in Workflows
[Section titled “Consuming Audit Reports in Workflows”](#consuming-audit-reports-in-workflows)
When running locally, all three audit commands accept `--json` to write structured output to stdout. Pipe through `jq` to extract the fields a model needs.
| Command | Use case |
| ---------------------------------------- | --------------------------------------------------------- |
| `gh aw audit --json` | Single run — `key_findings`, `recommendations`, `metrics` |
| `gh aw logs [workflow] --last 10 --json` | Trend analysis — `per_run_breakdown`, `domain_inventory` |
| `gh aw audit --json` | Before/after — `run_metrics_diff`, `firewall_diff` |
Inside GitHub Actions workflows, agents access these commands through the `agentic-workflows` MCP tool rather than calling the CLI directly.
### Posting findings as a PR comment
[Section titled “Posting findings as a PR comment”](#posting-findings-as-a-pr-comment)
```aw
---
description: Post audit findings as a PR comment after each agent run
on:
workflow_run:
workflows: ['my-workflow']
types: [completed]
engine: copilot
tools:
github:
toolsets: [pull_requests]
agentic-workflows:
permissions:
contents: read
actions: read
pull-requests: write
---
# Summarize Audit Findings
Use the `agentic-workflows` MCP tool `audit` with run ID ${{ github.event.workflow_run.id }}, identify the pull request that triggered it, and post a comment summarizing key findings and blocked domains. Highlight issues with severity `high` or `critical`. If there are no findings, post a brief "no issues found" comment.
```
### Detecting regressions with diff
[Section titled “Detecting regressions with diff”](#detecting-regressions-with-diff)
```aw
---
description: Detect regressions between two workflow runs
on:
workflow_dispatch:
inputs:
base_run_id:
description: 'Baseline run ID'
required: true
current_run_id:
description: 'Current run ID to compare'
required: true
engine: copilot
tools:
github:
toolsets: [issues]
agentic-workflows:
permissions:
contents: read
actions: read
issues: write
---
# Regression Detection
Use the `agentic-workflows` MCP tool `audit` with run IDs ${{ inputs.base_run_id }} and ${{ inputs.current_run_id }} to compare the two runs. Check for new blocked domains, increased MCP error rates, cost increase > 20%, or token usage increase > 50%. If regressions are found, open a GitHub issue with a table from `run_metrics_diff`, affected domains from `firewall_diff`, and affected MCP tools from `mcp_tools_diff`.
```
### Filing issues from audit findings
[Section titled “Filing issues from audit findings”](#filing-issues-from-audit-findings)
```aw
---
description: File GitHub issues for high-severity audit findings
on:
workflow_run:
workflows: ['my-workflow']
types: [completed]
engine: copilot
tools:
github:
toolsets: [issues]
agentic-workflows:
permissions:
contents: read
actions: read
issues: write
---
# Auto-File Issues for Critical Findings
Use the `agentic-workflows` MCP tool `audit` with run ID ${{ github.event.workflow_run.id }}. Filter `key_findings` for severity `high` or `critical`. For each finding without a matching open issue, create one with the finding title, description, impact, and recommendations, labelled `audit-finding`. If no critical findings, call the `noop` safe output tool.
```
### Weekly audit monitoring agent
[Section titled “Weekly audit monitoring agent”](#weekly-audit-monitoring-agent)
```aw
---
description: Weekly audit digest with trend analysis
on:
schedule: weekly
engine: copilot
tools:
github:
toolsets: [discussions]
agentic-workflows:
cache-memory:
key: audit-monitoring-trends
permissions:
contents: read
actions: read
discussions: write
---
# Weekly Audit Monitoring Digest
1. Use the `agentic-workflows` MCP tool `logs` with parameters `workflow: my-workflow, last: 10` and read `/tmp/gh-aw/cache-memory/audit-trends.json` as the previous baseline.
2. Detect: cost spikes (`cost_spike: true` in `per_run_breakdown`), new denied domains in `domain_inventory`, MCP servers with `error_rate > 0.10` or `unreliable: true`, and week-over-week changes in `error_trend.runs_with_errors`.
3. Create a GitHub discussion "Audit Digest — [YYYY-MM-DD]" with an executive summary, anomalies table, and MCP health table.
4. Update `/tmp/gh-aw/cache-memory/audit-trends.json` with rolling averages (cost, tokens, error count, deny rate), keeping only the last 30 days.
```
Top-level fields (`key_findings`, `recommendations`, `metrics`, `firewall_analysis`, `mcp_tool_usage`) are stable; nested sub-fields may be extended but are not removed without deprecation. Add `--parse` to populate `behavior_fingerprint` and `agentic_assessments`. Cross-run JSON can be large — extract only the slices your model needs.
# Authentication
> Comprehensive reference for GitHub Actions secrets, GitHub tokens and GitHub Apps in gh-aw
This page describes authentication settings for GitHub Agentic Workflows.
## Which secret do I need?
[Section titled “Which secret do I need?”](#which-secret-do-i-need)
Configure one GitHub Actions secret per engine before running your first workflow:
| Engine | Required secret | Alternative | Notes |
| --------------------- | ----------------------------------------------- | --------------- | --------------------------------------------------------------------------------- |
| **Copilot** (default) | [`COPILOT_GITHUB_TOKEN`](#copilot_github_token) | — | Fine-grained PAT with Copilot Requests permission |
| **Claude** | [`ANTHROPIC_API_KEY`](#anthropic_api_key) | — | API key from Anthropic Console |
| **Codex** | [`OPENAI_API_KEY`](#openai_api_key) | `CODEX_API_KEY` | Runtime uses `CODEX_API_KEY` if present, otherwise falls back to `OPENAI_API_KEY` |
| **Gemini** | [`GEMINI_API_KEY`](#gemini_api_key) | — | API key from Google AI Studio |
Most workflows will run without any additional secrets or additional authentication beyond this one engine secret.
## Additional Authentication
[Section titled “Additional Authentication”](#additional-authentication)
Some workflows need additional authentication. These can be tokens added as secrets and referenced in your workflow, or GitHub App can be used.
Workflows using the following **read** operations from GitHub require [Additional Authentication for GitHub Tools](/gh-aw/reference/github-tools/#additional-authentication-for-github-tools), via either a secret containing a PAT or GitHub App:
* **Read from multiple repositories**
* **Read from projects**
* **GitHub tools remote mode**
Workflows using the following features of [Safe Outputs](/gh-aw/reference/safe-outputs/) require additional authentication, via either a secret containing a PAT or GitHub App:
* [**Safe outputs writing cross-repo**](/gh-aw/reference/safe-outputs/#cross-repository-operations)
* [**Safe outputs assigning Copilot coding agent to issues/PRs**](/gh-aw/reference/assign-to-copilot/)
* [**Safe outputs updating GitHub Projects**](/gh-aw/patterns/project-ops/#project-token-authentication)
* [**Safe outputs triggering CI on PRs**](/gh-aw/reference/triggering-ci/)
Workflows using custom MCP tools or safe outputs may require additional authentication depending on the operations performed.
## How do I add a GitHub Actions secret to my repository?
[Section titled “How do I add a GitHub Actions secret to my repository?”](#how-do-i-add-a-github-actions-secret-to-my-repository)
You can add secrets manually in the GitHub UI or use the CLI for a streamlined experience.
### Adding secrets using the CLI
[Section titled “Adding secrets using the CLI”](#adding-secrets-using-the-cli)
```bash
gh aw secrets set COPILOT_GITHUB_TOKEN --value "YOUR_COPILOT_PAT"
```
You can also check existing secrets with:
```bash
gh aw secrets bootstrap
```
If you’re working in Codespaces, use the GitHub UI method below to add secrets.
### Adding secrets using the GitHub UI
[Section titled “Adding secrets using the GitHub UI”](#adding-secrets-using-the-github-ui)
1. Go to your repository on GitHub
2. Click on “Settings” → “Secrets and variables” → “Actions”
3. Click “New repository secret” and add the token name and value

## GitHub Actions secrets for AI engines
[Section titled “GitHub Actions secrets for AI engines”](#github-actions-secrets-for-ai-engines)
A reference for all GitHub Actions secrets used by GitHub Agentic Workflows for AI engine authentication:
### `COPILOT_GITHUB_TOKEN`
[Section titled “COPILOT\_GITHUB\_TOKEN”](#copilot_github_token)
If using Copilot as your AI engine, you need a GitHub Actions Secret set to a GitHub Personal Access Token (PAT) to authenticate Copilot CLI.
**Setup**:
[**Create a fine-grained PAT**](https://github.com/settings/personal-access-tokens/new?name=COPILOT_GITHUB_TOKEN\&description=GitHub+Agentic+Workflows+-+Copilot+engine+authentication\&user_copilot_requests=read) (this link pre-fills the token name, description, and Copilot Requests permission). Verify the following settings before generating:
1. **Resource owner** is your **user account**, not an organization.
2. Under **Permissions → Account permissions**, **Copilot Requests** is set to **Read**.
3. Click **Generate token** and copy the token value.
4. Add the PAT to your GitHub Actions repository secrets as `COPILOT_GITHUB_TOKEN`, either by CLI or GitHub UI.
```bash
gh aw secrets set COPILOT_GITHUB_TOKEN --value ""
```
**Custom endpoints**:
To route Copilot CLI through a custom endpoint (e.g., a corporate proxy or GHE Cloud data residency instance), set `GITHUB_COPILOT_BASE_URL` in `engine.env`. See [Custom API Endpoints via Environment Variables](/gh-aw/reference/engines/#custom-api-endpoints-via-environment-variables) for details. `COPILOT_GITHUB_TOKEN` must still be a fine-grained PAT — GitHub Apps and OAuth tokens are not supported for this secret.
**Troubleshooting**:
If your workflow fails at the Copilot inference step even with the token set, verify that the token owner’s account has an active Copilot license. See [Copilot License or Inference Access Issues](/gh-aw/troubleshooting/common-issues/#copilot-license-or-inference-access-issues) for a local diagnostic step.
***
### `ANTHROPIC_API_KEY`
[Section titled “ANTHROPIC\_API\_KEY”](#anthropic_api_key)
If using the Claude by Anthropic engine, you need to set a GitHub Actions secret `ANTHROPIC_API_KEY` to be an API key from Anthropic.
**Setup**:
1. Create an API key at
2. Add it to your repository secrets, either by CLI or GitHub UI:
```bash
gh aw secrets set ANTHROPIC_API_KEY --value "YOUR_ANTHROPIC_API_KEY"
```
**Custom endpoints**:
To route Claude through a custom Anthropic-compatible endpoint (e.g., an internal proxy or Azure-hosted model), set `ANTHROPIC_BASE_URL` in `engine.env` and store any additional credentials as secrets. See [Custom API Endpoints via Environment Variables](/gh-aw/reference/engines/#custom-api-endpoints-via-environment-variables) for an example.
**`CLAUDE_CODE_OAUTH_TOKEN`**:
`CLAUDE_CODE_OAUTH_TOKEN` is not supported by GitHub Agentic Workflows. The only supported authentication method for the Claude engine is `ANTHROPIC_API_KEY`. Provider-based OAuth authentication (such as billing through a Claude Teams or Claude Max subscription) is not supported. If you have set `CLAUDE_CODE_OAUTH_TOKEN` as a repository secret, it will be ignored — configure `ANTHROPIC_API_KEY` instead.
See also [AI Engines](/gh-aw/reference/engines/#available-coding-agents) for additional configuration needed when using Claude with GitHub MCP.
***
### `OPENAI_API_KEY`
[Section titled “OPENAI\_API\_KEY”](#openai_api_key)
If using the Codex by OpenAI engine, you need to set a GitHub Actions secret `OPENAI_API_KEY` with an API key from OpenAI.
**Setup**:
1. Create an API key at
2. Add it to your repository secrets, either by CLI or GitHub UI:
```bash
gh aw secrets set OPENAI_API_KEY --value "YOUR_OPENAI_API_KEY"
```
**`CODEX_API_KEY` alternative**:
Both `CODEX_API_KEY` and `OPENAI_API_KEY` are accepted. The runtime tries `CODEX_API_KEY` first. If you have already stored the key under `CODEX_API_KEY`, there is no need to add `OPENAI_API_KEY` as well.
**Azure OpenAI and custom endpoints**:
To use Azure OpenAI or an internal LLM router instead of the default OpenAI endpoint, set `OPENAI_BASE_URL` in `engine.env` and store the corresponding key as a GitHub Actions secret referenced from `engine.env`:
```aw
engine:
id: codex
model: gpt-4o
env:
OPENAI_BASE_URL: "https://my-azure-endpoint.openai.azure.com/openai/deployments/gpt-4o"
OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
network:
allowed:
- github.com
- my-azure-endpoint.openai.azure.com
```
`AZURE_OPENAI_API_KEY` is a GitHub Actions repository secret you configure separately with `gh aw secrets set AZURE_OPENAI_API_KEY --value ""`. Do not embed raw key values directly in the frontmatter.
See also [AI Engines](/gh-aw/reference/engines/#available-coding-agents) for additional configuration needed when using Codex with GitHub MCP.
***
### `GEMINI_API_KEY`
[Section titled “GEMINI\_API\_KEY”](#gemini_api_key)
If using the Gemini by Google engine, you need to set a GitHub Actions secret `GEMINI_API_KEY` with an API key from Google AI Studio.
**Setup**:
1. Create an API key at
2. Add it to your repository secrets, either by CLI or GitHub UI:
```bash
gh aw secrets set GEMINI_API_KEY --value "YOUR_GEMINI_API_KEY"
```
See also [AI Engines](/gh-aw/reference/engines/#available-coding-agents) for additional configuration needed when using Gemini with GitHub MCP.
***
## Troubleshooting auth errors
[Section titled “Troubleshooting auth errors”](#troubleshooting-auth-errors)
Common authentication errors and how to resolve them:
**`403 "Resource not accessible by personal access token"` (Copilot)**
The PAT is missing the required permission. Use a fine-grained PAT with **Account permissions → Copilot Requests: Read**. The resource owner must be your personal account, not an organization. See [`COPILOT_GITHUB_TOKEN`](#copilot_github_token) for the setup link.
**`401 Unauthorized` or `403 Forbidden` (Claude)**
The `ANTHROPIC_API_KEY` secret is missing, expired, or invalid. Verify the key is active in the [Anthropic Console](https://console.anthropic.com/). Re-set the secret with `gh aw secrets set ANTHROPIC_API_KEY --value ""`. Also check that you have not accidentally set `CLAUDE_CODE_OAUTH_TOKEN` instead — it is not supported.
**`401 Unauthorized` or `403 Forbidden` (Codex)**
The `OPENAI_API_KEY` (or `CODEX_API_KEY`) secret is missing, expired, or has insufficient quota. Verify the key at . If using a custom endpoint, confirm `OPENAI_BASE_URL` points to a reachable host and that the host is listed under `network.allowed`.
**`401 Unauthorized` (Gemini)**
The `GEMINI_API_KEY` secret is missing or invalid. Generate a new key at .
**Copilot license or inference access errors**
If the token is correctly configured but Copilot fails at the inference step, the PAT owner’s account may lack an active Copilot subscription. See [Copilot License or Inference Access Issues](/gh-aw/troubleshooting/common-issues/#copilot-license-or-inference-access-issues) for a local diagnostic command.
**`Error loading models: 400 Bad Request` (Copilot on GHES)**
Copilot is not licensed at the enterprise level or the API proxy is routing incorrectly. See [Copilot Engine Prerequisites on GHES](/gh-aw/troubleshooting/common-issues/#copilot-engine-prerequisites-on-ghes) for the full checklist.
***
## Using a GitHub App for Authentication
[Section titled “Using a GitHub App for Authentication”](#using-a-github-app-for-authentication)
For enhanced security with short-lived tokens, you may configure a GitHub App instead of using PATs.
This does not apply to `COPILOT_GITHUB_TOKEN`, which must currently be a PAT. A single GitHub App can be used for all other GitHub authentication needs in GitHub Agentic Workflows, including tool authentication and safe outputs.
After creating your app, configure it in your workflow:
```yaml
permissions:
contents: read
issues: read
tools:
github:
toolsets: [repos, issues, pull_requests]
github-app:
client-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: "my-org" # Optional: defaults to current repo owner
repositories: ["repo1", "repo2"] # Optional: defaults to current repo only
```
Make sure you set up repository variables and secrets:
```bash
gh variable set APP_ID --body "123456"
gh aw secrets set APP_PRIVATE_KEY --value "$(cat path/to/private-key.pem)"
```
At workflow start, a token is automatically minted with **permissions matching your job’s `permissions:` field**. The token is passed to the GitHub MCP server and automatically revoked at workflow end (even on failure).
You can also use GitHub App tokens for safe outputs operations:
```yaml
safe-outputs:
github-app:
client-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: "my-org" # optional: installation owner
repositories: ["repo1", "repo2"] # optional: scope to specific repos
create-issue:
```
When you configure `github-app:` for safe outputs, tokens are minted with permissions specific to the safe output operations being performed, rather than the broader job-level permissions. This provides enhanced security by ensuring that tokens have the minimum necessary permissions for their specific use case.
For both tool authentication and safe outputs, you can scope the GitHub App token to specific repositories for enhanced security. This limits the token’s access to only the repositories it needs to interact with.
* Omit `repositories` field - Current repository only (default)
* `repositories: ["*"]` - Org-wide access (all repos in the installation)
* `repositories: ["repo1", "repo2"]` - Specific repositories only
### Gracefully Skip Minting When Keys Are Missing (`ignore-if-missing:`)
[Section titled “Gracefully Skip Minting When Keys Are Missing (ignore-if-missing:)”](#gracefully-skip-minting-when-keys-are-missing-ignore-if-missing)
By default, jobs fail when `client-id` or `private-key` resolve to empty strings at runtime — for example, on fork pull requests where App secrets are unavailable. Set `ignore-if-missing: true` to skip the token mint step instead and fall back to the standard non-App token chain (`secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN`):
```yaml
safe-outputs:
github-app:
client-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
ignore-if-missing: true
create-issue:
```
The same field is accepted under `tools.github.github-app:` and applies consistently to all token mint paths (safe outputs, activation, pre-activation, and checkout). Default behavior (fail when keys are empty) is unchanged when the field is omitted or `false`.
***
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Engines](/gh-aw/reference/engines/) - Engine-specific authentication
* [Safe Outputs](/gh-aw/reference/safe-outputs/) - Safe output token configuration
* [Tools](/gh-aw/reference/tools/) - Tool authentication and modes
* [Permissions](/gh-aw/reference/permissions/) - Permission model overview
# Authentication (Projects)
> Reference for authenticating GitHub Projects read and write operations in gh-aw
GitHub Projects operations require additional authentication because the default `GITHUB_TOKEN` is repository-scoped and cannot access the Projects GraphQL API for read or write operations.
## Why a separate token is needed
[Section titled “Why a separate token is needed”](#why-a-separate-token-is-needed)
The standard `GITHUB_TOKEN` provided to every GitHub Actions workflow has repository-level scope only. GitHub Projects (both user-owned and organization-owned) sit outside that scope, so any workflow step that reads project fields or writes updates must supply a token with explicit Projects permissions.
This applies to:
* [GitHub tools `projects` toolset](/gh-aw/reference/github-tools/#additional-authentication-for-github-tools) — reads project items and field values
* [`update-project` safe output](/gh-aw/reference/safe-outputs/#project-board-updates-update-project) — adds items and updates fields
* [`create-project` safe output](/gh-aw/reference/safe-outputs/#project-creation-create-project) — creates new project boards
* [`create-project-status-update` safe output](/gh-aw/reference/safe-outputs/#project-status-updates-create-project-status-update) — posts status updates
## Personal Access Tokens
[Section titled “Personal Access Tokens”](#personal-access-tokens)
### User-owned projects
[Section titled “User-owned projects”](#user-owned-projects)
Use a [classic PAT](https://github.com/settings/tokens/new) with the following scopes:
* `project`
* `repo` (required if the project contains items from private repositories)
### Organization-owned projects
[Section titled “Organization-owned projects”](#organization-owned-projects)
Use a [fine-grained PAT](https://github.com/settings/personal-access-tokens/new?name=GH_AW_WRITE_PROJECT_TOKEN\&description=GitHub+Agentic+Workflows+-+Projects+authentication\&contents=read\&issues=read\&pull_requests=read) with these settings:
* **Resource owner**: the organization that owns the project
* **Repository access**: the repositories that will run the workflow
* **Repository permissions**: `Contents: Read`, and optionally `Issues: Read` / `Pull requests: Read`
* **Organization permissions**: `Projects: Read and write`
## GitHub App tokens
[Section titled “GitHub App tokens”](#github-app-tokens)
For organization-wide standardization, a GitHub App can be used instead of PATs. The app must have **Organization projects: Read and write** permission.
See [Using a GitHub App for Authentication](/gh-aw/reference/auth/#using-a-github-app-for-authentication) for setup instructions.
## Recommended secret layout
[Section titled “Recommended secret layout”](#recommended-secret-layout)
Use separate read and write tokens to enforce least privilege:
```bash
gh aw secrets set GH_AW_READ_PROJECT_TOKEN --value ""
gh aw secrets set GH_AW_WRITE_PROJECT_TOKEN --value ""
```
Reference each token in the workflow where it is needed:
```aw
tools:
github:
mode: remote
toolsets: [projects]
github-token: ${{ secrets.GH_AW_READ_PROJECT_TOKEN }}
safe-outputs:
update-project:
project-url: https://github.com/orgs/my-org/projects/1
github-token: ${{ secrets.GH_AW_WRITE_PROJECT_TOKEN }}
```
The magic secret `GH_AW_GITHUB_MCP_SERVER_TOKEN` is recognized by GitHub Agentic Workflows and does not need to be explicitly referenced in your workflow — if it is present in the repository, it is used automatically for all GitHub tools toolsets, including `projects`.
## Related documentation
[Section titled “Related documentation”](#related-documentation)
* [Authentication](/gh-aw/reference/auth/) — AI engine secrets and GitHub App setup
* [GitHub Tools](/gh-aw/reference/github-tools/) — toolset configuration and additional authentication
* [Safe Outputs](/gh-aw/reference/safe-outputs/) — write operations and token configuration
* [ProjectOps pattern](/gh-aw/patterns/project-ops/) — end-to-end example with project boards
# Package Manifest (aw.yml)
> Reference for the aw.yml package manifest used by gh aw add and gh aw compile.
Use `aw.yml` to describe an installable agentic workflow package. `gh aw add` uses this manifest when installing packages, and `gh aw compile` validates repository-root manifests before compilation.
For the normative file-format definition, see the [Package Management (Spec)](/gh-aw/reference/repository-package-manifest-specification/).
## Package reference formats
[Section titled “Package reference formats”](#package-reference-formats)
Repository references support two forms:
* `OWNER/REPO`
* `OWNER/REPO/PATH/TO/PACKAGE`
The package root is the folder that contains `aw.yml`.
## Fields
[Section titled “Fields”](#fields)
| Field | Type | Required | Notes |
| ------------------ | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `manifest-version` | string | No | Current supported value: `"1"`. Defaults to `"1"` when omitted. |
| `min-version` | string | No | Minimum compatible `gh aw` version in `vMAJOR.minor.patch` form, such as `v0.38.0`. |
| `name` | string | Yes | Human-readable package name. Must be non-empty after trimming whitespace. |
| `emoji` | string | No | Optional package emoji for display in package metadata. |
| `description` | string | No | Optional package description. `gh aw add` warns when it exceeds 255 characters. |
| `files` | array of strings | No | Package-root-relative paths. Agentic markdown workflows under `workflows/` or `.github/workflows/`; raw GitHub Actions YAML (`.yml`) is also accepted as direct children of `.github/workflows/`. |
## Installable workflows
[Section titled “Installable workflows”](#installable-workflows)
If `files` is present, valid entries become the install bundle. Two entry kinds are supported:
* **Agentic workflow markdown** — paths ending in `.md` under `workflows/` or `.github/workflows/`. `gh aw add` compiles these to lock files and fetches their dependencies.
* **Raw GitHub Actions YAML** — paths ending in `.yml` (but not `.lock.yml`) that are direct children of `.github/workflows/`. `gh aw add` copies these verbatim to `.github/workflows/.yml` with no frontmatter processing, no dependency fetch, and no compilation. Nested subdirectories under `.github/workflows/` and `.yml` files under `workflows/` are not accepted.
If `files` is omitted, or no valid entries remain after filtering, `gh aw add` discovers installable markdown files under:
* `workflows/`
* `.github/workflows/`
If no installable workflow files are resolved, validation fails.
## Package documentation
[Section titled “Package documentation”](#package-documentation)
Package documentation must be `README.md` at the package root. The manifest does not support a `docs` field.
Missing `README.md` causes package validation to fail.
## Example
[Section titled “Example”](#example)
```yaml
manifest-version: "1"
min-version: v0.38.0
name: Repo Assist
emoji:
description: Friendly repository automation for review and issue triage
files:
- workflows/review.md # agentic workflow — compiled on install
- .github/workflows/nightly-review.md
- .github/workflows/ci.yml # raw Actions YAML — copied verbatim
```
# Cache Memory
> Guide to using cache-memory for persistent file storage across workflow runs with GitHub Actions cache.
Cache memory provides persistent file storage across workflow runs via GitHub Actions cache with 7-day retention. The compiler automatically configures the cache directory, restore/save operations, and progressive fallback keys at `/tmp/gh-aw/cache-memory/` (default) or `/tmp/gh-aw/cache-memory-{id}/` (additional caches).
## Enabling Cache Memory
[Section titled “Enabling Cache Memory”](#enabling-cache-memory)
```aw
---
tools:
cache-memory: true
---
```
Stores files at `/tmp/gh-aw/cache-memory/` using a workflow-scoped cache key. Use standard file operations to store/retrieve JSON/YAML, text files, or subdirectories.
## Advanced Configuration
[Section titled “Advanced Configuration”](#advanced-configuration)
```aw
---
tools:
cache-memory:
key: custom-memory-${{ github.repository_owner }}
retention-days: 30 # 1-90 days, extends access beyond cache expiration
allowed-extensions: [".json", ".txt", ".md"] # Restrict file types (default: empty/all files allowed)
---
```
Note
Do not include `${{ github.run_id }}` in a user-supplied key — the compiler appends it automatically to the save key and generates stable restore-keys from the prefix.
### File Type Restrictions
[Section titled “File Type Restrictions”](#file-type-restrictions)
The `allowed-extensions` field restricts which file types can be written to cache-memory. By default, all file types are allowed (empty array). When specified, only files with listed extensions can be stored.
```aw
---
tools:
cache-memory:
allowed-extensions: [".json", ".jsonl", ".txt"] # Only these extensions allowed
---
```
If files with disallowed extensions are found, the workflow will report validation failures.
## Multiple Configurations
[Section titled “Multiple Configurations”](#multiple-configurations)
```aw
---
tools:
cache-memory:
- id: default
key: memory-default
- id: session
key: memory-session-${{ github.run_id }}
- id: logs
retention-days: 7
---
```
Mounts at `/tmp/gh-aw/cache-memory/` (default) or `/tmp/gh-aw/cache-memory-{id}/`. The `id` determines the folder name; `key` defaults to a workflow-scoped prefix derived from the sanitized workflow name.
## Merging from Shared Workflows
[Section titled “Merging from Shared Workflows”](#merging-from-shared-workflows)
```aw
---
imports:
- shared/mcp/server-memory.md
tools:
cache-memory: true
---
```
Merge rules: **Single→Single** (local overrides), **Single→Multiple** (local converts to array), **Multiple→Multiple** (merge by `id`, local wins).
## Behavior
[Section titled “Behavior”](#behavior)
GitHub Actions cache: 7-day retention, 10GB per repo, LRU eviction. Add `retention-days` to upload artifacts (1-90 days) for extended access.
Caches are accessible across branches with unique per-run save keys. The compiler automatically generates a restore-keys prefix by stripping `${{ github.run_id }}` from the save key, so each run can fall back to the previous run’s cache. For `scope: repo`, an additional restore key without the workflow ID is added to allow cross-workflow cache sharing.
Custom user-supplied keys auto-append `-${{ github.run_id }}` if not already present.
## Best Practices
[Section titled “Best Practices”](#best-practices)
Use descriptive file/directory names, hierarchical cache keys (`project-${{ github.repository_owner }}-${{ github.workflow }}`), and appropriate scope (workflow-specific default or repository/user-wide). Monitor growth within 10GB limit.
## Comparison with Repo Memory
[Section titled “Comparison with Repo Memory”](#comparison-with-repo-memory)
| Feature | Cache Memory | Repo Memory |
| --------------- | -------------------- | ----------------- |
| Storage | GitHub Actions Cache | Git Branches |
| Retention | 7 days | Unlimited |
| Size Limit | 10GB/repo | Repository limits |
| Version Control | No | Yes |
| Performance | Fast | Slower |
| Best For | Temporary/sessions | Long-term/history |
For unlimited retention with version control, see [Repo Memory](/gh-aw/reference/repo-memory/).
## Automatic Cleanup
[Section titled “Automatic Cleanup”](#automatic-cleanup)
The [agentic maintenance](/gh-aw/reference/ephemerals/#cache-memory-cleanup) workflow automatically cleans up outdated cache-memory entries on a schedule. Caches are grouped by key prefix (everything before the run ID), and only the latest entry per group is kept. Older entries are deleted to prevent unbounded storage growth.
You can also trigger cleanup manually from the GitHub Actions UI by running the `Agentic Maintenance` workflow with the `clean_cache_memories` operation.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
* **Files not persisting**: Check cache key consistency and logs for restore/save messages.
* **File access issues**: Create subdirectories first, verify permissions, use absolute paths.
* **Cache size issues**: Track growth, clear periodically, or use time-based keys for auto-expiration.
* **Cache path misconfiguration**: When the agent calls `missing_data` with `reason: "cache_memory_miss"`, the conclusion handler automatically opens a failure issue flagging a likely cache path problem. Check that the agent prompt references the correct path (`/tmp/gh-aw/cache-memory/` by default, or `/tmp/gh-aw/cache-memory-{id}/` for named caches) and that the cache key is consistent across runs.
## Integrity-Aware Caching
[Section titled “Integrity-Aware Caching”](#integrity-aware-caching)
When a workflow uses `tools.github.min-integrity`, cache-memory automatically applies integrity-level isolation. Cache keys include the workflow’s integrity level and a hash of the guard policy so that changing any policy field forces a cache miss.
The compiler generates git-backed branching steps around the agent. Before the agent runs, it checks out the matching integrity branch and merges down from all higher-integrity branches (higher integrity always wins conflicts). After the agent runs, changes are committed to that branch. The agent itself sees only plain files — the `.git/` directory rides along transparently in the Actions cache tarball.
### Merge semantics
[Section titled “Merge semantics”](#merge-semantics)
| Run integrity | Sees data written by | Cannot see |
| ------------- | ------------------------------------ | -------------------------------- |
| `merged` | `merged` only | `approved`, `unapproved`, `none` |
| `approved` | `approved` + `merged` | `unapproved`, `none` |
| `unapproved` | `unapproved` + `approved` + `merged` | `none` |
| `none` | all levels | — |
This prevents a lower-integrity agent from poisoning data that a higher-integrity run would later read.
Note
Existing caches will get a cache miss on first run after upgrading to a version that includes this feature — intentional, as legacy data has no integrity provenance.
## Security
[Section titled “Security”](#security)
Don’t store sensitive data in cache memory. Cache memory follows repository permissions.
Logs access. With [threat detection](/gh-aw/reference/threat-detection/), cache saves only after validation succeeds (restore→modify→upload artifact→validate→save).
## Examples
[Section titled “Examples”](#examples)
See [Grumpy Code Reviewer](https://github.com/github/gh-aw/blob/main/.github/workflows/grumpy-reviewer.md) for tracking PR review history.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Repo Memory](/gh-aw/reference/repo-memory/) - Git branch-based persistent storage with unlimited retention
* [Frontmatter](/gh-aw/reference/frontmatter/) - Complete frontmatter configuration guide
* [Safe Outputs](/gh-aw/reference/safe-outputs/) - Output processing and automation
* [GitHub Actions Cache Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows) - Official GitHub cache documentation
# GitHub Repository Checkout
> Configure how actions/checkout is invoked in the agent job — disable checkout, override settings, check out multiple repositories, fetch additional refs, and mark a primary target repository.
The `checkout:` frontmatter field controls how `actions/checkout` is invoked in the agent job. Configure custom checkout settings, check out multiple repositories, or disable checkout entirely.
By default, the agent checks out the repository where the workflow is running with a shallow fetch (`fetch-depth: 1`). If triggered by a pull request event, it also checks out the PR head ref. For most workflows, this default checkout is sufficient and no `checkout:` configuration is necessary.
Use `checkout:` when you need to check out additional branches, check out multiple repositories, or to disable checkout entirely for workflows that don’t need to access code or can access code dynamically through the GitHub Tools.
## Custom Checkout Settings
[Section titled “Custom Checkout Settings”](#custom-checkout-settings)
You can use `checkout:` to override default checkout settings (e.g., fetch depth, sparse checkout) without needing to define a custom job:
```yaml
checkout:
fetch-depth: 0 # Full git history
github-token: ${{ secrets.MY_TOKEN }} # Custom authentication
```
Or use GitHub App authentication:
```yaml
checkout:
fetch-depth: 0
github-app:
client-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
```
You can also use `checkout:` to check out additional repositories alongside the main repository:
```yaml
checkout:
- fetch-depth: 0
- repository: owner/other-repo
path: ./libs/other
ref: main
github-token: ${{ secrets.CROSS_REPO_PAT }}
```
## Configuration Options
[Section titled “Configuration Options”](#configuration-options)
| Field | Type | Description |
| ----------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `repository` | string | Repository in `owner/repo` format. Defaults to the current repository. |
| `ref` | string | Branch, tag, or SHA to checkout. Defaults to the triggering ref. |
| `path` | string | Path within `GITHUB_WORKSPACE` to place the checkout. Defaults to workspace root. |
| `github-token` | string | Token for authentication. Use `${{ secrets.MY_TOKEN }}` syntax. |
| `github-app` | object | GitHub App credentials (`client-id` or `app-id` (deprecated), `private-key`, optional `owner`, `repositories`). Mutually exclusive with `github-token`. `app` is a deprecated alias for the field name. Run `gh aw fix` to auto-migrate `app-id` to `client-id`. |
| `fetch-depth` | integer | Commits to fetch. `0` = full history, `1` = shallow clone (default). |
| `fetch` | string \| string\[] | Additional Git refs to fetch after checkout. See [Fetching Additional Refs](#fetching-additional-refs). |
| `sparse-checkout` | string | Newline-separated patterns for sparse checkout (e.g., `.github/\nsrc/`). |
| `submodules` | string/bool | Submodule handling: `"recursive"`, `"true"`, or `"false"`. |
| `lfs` | boolean | Download Git LFS objects. |
| `current` | boolean | Marks this checkout as the primary working repository. The agent uses this as the default target for all GitHub operations. Only one checkout may set `current: true`; the compiler rejects workflows where multiple checkouts enable it. |
| `force-clean-git-credentials` | boolean | When `true`, the checkout step is generated with `persist-credentials: true` and followed by a dedicated cleanup step that scrubs both repo and submodule git credentials. Use this for submodule-heavy or sparse checkouts where the default `persist-credentials: false` post-step cleanup fails. See [Cleaning Submodule Credentials](#cleaning-submodule-credentials). |
## Fetching Additional Refs
[Section titled “Fetching Additional Refs”](#fetching-additional-refs)
By default, `actions/checkout` performs a shallow clone (`fetch-depth: 1`) of a single ref. For workflows that need to work with other branches — for example, a scheduled workflow that must push changes to open pull-request branches — use the `fetch:` option to retrieve additional refs after the checkout step.
A dedicated git fetch step is emitted after the `actions/checkout` step. Authentication re-uses the checkout token (or falls back to `github.token`) via a transient `http.extraheader` credential — no credentials are persisted to disk, consistent with the enforced `persist-credentials: false` policy.
| Value | Description |
| --------------------- | -------------------------------------------------- |
| `"*"` | All remote branches. |
| `"refs/pulls/open/*"` | All open pull-request head refs (GH-AW shorthand). |
| `"main"` | A specific branch name. |
| `"feature/*"` | A glob pattern matching branch names. |
```yaml
checkout:
- fetch: ["*"] # fetch all branches (default checkout)
fetch-depth: 0 # fetch full history to ensure we can see all commits and PR details
```
```yaml
checkout:
- repository: githubnext/gh-aw-side-repo
github-token: ${{ secrets.GH_AW_SIDE_REPO_PAT }}
fetch: ["refs/pulls/open/*"] # fetch all open PR refs after checkout
fetch-depth: 0 # fetch full history to ensure we can see all commits and PR details
```
```yaml
checkout:
- repository: org/target-repo
github-token: ${{ secrets.CROSS_REPO_PAT }}
fetch: ["main", "feature/*"] # fetch specific branches
fetch-depth: 0 # fetch full history to ensure we can see all commits and PR details
```
Note
If a branch you need is not available after checkout and is not covered by a `fetch:` pattern, and you’re in a private or internal repo, then the agent cannot access its Git history except inefficiently, file by file, via the GitHub MCP. For private repositories, it will be unable to fetch or explore additional branches. If the branch is required and unavailable, configure the appropriate pattern in `fetch:` (e.g., `fetch: ["*"]` for all branches, or `fetch: ["refs/pulls/open/*"]` for PR branches) and recompile the workflow.
## Disabling Checkout (`checkout: false`)
[Section titled “Disabling Checkout (checkout: false)”](#disabling-checkout-checkout-false)
Set `checkout: false` to suppress the default `actions/checkout` step entirely. Use this for workflows that access repositories through MCP servers or other mechanisms that do not require a local clone:
```yaml
checkout: false
```
This is equivalent to omitting the checkout step from the agent job. Custom dev-mode steps (such as “Checkout actions folder”) are unaffected.
## Marking a Primary Repository (`current: true`)
[Section titled “Marking a Primary Repository (current: true)”](#marking-a-primary-repository-current-true)
When a workflow running from a central repository targets a different repository, use `current: true` to tell the agent which repository to treat as its primary working target. The agent uses this as the default for all GitHub operations (creating issues, opening PRs, reading content) unless the prompt instructs otherwise. When omitted, the agent defaults to the repository where the workflow is running.
```yaml
checkout:
- repository: org/target-repo
path: ./target
github-token: ${{ secrets.CROSS_REPO_PAT }}
current: true # agent's primary target
```
Caution
`current: true` only annotates the agent’s system prompt to identify the target repository — it does **not** automatically change the working directory. If the agent needs to run local tools (tests, linters, build scripts) against the checked-out repository, add an explicit `cd` instruction to the prompt:
```plaintext
Navigate into the folder where the target repository has been checked out into: cd ${{ github.workspace }}/target
```
Without this instruction, the agent starts in `$GITHUB_WORKSPACE` (the side repository checkout) and must infer the correct directory on its own.
## Cleaning Submodule Credentials
[Section titled “Cleaning Submodule Credentials”](#cleaning-submodule-credentials)
By default, generated checkout steps set `persist-credentials: false`, which causes `actions/checkout` to remove credentials in its post-step. In repositories with submodules or sparse checkouts, that post-step can fail with missing submodule URL or path errors.
Set `force-clean-git-credentials: true` on a checkout target to opt into an explicit cleanup step instead. The compiler emits the checkout with `persist-credentials: true`, then injects a `Clean git credentials after checkout` step immediately after it. The cleanup removes the credential helper and `http.*.extraheader` entries from both `.git/config` and any `.git/modules/*/config`, including nested submodules.
```yaml
checkout:
- repository: org/monorepo-with-submodules
submodules: recursive
force-clean-git-credentials: true
```
## Checkout Merging
[Section titled “Checkout Merging”](#checkout-merging)
Multiple `checkout:` configurations can target the same path and repository. This is useful for monorepos where different parts of the repository must be merged into the same workspace directory with different settings (e.g., sparse checkout for some paths, full checkout for others).
When multiple `checkout:` entries target the same repository and path, their configurations are merged with the following rules:
* **Fetch depth**: Deepest value wins (`0` = full history always takes precedence)
* **Fetch refs**: Merged (union of all patterns; duplicates are removed)
* **Sparse patterns**: Merged (union of all patterns)
* **LFS**: OR-ed (if any config enables `lfs`, the merged configuration enables it)
* **Submodules**: First non-empty value wins for each `(repository, path)`; once set, later values are ignored
* **Ref/Token/App**: First-seen wins
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Cross-Repository Operations](/gh-aw/reference/cross-repository/) - Reading and writing across multiple repositories
* [Authentication Reference](/gh-aw/reference/auth/) - PAT and GitHub App setup
* [Multi-Repository Examples](/gh-aw/examples/multi-repo/) - Complete working examples
# Command Triggers
> Learn about slash command triggers and context text functionality for agentic workflows, including special @mention triggers for interactive automation.
GitHub Agentic Workflows add the convenience `slash_command:` trigger to create workflows that respond to `/my-bots` in issues and comments.
```yaml
on:
slash_command:
name: my-bot # Optional: defaults to filename without .md extension
```
You can also use shorthand formats:
```yaml
on:
slash_command: "my-bot" # Shorthand: string directly specifies command name
```
```yaml
on: /my-bot # Ultra-short: slash prefix automatically expands to slash_command + workflow_dispatch
```
## Multiple Command Identifiers
[Section titled “Multiple Command Identifiers”](#multiple-command-identifiers)
A single workflow can respond to multiple slash command names by providing an array of command identifiers:
```yaml
on:
slash_command:
name: ["cmd.add", "cmd.remove", "cmd.list"]
```
When triggered, the matched command is available as `needs.activation.outputs.slash_command`, allowing your workflow to determine which command was used:
```aw
---
on:
slash_command:
name: ["summarize", "summary", "tldr"]
---
# Multi-Command Handler
You invoked the workflow using: `/${{ needs.activation.outputs.slash_command }}`
Now analyzing the content...
```
This feature enables command aliases and grouped command handlers without workflow duplication.
This automatically creates issue/PR triggers (`opened`, `edited`, `reopened`), comment triggers (`created`, `edited`), and conditional execution matching `/command-name` mentions.
**Code availability:** When a command is triggered from a pull request body, PR comment, or PR review comment, the coding agent has access to both the PR branch and the default branch.
The command must be the **first word** of the comment or body text to trigger the workflow. This prevents accidental triggers when the command is mentioned elsewhere in the content.
You can combine `slash_command:` with other events like `workflow_dispatch` or `schedule`:
```yaml
on:
slash_command:
name: my-bot
workflow_dispatch:
schedule: weekly on monday
```
### Centralized trigger strategy
[Section titled “Centralized trigger strategy”](#centralized-trigger-strategy)
Set `on.slash_command.strategy: centralized` to opt a workflow into centralized slash-command routing. When enabled, the workflow compiles as `workflow_dispatch`-centric, and the compiler generates one shared `agentic_commands.yml` workflow that listens to merged slash-command events and dispatches matching target workflows with `aw_context`.
```yaml
on:
slash_command:
name: my-bot
strategy: centralized
```
**Note**: With default inline strategy, you cannot combine `slash_command` with `issues`, `issue_comment`, or `pull_request` as they would conflict. With `strategy: centralized`, non-slash events are preserved because slash matching is handled in the generated central trigger workflow.
**Exception for Label-Only Events**: You CAN combine `slash_command` with `issues` or `pull_request` if those events are configured for label-only triggers (`labeled` or `unlabeled` types only). This allows workflows to respond to slash commands while also reacting to label changes.
### Combining `slash_command` with `bots:`
[Section titled “Combining slash\_command with bots:”](#combining-slash_command-with-bots)
Concurrency clash
Combining `slash_command` with `on.bots:` produces a compile-time warning. When a bot listed in `bots:` posts a comment that begins with the slash command text (e.g., `/command-name`), the command check passes and the bot triggers the workflow — occupying the concurrency slot and potentially blocking a simultaneous manual invocation, since `cancel-in-progress` is disabled for command-trigger workflows.
To ensure the workflow only runs on explicit user commands, remove the `bots:` field.
```yaml
# This configuration produces a compile-time warning:
on:
slash_command:
name: rust-review
events: [pull_request, pull_request_comment]
bots:
- "copilot[bot]"
```
```yaml
on:
slash_command: deploy
issues:
types: [labeled, unlabeled] # Valid: label-only triggers don't conflict
```
This pattern is useful when you want a workflow that can be triggered both manually via commands and automatically when labels change.
## Filtering Command Events
[Section titled “Filtering Command Events”](#filtering-command-events)
By default, command triggers listen to all comment-related events, which can create skipped runs in the Actions UI. Use the `events:` field to restrict where commands are active:
```yaml
on:
slash_command:
name: my-bot
events: [issues, issue_comment] # Only in issue bodies and issue comments
```
**Supported events:** `issues`, `issue_comment`, `pull_request`, `pull_request_comment`, `pull_request_review_comment`, `discussion`, `discussion_comment`, or `*` (all, default).
Note
Both `issue_comment` and `pull_request_comment` map to GitHub Actions’ `issue_comment` event with automatic filtering to distinguish between issue and PR comments.
### Example command workflow
[Section titled “Example command workflow”](#example-command-workflow)
Issue-only command (avoids skipped runs from PR events):
```yaml
on:
slash_command:
name: investigate
events: [issues, issue_comment]
```
PR-only command:
```yaml
on:
slash_command:
name: code-review
events: [pull_request, pull_request_comment]
```
## Context Text
[Section titled “Context Text”](#context-text)
All workflows access `steps.sanitized.outputs.text`, which provides **sanitized** context: for issues and PRs, it’s `title + "\n\n" + body`; for comments and reviews, it’s the body content.
```aw
# Analyze this content: "${{ steps.sanitized.outputs.text }}"
```
**Why sanitized context?** The sanitized text neutralizes @mentions and bot triggers (like `fixes #123`), protects against XML injection, filters URIs to trusted HTTPS domains, limits content size (0.5MB max, 65k lines), and strips ANSI escape sequences.
**Comparison:**
```aw
# RECOMMENDED: Secure sanitized context
Analyze this issue: "${{ steps.sanitized.outputs.text }}"
# DISCOURAGED: Raw context values (security risks)
Title: "${{ github.event.issue.title }}"
Body: "${{ github.event.issue.body }}"
```
## Reactions and Status Comments
[Section titled “Reactions and Status Comments”](#reactions-and-status-comments)
Command workflows enable `reaction: eyes` () and `status-comment: true` by default. The reaction adds a visual indicator to triggering comments; the status comment posts a started/completed notification with a workflow run link.
Customize or disable either:
```yaml
on:
slash_command:
name: my-bot
reaction: "rocket" # Override default "eyes"
status-comment: false # Disable the status comment
```
To disable the reaction entirely, use `reaction: none`.
See [Reactions and Status Comments](/gh-aw/reference/triggers/#reactions-reaction) for all available reactions and detailed behavior.
## Slash Commands from a Side Repository
[Section titled “Slash Commands from a Side Repository”](#slash-commands-from-a-side-repository)
GitHub Actions only delivers events to the repository where they occur. When workflows live in a separate side repository, events from the main repository are never delivered there. **Slash command triggers cannot be used directly in a workflow hosted in a side repository.**
The recommended solution is a **bridge pattern**: a thin relay workflow in the main repository receives the slash command and forwards it to the side repository via `workflow_dispatch`.
See [Triage from Side Repo](/gh-aw/examples/multi-repo/triage-from-side-repo/) for a full walkthrough with examples and trade-offs.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Frontmatter](/gh-aw/reference/frontmatter/) - All configuration options for workflows
* [Workflow Structure](/gh-aw/reference/workflow-structure/) - Directory layout and organization
* [CLI Commands](/gh-aw/setup/cli/) - CLI commands for workflow management
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Running workflows from a separate repository
* [ChatOps](/gh-aw/patterns/chat-ops/) - Interactive automation with slash commands
# Compilation Process
> Advanced technical documentation on how GitHub Agentic Workflows compiles markdown files into GitHub Actions YAML, including job orchestration, action pinning, artifacts, and MCP integration.
This guide documents the internal compilation process that transforms markdown workflow files into executable GitHub Actions YAML. Understanding this process helps when debugging workflows, optimizing performance, or contributing to the project.
## Overview
[Section titled “Overview”](#overview)
The `gh aw compile` command transforms a markdown workflow file into a complete GitHub Actions `.lock.yml` by embedding frontmatter and setting up runtime loading of the markdown body. The process runs five compilation phases (parsing, validation, job construction, dependency resolution, and YAML generation) described below.
When the workflow runs, the markdown body is loaded at runtime — you can edit instructions without recompilation. See [Editing Workflows](/gh-aw/guides/editing-workflows/) for details.
## Compilation Phases
[Section titled “Compilation Phases”](#compilation-phases)
### Phase 1: Parsing and Validation
[Section titled “Phase 1: Parsing and Validation”](#phase-1-parsing-and-validation)
The compiler extracts the YAML frontmatter, validates it against the workflow schema, validates expression safety (only allow-listed GitHub Actions expressions), and resolves imports.
#### Import Resolution
[Section titled “Import Resolution”](#import-resolution)
Imports are resolved with a deterministic breadth-first traversal: starting from `imports:` in the main workflow, each file is loaded, its configurations are extracted, and any nested imports are appended to the queue. Visited files are tracked to detect cycles.
| Field | Merge strategy |
| ------------ | ------------------------------------------------------------------ |
| Tools | Deep merge; arrays concatenated and deduplicated |
| MCP servers | Imported servers override main-workflow servers with the same name |
| Network | Union of allowed domains, deduplicated and sorted |
| Permissions | Validation only — main must satisfy imported requirements |
| Safe outputs | Main workflow overrides imported configurations per type |
| Runtimes | Main workflow versions override imported versions |
Processing order follows BFS:
```plaintext
Main Workflow
├── import-a.md → Processed 1st
│ ├── nested-1.md → Processed 3rd (after import-b)
│ └── nested-2.md → Processed 4th
└── import-b.md → Processed 2nd
└── nested-3.md → Processed 5th
```
See [Imports Reference](/gh-aw/reference/imports/) for complete merge semantics.
### Phases 2–5: Building the Workflow
[Section titled “Phases 2–5: Building the Workflow”](#phases-25-building-the-workflow)
| Phase | Steps |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **2 Job Construction** | Builds specialized jobs: pre-activation (if needed), activation, agent, safe outputs, safe-jobs, and custom jobs |
| **3 Dependency Resolution** | Validates job dependencies, detects circular references, computes topological order, generates Mermaid graph |
| **4 Action Pinning** | Pins all actions to SHAs: check cache → GitHub API → embedded pins → add version comment (e.g., `actions/checkout@sha # v6`) |
| **5 YAML Generation** | Assembles final `.lock.yml`: header with metadata, Mermaid dependency graph, alphabetical jobs, embedded original prompt |
## Job Types
[Section titled “Job Types”](#job-types)
The compilation process generates specialized jobs based on workflow configuration:
| Job | Trigger | Purpose | Key Dependencies |
| -------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| **pre\_activation** | Role checks, stop-after deadlines, skip-if-match, or command triggers | Validates permissions, deadlines, and conditions before AI execution | None (runs first) |
| **activation** | Always | Prepares workflow context, sanitizes event text, validates lock file freshness | `pre_activation` (if exists) |
| **agent** | Always | Core job that executes AI agent with configured engine, tools, and Model Context Protocol (MCP) servers | `activation` |
| **detection** | `safe-outputs.threat-detection:` configured | Scans agent output for security threats before processing | `agent` |
| **Safe output jobs** | Corresponding `safe-outputs.*:` configured | Process agent output to perform GitHub API operations (create issues/PRs, add comments, upload assets, etc.) | `agent`, `detection` (if exists) |
| **conclusion** | Always (if safe outputs exist) | Aggregates results and generates workflow summary | All safe output jobs |
### Agent Job Steps
[Section titled “Agent Job Steps”](#agent-job-steps)
The agent job runs: repository checkout and runtime setup (Node.js, Python, Go) → cache restoration → MCP container initialization → prompt generation from the markdown body → engine execution (Copilot, Claude, or Codex) → output upload as a GitHub Actions artifact → cache persistence. Key environment variables: `GH_AW_PROMPT` (prompt file), `GH_AW_SAFE_OUTPUTS` (output JSON), `GITHUB_TOKEN`.
### Safe Output Jobs
[Section titled “Safe Output Jobs”](#safe-output-jobs)
Every safe output job follows the same pattern: download the agent artifact, parse its JSON, execute the corresponding GitHub API operation with the right permissions, and link to related items. Available types include `create_issue`, `create_discussion`, `add_comment`, `create_pull_request`, `create_pr_review_comment`, `create_code_scanning_alert`, `add_labels`, `assign_milestone`, `update_issue`, `update_release`, `push_to_pr_branch`, `upload_assets`, `update_project`, `missing_tool`, and `noop`.
### Custom Jobs
[Section titled “Custom Jobs”](#custom-jobs)
Use `safe-outputs.jobs:` for custom jobs with full GitHub Actions syntax, or `jobs:` for additional workflow jobs with user-defined dependencies. See [DeterministicOps](/gh-aw/patterns/deterministic-ops/) for examples of multi-stage workflows combining deterministic computation with AI reasoning.
## Job Dependency Graphs
[Section titled “Job Dependency Graphs”](#job-dependency-graphs)
Jobs execute in topological order based on dependencies. Here’s a comprehensive example:
```
graph LR
pre_activation["pre_activation"]
activation["activation"]
agent["agent"]
detection["detection"]
create_issue["create_issue"]
add_comment["add_comment"]
conclusion["conclusion"]
pre_activation --> activation
activation --> agent
agent --> detection
agent --> create_issue
agent --> add_comment
detection --> create_issue
detection --> add_comment
create_issue --> add_comment
create_issue --> conclusion
add_comment --> conclusion
```
**Execution flow**: Pre-activation validates permissions → Activation prepares context → Agent executes AI → Detection scans output → Safe outputs run in parallel → Add comment waits for created items → Conclusion summarizes results. Safe output jobs without cross-dependencies run concurrently; when threat detection is enabled, safe outputs depend on both agent and detection jobs.
## Why Detection, Safe Outputs, and Conclusion Are Separate Jobs
[Section titled “Why Detection, Safe Outputs, and Conclusion Are Separate Jobs”](#why-detection-safe-outputs-and-conclusion-are-separate-jobs)
A typical compiled workflow contains these post-agent jobs:
```
flowchart TD
activation["activation ubuntu-slim contents: read"] --> agent["agent ubuntu-latest READ-ONLY permissions concurrency group"]
agent --> detection["detection ubuntu-latest contents: read concurrency group RUNS AI ENGINE"]
agent --> conclusion["conclusion ubuntu-slim issues: write pr: write"]
detection --> safe_outputs["safe_outputs ubuntu-slim contents: write issues: write pr: write"]
detection --> conclusion
safe_outputs --> conclusion
detection --> update_cache_memory["update_cache_memory ubuntu-latest contents: read"]
update_cache_memory --> conclusion
activation --> safe_outputs
activation --> conclusion
```
These three jobs form a **sequential security pipeline** rooted in [Plan-Level Trust](/gh-aw/introduction/architecture/) — AI reasoning (read-only) is separated from write operations. They cannot be merged because GitHub Actions permissions are per-job and immutable for the duration of a job:
| Job | Key Permissions | Rationale |
| ----------------- | ------------------------------------------------------------- | --------------------------------------------- |
| **detection** | `contents: read` | Runs AI analysis — must not have write access |
| **safe\_outputs** | `contents: write`, `issues: write`, `pull-requests: write` | Executes GitHub API write operations |
| **conclusion** | `issues: write`, `pull-requests: write`, `discussions: write` | Updates comments, handles failures |
A combined job would hold write permissions while running threat detection, defeating least privilege and letting a compromised agent bypass the gate. Job-level isolation also enables:
* **Hard gating.** The `safe_outputs` job condition `needs.detection.outputs.success == 'true'` prevents the runner from starting at all if detection fails. Step-level `if` checks within one job are weaker.
* **`always()` semantics for `conclusion`.** It inspects upstream results via `needs.agent.result` to log errors and report missing tools even when writes fail.
* **Right-sized runners.** Detection needs `ubuntu-latest` for AI execution; safe\_outputs and conclusion use the lightweight `ubuntu-slim`.
* **Concurrency isolation.** Detection shares a concurrency group with the agent job to serialize AI execution; safe\_outputs intentionally does not, so it can run alongside other workflows’ detection phases.
* **Artifact-based handoff.** The agent writes `agent_output.json`; detection emits `success`; safe\_outputs only downloads the artifact if approved. A shared filesystem in a single job would allow output tampering between phases.
## Action Pinning
[Section titled “Action Pinning”](#action-pinning)
All GitHub Actions are pinned to commit SHAs (e.g., `actions/checkout@b4ffde6...11 # v6`) to defend against supply chain attacks — tags can be moved, SHAs cannot. Resolution order is cache (`.github/aw/actions-lock.json`) → GitHub API → embedded pins.
### The actions-lock.json Cache
[Section titled “The actions-lock.json Cache”](#the-actions-lockjson-cache)
`.github/aw/actions-lock.json` caches resolved `action@version` → SHA mappings so compilation produces consistent results regardless of the available token. Resolving a tag to a SHA requires GitHub API access, which fails under restricted tokens — notably the GitHub Copilot Coding Agent (CCA) token. With the cache, CCA and similar restricted environments reuse SHAs from a prior compile run with a broader-scope token.
**Commit `actions-lock.json` to version control** so every contributor and automated tool uses the same immutable pins. Refresh with `gh aw update-actions`, or delete and recompile with a permissive token to force full re-resolution.
## The gh-aw-actions Repository
[Section titled “The gh-aw-actions Repository”](#the-gh-aw-actions-repository)
`github/gh-aw-actions` contains the reusable actions that power compiled workflows. Every action step in a generated `.lock.yml` references it (usually by commit SHA, occasionally by a stable tag like `v0` when SHA resolution is unavailable):
```yaml
uses: github/gh-aw-actions/setup@abc1234...
```
Never edit these references by hand — run `gh aw compile` or `gh aw update-actions` to regenerate them. Use `--actions-repo` (with `--action-mode action`) to compile against a fork or specific tag during development; see [Compilation Commands](#compilation-commands).
### Dependabot and gh-aw-actions
[Section titled “Dependabot and gh-aw-actions”](#dependabot-and-gh-aw-actions)
Dependabot may open PRs to bump `github/gh-aw-actions` to a newer SHA. **Do not merge them** — pin updates must come from `gh aw compile`, which coordinates pins across all compiled workflows from a single release. `gh aw compile` automatically inserts an ignore rule when a `github-actions` update block exists in `.github/dependabot.yml`. When enabling Dependabot from scratch, use:
```yaml
updates:
- package-ecosystem: github-actions
directory: "/.github/workflows"
ignore:
- dependency-name: "github/gh-aw-actions/**" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
```
## Artifacts Created
[Section titled “Artifacts Created”](#artifacts-created)
Workflows generate several artifacts during execution:
| Artifact | Location | Purpose | Lifecycle |
| --------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| **agent\_output.json** | `/tmp/gh-aw/safeoutputs/` | AI agent output with structured safe output data (create\_issue, add\_comment, etc.) | Uploaded by agent job, downloaded by safe output jobs, auto-deleted after 90 days |
| **agent\_usage.json** | `/tmp/gh-aw/` | Aggregated token counts: `{"input_tokens":…,"output_tokens":…,"cache_read_tokens":…,"cache_write_tokens":…}` | Bundled in the unified agent artifact when the firewall is enabled; accessible to third-party tools without parsing step summaries |
| **prompt.txt** | `/tmp/gh-aw/aw-prompts/` | Generated prompt sent to AI agent (includes markdown instructions, imports, context variables) | Retained for debugging and reproduction |
| **firewall-audit-logs** | See structure below | Dedicated artifact for AWF audit/observability logs (token usage, network policy, audit trail) | Uploaded by all firewall-enabled workflows; analyzed by `gh aw logs --artifacts firewall` |
| **firewall-logs/** | `/tmp/gh-aw/sandbox/firewall/logs/` | Network access logs in Squid format (when `network.firewall:` enabled) | Analyzed by `gh aw logs` command |
| **cache-memory/** | `/tmp/gh-aw/cache-memory/` | Persistent agent memory across runs (when `tools.cache-memory:` configured) | Restored at start, saved at end via GitHub Actions cache |
| **patches/**, **sarif/**, **metadata/** | Various | Safe output data (git patches, SARIF files, metadata JSON) | Temporary, cleaned after processing |
### `firewall-audit-logs` Artifact Structure
[Section titled “firewall-audit-logs Artifact Structure”](#firewall-audit-logs-artifact-structure)
The `firewall-audit-logs` artifact is a dedicated multi-file artifact uploaded by all firewall-enabled workflows. It is **separate** from the unified `agent` artifact. Downstream workflows that need token usage data or firewall audit logs must download this artifact specifically.
```plaintext
firewall-audit-logs/
├── api-proxy-logs/
│ └── token-usage.jsonl ← Token usage data per request
├── squid-logs/
│ └── access.log ← Network policy log (allow/deny)
├── audit.jsonl ← Firewall audit trail
└── policy-manifest.json ← Policy configuration snapshot
```
> **Tip:** Use `gh aw logs --artifacts firewall` to download and analyze firewall data instead of `gh run download` directly. The CLI handles artifact naming and backward compatibility automatically. See the [Artifacts reference](/gh-aw/reference/artifacts/) for the complete artifact naming guide.
## MCP Server Integration
[Section titled “MCP Server Integration”](#mcp-server-integration)
Model Context Protocol (MCP) servers provide tools to AI agents. Compilation emits `mcp-config.json` from the workflow’s tool configuration. Local servers run in Docker containers with auto-generated Dockerfiles and connect via stdio; HTTP servers connect directly with configured headers and authentication. `allowed:` restricts which tools the agent sees, and secrets inject through Dockerfile env vars (local) or config references (HTTP). At runtime, MCP containers start after runtime setup, the engine executes with tool access, then containers stop.
## Pre-Activation Job
[Section titled “Pre-Activation Job”](#pre-activation-job)
Pre-activation runs gating checks sequentially before any AI execution. Any failure sets `activated=false`, skipping downstream jobs and saving costs:
* **Role checks** (`roles:`) — actor has admin/maintainer/write permission
* **Stop-after** (`on.stop-after:`) — workflow has not passed its deadline (e.g., `+30d`, `2024-12-31`)
* **Skip-if-match** (`skip-if-match:`) — no existing item matches the dedup criteria
* **Command position** (`on.slash_command:`) — slash command appears in the first 3 lines
## Compilation Commands
[Section titled “Compilation Commands”](#compilation-commands)
| Command | Description |
| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `gh aw compile` | Compile all workflows in `.github/workflows/` |
| `gh aw compile my-workflow` | Compile specific workflow |
| `gh aw compile --verbose` | Enable verbose output |
| `gh aw compile --strict` | Enhanced security validation |
| `gh aw compile --no-emit` | Validate without generating files |
| `gh aw compile --actionlint --zizmor --poutine` | Run security scanners |
| `gh aw compile --purge` | Remove orphaned `.lock.yml` files |
| `gh aw compile --output /path/to/output` | Custom output directory |
| `gh aw compile --action-mode action --actions-repo owner/repo` | Compile using a custom actions repository (requires `--action-mode action`) |
| `gh aw compile --action-mode action --actions-repo owner/repo --action-tag branch-or-sha` | Compile against a specific branch or SHA in a fork |
| `gh aw compile --action-tag v1.2.3` | Pin action references to a specific tag or SHA (implies release mode) |
| `gh aw validate` | Validate all workflows (compile + all linters, no file output) |
| `gh aw validate my-workflow` | Validate a specific workflow |
| `gh aw validate --json` | Validate and output results in JSON format |
| `gh aw validate --strict` | Validate with strict mode enforced |
Tip
Compilation is only required when changing **frontmatter configuration**. The **markdown body** (AI instructions) is loaded at runtime and can be edited without recompilation. See [Editing Workflows](/gh-aw/guides/editing-workflows/) for details.
Note
The `--actions-repo` flag overrides the default `github/gh-aw-actions` repository used when `--action-mode action` is set. Use it together with `--action-tag` to compile against a branch or fork during development.
## Debugging Compilation
[Section titled “Debugging Compilation”](#debugging-compilation)
Run `DEBUG=workflow:* gh aw compile my-workflow --verbose` to trace job creation, action pin resolution, tool configuration, and MCP setup. Inspect generated `.lock.yml` files for header comments, the Mermaid dependency graph, job structure, SHA pins, and MCP config. Common fixes: circular dependencies → review `needs:` clauses; missing action pin → add to `action_pins.json` or enable dynamic resolution; invalid MCP config → verify `command`, `args`, `env`.
## Performance
[Section titled “Performance”](#performance)
Simple workflows compile in \~100ms; workflows with imports in \~500ms; workflows that resolve action SHAs dynamically in \~2s. To keep compilation fast, commit `.github/aw/actions-lock.json` and minimize import depth. At runtime, safe output jobs without cross-dependencies run in parallel; enable `cache:` and `cache-memory:` for further speedups.
## Advanced Topics
[Section titled “Advanced Topics”](#advanced-topics)
* **Custom engines**: implement an engine that returns GitHub Actions steps and tool access, then register it with the framework.
* **Schema extension**: add frontmatter fields by updating the workflow schema, rebuilding (`make build`), and wiring up parser handling.
* **Workflow manifest**: imported files are tracked in lock file headers for update detection and audit trails.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Editing Workflows](/gh-aw/guides/editing-workflows/) - When to recompile vs edit directly
* [Frontmatter Reference](/gh-aw/reference/frontmatter/) - All configuration options
* [Tools Reference](/gh-aw/reference/tools/) - Tool configuration guide
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - Output processing
* [Engines Reference](/gh-aw/reference/engines/) - AI engine configuration
* [Network Reference](/gh-aw/reference/network/) - Network permissions
# Concurrency Control
> Complete guide to concurrency control in GitHub Agentic Workflows, including agent job concurrency configuration and engine isolation.
GitHub Agentic Workflows uses dual-level concurrency control to prevent resource exhaustion and ensure predictable execution:
* **Per-workflow**: Limits based on workflow name and trigger context (issue, PR, branch)
* **Per-engine**: Limits AI execution across all workflows via `engine.concurrency`
## Per-Workflow Concurrency
[Section titled “Per-Workflow Concurrency”](#per-workflow-concurrency)
Workflow-level concurrency groups include the workflow name plus context-specific identifiers:
| Trigger Type | Concurrency Group | Cancel In Progress |
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------- |
| Issues | `gh-aw-${{ github.workflow }}-${{ issue.number }}` | No |
| Pull Requests | `gh-aw-${{ github.workflow }}-${{ pr.number \|\| ref }}` | Yes (new commits cancel outdated runs) |
| Push | `gh-aw-${{ github.workflow }}-${{ github.ref }}` | No |
| Schedule/Other | `gh-aw-${{ github.workflow }}` | No |
| Label-triggered (label trigger shorthand or label\_command) | `gh-aw-${{ github.workflow }}-${{ entity.number }}-${{ github.event.label.name }}` | Yes for PRs, No otherwise |
This ensures workflows on different issues, PRs, or branches run concurrently without interference.
## Per-Engine Concurrency
[Section titled “Per-Engine Concurrency”](#per-engine-concurrency)
The default per-engine pattern `gh-aw-{engine-id}` ensures only one agent job runs per engine across all workflows, preventing AI resource exhaustion. The group includes only the engine ID and `gh-aw-` prefix — workflow name, issue/PR numbers, and branches are excluded.
```yaml
jobs:
agent:
concurrency:
group: "gh-aw-{engine-id}"
```
## Custom Concurrency
[Section titled “Custom Concurrency”](#custom-concurrency)
Override either level independently:
```yaml
---
on: push
concurrency: # Workflow-level
group: custom-group-${{ github.ref }}
cancel-in-progress: true
engine:
id: copilot
concurrency: # Engine-level
group: "gh-aw-copilot-${{ github.workflow }}"
tools:
github:
allowed: [list_issues]
---
```
## Safe Outputs Job Concurrency
[Section titled “Safe Outputs Job Concurrency”](#safe-outputs-job-concurrency)
The `safe_outputs` job runs independently from the agent job and can process outputs concurrently across workflow runs. Use `safe-outputs.concurrency-group` to serialize access when needed:
```yaml
safe-outputs:
concurrency-group: "safe-outputs-${{ github.repository }}"
create-issue:
```
When set, the `safe_outputs` job uses `cancel-in-progress: false` — meaning queued runs wait for the in-progress run to finish rather than being cancelled. This is useful for workflows that create issues or pull requests where duplicate operations would be undesirable.
See [Safe Outputs](/gh-aw/reference/safe-outputs/#safe-outputs-job-concurrency-concurrency-group) for details.
## Queue Behavior (`queue`)
[Section titled “Queue Behavior (queue)”](#queue-behavior-queue)
GitHub Actions concurrency groups accept an optional `queue` field that controls how multiple pending runs in the same group are handled. The gh-aw compiler preserves this field in both top-level and per-engine concurrency blocks:
| Value | Behavior |
| -------------------------- | ------------------------------------------------------------------------ |
| `single` (Actions default) | Only the latest pending run is kept; earlier pending runs are discarded. |
| `max` | All pending runs queue and run in arrival order. |
```yaml
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
queue: max
```
Compiler-generated concurrency groups (agent, output, and conclusion jobs) emit `queue: max` by default so back-to-back triggers run sequentially rather than being dropped. Set `features.group-concurrency-queue: false` to omit `queue` from generated groups and revert to the Actions default:
```yaml
features:
group-concurrency-queue: false
```
## Conclusion Job Concurrency
[Section titled “Conclusion Job Concurrency”](#conclusion-job-concurrency)
The `conclusion` job — which handles reporting and post-agent cleanup — automatically receives a workflow-specific concurrency group derived from the workflow filename:
```yaml
conclusion:
concurrency:
group: "gh-aw-conclusion-my-workflow"
cancel-in-progress: false
queue: max
```
This prevents conclusion jobs from colliding when multiple agents run the same workflow concurrently. The group uses `cancel-in-progress: false` so queued conclusion runs complete in order rather than being discarded, and `queue: max` preserves arrival order for queued runs (see [Queue Behavior](#queue-behavior-queue)).
This concurrency group is set automatically during compilation and requires no manual configuration.
When `concurrency.job-discriminator` is set, the discriminator is also appended to the conclusion job’s concurrency group, making each run’s group distinct:
```yaml
concurrency:
job-discriminator: ${{ github.event.issue.number || github.run_id }}
```
This generates a group like `gh-aw-conclusion-my-workflow-${{ github.event.issue.number || github.run_id }}`, preventing concurrent runs for different issues or inputs from competing for the same conclusion slot.
## Fan-Out Concurrency (`job-discriminator`)
[Section titled “Fan-Out Concurrency (job-discriminator)”](#fan-out-concurrency-job-discriminator)
When multiple workflow instances are dispatched concurrently with different inputs (fan-out pattern), compiler-generated job-level concurrency groups are static across all runs — causing all but the latest dispatched run to be cancelled as they compete for the same slot.
Use `concurrency.job-discriminator` to append a unique expression to compiler-generated job-level concurrency groups (`agent`, `output`, and `conclusion` jobs), making each dispatched run’s group distinct:
```yaml
concurrency:
job-discriminator: ${{ inputs.finding_id }}
```
This generates a unique job-level concurrency group per dispatched run, preventing fan-out cancellations while preserving the per-workflow concurrency group at the workflow level.
Common expressions:
| Scenario | Expression |
| ------------------------------------------ | ----------------------------------------------- |
| Fan-out by a specific input | `${{ inputs.finding_id }}` |
| Universal uniqueness (e.g. scheduled runs) | `${{ github.run_id }}` |
| Dispatched or scheduled fallback | `${{ inputs.organization \|\| github.run_id }}` |
Note
`job-discriminator` is a gh-aw extension and is stripped from the compiled lock file. It does not appear in the generated GitHub Actions YAML.
Note
`job-discriminator` has no effect on workflows triggered by `workflow_dispatch`-only, `push`, or `pull_request` events, or when the engine provides an explicit job-level concurrency configuration.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Frontmatter](/gh-aw/reference/frontmatter/) - Complete frontmatter reference
* [Safe Outputs](/gh-aw/reference/safe-outputs/) - Safe output processing and job configuration
# Importing Copilot Agent Files
> Import and reuse Copilot agent files with GitHub Agentic Workflows
“Custom agents” is a term used in GitHub Copilot for specialized prompts for behaviors for specific tasks. They are markdown files stored in the `.github/agents/` directory and imported via the `imports` field. Copilot supports agent files natively, while other engines (Claude, Codex) inject the markdown body as a prompt.
A typical custom agent file looks like this:
.github/agents/my-agent.md
```markdown
---
name: My Copilot Agent
description: Specialized prompt for code review tasks
---
# Agent Instructions
You are a specialized code review agent. Focus on:
- Code quality and best practices
- Security vulnerabilities
- Performance optimization
```
## Using Copilot Agent Files from Agentic Workflows
[Section titled “Using Copilot Agent Files from Agentic Workflows”](#using-copilot-agent-files-from-agentic-workflows)
Import Copilot agent files in your workflow using the `imports` field. Agent files can be imported from local `.github/agents/` directories or from external repositories.
### Local Agent File Import
[Section titled “Local Agent File Import”](#local-agent-file-import)
Import an agent from your repository:
```yaml
---
on: pull_request
engine: copilot
imports:
- .github/agents/my-agent.md
---
Review the pull request and provide feedback.
```
### Remote Agent File Import
[Section titled “Remote Agent File Import”](#remote-agent-file-import)
Import an agent file from an external repository using the `owner/repo/path@ref` format:
```yaml
---
on: pull_request
engine: copilot
imports:
- acme-org/shared-agents/.github/agents/code-reviewer.md@v1.0.0
---
Perform comprehensive code review using shared agent instructions.
```
The agent instructions are merged with the workflow prompt, customizing the AI engine’s behavior for specific tasks.
## Agent File Requirements
[Section titled “Agent File Requirements”](#agent-file-requirements)
* **Location**: Must be in a `.github/agents/` directory (local or remote repository)
* **Format**: Markdown with YAML frontmatter
* **Frontmatter**: Can include `name`, `description`, `tools`, and `mcp-servers`
* **One per workflow**: Only one agent file can be imported per workflow
* **Caching**: Remote agent files are cached by commit SHA in `.github/aw/imports/`
## Copilot Agent File Collections
[Section titled “Copilot Agent File Collections”](#copilot-agent-file-collections)
Organizations can create libraries of specialized custom agent files:
```text
acme-org/ai-agents/
└── .github/
└── agents/
├── code-reviewer.md # General code review
├── security-auditor.md # Security-focused analysis
├── performance-analyst.md # Performance optimization
├── accessibility-checker.md # WCAG compliance
└── documentation-writer.md # Technical documentation
```
Teams import agent files based on workflow needs:
Security-focused PR review
```yaml
---
on: pull_request
engine: copilot
imports:
- acme-org/ai-agents/.github/agents/security-auditor.md@v2.0.0
- acme-org/ai-agents/.github/agents/code-reviewer.md@v1.5.0
---
# Security Review
Perform comprehensive security review of this pull request.
```
## Combining Copilot Agent Files with Other Imports
[Section titled “Combining Copilot Agent Files with Other Imports”](#combining-copilot-agent-files-with-other-imports)
You can mix custom agent file imports with tool configurations and shared components:
```yaml
---
on: pull_request
engine: copilot
imports:
# Import specialized custom agent file
- acme-org/ai-agents/.github/agents/security-auditor.md@v2.0.0
# Import tool configurations
- acme-org/workflow-library/shared/tools/github-standard.md@v1.0.0
# Import MCP servers
- acme-org/workflow-library/shared/mcp/database.md@v1.0.0
# Import security policies
- acme-org/workflow-library/shared/config/security-policies.md@v1.0.0
permissions:
contents: read
safe-outputs:
create-pull-request-review-comment:
max: 10
---
# Comprehensive Security Review
Perform detailed security analysis using specialized agent files and tools.
```
## Defining Copilot Sub-agents Inline
[Section titled “Defining Copilot Sub-agents Inline”](#defining-copilot-sub-agents-inline)
Instead of (or alongside) importing agent files from `.github/agents/`, you can define agents directly inside the workflow markdown. See [Inline Sub-Agents](/gh-aw/reference/inline-sub-agents/) for the complete syntax reference, including name constraints and frontmatter fields.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Imports Reference](/gh-aw/reference/imports/) - Complete import system documentation
* [Inline Sub-Agents](/gh-aw/reference/inline-sub-agents/) - Defining Copilot sub-agents inside a workflow file
* [Reusing Workflows](/gh-aw/guides/packaging-imports/) - Managing workflow imports
* [Frontmatter](/gh-aw/reference/frontmatter/) - Configuration options reference
# Cost Management
> Understand and control the cost of running GitHub Agentic Workflows, including Actions minutes, inference billing, and strategies to reduce spend.
The cost of running an agentic workflow is the sum of two components: **GitHub Actions minutes** consumed by the workflow jobs, and **inference costs** charged by the AI provider for each agent run.
## Cost Components
[Section titled “Cost Components”](#cost-components)
### GitHub Actions Minutes
[Section titled “GitHub Actions Minutes”](#github-actions-minutes)
Every workflow job consumes Actions compute time billed at standard [GitHub Actions pricing](https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-actions/about-billing-for-github-actions). A typical agentic workflow run includes at least two jobs:
| Job | Purpose | Typical duration |
| -------------------------- | ----------------------------------------------------------------------------------- | ---------------- |
| Pre-activation / detection | Validates the trigger, runs membership checks, evaluates `skip-if-match` conditions | 10–30 seconds |
| Agent | Runs the AI engine and executes tools | 1–15 minutes |
Each job also incurs approximately 1.5 minutes of runner setup overhead on top of its execution time.
### Inference Costs
[Section titled “Inference Costs”](#inference-costs)
The agent job invokes an AI engine to process the prompt and call tools. Inference is billed by the provider:
| Engine | Billed to | Unit |
| --------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `copilot` | Account owning [`COPILOT_GITHUB_TOKEN`](/gh-aw/reference/auth/#copilot_github_token) | Premium requests (1–2 per run; see [Copilot billing](https://docs.github.com/en/copilot/about-github-copilot/subscription-plans-for-github-copilot)) |
| `claude` | Anthropic account for [`ANTHROPIC_API_KEY`](/gh-aw/reference/auth/#anthropic_api_key) | Tokens |
| `codex` | OpenAI account for [`OPENAI_API_KEY`](/gh-aw/reference/auth/#openai_api_key) | Tokens |
Note
For Copilot, inference is charged to the individual account owning `COPILOT_GITHUB_TOKEN`, not the repository or organization. Use a dedicated service account to track spend per workflow.
## Monitoring Costs with `gh aw logs`
[Section titled “Monitoring Costs with gh aw logs”](#monitoring-costs-with-gh-aw-logs)
The `gh aw logs` command surfaces per-run metrics — elapsed duration, token usage, and estimated inference cost — before you decide what to optimize. Use `gh aw audit ` to deep-dive into a single run’s token usage, tool calls, and inference spend; its **Metrics** and **Performance Metrics** sections cover token counts, effective tokens, turn counts, and estimated cost in one place. For cost trends across multiple runs, use `gh aw logs --format markdown [workflow]` to generate a cross-run report with anomaly detection.
### View recent run durations
[Section titled “View recent run durations”](#view-recent-run-durations)
```bash
# Overview table for all agentic workflows (last 10 runs)
gh aw logs
# Narrow to a single workflow
gh aw logs issue-triage-agent
# Last 30 days for Copilot workflows
gh aw logs --engine copilot --start-date -30d
```
The overview table includes a **Duration** column showing elapsed wall-clock time per run. Because GitHub Actions bills compute time by the minute (rounded up per job), duration is the primary indicator of Actions spend.
### Export metrics as JSON
[Section titled “Export metrics as JSON”](#export-metrics-as-json)
Use `--json` to get structured output suitable for scripting or trend analysis:
```bash
# Write JSON to a file for further processing
gh aw logs --start-date -1w --json > /tmp/logs.json
# List per-run duration, tokens, and cost across all workflows
gh aw logs --start-date -30d --json | \
jq '.runs[] | {workflow: .workflow_name, duration: .duration, cost: .estimated_cost}'
# Total cost grouped by workflow over the past 30 days
gh aw logs --start-date -30d --json | \
jq '[.runs[]] | group_by(.workflow_name) |
map({workflow: .[0].workflow_name, runs: length, total_cost: (map(.estimated_cost) | add // 0)})'
```
Each run under `.runs[]` includes `duration`, `token_usage`, `estimated_cost`, `workflow_name`, and `agent`. For orchestrated workflows, the same JSON includes deterministic lineage under `.episodes[]` and `.edges[]` — see the next section.
### Interpret Episode-Level Cost
[Section titled “Interpret Episode-Level Cost”](#interpret-episode-level-cost)
`gh aw logs --json` emits three views of the same data: `.runs[]` (individual workflow runs), `.episodes[]` (related runs grouped into one logical execution — orchestrator, workers, `workflow_call` follow-ups, and reporting passes), and `.edges[]` (the inferred parent-child lineage). Use `.runs[]` to find which specific run was expensive; use `.episodes[]` to answer “what did this job cost end-to-end?”. For non-orchestrated workflows, an episode collapses to a single run and the two views are equivalent.
Useful episode fields for cost analysis:
| Field | Meaning |
| ----------------------------------------- | ------------------------------------------------------------------------------- |
| `total_runs` | Workflow runs in the logical execution |
| `total_tokens` / `total_effective_tokens` | Raw and effective token aggregates; prefer `total_effective_tokens` for Copilot |
| `total_duration` | Wall-clock duration across grouped runs |
| `primary_workflow` | Main workflow label |
| `resource_heavy_node_count` | Runs flagged as resource-heavy |
| `blocked_request_count` | Aggregate blocked-network pressure |
For Copilot runs, treat `total_estimated_cost` as a heuristic — Copilot does not expose billing-grade cost data, so `total_effective_tokens` is the more reliable proxy.
Safe-output actuation also appears in both `gh aw logs --json` (run- and repo-level) and `gh aw audit ` (under `safe_output_summary`). The relevant fields — `temporary_id_map_status`, `temporary_id_mappings`, `chained_target_count`, `chained_followup_action_count`, `delegated_temp_target_count`, `closed_temp_target_count`, and their repo-level aggregates — show how often a workflow follows up on its own outputs. When `temporary_id_map_status` is `missing` or `invalid`, chain counts fall back to `0` rather than guessing from incomplete data.
```bash
# Top 10 heaviest logical executions over the past 30 days by effective tokens
gh aw logs --start-date -30d --json | \
jq '[.episodes[] | {episode: .episode_id, workflow: .primary_workflow, runs: .total_runs, effective_tokens: (.total_effective_tokens // 0)}]
| sort_by(.effective_tokens) | reverse | .[:10]'
```
## Trigger Frequency and Cost Risk
[Section titled “Trigger Frequency and Cost Risk”](#trigger-frequency-and-cost-risk)
The primary cost lever for most workflows is how often they run. Some events are inherently high-frequency:
| Trigger type | Risk | Notes |
| ---------------------------------------------- | --------------- | ------------------------------------------------------- |
| `push` | High | Every commit to any matching branch fires the workflow |
| `pull_request` | Medium–High | Fires on open, sync, re-open, label, and other subtypes |
| `issues` | Medium–High | Fires on open, close, label, edit, and other subtypes |
| `check_run`, `check_suite` | High | Can fire many times per push in busy repositories |
| `issue_comment`, `pull_request_review_comment` | Medium | Scales with comment activity |
| `schedule` | Low–Predictable | Fires at a fixed cadence; easy to budget |
| `workflow_dispatch` | Low | Human-initiated; naturally rate-limited |
Danger
Attaching an agentic workflow to `push`, `check_run`, or `check_suite` in an active repository can generate hundreds of runs per day. Start with `schedule` or `workflow_dispatch` while evaluating cost, then move to event-based triggers with safeguards in place.
## Reducing Cost
[Section titled “Reducing Cost”](#reducing-cost)
### Use Deterministic Checks to Skip the Agent
[Section titled “Use Deterministic Checks to Skip the Agent”](#use-deterministic-checks-to-skip-the-agent)
The most effective cost reduction is skipping the agent job entirely when it is not needed. The `skip-if-match` and `skip-if-no-match` conditions run during the low-cost pre-activation job and cancel the workflow before the agent starts:
```aw
on:
issues:
types: [opened]
skip-if-match: 'label:duplicate OR label:wont-fix'
```
```aw
on:
issues:
types: [labeled]
skip-if-no-match: 'label:needs-triage'
```
Use these to filter out noise before incurring inference costs. See [Triggers](/gh-aw/reference/triggers/) for the full syntax.
### Choose a Cheaper Model
[Section titled “Choose a Cheaper Model”](#choose-a-cheaper-model)
The `engine.model` field selects the AI model. Smaller or faster models cost significantly less per token while still handling many routine tasks:
```aw
engine:
id: copilot
model: gpt-4.1-mini
```
```aw
engine:
id: claude
model: claude-haiku-4-5
```
Reserve frontier models (GPT-5, Claude Sonnet, etc.) for complex tasks. Use lighter models for triage, labeling, summarization, and other structured outputs.
### Limit Context Size
[Section titled “Limit Context Size”](#limit-context-size)
Inference cost scales with prompt size. Write focused prompts, avoid whole-file reads when only a few lines matter, cap result counts in tool calls, and use `imports` to compose a smaller subset of prompt sections at runtime.
### Rate Limiting and Concurrency
[Section titled “Rate Limiting and Concurrency”](#rate-limiting-and-concurrency)
Use `user-rate-limit` to cap how many times a user can trigger the workflow in a given window, and rely on concurrency controls to serialize runs rather than letting them pile up:
```aw
user-rate-limit:
max-runs-per-window: 3
window: 60 # 3 runs per hour per user
```
See [Rate Limiting Controls](/gh-aw/reference/rate-limiting-controls/) and [Concurrency](/gh-aw/reference/concurrency/) for details.
### Use Schedules for Predictable Budgets
[Section titled “Use Schedules for Predictable Budgets”](#use-schedules-for-predictable-budgets)
Scheduled workflows fire at a fixed cadence, making cost easy to estimate and cap:
```aw
schedule: daily on weekdays
```
One scheduled run per weekday = five agent invocations per week. See [Schedule Syntax](/gh-aw/reference/schedule-syntax/) for the full fuzzy schedule syntax.
## Agentic Cost Optimization
[Section titled “Agentic Cost Optimization”](#agentic-cost-optimization)
The `agentic-workflows` MCP tool exposes the same operations as the CLI (`logs`, `audit`, `status`) to any workflow agent, so a scheduled meta-agent can inspect and optimize other agentic workflows automatically — fetching aggregate cost data, deep-diving into individual runs, and proposing frontmatter changes (cheaper model, tighter `skip-if-match`, lower `user-rate-limit`) via a pull request.
```aw
description: Weekly Actions minutes cost report
on: weekly
permissions:
actions: read
engine: copilot
tools:
agentic-workflows:
```
### What to Optimize Automatically
[Section titled “What to Optimize Automatically”](#what-to-optimize-automatically)
| Signal | Automatic action |
| ------------------------------------------ | ------------------------------------------------------------------------ |
| High token count per run | Switch to a smaller model (`gpt-4.1-mini`, `claude-haiku-4-5`) |
| Frequent runs with no safe-output produced | Add or tighten `skip-if-match` |
| Long queue times due to concurrency | Lower `user-rate-limit.max-runs-per-window` or add a `concurrency` group |
| Workflow running too often | Change trigger to `schedule` or add `workflow_dispatch` |
Note
The `agentic-workflows` tool requires `actions: read` permission and is configured under the `tools:` frontmatter key. See [GH-AW as an MCP Server](/gh-aw/reference/gh-aw-as-mcp-server/) for available operations.
## Common Scenario Estimates
[Section titled “Common Scenario Estimates”](#common-scenario-estimates)
These are rough estimates to help with budgeting. Actual costs vary by prompt size, tool usage, model, and provider pricing.
| Scenario | Frequency | Actions minutes/month | Inference/month |
| ----------------------------------------------------- | --------------- | --------------------- | -------------------------------- |
| Weekly digest (schedule, 1 repo) | 4×/month | \~1 min | \~4–8 premium requests (Copilot) |
| Issue triage (issues opened, 20/month) | 20×/month | \~10 min | \~20–40 premium requests |
| PR review on every push (busy repo, 100 pushes/month) | 100×/month | \~100 min | \~100–200 premium requests |
| On-demand via slash command | User-controlled | Varies | Varies |
Tip
Create separate `COPILOT_GITHUB_TOKEN` service accounts per repository or team to attribute spend by workflow.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Audit Commands](/gh-aw/reference/audit/) - Single-run analysis, diff, and cross-run reporting
* [Artifacts](/gh-aw/reference/artifacts/) - Artifact names, directory structures, and token usage file locations
* [Effective Tokens Specification](/gh-aw/reference/effective-tokens-specification/) - How effective token counts are computed
* [Triggers](/gh-aw/reference/triggers/) - Configuring workflow triggers and skip conditions
* [Rate Limiting Controls](/gh-aw/reference/rate-limiting-controls/) - Preventing runaway workflows
* [Concurrency](/gh-aw/reference/concurrency/) - Serializing workflow execution
* [AI Engines](/gh-aw/reference/engines/) - Engine and model configuration
* [Schedule Syntax](/gh-aw/reference/schedule-syntax/) - Cron schedule format
* [GH-AW as an MCP Server](/gh-aw/reference/gh-aw-as-mcp-server/) - `agentic-workflows` tool for self-inspection
* [FAQ](/gh-aw/reference/faq/) - Common questions including cost and billing
# Cross-Repository Operations
> Configure workflows to access, modify, and operate across multiple GitHub repositories using checkout, target-repo, and allowed-repos settings
Cross-repository operations enable workflows to access code from multiple repositories and create resources (issues, PRs, comments) in external repositories. This page documents all declarative frontmatter features for cross-repository workflows.
Cross-repository features fall into three categories:
1. **Cross-Repository Checkout** - Check out code from other repositories
2. **Cross-Repository Reading** - Read issues, pull requests and other information from other repositories
3. **Cross-Repository Safe Outputs** - Create issues, PRs, comments, and other resources in external repositories using `target-repo` and `allowed-repos` in safe outputs
All require additional authentication.
## Cross-Repository Checkout (`checkout:`)
[Section titled “Cross-Repository Checkout (checkout:)”](#cross-repository-checkout-checkout)
The `checkout:` frontmatter field controls how `actions/checkout` is invoked in the agent job. Use it to check out one or more repositories, override fetch depth or sparse-checkout settings, fetch additional refs (e.g., all open PR branches), or disable checkout entirely with `checkout: false`.
For multi-repository workflows, list multiple entries to clone several repos into the workspace. Mark the agent’s primary target with `current: true` when working from a central repository that targets a different repo.
```yaml
checkout:
- fetch-depth: 0 # checkout this repository with full history
fetch: ["refs/pulls/open/*"] # fetch all open PR branches after checkout
- repository: owner/other-repo # another repository to check out
path: ./libs/other # path within workspace to check out to
github-token: ${{ secrets.CROSS_REPO_PAT }} # additional auth for cross-repo access
```
See [GitHub Repository Checkout](/gh-aw/reference/checkout/) for the full configuration reference, including fetch options, sparse checkout, merging rules, and examples.
## Cross-Repository Reading
[Section titled “Cross-Repository Reading”](#cross-repository-reading)
The [GitHub Tools](/gh-aw/reference/github-tools/) are used to read information such as issues and pull requests from repositories. By default, these tools can access the current repository and all public repositories (if permitted by the network firewall).
### Authorizing Additional Cross-Repository Reading
[Section titled “Authorizing Additional Cross-Repository Reading”](#authorizing-additional-cross-repository-reading)
To read from other private repositories, you must configure additional authorization. Configure a PAT or GitHub App in your GitHub Tools configuration:
```yaml
tools:
github:
toolsets: [repos, issues, pull_requests]
github-token: ${{ secrets.CROSS_REPO_PAT }}
```
This enables operations like:
* Reading files and searching code in external repositories dynamically, even if the repository is not checked out
* Querying issues and pull requests from other repos
* Accessing commits, releases, and workflow runs across repositories
* Reading organization-level information
See [Additional Authentication for GitHub Tools](/gh-aw/reference/github-tools/#additional-authentication-for-github-tools) for full details on creating a PAT, using a GitHub App, or using the magic secret `GH_AW_GITHUB_MCP_SERVER_TOKEN`.
### Restricting Cross-Repository Reading (`tools.github.allowed-repos`)
[Section titled “Restricting Cross-Repository Reading (tools.github.allowed-repos)”](#restricting-cross-repository-reading-toolsgithuballowed-repos)
You can also configure the GitHub Tools to be restricted in which repositories can be accessed via the GitHub tools during AI engine execution by using the `tools.github.allowed-repos` setting. This is a guardrail to prevent unintended access to repositories.
The setting `tools.github.allowed-repos` specifies which repositories the agent can access through GitHub tools:
* `"all"` — All repositories accessible by the configured token
* `"public"` — Public repositories only
* `"current"` — The repository where the workflow is running (normalized to `${{ github.repository }}` in the emitted guard policy)
* `"${{ github.repository }}"` — Equivalent to `"current"`, kept for backward compatibility
* Array of patterns — Specific repositories and wildcards:
* `"owner/repo"` — Exact repository match
* `"owner/*"` — All repositories under an owner
* `"owner/prefix*"` — Repositories with a name prefix under an owner
This defaults to `"all"` when omitted. Patterns must be lowercase. Wildcards are only permitted at the end of the repository name component.
Use `current` in reusable or generated workflows that need to express “this repository only” without hard-coding `owner/repo`:
```yaml
tools:
github:
toolsets: [issues, pull_requests]
allowed-repos: current
min-integrity: approved
```
For example:
```yaml
tools:
github:
mode: remote
toolsets: [default]
allowed-repos:
- "myorg/*"
- "partner/shared-repo"
- "myorg/api-*"
min-integrity: approved
```
## Cross-Repository Safe Outputs
[Section titled “Cross-Repository Safe Outputs”](#cross-repository-safe-outputs)
Most safe output types support creating resources in external repositories using `target-repo` and `allowed-repos` parameters.
### Target Repository (`safe-outputs.*.target-repo`)
[Section titled “Target Repository (safe-outputs.\*.target-repo)”](#target-repository-safe-outputstarget-repo)
Specify a single target repository for resource creation:
```yaml
safe-outputs:
github-token: ${{ secrets.CROSS_REPO_PAT }}
create-issue:
target-repo: "org/tracking-repo"
title-prefix: "[component] "
```
Without `target-repo`, safe outputs operate on the repository where the workflow is running.
### Wildcard Target Repository (`target-repo: "*"`)
[Section titled “Wildcard Target Repository (target-repo: "\*")”](#wildcard-target-repository-target-repo-)
Set `target-repo: "*"` to allow the agent to dynamically target any repository at runtime. When configured, the agent receives a `repo` parameter in its tool call where it supplies the target repository in `owner/repo` format:
```yaml
safe-outputs:
github-token: ${{ secrets.CROSS_REPO_PAT }}
create-issue:
target-repo: "*"
title-prefix: "[component] "
```
Use this when the target repository is not known at workflow authoring time — for example, when building a workflow that routes issues to different repositories based on labels or content.
Note
The following safe-output types do **not** support `target-repo: "*"`: `create-pull-request-review-comment`, `reply-to-pull-request-review-comment`, `submit-pull-request-review`, `create-agent-session`, and `manage-project-items`. Use an explicit `owner/repo` value or `allowed-repos` for these types.
### Allowed Repositories (`safe-outputs.*.allowed-repos`)
[Section titled “Allowed Repositories (safe-outputs.\*.allowed-repos)”](#allowed-repositories-safe-outputsallowed-repos)
Allow your agentic workflow to dynamically select from multiple repositories:
```yaml
safe-outputs:
github-token: ${{ secrets.CROSS_REPO_PAT }}
create-issue:
target-repo: "org/default-repo"
allowed-repos: ["org/repo-a", "org/repo-b", "org/repo-c"]
title-prefix: "[cross-repo] "
```
When `allowed-repos` is specified:
* The agentic step can include a `repo` field to select which repository
* Target repository (from `target-repo` or current repo) is always implicitly allowed
* Creates a union of allowed destinations
### Checkout Requirement for `push-to-pull-request-branch`
[Section titled “Checkout Requirement for push-to-pull-request-branch”](#checkout-requirement-for-push-to-pull-request-branch)
Unlike other safe output types, `push-to-pull-request-branch` with `target-repo` requires the target repository to be **checked out into the workflow workspace** using the `checkout:` frontmatter field with a `path:` specified. Without a checkout, the agent has no local git history to create and push a patch from.
See the [Scheduled Push to Pull-Request Branch](#example-scheduled-push-to-pull-request-branch) example and the [Push to PR Branch cross-repo usage](/gh-aw/reference/safe-outputs-pull-requests/#cross-repo-usage) documentation for a complete setup.
## Examples
[Section titled “Examples”](#examples)
### Example: Monorepo Development
[Section titled “Example: Monorepo Development”](#example-monorepo-development)
This uses multiple `checkout:` entries to check out different parts of the same repository with different settings:
```aw
---
on:
pull_request:
types: [opened, synchronize]
checkout:
- fetch-depth: 0
- repository: org/shared-libs
path: ./libs/shared
ref: main
github-token: ${{ secrets.LIBS_PAT }}
- repository: org/config-repo
path: ./config
sparse-checkout: |
defaults/
overrides/
permissions:
contents: read
pull-requests: read
---
# Cross-Repo PR Analysis
Analyze this PR considering shared library compatibility and configuration standards.
Check compatibility with shared libraries in `./libs/shared` and verify configuration against standards in `./config`.
```
### Example: Hub-and-Spoke Tracking
[Section titled “Example: Hub-and-Spoke Tracking”](#example-hub-and-spoke-tracking)
Create issues in a central tracking repo when issues open in component repos using `target-repo` on `create-issue`. See the [MultiRepoOps pattern](/gh-aw/patterns/multi-repo-ops/) for a complete walkthrough including hub-and-spoke, upstream-to-downstream, and org-wide broadcast topologies.
### Example: Cross-Repository Analysis
[Section titled “Example: Cross-Repository Analysis”](#example-cross-repository-analysis)
Use `tools.github` with `github-token` to read from multiple repositories, then write results back with `add-comment` and `target-repo`. See [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) for examples.
### Example: Deterministic Multi-Repo Workflows
[Section titled “Example: Deterministic Multi-Repo Workflows”](#example-deterministic-multi-repo-workflows)
For direct repository access without agent involvement, use custom steps with `actions/checkout`:
```aw
---
engine:
id: claude
steps:
- name: Checkout main repo
uses: actions/checkout@v6
with:
path: main-repo
- name: Checkout secondary repo
uses: actions/checkout@v6
with:
repository: org/secondary-repo
token: ${{ secrets.CROSS_REPO_PAT }}
path: secondary-repo
permissions:
contents: read
---
# Compare Repositories
Compare code structure between main-repo and secondary-repo.
```
This approach provides full control over checkout timing and configuration.
### Example: Scheduled Push to Pull-Request Branch
[Section titled “Example: Scheduled Push to Pull-Request Branch”](#example-scheduled-push-to-pull-request-branch)
A scheduled workflow that automatically pushes changes to open pull-request branches in another repository needs to fetch those branches after checkout. Without `fetch:`, only the default branch (usually `main`) is available.
```aw
---
on:
schedule: hourly
checkout:
- repository: org/target-repo
github-token: ${{ secrets.GH_AW_SIDE_REPO_PAT }}
fetch: ["refs/pulls/open/*"] # fetch all open PR branches after checkout
current: true
permissions:
contents: read
safe-outputs:
github-token: ${{ secrets.GH_AW_SIDE_REPO_PAT }}
push-to-pull-request-branch:
target-repo: "org/target-repo"
---
# Auto-Update PR Branches
Check open pull requests in org/target-repo and apply any pending automated
updates to each PR branch.
```
`fetch: ["refs/pulls/open/*"]` causes a `git fetch` step to run after `actions/checkout`, downloading all open PR head refs into the workspace. The agent can then inspect and modify those branches directly.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [GitHub Repository Checkout](/gh-aw/reference/checkout/) - Full checkout configuration reference
* [MultiRepoOps Pattern](/gh-aw/patterns/multi-repo-ops/) - Cross-repository workflow pattern
* [MultiRepoOps — Central Control Plane](/gh-aw/patterns/multi-repo-ops/#the-central-control-plane-pattern-org-wide-rollouts) — Central control plane pattern
* [GitHub Tools Reference](/gh-aw/reference/github-tools/) - Complete GitHub Tools configuration
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - Complete safe output configuration
* [Authentication Reference](/gh-aw/reference/auth/) - PAT and GitHub App setup
* [Multi-Repository Examples](/gh-aw/examples/multi-repo/) - Complete working examples
# Copilot Agent Files support for Agentic Workflows
> How to create, update, import, and debug agentic workflows using our AI agent.
“Custom Agents” are added prompts that can be used with Copilot, Copilot CLI and VSCode Agent Mode to provide specialized behavior for specific tasks.
In this guide, we show you how to install and use the custom agent `agentic-workflows` to create, update, import, and debug agentic workflows in your repository.
## Installing the Copilot Agent Files for Agentic Workflows
[Section titled “Installing the Copilot Agent Files for Agentic Workflows”](#installing-the-copilot-agent-files-for-agentic-workflows)
Follow these steps to set up your repository for agentic workflows using the custom `agentic-workflows` agent.
1. **Start your coding agent**.
* Navigate to your repository on and click the “Agents” tab, or
* Start [VSCode Agent Mode](https://code.visualstudio.com/docs/copilot/agents/overview), or
* Start your coding agent in your repository
2. **Install the Copilot Agent Files for Agentic Workflows into your repository**.
```text
Initialize this repository for GitHub Agentic Workflows using https://github.com/github/gh-aw/blob/main/install.md
```
Alternatively just run
```bash
gh aw init
```
After initialization, you’ll have `.github/agents/agentic-workflows.agent.md`, a [Copilot agent file](/gh-aw/reference/glossary/#agent-files) that registers the `/agent agentic-workflows` command in Copilot Chat.
## Using the Copilot Agent Files for Agentic Workflows
[Section titled “Using the Copilot Agent Files for Agentic Workflows”](#using-the-copilot-agent-files-for-agentic-workflows)
Once your repository is set up for agentic workflows, you can use the `agentic-workflows` agent from VSCode or GitHub.com to perform a variety of tasks:
### Creating New Agentic Workflows
[Section titled “Creating New Agentic Workflows”](#creating-new-agentic-workflows)
Navigate to your repository on and click the “Agents” tab, then use this prompt:
```text
# Create a new workflow
/agent agentic-workflows create a workflow that triages issues
```
The agent will generate a workflow file in `.github/workflows/`, write the frontmatter and prompt, configure tools and permissions, and compile to `.lock.yml`.
### Updating Existing Workflows
[Section titled “Updating Existing Workflows”](#updating-existing-workflows)
Modify or improve existing workflows using natural language prompts:
```text
/agent agentic-workflows update the issue-triage workflow to add web-fetch tool and
improve the prompt for better accuracy
```
### Upgrading Agentic Workflows
[Section titled “Upgrading Agentic Workflows”](#upgrading-agentic-workflows)
Keep workflows up-to-date with the latest `gh-aw` versions and features:
```text
/agent agentic-workflows upgrade all workflows to latest version
```
### Importing Workflows
[Section titled “Importing Workflows”](#importing-workflows)
Import workflows from any accessible GitHub repository:
```text
/agent agentic-workflows import workflow from https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md
```
When importing, you can specify customizations such as engine or tools:
```text
/agent agentic-workflows import issue-triage from githubnext/agentics and use claude engine
```
### Debugging Agentic Workflows
[Section titled “Debugging Agentic Workflows”](#debugging-agentic-workflows)
When workflows fail or behave unexpectedly, use the agentic-workflows agent to investigate and diagnose issues:
```text
/agent agentic-workflows debug why is my issue-triage workflow failing?
```
For the fastest diagnosis, pass the full run URL from the GitHub Actions page:
```text
/agent agentic-workflows debug https://github.com/OWNER/REPO/actions/runs/RUN_ID
```
The agent audits logs, identifies the root cause, and suggests targeted fixes. It handles permission errors, missing tools, network access issues, and safe-output problems — just describe the issue in natural language.
### Self-Contained Debugging (Without Copilot)
[Section titled “Self-Contained Debugging (Without Copilot)”](#self-contained-debugging-without-copilot)
If your repository is not yet set up with the `agentic-workflows` agent, or if you prefer to use a different AI assistant, use the standalone debugging prompt by sharing its URL:
```text
Debug this workflow run using https://raw.githubusercontent.com/github/gh-aw/main/debug.md
The failed workflow run is at https://github.com/OWNER/REPO/actions/runs/RUN_ID
```
Copy debug instructions
The `debug.md` file is a self-contained prompt that works with any coding agent or AI assistant. It guides the agent to install `gh aw`, analyze the run logs, identify the root cause, and open a pull request with the fix.
## Creating Agentic Workflows with an AI Chatbot
[Section titled “Creating Agentic Workflows with an AI Chatbot”](#creating-agentic-workflows-with-an-ai-chatbot)
If you prefer to use an AI chatbot to author agentic workflows, use the [agentic-chat instructions](https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/agentic-chat.md) with any conversational AI application. Copy agentic-chat instructions
Copy the instructions into your AI chat interface, describe your workflow goal, and the assistant will generate a structured task description you can use in your workflow. It focuses on clear, actionable specifications rather than implementation details.
## Dictating Agentic Workflows
[Section titled “Dictating Agentic Workflows”](#dictating-agentic-workflows)
When creating agentic workflows using speech-to-text (dictation), you may encounter terminology mismatches and formatting issues common to voice recognition systems. To help correct these issues, use the [dictation instructions prompt](https://raw.githubusercontent.com/github/gh-aw/main/DICTATION.md) or Copy dictation instructions .
This prompt corrects terminology (e.g., “ghaw” → “gh-aw”), removes filler words, and transforms dictated sentences into clear, imperative task descriptions. Load it into your AI assistant before or after dictating to improve accuracy.
# Custom Safe Outputs
> How to create custom safe outputs for third-party integrations using custom jobs and MCP servers.
Custom safe outputs extend built-in GitHub operations to integrate with third-party services — Slack, Discord, Notion, Jira, databases, or any external API requiring authentication. Use them for any write operation that built-in safe outputs don’t cover.
## Quick Start
[Section titled “Quick Start”](#quick-start)
Here’s a minimal custom safe output that sends a Slack message:
.github/workflows/shared/slack-notify.md
```yaml
---
safe-outputs:
jobs:
slack-notify:
description: "Send a message to Slack"
runs-on: ubuntu-latest
output: "Message sent to Slack!"
inputs:
message:
description: "The message to send"
required: true
type: string
steps:
- name: Send Slack message
env:
SLACK_WEBHOOK: "${{ secrets.SLACK_WEBHOOK }}"
run: |
if [ -f "$GH_AW_AGENT_OUTPUT" ]; then
MESSAGE=$(cat "$GH_AW_AGENT_OUTPUT" | jq -r '.items[] | select(.type == "slack_notify") | .message')
# Use jq to safely escape JSON content
PAYLOAD=$(jq -n --arg text "$MESSAGE" '{text: $text}')
curl -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "$PAYLOAD"
else
echo "No agent output found"
exit 1
fi
---
```
Use it in a workflow:
.github/workflows/issue-notifier.md
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
imports:
- shared/slack-notify.md
---
# Issue Notifier
A new issue was opened: "${{ steps.sanitized.outputs.text }}"
Summarize the issue and use the slack-notify tool to send a notification.
```
The agent can now call `slack-notify` with a message, and the custom job executes with access to the `SLACK_WEBHOOK` secret.
## Architecture
[Section titled “Architecture”](#architecture)
Custom safe outputs separate read and write operations: agents use read-only Model Context Protocol (MCP) servers with `allowed:` tool lists, while custom jobs handle write operations with secret access after agent completion.
```text
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Agent (AI) │────│ MCP Server │────│ External API │
│ │ │ (read-only) │ │ (GET requests) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
│ calls safe-job tool
▼
┌─────────────────┐ ┌─────────────────┐
│ Custom Job │────│ External API │
│ (with secrets) │ │ (POST/PUT) │
└─────────────────┘ └─────────────────┘
```
## Creating a Custom Safe Output
[Section titled “Creating a Custom Safe Output”](#creating-a-custom-safe-output)
### Step 1: Define the Shared Configuration
[Section titled “Step 1: Define the Shared Configuration”](#step-1-define-the-shared-configuration)
In a shared file, define the read-only MCP server and the custom job together:
```yaml
---
mcp-servers:
notion:
container: "mcp/notion"
env:
NOTION_TOKEN: "${{ secrets.NOTION_TOKEN }}"
allowed:
- "search_pages"
- "get_page"
- "get_database"
- "query_database"
safe-outputs:
jobs:
notion-add-comment:
description: "Add a comment to a Notion page"
runs-on: ubuntu-latest
output: "Comment added to Notion successfully!"
permissions:
contents: read
inputs:
page_id:
description: "The Notion page ID to add a comment to"
required: true
type: string
comment:
description: "The comment text to add"
required: true
type: string
steps:
- name: Add comment to Notion page
uses: actions/github-script@v8
env:
NOTION_TOKEN: "${{ secrets.NOTION_TOKEN }}"
with:
script: |
const fs = require('fs');
const notionToken = process.env.NOTION_TOKEN;
const outputFile = process.env.GH_AW_AGENT_OUTPUT;
if (!notionToken) {
core.setFailed('NOTION_TOKEN secret is not configured');
return;
}
if (!outputFile) {
core.info('No GH_AW_AGENT_OUTPUT environment variable found');
return;
}
// Read and parse agent output
const fileContent = fs.readFileSync(outputFile, 'utf8');
const agentOutput = JSON.parse(fileContent);
// Filter for notion-add-comment items (job name with dashes → underscores)
const items = agentOutput.items.filter(item => item.type === 'notion_add_comment');
for (const item of items) {
const pageId = item.page_id;
const comment = item.comment;
core.info(`Adding comment to Notion page: ${pageId}`);
try {
const response = await fetch('https://api.notion.com/v1/comments', {
method: 'POST',
headers: {
'Authorization': `Bearer ${notionToken}`,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json'
},
body: JSON.stringify({
parent: { page_id: pageId },
rich_text: [{
type: 'text',
text: { content: comment }
}]
})
});
if (!response.ok) {
const errorData = await response.text();
core.setFailed(`Notion API error (${response.status}): ${errorData}`);
return;
}
const data = await response.json();
core.info('Comment added successfully');
core.info(`Comment ID: ${data.id}`);
} catch (error) {
core.setFailed(`Failed to add comment: ${error.message}`);
return;
}
}
---
```
Use `container:` for Docker servers or `command:`/`args:` for npx. List only read-only tools in `allowed`. All jobs require `description` and `inputs`. Use `output` for success messages and `actions/github-script@v8` for API calls with `core.setFailed()` error handling.
### Step 2: Use in Workflow
[Section titled “Step 2: Use in Workflow”](#step-2-use-in-workflow)
Import the configuration:
```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
actions: read
imports:
- shared/mcp/notion.md
---
# Issue Summary to Notion
Analyze the issue: "${{ steps.sanitized.outputs.text }}"
Search for the GitHub Issues page in Notion using the read-only Notion tools, then add a summary comment using the notion-add-comment safe-job.
```
The agent uses read-only tools to query, then calls the safe-job which executes with write permissions after completion.
## Safe Job Reference
[Section titled “Safe Job Reference”](#safe-job-reference)
### Job Properties
[Section titled “Job Properties”](#job-properties)
| Property | Type | Required | Description |
| ----------------- | --------------- | -------- | -------------------------------------------------------------------------------------- |
| `description` | string | Yes | Tool description shown to the agent |
| `runs-on` | string | Yes | GitHub Actions runner (e.g., `ubuntu-latest`) |
| `inputs` | object | Yes | Tool parameters (see [Input Types](#input-types)) |
| `steps` | array | Yes | GitHub Actions steps to execute |
| `output` | string | No | Success message returned to the agent |
| `needs` | string or array | No | Jobs that must complete before this job runs (see [Job Ordering](#job-ordering-needs)) |
| `permissions` | object | No | GitHub token permissions for the job |
| `env` | object | No | Environment variables for all steps |
| `if` | string | No | Conditional execution expression |
| `timeout-minutes` | number | No | Maximum job duration (GitHub Actions default: 360) |
### Job Ordering (`needs:`)
[Section titled “Job Ordering (needs:)”](#job-ordering-needs)
Use `needs:` to sequence a custom safe-output job relative to other jobs in the compiled workflow. Unlike manually patching `needs:` in the lock file (which gets overwritten on every recompile), `needs:` declared in the frontmatter persists across recompiles.
```yaml
safe-outputs:
create-issue: {}
jobs:
post-process:
needs: safe_outputs # runs after the consolidated safe-outputs job
steps:
- run: echo "post-processing"
```
The compiler validates each `needs:` entry at compile time and fails with a clear error if the target does not exist. Target names with dashes are automatically normalized to underscores (e.g., `safe-outputs` → `safe_outputs`).
Valid `needs:` targets for custom safe-jobs:
| Target | Available when |
| --------------- | ------------------------------------------------------------------------ |
| `agent` | Always |
| `safe_outputs` | At least one builtin handler, script, action, or user step is configured |
| `detection` | Threat detection is enabled |
| `upload_assets` | `upload-asset` is configured |
| `unlock` | `lock-for-agent` is enabled |
| `` | That job exists in `safe-outputs.jobs` |
Self-dependencies and cycles between custom jobs are also caught at compile time.
### Input Types
[Section titled “Input Types”](#input-types)
All jobs must define `inputs`:
| Type | Description |
| --------- | ---------------------------------------------- |
| `string` | Text input |
| `boolean` | True/false (as strings: `"true"` or `"false"`) |
| `choice` | Selection from predefined options |
```yaml
inputs:
message:
description: "Message content"
required: true
type: string
notify:
description: "Send notification"
required: false
type: boolean
default: "true"
environment:
description: "Target environment"
required: true
type: choice
options: ["staging", "production"]
```
### Environment Variables
[Section titled “Environment Variables”](#environment-variables)
Custom safe-output jobs have access to these environment variables:
| Variable | Description |
| --------------------------- | ---------------------------------------------------- |
| `GH_AW_AGENT_OUTPUT` | Path to JSON file containing the agent’s output data |
| `GH_AW_SAFE_OUTPUTS_STAGED` | Set to `"true"` when running in staged/preview mode |
### Accessing Agent Output
[Section titled “Accessing Agent Output”](#accessing-agent-output)
Custom safe-output jobs receive the agent’s data through the `GH_AW_AGENT_OUTPUT` environment variable, which contains a path to a JSON file. This file has the structure:
```json
{
"items": [
{
"type": "job_name_with_underscores",
"field1": "value1",
"field2": "value2"
}
]
}
```
The `type` field matches your job name with dashes converted to underscores (e.g., job `webhook-notify` → type `webhook_notify`).
#### Example
[Section titled “Example”](#example)
```yaml
steps:
- name: Process output
run: |
if [ -f "$GH_AW_AGENT_OUTPUT" ]; then
MESSAGE=$(cat "$GH_AW_AGENT_OUTPUT" | jq -r '.items[] | select(.type == "my_job") | .message')
echo "Message: $MESSAGE"
else
echo "No agent output found"
exit 1
fi
```
The `inputs:` schema serves as both the MCP tool definition visible to the agent and validation for the output fields written to `GH_AW_AGENT_OUTPUT`.
## Inline Script Handlers (`safe-outputs.scripts`)
[Section titled “Inline Script Handlers (safe-outputs.scripts)”](#inline-script-handlers-safe-outputsscripts)
Use `safe-outputs.scripts` to define lightweight inline JavaScript handlers that execute inside the consolidated safe-outputs job handler loop. Unlike `jobs` (which create a separate GitHub Actions job for each tool call), scripts run in-process alongside the built-in safe-output handlers — there is no extra job allocation or startup overhead.
**When to use scripts vs jobs:**
| | Scripts | Jobs |
| --------- | -------------------------------------------------------------- | ------------------------------------ |
| Execution | In-process, in the consolidated safe-outputs job | Separate GitHub Actions job |
| Startup | Fast (no job scheduling) | Slower (new job per call) |
| Secrets | Not directly available — use for lightweight logic | Full access to repository secrets |
| Use case | Lightweight processing, logging, notifications without secrets | External API calls requiring secrets |
### Defining a Script
[Section titled “Defining a Script”](#defining-a-script)
Under `safe-outputs.scripts`, define each handler with a `description`, `inputs`, and `script` body:
.github/workflows/my-workflow\.md
```yaml
---
safe-outputs:
scripts:
post-slack-message:
description: Post a message to a Slack channel
inputs:
channel:
description: Slack channel name
required: true
type: string
message:
description: Message text
required: true
type: string
script: |
const targetChannel = item.channel || "#general";
const text = item.message || "(no message)";
core.info(`Posting to ${targetChannel}: ${text}`);
return { success: true, channel: targetChannel };
---
```
The agent calls `post_slack_message` (dashes normalized to underscores) and the script runs synchronously in the handler loop.
### Script Body Context
[Section titled “Script Body Context”](#script-body-context)
Write only the handler body — the compiler wraps it automatically. Inside the body you have access to:
| Variable | Description |
| ---------------------- | ----------------------------------------------------------------------------- |
| `item` | Runtime message object with field values matching your `inputs` schema |
| `core` | `@actions/core` for logging (`core.info()`, `core.warning()`, `core.error()`) |
| `resolvedTemporaryIds` | Map of temporary object IDs resolved at runtime |
Each input declared in `inputs` is also destructured into a local variable. For example, an `inputs.channel` entry is available as `item.channel`.
```javascript
// Example: access inputs via item
const channel = item.channel;
const message = item.message;
core.info(`Sending to ${channel}: ${message}`);
return { sent: true };
```
Note
Script names with dashes are normalized to underscores when registered as MCP tools (e.g., `post-slack-message` becomes `post_slack_message`). The normalized name is what the agent uses to call the tool.
### Script Reference
[Section titled “Script Reference”](#script-reference)
| Property | Type | Required | Description |
| ------------- | ------ | -------- | -------------------------------------------- |
| `description` | string | Yes | Tool description shown to the agent |
| `inputs` | object | Yes | Tool parameters (same schema as custom jobs) |
| `script` | string | Yes | JavaScript handler body |
Scripts support the same `inputs` types as custom jobs: `string`, `boolean`, and `number`.
## GitHub Action Wrappers (`safe-outputs.actions`)
[Section titled “GitHub Action Wrappers (safe-outputs.actions)”](#github-action-wrappers-safe-outputsactions)
Use `safe-outputs.actions` to mount any public GitHub Action as a once-callable MCP tool. At compile time, `gh aw compile` fetches the action’s `action.yml` to resolve its inputs and pins the action reference to a specific SHA. The agent can call the tool once per workflow run; the action executes inside the consolidated safe-outputs job.
**When to use actions vs scripts vs jobs:**
| | Actions | Scripts | Jobs |
| --------- | ----------------------------------------------- | ------------------------------------------------ | --------------------------------- |
| Execution | In the consolidated safe-outputs job, as a step | In-process, in the consolidated safe-outputs job | Separate GitHub Actions job |
| Reuse | Any public GitHub Action | Custom inline JavaScript | Custom inline YAML job |
| Secrets | Full access via `env:` | Not directly available | Full access to repository secrets |
| Use case | Reuse existing marketplace actions | Lightweight logic | Complex multi-step workflows |
### Defining an Action
[Section titled “Defining an Action”](#defining-an-action)
Under `safe-outputs.actions`, define each action with a `uses` field (matching GitHub Actions `uses` syntax) and an optional `description` override:
.github/workflows/my-workflow\.md
```yaml
---
safe-outputs:
actions:
add-smoked-label:
uses: actions-ecosystem/action-add-labels@v1
description: Add the 'smoked' label to the current pull request
env:
GITHUB_TOKEN: ${{ github.token }}
---
```
The agent calls `add_smoked_label` (dashes normalized to underscores). The action’s declared inputs become the tool’s parameters — values are passed as step inputs at runtime.
### Action Reference
[Section titled “Action Reference”](#action-reference)
| Property | Type | Required | Description |
| ------------- | ------ | -------- | ---------------------------------------------------------------------------- |
| `uses` | string | Yes | Action reference (`owner/repo@ref` or `./path/to/local-action`) |
| `description` | string | No | Tool description shown to the agent (overrides the action’s own description) |
| `env` | object | No | Additional environment variables injected into the action step |
Note
Action names with dashes are normalized to underscores when registered as MCP tools (e.g., `add-smoked-label` becomes `add_smoked_label`). The normalized name is what the agent uses to call the tool.
Tip
Action references are pinned to a SHA at compile time for reproducibility. Run `gh aw compile` again to update pinned SHAs after an upstream action release.
## Importing Custom Jobs
[Section titled “Importing Custom Jobs”](#importing-custom-jobs)
Define jobs in shared files under `.github/workflows/shared/` and import them:
```aw
---
on: issues
permissions:
contents: read
imports:
- shared/slack-notify.md
- shared/jira-integration.md
---
# Issue Handler
Handle the issue and notify via Slack and Jira.
```
Jobs with duplicate names cause compilation errors - rename to resolve conflicts.
## Error Handling
[Section titled “Error Handling”](#error-handling)
Use `core.setFailed()` for errors and validate required inputs:
```javascript
if (!process.env.API_KEY) {
core.setFailed('API_KEY secret is not configured');
return;
}
try {
const response = await fetch(url);
if (!response.ok) {
core.setFailed(`API error (${response.status}): ${await response.text()}`);
return;
}
core.info('Operation completed successfully');
} catch (error) {
core.setFailed(`Request failed: ${error.message}`);
}
```
## Security
[Section titled “Security”](#security)
Store secrets in GitHub Secrets and pass via environment variables. Limit job permissions to minimum required and validate all inputs.
## Staged Mode Support
[Section titled “Staged Mode Support”](#staged-mode-support)
When `GH_AW_SAFE_OUTPUTS_STAGED === 'true'`, skip the real operation and display a preview using `core.summary`. See [Staged Mode](/gh-aw/reference/staged-mode/#staged-mode-for-custom-safe-output-jobs) for a complete example.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
| Issue | Solution |
| ----------------------------------- | -------------------------------------------------------------------------------------- |
| Job or script not appearing as tool | Ensure `inputs` and `description` are defined; verify import path; run `gh aw compile` |
| Secrets not available | Check secret exists in repository settings and name matches exactly (case-sensitive) |
| Job fails silently | Add `core.info()` logging and ensure `core.setFailed()` is called on errors |
| Agent calls wrong tool | Make `description` specific and unique; explicitly mention job name in prompt |
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [DeterministicOps](/gh-aw/patterns/deterministic-ops/) - Mixing computation and AI reasoning
* [Safe Outputs](/gh-aw/reference/safe-outputs/) - Built-in safe output types
* [MCPs](/gh-aw/guides/mcps/) - Model Context Protocol setup
* [Frontmatter](/gh-aw/reference/frontmatter/) - All configuration options
* [Imports](/gh-aw/reference/imports/) - Sharing workflow configurations
# Dependabot Manifest Generation
> Automatic dependency manifest generation for tracking runtime dependencies in agentic workflows, enabling Dependabot to detect and update outdated tools.
The `gh aw compile --dependabot` command scans workflows for runtime tools (`npx`, `pip install`, `go install`), generates dependency manifests (`package.json`, `requirements.txt`, `go.mod`), and configures Dependabot to monitor for updates
## Usage
[Section titled “Usage”](#usage)
Run `gh aw compile --dependabot` to compile all workflows and generate manifests in `.github/workflows/`.
Caution
Must compile **all workflows** - cannot be used with specific files or `--dir` flag.
**Prerequisites**: Node.js/npm required for `package-lock.json` generation. Pip and Go manifests generate without additional tools.
## Compiler-managed `gh-aw-actions` ignore rule
[Section titled “Compiler-managed gh-aw-actions ignore rule”](#compiler-managed-gh-aw-actions-ignore-rule)
`gh aw compile` always reconciles the compiler-managed ignore rule for `github/gh-aw-actions/**` when your repository already has a `github-actions` update block in `.github/dependabot.yml` (this is not limited to `--dependabot` runs).
* No-op if `.github/dependabot.yml` does not exist
* No-op if there is no `package-ecosystem: github-actions` update block
* Preserves user-defined `ignore` entries
```yaml
updates:
- package-ecosystem: github-actions
directory: "/.github/workflows"
schedule:
interval: weekly
ignore:
- dependency-name: "github/gh-aw-actions/**" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
- dependency-name: "actions/checkout" # user-defined, preserved
```
## Generated Files
[Section titled “Generated Files”](#generated-files)
| Ecosystem | Manifest | Lock File |
| --------- | ------------------ | ----------------------------------------------------------- |
| **npm** | `package.json` | `package-lock.json` (via `npm install --package-lock-only`) |
| **pip** | `requirements.txt` | - |
| **Go** | `go.mod` | - |
All ecosystems update `.github/dependabot.yml` with weekly update schedules. Existing configurations are preserved; only missing ecosystems are added.
## Handling Dependabot PRs
[Section titled “Handling Dependabot PRs”](#handling-dependabot-prs)
Caution
**Never merge Dependabot PRs that only modify manifest files.** These changes are overwritten on next compilation.
**Correct workflow**: Update source `.md` files, then recompile to regenerate manifests.
```bash
# Find affected workflows
grep -r "@playwright/test@1.41.0" .github/workflows/*.md
# Edit workflow .md files (change version)
# npx @playwright/test@1.41.0 → npx @playwright/test@1.42.0
# Regenerate manifests
gh aw compile --dependabot
# Commit (Dependabot auto-closes its PR)
git add .github/workflows/
git commit -m "chore: update @playwright/test to 1.42.0"
git push
```
### Handling Transitive Dependencies (MCP Servers)
[Section titled “Handling Transitive Dependencies (MCP Servers)”](#handling-transitive-dependencies-mcp-servers)
When Dependabot flags transitive dependencies (e.g., `@modelcontextprotocol/sdk`, `hono` from `@sentry/mcp-server`), update the **shared MCP configuration** instead:
```bash
# Locate the shared MCP config (e.g., .github/workflows/shared/mcp/sentry.md)
# Update the version in the args array:
# args: ["@sentry/mcp-server@0.27.0"] → args: ["@sentry/mcp-server@0.29.0"]
# Regenerate manifests
gh aw compile --dependabot
# Regenerate package-lock.json to pick up transitive dependency updates
cd .github/workflows && npm install --package-lock-only
# Commit changes
git add .github/workflows/
git commit -m "chore: update @sentry/mcp-server to 0.29.0"
git push
```
**Why?** The compiler generates `package.json` from MCP server configurations in workflow files. Directly editing `package.json` will be overwritten on next compilation.
## AI Agent Prompt Template
[Section titled “AI Agent Prompt Template”](#ai-agent-prompt-template)
```markdown
A Dependabot PR updated dependencies in .github/workflows/.
Fix workflow:
1. Identify which .md files reference the outdated dependency
2. Update versions in workflow files
3. Run `gh aw compile --dependabot` to regenerate manifests
4. Verify manifests match the Dependabot PR
5. Commit and push (Dependabot auto-closes)
Affected PR: [link]
Updated dependency: [name@version]
```
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
| Issue | Solution |
| --------------------------------- | ---------------------------------------------------------------------- |
| **package-lock.json not created** | Install Node.js/npm from [nodejs.org](https://nodejs.org/) |
| **Dependency not detected** | Avoid shell variables (`${TOOL}`); use literal package names |
| **Dependabot not opening PRs** | Verify `.github/dependabot.yml` is valid YAML and manifest files exist |
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [CLI Commands](/gh-aw/setup/cli/#compile) - Complete compile command reference
* [Compilation Process](/gh-aw/reference/compilation-process/) - How compilation works
* [GitHub Dependabot Docs](https://docs.github.com/en/code-security/dependabot) - Official Dependabot guide
# APM Dependencies
> Install and manage APM (Agent Package Manager) packages in your agentic workflows, including skills, prompts, instructions, agents, hooks, and plugins.
[APM (Agent Package Manager)](https://microsoft.github.io/apm/) manages AI agent primitives such as skills, prompts, instructions, agents, hooks, and plugins (including the Claude `plugin.json` specification). Packages can depend on other packages and APM resolves the full dependency tree.
APM is configured by importing the `shared/apm.md` workflow, which creates a dedicated `apm` job that packs packages and uploads the bundle as a GitHub Actions artifact. The agent job then downloads and unpacks the bundle for deterministic startup.
## Where `shared/apm.md` comes from
[Section titled “Where shared/apm.md comes from”](#where-sharedapmmd-comes-from)
`shared/apm.md` is a **local workflow file** that gh-aw resolves at `.github/workflows/shared/apm.md` in your repository — it is not a remote import (the `uses:` syntax inside `imports:` is gh-aw’s local-import shape, not GitHub Actions’ `uses: owner/repo@ref`).
The canonical source is maintained in [microsoft/apm](https://github.com/microsoft/apm/blob/main/.github/workflows/shared/apm.md). Add it to your repository with:
```bash
gh aw add microsoft/apm/.github/workflows/shared/apm.md --dir shared
```
Running `gh aw update` will keep your vendored copy in sync with the canonical source. The `shared/apm.md` file declares a `redirect` to the `microsoft/apm` library, so any copy sourced from gh-aw will automatically follow the redirect and rewrite its `source` field to track the canonical location on the next `gh aw update` run.
The canonical version pins `microsoft/apm-action@v1.5.0` and supports multi-org GitHub App authentication (`apps:[]`) and multi-bundle restore.
## Usage
[Section titled “Usage”](#usage)
Import `shared/apm.md` and supply the list of packages via the `packages` parameter:
```aw
imports:
- uses: shared/apm.md
with:
packages:
- microsoft/apm-sample-package
- github/awesome-copilot/skills/review-and-refactor
- anthropics/skills/skills/frontend-design
```
## Reproducibility and governance
[Section titled “Reproducibility and governance”](#reproducibility-and-governance)
APM lock files (`apm.lock`) pin every package to an exact commit SHA, so the same versions are installed on every run. Lock file diffs appear in pull requests and are reviewable before merge, giving teams and enterprises a clear audit trail and the ability to govern which agent context is in use. See the [APM governance guide](https://microsoft.github.io/apm/enterprise/governance/) for details on policy enforcement and access controls.
## Package reference formats
[Section titled “Package reference formats”](#package-reference-formats)
Each entry in `packages` is an APM package reference. Supported formats:
| Format | Description |
| ------------------------------ | ------------------------------------------------------------------------- |
| `owner/repo` | Full APM package |
| `owner/repo/path/to/primitive` | Individual primitive (skill, instruction, plugin, etc.) from a repository |
| `owner/repo#ref` | Package pinned to a tag, branch, or commit SHA |
### Examples
[Section titled “Examples”](#examples)
```aw
imports:
- uses: shared/apm.md
with:
packages:
# Full APM package
- microsoft/apm-sample-package
# Individual primitive from any repository
- github/awesome-copilot/skills/review-and-refactor
# Plugin (Claude plugin.json format)
- github/awesome-copilot/plugins/context-engineering
# Version-pinned to a tag
- microsoft/apm-sample-package#v2.0
# Version-pinned to a branch
- microsoft/apm-sample-package#main
```
## How it works
[Section titled “How it works”](#how-it-works)
The `shared/apm.md` import adds a dedicated `apm` job to the compiled workflow. This job runs `microsoft/apm-action` to install packages and create a bundle archive, which is uploaded as a GitHub Actions artifact. The agent job downloads and restores the bundle as pre-steps, making all skills and tools available at runtime.
Packages are fetched using the cascading token fallback: `GH_AW_PLUGINS_TOKEN` → `GH_AW_GITHUB_TOKEN` → `GITHUB_TOKEN`.
To reproduce or debug the pack/unpack flow locally, run `apm pack` and `apm unpack` directly. See the [pack and distribute guide](https://microsoft.github.io/apm/guides/pack-distribute/) for instructions.
## Reference
[Section titled “Reference”](#reference)
| Resource | URL |
| ---------------------------- | ---------------------------------------------------------------------------- |
| APM documentation | |
| APM governance guide | |
| Pack and distribute guide | |
| gh-aw integration (APM docs) | |
| apm-action (GitHub) | |
| microsoft/apm (GitHub) | |
| shared/apm.md (canonical) | |
# Workflow Editors
> A curated list of editors for authoring and previewing agentic workflows.
The following editors can be used to author, compile, and preview agentic workflows. Some are built-in tools maintained alongside gh-aw; others are community-created projects.
### Compiler Playground
[Section titled “Compiler Playground”](#compiler-playground)
An **experimental** interactive browser-based playground that runs the gh-aw compiler entirely in the browser using [WebAssembly](/gh-aw/reference/wasm-compilation/). It demonstrates how to use the WASM build of the compiler directly in a webpage and shows how to compile workflows in the browser using the WASM-based execution engine.
*

## Community editors
[Section titled “Community editors”](#community-editors)
Note
Community editors are created and maintained by independent contributors. They are not officially supported by the gh-aw project.
### Agentic Prompt Generator
[Section titled “Agentic Prompt Generator”](#agentic-prompt-generator)
A web-based tool for generating and editing agentic workflow prompts. It provides an interactive interface to help author workflow prompts for agentic workflows.
*

### Graphical Workflow Editor
[Section titled “Graphical Workflow Editor”](#graphical-workflow-editor)
A visual, graphical workflow editor that provides a richer UI for editing agentic workflows. Rather than working directly with markdown and YAML, this editor focuses on a more interactive and visual editing experience.
*

# Effective Tokens Specification
> Formal specification defining Effective Tokens (ET), a normalized metric for measuring LLM token usage across token classes, model multipliers, and multi-agent execution graphs
# Effective Tokens Specification
[Section titled “Effective Tokens Specification”](#effective-tokens-specification)
**Version**: 0.2.0 **Status**: Draft **Publication Date**: 2026-04-02 **Editor**: GitHub Agentic Workflows Team **This Version**: [effective-tokens-specification](/gh-aw/reference/effective-tokens-specification/) **Latest Published Version**: This document
***
## Abstract
[Section titled “Abstract”](#abstract)
This specification defines **Effective Tokens (ET)**, a normalized unit for measuring Large Language Model (LLM) usage across token classes, model-relative computational intensity, and multi-invocation execution graphs. ET provides a single unified metric for composite LLM workloads including multi-step pipelines, tool-augmented calls, sub-agent orchestration, and recursive inference.
## Status of This Document
[Section titled “Status of This Document”](#status-of-this-document)
This section describes the status of this document at the time of publication. This is a draft specification and may be updated, replaced, or made obsolete by other documents at any time.
This document is governed by the GitHub Agentic Workflows project specifications process.
## Table of Contents
[Section titled “Table of Contents”](#table-of-contents)
1. [Introduction](#1-introduction)
2. [Conformance](#2-conformance)
3. [Terminology](#3-terminology)
4. [Token Accounting Model](#4-token-accounting-model)
5. [Multi-Invocation Aggregation](#5-multi-invocation-aggregation)
6. [Execution Graph Requirements](#6-execution-graph-requirements)
7. [Reporting](#7-reporting)
8. [Implementation Requirements](#8-implementation-requirements)
9. [Extensibility](#9-extensibility)
10. [Compliance Testing](#10-compliance-testing)
11. [Appendices](#appendices)
12. [Model Multiplier Registry](#model-multiplier-registry)
13. [Sync Notes](#sync-notes)
14. [References](#references)
15. [Change Log](#change-log)
***
## 1. Introduction
[Section titled “1. Introduction”](#1-introduction)
### 1.1 Purpose
[Section titled “1.1 Purpose”](#11-purpose)
Token counts reported by LLM APIs are not directly comparable: different token classes (input, cached, output, reasoning) carry different computational costs, and different models have different relative costs. Effective Tokens normalizes these variables into a single scalar that reflects true computational intensity, enabling consistent measurement and comparison across complex multi-agent systems.
### 1.2 Scope
[Section titled “1.2 Scope”](#12-scope)
This specification covers:
* Definition of token classes and their default weights
* The per-invocation ET computation formula
* Aggregation across multi-invocation execution graphs
* Structural requirements for invocation nodes and summary reports
This specification does NOT cover:
* Billing, pricing, or cost allocation
* Model selection or routing strategies
* Streaming or partial token reporting
### 1.3 Design Goals
[Section titled “1.3 Design Goals”](#13-design-goals)
An ET implementation:
1. Preserves raw token counts per invocation
2. Normalizes across token classes using disclosed weights
3. Normalizes across models using per-model multipliers
4. Supports aggregation across any number of invocations
5. Produces a single reproducible metric from identical inputs
6. Carries no dependency on billing or pricing systems
***
## 2. Conformance
[Section titled “2. Conformance”](#2-conformance)
### 2.1 Conformance Classes
[Section titled “2.1 Conformance Classes”](#21-conformance-classes)
**Conforming implementation**: An implementation that satisfies all MUST/SHALL requirements in this specification.
**Partially conforming implementation**: An implementation that satisfies core accounting requirements (Sections 4–5) but omits optional fields or extensions.
### 2.2 Requirements Notation
[Section titled “2.2 Requirements Notation”](#22-requirements-notation)
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
### 2.3 Compliance Levels
[Section titled “2.3 Compliance Levels”](#23-compliance-levels)
* **Level 1 – Basic**: Single-invocation ET computation (Section 4)
* **Level 2 – Standard**: Multi-invocation aggregation and execution graph (Sections 5–6)
* **Level 3 – Complete**: Full reporting and extensibility support (Sections 7–9)
***
## 3. Terminology
[Section titled “3. Terminology”](#3-terminology)
### 3.1 Token Classes
[Section titled “3.1 Token Classes”](#31-token-classes)
| Class | Symbol | Description |
| ------------------- | ------ | ------------------------------------------------ |
| Input Tokens | I | Tokens newly processed by the model |
| Cached Input Tokens | C | Tokens served via cache or prefix reuse |
| Output Tokens | O | Tokens generated by the model |
| Reasoning Tokens | R | Internal tokens used during inference (optional) |
### 3.2 Model Multiplier
[Section titled “3.2 Model Multiplier”](#32-model-multiplier)
The **Copilot Multiplier** (`m`) is a scalar representing the relative computational intensity of a model versus a defined baseline. Its value is model-specific and MUST be disclosed by the implementation.
### 3.3 Invocation
[Section titled “3.3 Invocation”](#33-invocation)
A single LLM request-response cycle. Each invocation produces one set of token counts and yields one ET value.
### 3.4 Sub-Agent
[Section titled “3.4 Sub-Agent”](#34-sub-agent)
Any invocation triggered by another LLM call or orchestration layer. Examples include tool-using agents, retrieval-augmented calls, planning/execution agents, and recursively delegated LLM calls.
### 3.5 Execution Graph
[Section titled “3.5 Execution Graph”](#35-execution-graph)
A directed structure representing all invocations associated with a single top-level request. The root node has no parent; sub-agents reference their triggering invocation as their parent.
### 3.6 Execution-Graph Traversal Entities
[Section titled “3.6 Execution-Graph Traversal Entities”](#36-execution-graph-traversal-entities)
For deterministic aggregation and reporting, implementations MUST distinguish the following traversal entities when processing an execution graph:
* **Local invocation cost**: The ET computed from the current node’s own `usage.*` payload only.
* **Descendant contribution**: The subtotal accumulated from child nodes and deeper descendants before the current node’s local invocation cost is added.
* **Observed subtree**: A subtree whose invocation nodes have concrete usage payloads and therefore contribute measured ET rather than fallback zeros.
* **Unobservable subtree**: A subtree whose invocation nodes are known to exist but whose concrete usage payloads are unavailable; these nodes remain part of traversal order even when their ET is serialized as `0`.
***
## 4. Token Accounting Model
[Section titled “4. Token Accounting Model”](#4-token-accounting-model)
### 4.1 Raw Token Count
[Section titled “4.1 Raw Token Count”](#41-raw-token-count)
For each invocation, the raw total is:
```plaintext
raw_total_tokens = I + C + O + R
```
### 4.2 Token Class Weights
[Section titled “4.2 Token Class Weights”](#42-token-class-weights)
Default weights for the four token classes are:
| Token Class | Symbol | Default Weight |
| ------------ | --------- | -------------- |
| Input | w\_in | 1.0 |
| Cached Input | w\_cache | 0.1 |
| Output | w\_out | 4.0 |
| Reasoning | w\_reason | 4.0 |
Implementations MAY override these values but MUST disclose the weights used in any reported output.
### 4.3 Base Weighted Tokens
[Section titled “4.3 Base Weighted Tokens”](#43-base-weighted-tokens)
Per invocation:
```plaintext
effective_input_tokens = max(I - C, 0)
base_weighted_tokens =
(w_in × effective_input_tokens) + (w_cache × C) + (w_out × O) + (w_reason × R)
```
When providers report cached reads (`C`) as part of input tokens (`I`), implementations MUST subtract cached input from `I` before applying `w_in` to avoid double counting.
To avoid ambiguity, conforming implementations MUST treat these symbols as follows:
* `I`: total reported input tokens for the invocation
* `C`: cached subset of that same input
* `w_in` MUST be applied only to `max(I - C, 0)` (the non-cached portion)
* `w_cache` MUST be applied only to `C`
Implementations MUST NOT charge the cached portion twice (once via `w_in × I` and again via `w_cache × C`).
### 4.4 Effective Tokens Per Invocation
[Section titled “4.4 Effective Tokens Per Invocation”](#44-effective-tokens-per-invocation)
```plaintext
effective_tokens = m × base_weighted_tokens
```
***
## 5. Multi-Invocation Aggregation
[Section titled “5. Multi-Invocation Aggregation”](#5-multi-invocation-aggregation)
### 5.1 Total Effective Tokens
[Section titled “5.1 Total Effective Tokens”](#51-total-effective-tokens)
For a request involving N invocations:
```plaintext
ET_total = Σ (m_i × base_weighted_tokens_i)
```
Each invocation MAY use a different model and multiplier.
### 5.2 Total Raw Tokens
[Section titled “5.2 Total Raw Tokens”](#52-total-raw-tokens)
```plaintext
raw_total_tokens = Σ (I_i + C_i + O_i + R_i)
```
### 5.3 Invocation Count
[Section titled “5.3 Invocation Count”](#53-invocation-count)
```plaintext
total_invocations = N
```
This count MUST include the root call, all sub-agent calls, and all tool-triggered LLM calls.
***
## 6. Execution Graph Requirements
[Section titled “6. Execution Graph Requirements”](#6-execution-graph-requirements)
Implementations MUST represent multi-call workflows as a directed execution graph.
### 6.1 Node Schema
[Section titled “6.1 Node Schema”](#61-node-schema)
Each node (invocation) MUST conform to:
```json
{
"id": "string",
"parent_id": "string | null",
"model": {
"name": "string",
"copilot_multiplier": number
},
"usage": {
"input_tokens": number,
"cached_input_tokens": number,
"output_tokens": number,
"reasoning_tokens": number
},
"derived": {
"base_weighted_tokens": number,
"effective_tokens": number
},
"flagged": {
"code": "string",
"reason": "string"
}
}
```
### 6.2 Root Invocation
[Section titled “6.2 Root Invocation”](#62-root-invocation)
The root invocation MUST have `parent_id = null`. It represents the user-facing request that initiates the execution graph.
### 6.3 Sub-Agent Invocations
[Section titled “6.3 Sub-Agent Invocations”](#63-sub-agent-invocations)
Each sub-agent invocation MUST reference a valid `parent_id`. Sub-agent invocations MAY recursively spawn further invocations.
For execution graphs deeper than two levels, implementations MUST aggregate descendant Effective Tokens in stable post-order: fully observed leaf descendants first, then their nearest observed ancestors, and finally the parent node’s local invocation cost. When a parent has incomplete or unobservable descendants, the implementation MUST report the partial sum accumulated from the deepest observed descendants before adding any shallower fallback estimates, and SHOULD keep the parent node flagged until all known descendants are either observed or explicitly marked unobservable. Repeated computations over the same partially observed graph MUST produce the same partial-ordering and subtotal sequence.
Implementation ordering constraints for multi-invocation aggregation:
1. Traverse child subtrees in deterministic order (for example, stable sibling order by invocation ID or first-seen sequence).
2. For each subtree, aggregate fully observed deepest descendants before applying fallback estimates for unobservable nodes in that same subtree.
3. Add the current node’s local invocation ET only after all descendant contributions for that node are finalized.
***
## 7. Reporting
[Section titled “7. Reporting”](#7-reporting)
A conforming response MUST include a `summary` object alongside the `invocations` array:
```json
{
"summary": {
"total_invocations": number,
"raw_total_tokens": number,
"base_weighted_tokens": number,
"effective_tokens": number
},
"invocations": [ ... ]
}
```
### 7.1 OpenTelemetry Attribute Requirements
[Section titled “7.1 OpenTelemetry Attribute Requirements”](#71-opentelemetry-attribute-requirements)
Implementations that emit OpenTelemetry spans or metrics for token accounting MUST use the following normative attribute keys. These keys are not optional examples — they are required names for cross-implementation interoperability.
| OTel Attribute Key | Type | Description |
| --------------------------- | ------- | ---------------------------------------------------------------------------------- |
| `llm.token.effective_total` | integer | Total Effective Tokens for the invocation (ET as defined in §4.4) |
| `llm.token.input` | integer | Raw input token count for the invocation |
| `llm.token.output` | integer | Raw output token count for the invocation |
| `llm.token.cached_input` | integer | Number of input tokens served from cache |
| `llm.token.base_weighted` | integer | Base weighted token value before model multiplier is applied |
| `llm.model.multiplier` | float | The Copilot model multiplier (`m`) applied for this invocation |
| `llm.invocation.id` | string | Unique identifier for this invocation node (matches `id` field in execution graph) |
**R-OTL-001**: Implementations that emit OTel attributes for effective token data MUST use `llm.token.effective_total` as the attribute key for the ET value. Implementations MUST NOT use alternative keys (e.g., `effective_tokens`, `et_total`) for this attribute.
**R-OTL-002**: Implementations MUST emit `llm.token.input`, `llm.token.output`, and `llm.token.cached_input` as separate span attributes when per-class token counts are available. These three attributes MUST reflect raw (unweighted) token counts.
**R-OTL-003**: Implementations MUST emit `llm.token.base_weighted` as a span attribute when the base weighted token value is computed. This attribute allows consumers to audit the weighting step independently of the model multiplier.
**R-OTL-004**: When `llm.model.multiplier` is emitted, its value MUST match the multiplier used to compute `llm.token.effective_total` for the same span. Implementations MUST NOT omit `llm.model.multiplier` if `llm.token.effective_total` is present.
**R-OTL-005**: All OTel attribute keys defined in this section are versioned under this specification. Implementations MUST NOT rename or reuse these keys with different semantics without a specification revision.
***
## 8. Implementation Requirements
[Section titled “8. Implementation Requirements”](#8-implementation-requirements)
### 8.1 Completeness
[Section titled “8.1 Completeness”](#81-completeness)
All LLM calls MUST be included in the execution graph. Hidden or system-triggered calls MUST be counted.
### 8.2 Determinism
[Section titled “8.2 Determinism”](#82-determinism)
Given identical inputs and multipliers, ET MUST be reproducible. Implementations SHOULD NOT introduce non-deterministic factors into the computation.
### 8.3 Versioning
[Section titled “8.3 Versioning”](#83-versioning)
Implementations SHOULD version their token weights and model multipliers so that historical reports remain interpretable.
### 8.4 Partial Visibility
[Section titled “8.4 Partial Visibility”](#84-partial-visibility)
When sub-agents are not fully observable, implementations MUST still report aggregate totals. Invocation nodes with incomplete data SHOULD be flagged to indicate missing information.
### 8.5 Safeguards
[Section titled “8.5 Safeguards”](#85-safeguards)
Implementations must prevent unbounded ET accumulation from producing non-finite or non-interoperable outputs.
**R-SAFE-001**: ET aggregation logic **MUST** detect overflow and non-finite arithmetic states (`NaN`, `+Inf`, `-Inf`) before serializing output.
**R-SAFE-002**: Implementations **MUST** enforce a maximum ET ceiling of `9007199254740991` (`2^53 - 1`) for serialized numeric fields to preserve JavaScript-safe integer interoperability in cross-language pipelines.
**R-SAFE-003**: When computed ET exceeds the ceiling, implementations **MUST** clamp the reported `summary.effective_tokens` value to the ceiling and **MUST** emit a warning indicating that capping occurred.
**R-SAFE-003A**: When ET capping occurs, implementations **MUST** record a deterministic overflow condition using either `flagged.code = "ET_OVERFLOW"` on the affected root/subtree node or a deterministic error when no structured flag channel is available. The error/flag payload **MUST** include the ceiling value `9007199254740991` so operators can distinguish overflow from missing usage data.
**R-SAFE-004**: For long multi-agent chains, implementations **SHOULD** aggregate ET in a streaming manner (incremental updates per invocation) and **SHOULD** emit an early warning when running totals exceed 80% of the ceiling.
**R-SAFE-005**: For invocation nodes with incomplete usage payloads (unobservable sub-agents), implementations **MUST** serialize `usage.input_tokens`, `usage.cached_input_tokens`, `usage.output_tokens`, `usage.reasoning_tokens`, `derived.base_weighted_tokens`, and `derived.effective_tokens` as numeric zero (`0`) rather than omitting those fields.
**R-SAFE-006**: For invocation nodes that are incomplete/unobservable, implementations **MUST** include a `flagged` object with schema `{ "code": "UNOBSERVABLE_INVOCATION", "reason": string }`. For fully observed invocation nodes, implementations **MAY** omit `flagged`.
**R-SAFE-007**: Before ET computation begins, implementations **MUST** validate the active model multiplier registry described in [Model Multiplier Registry](#model-multiplier-registry). Registry validation **MUST** confirm that `version` and `reference_model` are non-empty strings and that the reference model has a numeric multiplier entry.
**R-SAFE-008**: Every declared token class weight and model multiplier loaded from the registry **MUST** be finite numeric data. `NaN`, infinite values, strings, `null`, and negative multiplier values **MUST** be rejected before any ET output is produced.
**R-SAFE-009**: If registry validation fails, implementations **MUST NOT** continue with partially parsed multiplier data. They **MUST** fail deterministically with an error that identifies the invalid registry field or model entry.
**R-SAFE-010**: When a runtime override or custom multiplier map is merged with the embedded registry, implementations **MUST** apply the same validation rules to the merged result before using it for ET computation.
***
## 9. Extensibility
[Section titled “9. Extensibility”](#9-extensibility)
Implementations MAY:
* Add new token classes (e.g., `tool_tokens`)
* Add latency or compute metadata per invocation node
* Support streaming or partial progress updates
Extensions MUST NOT alter the core ET definition or the default weight values without disclosure.
***
## 10. Compliance Testing
[Section titled “10. Compliance Testing”](#10-compliance-testing)
### 10.1 Test Suite Requirements
[Section titled “10.1 Test Suite Requirements”](#101-test-suite-requirements)
#### 10.1.1 Token Accounting Tests
[Section titled “10.1.1 Token Accounting Tests”](#1011-token-accounting-tests)
* **T-ET-001**: Single invocation with all four token classes produces correct `base_weighted_tokens`
* **T-ET-002**: Single invocation ET equals `m × base_weighted_tokens`
* **T-ET-003**: Zero-value token classes do not affect the result
* **T-ET-004**: Custom weights are applied when default weights are overridden
* **T-ET-005**: Cached/input overlap is not double counted (`w_in` applies to `max(I-C,0)`, not `I`)
* **T-ET-007**: Effective input is clamped at zero when `C > I` (`max(I-C,0)`)
#### 10.1.2 Aggregation Tests
[Section titled “10.1.2 Aggregation Tests”](#1012-aggregation-tests)
* **T-ET-010**: Multi-invocation `ET_total` equals the sum of per-invocation ET values
* **T-ET-011**: `raw_total_tokens` equals the sum of all raw tokens across all invocations
* **T-ET-012**: `total_invocations` count includes root, sub-agents, and tool-triggered calls
#### 10.1.3 Aggregation with Zero-ET Leaf Nodes
[Section titled “10.1.3 Aggregation with Zero-ET Leaf Nodes”](#1013-aggregation-with-zero-et-leaf-nodes)
* **T-ET-006**: Multi-invocation aggregation where one or more leaf invocation nodes have all token class values set to zero (simulating tool calls that produce no tokens, such as no-op tool invocations or tool calls whose usage data is unavailable). The implementation MUST:
1. Include the zero-ET invocation node in `total_invocations` count.
2. Contribute `0` to `ET_total` from that node (rather than omitting it).
3. Represent the node in the execution graph with all `usage.*` fields set to `0` and `derived.effective_tokens = 0`.
4. Not emit a warning or error solely because a leaf node has zero effective tokens.
#### 10.1.4 Execution Graph Tests
[Section titled “10.1.4 Execution Graph Tests”](#1014-execution-graph-tests)
* **T-ET-020**: Root node has `parent_id = null`
* **T-ET-021**: All sub-agent nodes reference a valid `parent_id`
* **T-ET-022**: Node schema includes all required fields
* **T-ET-032**: Deep (3+ level) execution graphs aggregate ET in deterministic post-order and keep partial subtotals stable under partial observability
#### 10.1.5 Reporting Tests
[Section titled “10.1.5 Reporting Tests”](#1015-reporting-tests)
* **T-ET-030**: Summary object is present in all conforming responses
* **T-ET-031**: Summary values are consistent with per-invocation data
### 10.2 Compliance Checklist
[Section titled “10.2 Compliance Checklist”](#102-compliance-checklist)
#### 10.2.1 Compliance Test Count Summary
[Section titled “10.2.1 Compliance Test Count Summary”](#1021-compliance-test-count-summary)
| Category | Count |
| ------------------- | ----- |
| Total tests defined | 16 |
| Required tests | 16 |
| Optional tests | 0 |
Count method: unique `T-ET-*` IDs in §10.1 (`001–005`, `006`, `007`, `010–012`, `020–022`, `030–032`).
| Requirement | Test ID | Level | Status |
| ----------------------------------- | ---------------------- | ----- | ----------- |
| Per-invocation base weighted tokens | T-ET-001–005, T-ET-007 | 1 | Implemented |
| Per-invocation ET computation | T-ET-002 | 1 | Implemented |
| Multi-invocation aggregation | T-ET-010–012 | 2 | Implemented |
| Zero-ET leaf node aggregation | T-ET-006 | 2 | Required |
| Execution graph node schema | T-ET-020–022 | 2 | Implemented |
| Deep graph post-order aggregation | T-ET-032 | 2 | Required |
| Summary reporting | T-ET-030–031 | 3 | Implemented |
| Custom weight disclosure | T-ET-004 | 1 | Implemented |
| Versioning of weights/multipliers | — | 3 | Recommended |
| Partial visibility flagging | — | 2 | Recommended |
***
## Appendices
[Section titled “Appendices”](#appendices)
### Appendix A: Worked Example
[Section titled “Appendix A: Worked Example”](#appendix-a-worked-example)
#### A.1 Scenario
[Section titled “A.1 Scenario”](#a1-scenario)
A request triggers three invocations: a root call, a retrieval sub-agent, and a final synthesis call.
#### A.2 Input Data
[Section titled “A.2 Input Data”](#a2-input-data)
```json
{
"invocations": [
{
"id": "root",
"parent_id": null,
"model": { "name": "model-a", "copilot_multiplier": 2.0 },
"usage": {
"input_tokens": 500,
"cached_input_tokens": 200,
"output_tokens": 150,
"reasoning_tokens": 0
}
},
{
"id": "retrieval",
"parent_id": "root",
"model": { "name": "model-b", "copilot_multiplier": 1.0 },
"usage": {
"input_tokens": 300,
"cached_input_tokens": 0,
"output_tokens": 100,
"reasoning_tokens": 0
}
},
{
"id": "synthesis",
"parent_id": "root",
"model": { "name": "model-a", "copilot_multiplier": 2.0 },
"usage": {
"input_tokens": 200,
"cached_input_tokens": 100,
"output_tokens": 250,
"reasoning_tokens": 0
}
}
]
}
```
#### A.3 Computation
[Section titled “A.3 Computation”](#a3-computation)
```plaintext
root:
base = (1.0 × max(500-200,0)) + (0.1 × 200) + (4.0 × 150) = 300 + 20 + 600 = 920
ET = 2.0 × 920 = 1840
retrieval:
base = (1.0 × 300) + (4.0 × 100) = 300 + 400 = 700
ET = 1.0 × 700 = 700
synthesis:
base = (1.0 × max(200-100,0)) + (0.1 × 100) + (4.0 × 250) = 100 + 10 + 1000 = 1110
ET = 2.0 × 1110 = 2220
```
#### A.4 Output
[Section titled “A.4 Output”](#a4-output)
```json
{
"summary": {
"total_invocations": 3,
"raw_total_tokens": 1800,
"base_weighted_tokens": 2730,
"effective_tokens": 4760
}
}
```
#### A.5 Input vs Cached Conformance Test Vectors
[Section titled “A.5 Input vs Cached Conformance Test Vectors”](#a5-input-vs-cached-conformance-test-vectors)
These vectors are normative examples for overlap handling and are intended to be asserted by conformance tests.
| Test ID | Inputs `(I,O,C,R)` | Base computation (default weights) | Expected `base_weighted_tokens` |
| -------- | ------------------ | ------------------------------------- | ------------------------------: |
| T-ET-005 | `(100, 0, 80, 0)` | `1.0×max(100-80,0) + 0.1×80 = 20 + 8` | 28 |
| T-ET-007 | `(50, 0, 80, 0)` | `1.0×max(50-80,0) + 0.1×80 = 0 + 8` | 8 |
#### A.6 Partial Observability Examples
[Section titled “A.6 Partial Observability Examples”](#a6-partial-observability-examples)
When some descendant invocations are unobservable, implementations still report deterministic partial totals and preserve stable ordering.
**Example A (deep graph with one unobservable leaf):**
```text
root
├─ planner
│ ├─ retrieval (observed ET=120)
│ │ └─ shard-1 (observed ET=60)
│ └─ shard-2 (unobservable fallback ET=25)
└─ synthesis (observed ET=40)
```
Deterministic post-order subtotal sequence:
1. `shard-1` → 60
2. `retrieval` local ET (120) → subtotal 180
3. `shard-2` fallback ET (25) → subtotal 205
4. `planner` local ET → subtotal
5. `synthesis` local ET → subtotal
6. `root` local ET → final total
**Example B (all descendants unobservable):**
If all descendants of a node are unobservable, that node MUST still be included with `derived.effective_tokens = 0` and `flagged.code = "UNOBSERVABLE_INVOCATION"` until concrete usage is observed.
### Appendix B: Core Formula Reference
[Section titled “Appendix B: Core Formula Reference”](#appendix-b-core-formula-reference)
```plaintext
ET_total = Σ [ m_i × (w_in × max(I_i - C_i, 0) + w_cache × C_i + w_out × O_i + w_reason × R_i) ]
```
With default weights:
```plaintext
ET_total = Σ [ m_i × (max(I_i - C_i, 0) + 0.1 C_i + 4 O_i + 4 R_i) ]
```
### Appendix C: Security Considerations
[Section titled “Appendix C: Security Considerations”](#appendix-c-security-considerations)
ET values are derived from token usage metadata. Implementations SHOULD treat per-invocation token data as potentially sensitive since usage patterns may reveal information about system prompts, model configurations, or user behavior. Aggregate ET values suitable for observability dashboards SHOULD be separated from detailed per-invocation data in access-controlled reporting systems.
***
## Model Multiplier Registry
[Section titled “Model Multiplier Registry”](#model-multiplier-registry)
### Registry Purpose
[Section titled “Registry Purpose”](#registry-purpose)
The **Copilot Multiplier** (`m`) used in the ET formula is a per-model scalar that represents each model’s computational cost relative to the reference model. To ensure reproducibility and transparency, multiplier values MUST be sourced from a disclosed, versioned registry.
### Normative Registry Source
[Section titled “Normative Registry Source”](#normative-registry-source)
The authoritative registry for `copilot_multiplier` values in this implementation is the file:
```plaintext
pkg/cli/data/model_multipliers.json
```
This file is embedded at compile time into the `gh-aw` binary using a Go `//go:embed` directive in `pkg/cli/effective_tokens.go`. The registry format is:
```json
{
"version": "string",
"description": "string",
"reference_model": "string",
"token_class_weights": {
"input": number,
"cached_input": number,
"output": number,
"reasoning": number,
"cache_write": number
},
"multipliers": {
"": number
}
}
```
### Registry Requirements
[Section titled “Registry Requirements”](#registry-requirements)
**R-REG-001**: The registry MUST declare a `version` field that changes whenever any multiplier value is added, removed, or modified.
**R-REG-002**: The registry MUST declare a `reference_model` field identifying the baseline model whose multiplier equals 1.0. All other multipliers are relative to this baseline.
**R-REG-003**: The registry MUST include `token_class_weights` for all four standard token classes: `input`, `cached_input`, `output`, and `reasoning`. A conforming implementation MUST use these weights as the default values for Section 4.2.
**R-REG-004**: Implementations MUST embed or bundle the registry at build time. Runtime fetching of multiplier values from an external source requires disclosure in reported output.
**R-REG-005**: When a model name is not present in the registry, implementations MUST treat the multiplier as `1.0` and SHOULD emit a warning noting that the model is unrecognized.
**R-REG-006**: Custom multipliers supplied by the caller (e.g., via API or configuration) MUST be merged with registry multipliers. Custom values take precedence and MUST be disclosed in any report that uses them.
**R-REG-007**: The registry MUST NOT contain placeholder values such as `TBD`, `null`, or empty strings for any model multiplier entry. Each declared model key MUST map to a numeric multiplier value.
**R-REG-008**: When adding support for a new model, maintainers MUST register the model in `pkg/cli/data/model_multipliers.json` with a concrete numeric multiplier before release. If calibration is incomplete, the model MUST be omitted from the registry and the implementation fallback behavior in R-REG-005 applies.
**R-REG-009**: When a model is scheduled for removal from the registry, it MUST remain in `pkg/cli/data/model_multipliers.json` with a `deprecated` marker in a comment or companion metadata field for at least one minor version before it is deleted. Implementations SHOULD emit a warning when a `deprecated` model is encountered at runtime, advising callers to migrate to a supported model. A model entry MUST NOT be silently removed between consecutive minor versions; removal without the one-version deprecation notice is a breaking change and MUST be accompanied by a major version bump of the registry `version` field.
### Registry Versioning
[Section titled “Registry Versioning”](#registry-versioning)
The `version` field in `model_multipliers.json` corresponds to the registry schema version, not the gh-aw binary version. Implementations SHOULD include the registry version in all ET summary reports to enable historical reconstruction.
***
## Sync Notes
[Section titled “Sync Notes”](#sync-notes)
The Effective Tokens registry is maintained in `pkg/cli/data/model_multipliers.json` and loaded by `pkg/cli/effective_tokens.go`.
To keep specification and implementation synchronized:
1. Update this specification’s registry requirements when adding, removing, or re-scaling model multipliers.
2. Update `pkg/cli/data/model_multipliers.json` in the same change.
3. When deprecating a model, add a `deprecated` comment alongside the entry and keep it in the registry for at least one minor version before removal (R-REG-009). Update the registry `version` field on removal.
4. Verify loading and fallback behavior in `pkg/cli/effective_tokens_test.go` (`TestModelMultipliersJSONEmbedded`, `TestResolveEffectiveWeightsDefault`, and inventory checks).
5. Run `make build` so the embedded registry is rebuilt into the `gh-aw` binary.
6. Re-run registry validation coverage after any registry edit so malformed multiplier entries fail before ET computation paths are exercised.
Conforming releases SHOULD include a test assertion for newly added model multipliers to ensure implementation-registry parity.
***
## References
[Section titled “References”](#references)
### Normative References
[Section titled “Normative References”](#normative-references)
* **\[RFC 2119]** Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, March 1997.
### Informative References
[Section titled “Informative References”](#informative-references)
* **\[OPENAI-USAGE]** OpenAI API Reference — Usage Objects.
* **\[ANTHROPIC-USAGE]** Anthropic API Reference — Token Usage.
***
## Change Log
[Section titled “Change Log”](#change-log)
### Version 0.3.0 (Draft)
[Section titled “Version 0.3.0 (Draft)”](#version-030-draft)
* **Added**: Model Multiplier Registry section with normative requirements R-REG-001 through R-REG-009
* **Added**: R-REG-009: model deprecation/sunset lifecycle norm (models must carry a `deprecated` marker for one minor version before removal)
* **Added**: Compliance test skeleton file `pkg/cli/effective_tokens_compliance_test.go` with Go test stubs for T-ET-001..T-ET-031
* **Added**: T-ET-032 requirement for deterministic post-order aggregation in deep (3+ level) partially observed execution graphs
* **Updated**: Compliance checklist §10.2 status column from “Required” to “Implemented” for all test IDs T-ET-001–T-ET-031 (all tests now implemented and passing)
* **Audit (Appendix C — Security)**: Verified Appendix C requirements against `pkg/cli/effective_tokens.go` and `pkg/cli/data/model_multipliers.json`. Findings:
* *Sensitive usage patterns* (Appendix C §1): Per-invocation token data is not exposed directly by the CLI; only aggregate `TotalEffectiveTokens` is surfaced in the audit output. Access control is delegated to GitHub repository permissions. **No gaps found.**
* *Aggregate vs. detailed data separation* (Appendix C §2): The `TokenUsageSummary.ByModel` map contains per-model breakdowns but is only logged at DEBUG level, not included in default CLI output. **No gaps found.**
* *Registry exposure*: The embedded `model_multipliers.json` contains only multiplier coefficients, not secrets or PII. **No gaps found.**
* *Follow-up*: The spec does not address token data leakage via OTEL attributes. This is tracked as a separate concern (see §7.3 of the Experiments Specification for precedent).
### Version 0.2.0 (Draft)
[Section titled “Version 0.2.0 (Draft)”](#version-020-draft)
* Adopted W3C-style specification format
* Added conformance levels (Basic, Standard, Complete)
* Added compliance testing section with test IDs
* Added Appendix C: Security Considerations
* Clarified partial visibility requirements
### Version 0.1.0 (Draft)
[Section titled “Version 0.1.0 (Draft)”](#version-010-draft)
* Initial definition of Effective Tokens metric
* Defined four token classes and default weights
* Defined per-invocation and multi-invocation formulas
* Defined execution graph node schema
***
*Copyright 2026 GitHub Agentic Workflows Team. All rights reserved.*
# AI Engines (aka Coding Agents)
> Complete guide to AI engines (coding agents) usable with GitHub Agentic Workflows, including Copilot, Claude, Codex, Gemini, Crush, OpenCode, and Pi with their specific configuration options.
GitHub Agentic Workflows use [AI Engines](/gh-aw/reference/glossary/#engine) (normally a coding agent) to interpret and execute natural language instructions.
## Available Coding Agents
[Section titled “Available Coding Agents”](#available-coding-agents)
Set `engine:` in your workflow frontmatter and configure the corresponding secret:
| Engine | `engine:` value | Required Secret |
| ------------------------------------------------------------------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/use-copilot-cli) (default) | `copilot` | [COPILOT\_GITHUB\_TOKEN](/gh-aw/reference/auth/#copilot_github_token) |
| [Claude by Anthropic (Claude Code)](https://www.anthropic.com/index/claude) | `claude` | [ANTHROPIC\_API\_KEY](/gh-aw/reference/auth/#anthropic_api_key) |
| [OpenAI Codex](https://openai.com/blog/openai-codex) | `codex` | [OPENAI\_API\_KEY](/gh-aw/reference/auth/#openai_api_key) |
| [Google Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | [GEMINI\_API\_KEY](/gh-aw/reference/auth/#gemini_api_key) |
| [Crush](https://github.com/charmbracelet/crush) (experimental) | `crush` | [COPILOT\_GITHUB\_TOKEN](/gh-aw/reference/auth/#copilot_github_token) |
| [OpenCode](https://opencode.ai) (experimental) | `opencode` | [COPILOT\_GITHUB\_TOKEN](/gh-aw/reference/auth/#copilot_github_token) |
| [Pi](https://www.npmjs.com/package/@earendil-works/pi-coding-agent) (experimental) | `pi` | [COPILOT\_GITHUB\_TOKEN](/gh-aw/reference/auth/#copilot_github_token) (default); switches to provider-specific secret when `model:` uses `provider/model` format |
Copilot CLI is the default — `engine:` can be omitted when using Copilot. See the linked authentication docs for secret setup instructions.
## Which engine should I choose?
[Section titled “Which engine should I choose?”](#which-engine-should-i-choose)
Choose the engine that best matches your needs and existing AI account: Copilot supports the broadest gh-aw feature set, including custom agents and autopilot-style continuations; Claude offers stronger control over turn limits (`max-turns`) for long reasoning sessions; and Gemini or Codex fit well when those models are already part of existing tooling or budget decisions. You can switch later by changing only `engine:` and the corresponding secret.
## Engine Feature Comparison
[Section titled “Engine Feature Comparison”](#engine-feature-comparison)
Not all features are available across all engines. The table below summarizes per-engine support for commonly used workflow options:
| Feature | Copilot | Claude | Codex | Gemini | Crush | OpenCode | Pi |
| ---------------------------------------- | :-----: | :-----: | :--------: | :-----: | :-----: | :------: | :-----: |
| `max-runs` (AWF invocation cap) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `max-turns` | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
| `max-continuations` | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| `tools.web-fetch` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `tools.web-search` | via MCP | via MCP | ✓ (opt-in) | via MCP | via MCP | via MCP | via MCP |
| `engine.agent` (custom agent file) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| `engine.api-target` (custom endpoint) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `engine.bare` (disable context loading) | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
| `engine.harness` (custom harness script) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| Tools allowlist | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ |
**Notes:**
* `max-runs` is a top-level frontmatter field that maps to `apiProxy.maxRuns` and is supported by all engines.
* `max-runs` defaults to `500` and `max-effective-tokens` defaults to `25000000` when omitted.
* `max-turns` limits the number of AI chat iterations per run (Claude only).
* `max-continuations` enables autopilot mode with multiple consecutive runs (Copilot only).
* `web-search` for Codex is disabled by default; add `tools: web-search:` to enable it. Other engines use a third-party MCP server — see [Using Web Search](/gh-aw/reference/web-search/).
* `engine.agent` references a `.github/agents/` file for custom Copilot agent behavior. See [Copilot Custom Configuration](#copilot-custom-configuration).
* `engine.bare` disables automatic context loading (memory files, custom instructions). See [Bare Mode](#bare-mode-bare) below.
* `engine.harness` allows replacing the built-in Copilot harness script. See [Custom Harness Script](#custom-harness-script-harness) below.
## Extended Coding Agent Configuration
[Section titled “Extended Coding Agent Configuration”](#extended-coding-agent-configuration)
Workflows can specify extended configuration for the coding agent:
```yaml
engine:
id: copilot
version: latest # defaults to latest
model: gpt-5 # example override; omit to use engine default
command: /usr/local/bin/copilot # custom executable path
args: ["--add-dir", "/workspace"] # custom CLI arguments
agent: agent-id # custom agent file identifier
api-target: api.acme.ghe.com # custom API endpoint hostname (GHEC/GHES)
```
### Pinning a Specific Engine Version
[Section titled “Pinning a Specific Engine Version”](#pinning-a-specific-engine-version)
By default, workflows install the latest available version of each engine CLI. To pin to a specific version, set `version` to the desired release:
| Engine | `id` | Example `version` |
| ------------------ | ---------- | ----------------- |
| GitHub Copilot CLI | `copilot` | `"0.0.422"` |
| Claude Code | `claude` | `"2.1.70"` |
| Codex | `codex` | `"0.111.0"` |
| Gemini CLI | `gemini` | `"0.31.0"` |
| Crush | `crush` | `"1.2.14"` |
| OpenCode | `opencode` | `"0.1.0"` |
| Pi | `pi` | `"0.72.1"` |
```yaml
engine:
id: copilot
version: "0.0.422"
```
Pinning is useful when you need reproducible builds or want to avoid breakage from a new CLI release while testing. Remember to update the pinned version periodically to pick up bug fixes and new features.
`version` also accepts a GitHub Actions expression string, enabling `workflow_call` reusable workflows to parameterize the engine version via caller inputs. Expressions are passed injection-safely through an environment variable rather than direct shell interpolation:
```yaml
on:
workflow_call:
inputs:
engine-version:
type: string
default: latest
---
engine:
id: copilot
version: ${{ inputs.engine-version }}
```
### Copilot Custom Configuration
[Section titled “Copilot Custom Configuration”](#copilot-custom-configuration)
Use `agent` to reference a custom agent file in `.github/agents/` (omit the `.agent.md` extension):
```yaml
engine:
id: copilot
agent: technical-doc-writer # .github/agents/technical-doc-writer.agent.md
```
See [Copilot Agent Files](/gh-aw/reference/copilot-custom-agents/) for details.
### Engine Environment Variables
[Section titled “Engine Environment Variables”](#engine-environment-variables)
All engines support custom environment variables through the `env` field:
```yaml
engine:
id: copilot
env:
DEBUG_MODE: "true"
AWS_REGION: us-west-2
CUSTOM_API_ENDPOINT: https://api.example.com
```
Environment variables can also be defined at workflow, job, step, and other scopes. See [Environment Variables](/gh-aw/reference/environment-variables/) for complete documentation on precedence and all 13 env scopes.
### Enterprise API Endpoint (`api-target`)
[Section titled “Enterprise API Endpoint (api-target)”](#enterprise-api-endpoint-api-target)
The `api-target` field specifies a custom API endpoint hostname for the agentic engine. Use this when running workflows against GitHub Enterprise Cloud (GHEC), GitHub Enterprise Server (GHES), or any custom AI endpoint.
For a complete setup and debugging walkthrough for GHE Cloud with data residency, see [Debugging GHE Cloud with Data Residency](/gh-aw/troubleshooting/debug-ghe/).
The value must be a hostname only — no protocol or path (e.g., `api.acme.ghe.com`, not `https://api.acme.ghe.com/v1`). The field works with any engine.
**GHEC example** — specify your tenant-specific Copilot endpoint:
```yaml
engine:
id: copilot
api-target: api.acme.ghe.com
network:
allowed:
- defaults
- acme.ghe.com
- api.acme.ghe.com
```
**GHES example** — use the enterprise Copilot endpoint:
```yaml
engine:
id: copilot
api-target: api.enterprise.githubcopilot.com
network:
allowed:
- defaults
- github.company.com
- api.enterprise.githubcopilot.com
```
The specified hostname must also be listed in `network.allowed` for the firewall to permit outbound requests.
#### Custom API Endpoints via Environment Variables
[Section titled “Custom API Endpoints via Environment Variables”](#custom-api-endpoints-via-environment-variables)
Set a base URL environment variable in `engine.env` to route API calls to an internal LLM router, Azure OpenAI deployment, or corporate proxy. AWF automatically extracts the hostname and applies it to the API proxy. The target domain must also appear in `network.allowed`.
| Engine | Environment variable |
| ---------------- | ------------------------- |
| `codex`, `crush` | `OPENAI_BASE_URL` |
| `claude` | `ANTHROPIC_BASE_URL` |
| `copilot` | `GITHUB_COPILOT_BASE_URL` |
| `gemini` | `GEMINI_API_BASE_URL` |
```yaml
engine:
id: codex
model: gpt-4o
env:
OPENAI_BASE_URL: "https://llm-router.internal.example.com/v1"
OPENAI_API_KEY: ${{ secrets.LLM_ROUTER_KEY }}
network:
allowed:
- github.com
- llm-router.internal.example.com
```
`GITHUB_COPILOT_BASE_URL` is a fallback — if both it and `engine.api-target` are set, `engine.api-target` takes precedence. Crush uses OpenAI-compatible API format; its `model` field uses `provider/model` format (e.g., `openai/gpt-4o`).
### Copilot Bring Your Own Key (BYOK) Mode
[Section titled “Copilot Bring Your Own Key (BYOK) Mode”](#copilot-bring-your-own-key-byok-mode)
The Copilot engine supports routing requests to an external LLM provider instead of GitHub’s default routing. This is useful when you want to use a different model or provider (e.g., OpenAI, Anthropic, Azure OpenAI, or a local Ollama/vLLM instance) while still using the Copilot CLI tooling.
Set `COPILOT_PROVIDER_BASE_URL` in `engine.env` to activate BYOK mode. The credential variables `COPILOT_PROVIDER_BASE_URL`, `COPILOT_PROVIDER_API_KEY`, and `COPILOT_PROVIDER_BEARER_TOKEN` are explicitly allowed to carry `${{ secrets.* }}` references in `engine.env` under strict mode — they are not leaked to the agent container. Other `COPILOT_PROVIDER_*` variables hold non-sensitive configuration and can be set as plain strings.
| Variable | Required | Description |
| ------------------------------------ | ---------- | ---------------------------------------------------------------------------------------------- |
| `COPILOT_PROVIDER_BASE_URL` | ✓ for BYOK | Base URL of the external provider (e.g. `https://api.openai.com/v1`) |
| `COPILOT_MODEL` | ✓ for BYOK | Model to use (e.g. `claude-sonnet-4`, `gpt-4o`); required by most providers |
| `COPILOT_PROVIDER_API_KEY` | Optional | API key for cloud providers (OpenAI, Anthropic, etc.); not needed for local providers |
| `COPILOT_PROVIDER_BEARER_TOKEN` | Optional | Bearer token alternative to `COPILOT_PROVIDER_API_KEY`; takes precedence when set |
| `COPILOT_PROVIDER_TYPE` | Optional | Provider format: `openai` (default), `azure`, or `anthropic` |
| `COPILOT_PROVIDER_WIRE_API` | Optional | Wire API variant: `completions` (default) or `responses` (for GPT-5 series) |
| `COPILOT_PROVIDER_MODEL_ID` | Optional | Model ID sent on the wire when it differs from `COPILOT_MODEL` (e.g. an Azure deployment name) |
| `COPILOT_PROVIDER_WIRE_MODEL` | Optional | Alternative to `COPILOT_PROVIDER_MODEL_ID` for overriding the wire model |
| `COPILOT_PROVIDER_MAX_PROMPT_TOKENS` | Optional | Override the maximum prompt token limit (otherwise resolved from model catalog) |
| `COPILOT_PROVIDER_MAX_OUTPUT_TOKENS` | Optional | Override the maximum output token limit |
**Example: OpenAI-compatible provider**
```yaml
engine:
id: copilot
env:
# REQUIRED — activates BYOK mode
COPILOT_PROVIDER_BASE_URL: ${{ secrets.PROVIDER_BASE_URL }}
# REQUIRED — a model must be specified for most external providers
COPILOT_MODEL: claude-sonnet-4
# OPTIONAL — API key for cloud providers; not needed for local providers
COPILOT_PROVIDER_API_KEY: ${{ secrets.PROVIDER_API_KEY }}
# OPTIONAL — set to "anthropic" or "azure" if needed (default: "openai")
# COPILOT_PROVIDER_TYPE: anthropic
network:
allowed:
- defaults
- your-provider-domain.example.com
```
**Example: Anthropic provider**
```yaml
engine:
id: copilot
env:
COPILOT_PROVIDER_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
COPILOT_MODEL: claude-sonnet-4
COPILOT_PROVIDER_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
COPILOT_PROVIDER_TYPE: anthropic
```
Note
`COPILOT_PROVIDER_BASE_URL`, `COPILOT_PROVIDER_API_KEY`, and `COPILOT_PROVIDER_BEARER_TOKEN` are recognized as engine credentials and are allowed to carry `${{ secrets.* }}` references in `engine.env` without triggering the strict-mode “secrets in env” warning. Other `COPILOT_PROVIDER_*` variables (type, model, token limits) hold non-sensitive configuration and can be set as plain strings. They may also use `${{ secrets.* }}` syntax if you prefer to keep them private, but this is not required.
Note
Credentials passed via `COPILOT_PROVIDER_*` variables are kept out of the agent container. Only the dummy API key that activates the Agentic Workflow Firewall (AWF) BYOK detection path is visible to the agent process; the real credential is isolated in the AWF API proxy sidecar. See the [AWF sandbox architecture](/gh-aw/reference/sandbox/) for details.
### Engine Command-Line Arguments
[Section titled “Engine Command-Line Arguments”](#engine-command-line-arguments)
All engines support custom command-line arguments through the `args` field, injected before the prompt:
```yaml
engine:
id: copilot
args: ["--add-dir", "/workspace", "--verbose"]
```
Arguments are added in order and placed before the `--prompt` flag. Consult the specific engine’s CLI documentation for available flags.
### Custom Engine Command
[Section titled “Custom Engine Command”](#custom-engine-command)
Override the default engine executable using the `command` field. Useful for testing pre-release versions, custom builds, or non-standard installations. Installation steps are automatically skipped.
```yaml
engine:
id: copilot
command: /usr/local/bin/copilot-dev # absolute path
args: ["--verbose"]
```
### Custom Harness Script (`harness`)
[Section titled “Custom Harness Script (harness)”](#custom-harness-script-harness)
The `harness` field lets you replace the built-in Node.js harness wrapper that the Copilot engine uses to launch the CLI. Use this when you need to customize startup behavior, inject pre/post hooks, or test an alternative harness implementation.
```yaml
engine:
id: copilot
harness: custom_copilot_harness.cjs
```
The value must be a bare filename — no directory separators, no `..`, and no shell metacharacters. It must end with `.js`, `.cjs`, or `.mjs`. When `harness` is set, AWF automatically ensures Node 24 is available in the runner environment.
Note
`engine.harness` is currently only applied during Copilot engine execution. Setting it on other engines has no effect.
**Validation rules:**
| Rule | Valid example | Invalid example |
| -------------------------------------- | ---------------- | -------------------- |
| Bare filename only | `my_harness.cjs` | `subdir/harness.cjs` |
| No path traversal | `harness.mjs` | `../harness.cjs` |
| Must start with `[A-Za-z0-9_]` | `harness.js` | `-harness.cjs` |
| Must end with `.js`, `.cjs`, or `.mjs` | `wrapper.cjs` | `harness.sh` |
### Bare Mode (`bare`)
[Section titled “Bare Mode (bare)”](#bare-mode-bare)
Set `engine.bare: true` to disable automatic loading of context and custom instructions by the engine. Use this when the workflow prompt is fully self-contained and you want to prevent the engine from reading memory files, AGENTS.md, or built-in system prompts that would otherwise be loaded automatically.
```yaml
engine:
id: claude
bare: true
```
The underlying mechanism is engine-specific:
| Engine | Effect |
| ------- | ----------------------------------------------------------------------------------------------------- |
| Copilot | Passes `--no-custom-instructions` — suppresses `.github/AGENTS.md` and user-level custom instructions |
| Claude | Passes `--bare` — suppresses CLAUDE.md memory files |
| Codex | Passes `--no-system-prompt` — suppresses the default system prompt |
| Gemini | Sets `GEMINI_SYSTEM_MD=/dev/null` — overrides the built-in system prompt with an empty file |
Defaults to `false`.
### Custom Token Weights (`token-weights`)
[Section titled “Custom Token Weights (token-weights)”](#custom-token-weights-token-weights)
Override the built-in token cost multipliers used when computing [Effective Tokens](/gh-aw/reference/effective-tokens-specification/). Useful when your workflow uses a custom model not in the built-in list, or when you want to adjust the relative cost ratios for your use case.
```yaml
engine:
id: claude
token-weights:
multipliers:
my-custom-model: 2.5 # 2.5x the cost of claude-sonnet-4.5
experimental-llm: 0.8 # Override an existing model's multiplier
token-class-weights:
output: 6.0 # Override output token weight (default: 4.0)
cached-input: 0.05 # Override cached input weight (default: 0.1)
```
`multipliers` is a map of model names to numeric multipliers relative to `claude-sonnet-4.5` (= 1.0). Keys are case-insensitive and support prefix matching. `token-class-weights` overrides the per-class weights applied before the model multiplier; the defaults are `input: 1.0`, `cached-input: 0.1`, `output: 4.0`, `reasoning: 4.0`, `cache-write: 1.0`.
Custom weights are embedded in the compiled workflow YAML and read by `gh aw logs` and `gh aw audit` when analyzing runs.
## Timeout Configuration
[Section titled “Timeout Configuration”](#timeout-configuration)
Repositories with long build or test cycles require careful timeout tuning at multiple levels. This section documents the timeout knobs available for each engine.
### Job-Level Timeout (`timeout-minutes`)
[Section titled “Job-Level Timeout (timeout-minutes)”](#job-level-timeout-timeout-minutes)
`timeout-minutes` sets the maximum wall-clock time for the entire agent job. This is the primary knob for repositories with long build times. The default is 20 minutes.
```yaml
timeout-minutes: 60 # allow up to 60 minutes for the agent job
```
See [Long Build Times](/gh-aw/reference/sandbox/#long-build-times) in the Sandbox reference for recommended values and concrete examples, including a 30-minute C++ workflow.
### Per-Tool-Call Timeout (`tools.timeout`)
[Section titled “Per-Tool-Call Timeout (tools.timeout)”](#per-tool-call-timeout-toolstimeout)
`tools.timeout` limits how long any single tool invocation may run, in seconds. Useful when individual `bash` commands (builds, test suites) take longer than an engine’s default:
```yaml
tools:
timeout: 300 # 5 minutes per tool call
```
| Engine | Default tool timeout |
| ------- | -------------------------------------- |
| Copilot | not enforced by gh-aw (engine-managed) |
| Claude | 60 s |
| Codex | 120 s |
| Gemini | not enforced by gh-aw (engine-managed) |
| Crush | not enforced by gh-aw (engine-managed) |
See [Tool Timeout Configuration](/gh-aw/reference/tools/#tool-timeout-configuration) for full documentation including `tools.startup-timeout`.
### Per-Engine Timeout Controls
[Section titled “Per-Engine Timeout Controls”](#per-engine-timeout-controls)
#### Copilot
[Section titled “Copilot”](#copilot)
Copilot does not expose a per-turn wall-clock time limit directly. Use `max-continuations` to control how many sequential agent runs are allowed in autopilot mode, and `timeout-minutes` for the overall job budget:
```yaml
engine:
id: copilot
max-continuations: 3 # up to 3 consecutive autopilot runs
timeout-minutes: 60
```
#### Claude
[Section titled “Claude”](#claude)
Claude supports `max-turns` to cap the number of AI iterations per run. Set it together with `tools.timeout` to control both breadth (number of turns) and depth (time per tool call):
```yaml
engine:
id: claude
max-turns: 20 # maximum number of agentic iterations
tools:
timeout: 600 # 10 minutes per bash/tool call
timeout-minutes: 60
```
The `CLAUDE_CODE_MAX_TURNS` environment variable is a Claude Code CLI equivalent of `max-turns`. When `max-turns` is set in frontmatter, gh-aw passes it to the Claude CLI automatically — you do not need to set this env var separately.
#### Codex, Gemini, and Crush
[Section titled “Codex, Gemini, and Crush”](#codex-gemini-and-crush)
These engines do not support `max-turns` or `max-continuations`. Use `timeout-minutes` and `tools.timeout` to bound execution:
```yaml
tools:
timeout: 300
timeout-minutes: 60
```
### Summary Table
[Section titled “Summary Table”](#summary-table)
| Timeout knob | Copilot | Claude | Codex | Gemini | Crush | OpenCode | Notes |
| ----------------------- | :-----: | :----: | :---: | :----: | :---: | :------: | ----------------------------------- |
| `timeout-minutes` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Job-level wall clock |
| `tools.timeout` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Per tool-call limit (seconds) |
| `tools.startup-timeout` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | MCP server startup limit |
| `max-turns` | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | Iteration budget (Claude only) |
| `max-continuations` | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | Autopilot run budget (Copilot only) |
## Claude Tool Enforcement Security Model
[Section titled “Claude Tool Enforcement Security Model”](#claude-tool-enforcement-security-model)
Claude Code uses one of two permission modes at runtime, and which mode is selected determines whether the declared `tools:` allowlist is enforced:
### `acceptEdits` mode (default)
[Section titled “acceptEdits mode (default)”](#acceptedits-mode-default)
By default, gh-aw starts Claude Code with `--permission-mode acceptEdits`. In this mode, Claude honors the `--allowed-tools` flag. The workflow’s declared `tools:` and `mcp-servers: allowed:` configuration is compiled into an explicit allowlist and passed to the Claude CLI. Only the tools listed there are accessible to the agent.
### `bypassPermissions` mode (unrestricted bash)
[Section titled “bypassPermissions mode (unrestricted bash)”](#bypasspermissions-mode-unrestricted-bash)
When the workflow grants unrestricted bash access — `bash: "*"`, `bash: [":*"]`, or `bash: null` — gh-aw switches to `--permission-mode bypassPermissions`. **In this mode, Claude Code silently ignores `--allowed-tools`.** Every tool exposed by the MCP gateway is reachable regardless of the workflow’s declared tool configuration.
Caution
Do not rely on `tools:` or `mcp-servers: allowed:` for security guarantees when unrestricted bash is granted. In `bypassPermissions` mode, the agent can already run arbitrary shell commands, so `--allowed-tools` provides no meaningful additional boundary.
### Gateway-side enforcement
[Section titled “Gateway-side enforcement”](#gateway-side-enforcement)
The **MCP gateway’s `allowed:` filter is the sole effective tool boundary in `bypassPermissions` mode** (and a second layer of enforcement in `acceptEdits` mode). gh-aw compiles the `allowed:` list from each `mcp-servers:` entry into the gateway configuration before the agent starts. The gateway enforces this list server-side, regardless of what the agent requests.
```yaml
mcp-servers:
notion:
container: "mcp/notion"
allowed: ["search_pages", "get_page"] # enforced at gateway level
```
### Summary
[Section titled “Summary”](#summary)
| Workflow config | Permission mode | `--allowed-tools` enforced? | Gateway `allowed:` enforced? |
| ------------------------------------------- | ------------------- | :-------------------------: | :--------------------------: |
| No unrestricted bash | `acceptEdits` | ✓ Yes | ✓ Yes |
| `bash: "*"` / `bash: [":*"]` / `bash: null` | `bypassPermissions` | ✗ No | ✓ Yes |
For workflows that must restrict which MCP tools are accessible, always specify `allowed:` on each `mcp-servers:` entry. This applies regardless of whether unrestricted bash is used.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Frontmatter](/gh-aw/reference/frontmatter/) - Complete configuration reference
* [Tools](/gh-aw/reference/tools/) - Available tools and MCP servers
* [Security Guide](/gh-aw/introduction/architecture/) - Security considerations for AI engines
* [MCPs](/gh-aw/guides/mcps/) - Model Context Protocol setup and configuration
* [Long Build Times](/gh-aw/reference/sandbox/#long-build-times) - Timeout tuning for large repositories
* [Self-Hosted Runners](/gh-aw/guides/self-hosted-runners/) - Fast hardware for long-running workflows
# Enterprise Configuration
> Configure GitHub Agentic Workflows for GitHub Enterprise Server (GHES) and GitHub Enterprise Cloud (GHEC), including artifact compatibility and CLI setup.
# Enterprise Configuration
[Section titled “Enterprise Configuration”](#enterprise-configuration)
This page covers configuration options specific to GitHub Enterprise Server (GHES) and GitHub Enterprise Cloud (GHEC) deployments.
## GitHub Enterprise Server (GHES) Compatibility
[Section titled “GitHub Enterprise Server (GHES) Compatibility”](#github-enterprise-server-ghes-compatibility)
### Artifact Compatibility Mode
[Section titled “Artifact Compatibility Mode”](#artifact-compatibility-mode)
GHES instances running versions that predate `@actions/artifact` v2.0.0 support cannot use `actions/upload-artifact@v4+` or `actions/download-artifact@v4+`. Attempting to run compiled workflows on these instances produces a `GHESNotSupportedError`.
gh-aw includes a GHES compatibility mode that instructs the compiler to emit `upload-artifact@v3.2.2` and `download-artifact@v3.1.0` instead of the latest v4+ versions.
#### Enable via `aw.json` (recommended)
[Section titled “Enable via aw.json (recommended)”](#enable-via-awjson-recommended)
Set `ghes: true` in `.github/workflows/aw.json` to apply GHES compatibility to every workflow compiled in the repository:
```json
{
"ghes": true
}
```
#### Auto-detection with `gh aw init`
[Section titled “Auto-detection with gh aw init”](#auto-detection-with-gh-aw-init)
Running `gh aw init` inside a GHES repository automatically detects the deployment and writes `ghes: true` to `.github/workflows/aw.json`. No manual configuration is required.
#### Enable via CLI flag
[Section titled “Enable via CLI flag”](#enable-via-cli-flag)
Pass `--ghes` to `gh aw compile` for a one-off compilation without modifying `aw.json`:
```bash
gh aw compile --ghes my-workflow.md
```
Note
The `--ghes` flag only affects the current compilation. Use `aw.json` to apply GHES compatibility permanently across all workflows in the repository.
## GitHub Enterprise Server CLI Setup
[Section titled “GitHub Enterprise Server CLI Setup”](#github-enterprise-server-cli-setup)
For `gh` CLI configuration, host authentication, and `GH_HOST` setup on GHES, see [GitHub Enterprise Server Support](/gh-aw/setup/cli/#github-enterprise-server-support) in the CLI reference.
## Copilot Engine on GHES
[Section titled “Copilot Engine on GHES”](#copilot-engine-on-ghes)
For Copilot-specific prerequisites, licensing requirements, and firewall configuration on GHES, see [Copilot Engine Prerequisites on GHES](/gh-aw/troubleshooting/common-issues/#copilot-engine-prerequisites-on-ghes).
# Environment Variables
> Reference for all environment variables in GitHub Agentic Workflows — CLI configuration, model overrides, guard policy fallbacks, and workflow-level scope precedence
Environment variables in GitHub Agentic Workflows can be defined at multiple scopes, each serving a specific purpose in the workflow lifecycle. Variables defined at more specific scopes override those at more general scopes, following GitHub Actions conventions while adding AWF-specific contexts.
## Environment Variable Scopes
[Section titled “Environment Variable Scopes”](#environment-variable-scopes)
GitHub Agentic Workflows supports environment variables in 13 distinct contexts:
| Scope | Syntax | Context | Typical Use |
| ----------------------- | ------------------------------ | ------------------------------------ | ------------------------- |
| **Workflow-level** | `env:` | All jobs | Shared configuration |
| **Job-level** | `jobs..env` | All steps in job | Job-specific config |
| **Step-level** | `steps[*].env` | Single step | Step-specific config |
| **Engine** | `engine.env` | AI engine | Engine secrets, timeouts |
| **Container** | `container.env` | Container runtime | Container settings |
| **Services** | `services..env` | Service containers | Database credentials |
| **Sandbox Agent** | `sandbox.agent.env` | Sandbox runtime | Sandbox configuration |
| **Sandbox MCP** | `sandbox.mcp.env` | Model Context Protocol (MCP) gateway | MCP debugging |
| **MCP Tools** | `tools..env` | MCP server process | MCP server secrets |
| **MCP Scripts** | `mcp-scripts..env` | MCP script execution | Tool-specific tokens |
| **Safe Outputs Global** | `safe-outputs.env` | All safe-output jobs | Shared safe-output config |
| **Safe Outputs Job** | `safe-outputs.jobs..env` | Specific safe-output job | Job-specific config |
| **GitHub Actions Step** | `githubActionsStep.env` | Pre-defined steps | Step configuration |
### Example Configurations
[Section titled “Example Configurations”](#example-configurations)
**Workflow-level shared configuration:**
```yaml
---
env:
NODE_ENV: production
API_ENDPOINT: https://api.example.com
---
```
**Job-specific overrides:**
```yaml
---
jobs:
validation:
env:
VALIDATION_MODE: strict
steps:
- run: npm run build
env:
BUILD_ENV: production # Overrides job and workflow levels
---
```
**AWF-specific contexts:**
```yaml
---
# Engine configuration
engine:
id: copilot
env:
OPENAI_API_KEY: ${{ secrets.CUSTOM_KEY }}
# MCP server with secrets
tools:
database:
command: npx
args: ["-y", "mcp-server-postgres"]
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
# Safe outputs with custom PAT
safe-outputs:
create-issue:
env:
GITHUB_TOKEN: ${{ secrets.CUSTOM_PAT }}
---
```
## Agent Step Summary (`GITHUB_STEP_SUMMARY`)
[Section titled “Agent Step Summary (GITHUB\_STEP\_SUMMARY)”](#agent-step-summary-github_step_summary)
Agents can write markdown content to the `$GITHUB_STEP_SUMMARY` environment variable to publish a formatted summary visible in the GitHub Actions run view.
Inside the AWF sandbox, `$GITHUB_STEP_SUMMARY` is redirected to a file at `/tmp/gh-aw/agent-step-summary.md`. After agent execution completes, the framework automatically appends the contents of that file to the real GitHub step summary. Secret redaction runs before the content is published.
Note
The first 2000 characters of the summary are appended. If the content is longer, a `[truncated: ...]` notice is included. Write your most important content first.
Example: an agent writing a brief analysis result to the step summary:
```bash
echo "## Analysis complete" >> "$GITHUB_STEP_SUMMARY"
echo "Found 3 issues across 12 files." >> "$GITHUB_STEP_SUMMARY"
```
The output appears in the **Summary** tab of the GitHub Actions workflow run.
## System-Injected Runtime Variables
[Section titled “System-Injected Runtime Variables”](#system-injected-runtime-variables)
GitHub Agentic Workflows automatically injects the following environment variables into every agentic engine execution step (both the main agent run and the threat detection run). These variables are read-only from the agent’s perspective and are useful for writing workflows or agents that need to detect their execution context.
| Variable | Value | Description |
| --------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GITHUB_AW` | `"true"` | Present in every gh-aw engine execution step. Agents can check for this variable to confirm they are running inside a GitHub Agentic Workflow. |
| `GH_AW_PHASE` | `"agent"` or `"detection"` | Identifies which execution phase is active. `"agent"` for the main run; `"detection"` for the threat-detection safety check run that precedes the main run. |
| `GH_AW_VERSION` | e.g. `"0.40.1"` | The gh-aw compiler version that generated the workflow. Useful for conditional logic that depends on a minimum feature version. |
These variables appear alongside other `GH_AW_*` context variables in the compiled workflow:
```yaml
env:
GITHUB_AW: "true"
GH_AW_PHASE: agent # or "detection"
GH_AW_VERSION: "0.40.1"
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
```
Note
These variables are injected by the compiler and cannot be overridden by user-defined `env:` blocks in the workflow frontmatter.
## CLI Configuration Variables
[Section titled “CLI Configuration Variables”](#cli-configuration-variables)
These variables configure the `gh aw` CLI tool. Set them in your local shell environment or as repository/organization variables in GitHub Actions.
| Variable | Default | Description |
| -------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DEBUG` | disabled | npm-style namespace debug logging. `DEBUG=*` enables all output; `DEBUG=cli:*,workflow:*` selects specific namespaces. Exclusions are supported: `DEBUG=*,-workflow:test`. Also activated when `ACTIONS_RUNNER_DEBUG=true`. |
| `DEBUG_COLORS` | `1` (enabled) | Set to `0` to disable ANSI colors in debug output. Colors are automatically disabled when output is not a TTY. |
| `ACCESSIBLE` | empty | Any non-empty value enables accessibility mode, which disables spinners and animations. Also enabled when `TERM=dumb` or `NO_COLOR` is set. |
| `NO_COLOR` | empty | Any non-empty value disables colored output and enables accessibility mode. Follows the [no-color.org](https://no-color.org/) standard. |
| `GH_AW_ACTION_MODE` | auto-detected | Overrides how JavaScript is embedded in compiled workflows. Valid values: `dev`, `release`, `script`, `action`. When unset, the CLI auto-detects the appropriate mode. |
| `GH_AW_FEATURES` | empty | Comma-separated list of experimental feature flags to enable globally. Values in workflow `features:` frontmatter take precedence over this variable. |
| `GH_AW_MAX_CONCURRENT_DOWNLOADS` | `10` | Maximum number of parallel log and artifact downloads for `gh aw logs`. Valid range: `1`–`100`. |
| `GH_AW_MCP_SERVER` | unset | When set, disables the automatic update check. Set automatically when `gh aw` runs as an MCP server subprocess — no manual configuration needed. |
**Enabling debug logging:**
```bash
# All namespaces
DEBUG=* gh aw compile
# Specific namespaces
DEBUG=cli:*,workflow:* gh aw compile
# Without colors
DEBUG_COLORS=0 DEBUG=* gh aw compile
```
***
## Model Override Variables
[Section titled “Model Override Variables”](#model-override-variables)
These variables override the default AI model used for agent runs and threat detection. Set them as GitHub Actions repository or organization variables to apply org-wide defaults without modifying workflow frontmatter.
Note
The `engine.model:` field in workflow frontmatter takes precedence over these variables.
### Agent runs
[Section titled “Agent runs”](#agent-runs)
| Variable | Engine |
| --------------------------- | ---------------- |
| `GH_AW_MODEL_AGENT_COPILOT` | GitHub Copilot |
| `GH_AW_MODEL_AGENT_CLAUDE` | Anthropic Claude |
| `GH_AW_MODEL_AGENT_CODEX` | OpenAI Codex |
| `GH_AW_MODEL_AGENT_GEMINI` | Google Gemini |
| `GH_AW_MODEL_AGENT_CRUSH` | Crush |
| `GH_AW_MODEL_AGENT_CUSTOM` | Custom engine |
### Detection runs
[Section titled “Detection runs”](#detection-runs)
| Variable | Engine |
| ------------------------------- | ---------------- |
| `GH_AW_MODEL_DETECTION_COPILOT` | GitHub Copilot |
| `GH_AW_MODEL_DETECTION_CLAUDE` | Anthropic Claude |
| `GH_AW_MODEL_DETECTION_CODEX` | OpenAI Codex |
| `GH_AW_MODEL_DETECTION_GEMINI` | Google Gemini |
| `GH_AW_MODEL_DETECTION_CRUSH` | Crush |
Set a model override as an organization variable:
```bash
gh variable set GH_AW_MODEL_AGENT_COPILOT --org my-org --body "gpt-5"
```
See [Engines](/gh-aw/reference/engines/) for available engine identifiers and model configuration options.
***
## Guard Policy Fallback Variables
[Section titled “Guard Policy Fallback Variables”](#guard-policy-fallback-variables)
These variables provide fallback values for guard policy fields when the corresponding `tools.github.*` configuration is absent from workflow frontmatter. Set them as GitHub Actions organization or repository variables to enforce a consistent policy across all workflows.
Note
Explicit `tools.github.*` values in workflow frontmatter always take precedence over these variables.
| Variable | Frontmatter field | Format | Description |
| ------------------------------ | ------------------------------ | --------------------------------------- | ------------------------------------------------------------------------- |
| `GH_AW_GITHUB_BLOCKED_USERS` | `tools.github.blocked-users` | Comma- or newline-separated usernames | GitHub usernames blocked from triggering agent runs |
| `GH_AW_GITHUB_APPROVAL_LABELS` | `tools.github.approval-labels` | Comma- or newline-separated label names | Labels that promote content to “approved” integrity for guard checks |
| `GH_AW_GITHUB_TRUSTED_USERS` | `tools.github.trusted-users` | Comma- or newline-separated usernames | GitHub usernames elevated to “approved” integrity, bypassing guard checks |
Set an org-wide blocked user list:
```bash
gh variable set GH_AW_GITHUB_BLOCKED_USERS --org my-org --body "bot-account1,bot-account2"
```
See [Tools Reference](/gh-aw/reference/tools/) for complete guard policy documentation.
***
## Precedence Rules
[Section titled “Precedence Rules”](#precedence-rules)
Environment variables follow a **most-specific-wins** model, consistent with GitHub Actions. Variables at more specific scopes completely override variables with the same name at less specific scopes.
### General Precedence (Highest to Lowest)
[Section titled “General Precedence (Highest to Lowest)”](#general-precedence-highest-to-lowest)
1. **Step-level** (`steps[*].env`, `githubActionsStep.env`)
2. **Job-level** (`jobs..env`)
3. **Workflow-level** (`env:`)
### Safe Outputs Precedence
[Section titled “Safe Outputs Precedence”](#safe-outputs-precedence)
1. **Job-specific** (`safe-outputs.jobs..env`)
2. **Global** (`safe-outputs.env`)
3. **Workflow-level** (`env:`)
### Context-Specific Scopes
[Section titled “Context-Specific Scopes”](#context-specific-scopes)
These scopes are independent and operate in different contexts: `engine.env`, `container.env`, `services..env`, `sandbox.agent.env`, `sandbox.mcp.env`, `tools..env`, `mcp-scripts..env`.
### Override Example
[Section titled “Override Example”](#override-example)
```yaml
---
env:
API_KEY: default-key
DEBUG: "false"
jobs:
test:
env:
API_KEY: test-key # Overrides workflow-level
EXTRA: "value"
steps:
- run: |
# API_KEY = "test-key" (job-level override)
# DEBUG = "false" (workflow-level inherited)
# EXTRA = "value" (job-level)
---
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Frontmatter Reference](/gh-aw/reference/frontmatter/) - Complete frontmatter configuration
* [Safe Outputs](/gh-aw/reference/safe-outputs/) - Safe output environment configuration
* [Sandbox](/gh-aw/reference/sandbox/) - Sandbox environment variables
* [Tools](/gh-aw/reference/tools/) - MCP tool configuration and guard policies
* [MCP Scripts](/gh-aw/reference/mcp-scripts/) - MCP script tool configuration
* [Engines](/gh-aw/reference/engines/) - AI engine configuration and model selection
* [GitHub Actions Environment Variables](https://docs.github.com/en/actions/learn-github-actions/variables) - GitHub Actions documentation
# Ephemerals
> Features for automatically expiring workflow resources and reducing noise in your repositories
GitHub Agentic Workflows includes several “ephemeral” features that automatically expire resources and reduce noise in your repositories. They control costs by stopping scheduled workflows at deadlines, auto-close issues and discussions, hide older comments, and isolate automation via the [side repository pattern](/gh-aw/patterns/multi-repo-ops/#the-side-repository-pattern-isolated-automation).
## Expiration Features
[Section titled “Expiration Features”](#expiration-features)
### Workflow Stop-After
[Section titled “Workflow Stop-After”](#workflow-stop-after)
Automatically disable workflow triggering after a deadline to control costs and prevent indefinite execution.
```yaml
on: weekly on monday
stop-after: "+25h" # 25 hours from compilation time
```
**Accepted formats**:
* **Absolute dates**: `YYYY-MM-DD`, `MM/DD/YYYY`, `DD/MM/YYYY`, `January 2 2006`, `1st June 2025`, ISO 8601
* **Relative deltas**: `+7d`, `+25h`, `+1d12h30m` (calculated from compilation time)
The minimum granularity is hours - minute-only units (e.g., `+30m`) are not allowed. Recompiling the workflow resets the stop time.
At the deadline, new runs are prevented while existing runs complete. The stop time persists through recompilation; use `gh aw compile --refresh-stop-time` to reset it. Common uses: trial periods, experimental features, orchestrated initiatives, and cost-controlled schedules.
See [Triggers Reference](/gh-aw/reference/triggers/#stop-after-configuration-stop-after) for complete documentation.
### Safe Output Expiration
[Section titled “Safe Output Expiration”](#safe-output-expiration)
Auto-close issues, discussions, and pull requests after a specified time period. This generates a maintenance workflow that runs automatically at appropriate intervals.
#### Issue Expiration
[Section titled “Issue Expiration”](#issue-expiration)
```yaml
safe-outputs:
create-issue:
expires: 7 # Auto-close after 7 days
labels: [automation, agentic]
```
#### Discussion Expiration
[Section titled “Discussion Expiration”](#discussion-expiration)
```yaml
safe-outputs:
create-discussion:
expires: 3 # Auto-close after 3 days as "OUTDATED"
category: "general"
```
#### Pull Request Expiration
[Section titled “Pull Request Expiration”](#pull-request-expiration)
```yaml
safe-outputs:
create-pull-request:
expires: 14 # Auto-close after 14 days (same-repo only)
draft: true
```
**Supported formats**:
* **Integer**: Number of days (e.g., `7` = 7 days)
* **Relative time**: `2h`, `7d`, `2w`, `1m`, `1y`
Hours less than 24 are treated as 1 day minimum for expiration calculation.
**Maintenance workflow frequency**: The generated `agentics-maintenance.yml` workflow runs at the minimum required frequency based on the shortest expiration time across all workflows:
| Shortest Expiration | Maintenance Frequency |
| ------------------- | --------------------- |
| 1 day or less | Every 2 hours |
| 2 days | Every 6 hours |
| 3-4 days | Every 12 hours |
| 5+ days | Daily |
**Expiration markers**: The system adds a visible checkbox line with an XML comment to the body of created items:
```markdown
- [x] expires on Jan 14, 2026, 3:30 PM UTC
```
The maintenance workflow searches for items with this expiration format (checked checkbox with the XML comment) and automatically closes them with appropriate comments and resolution reasons. Users can uncheck the checkbox to prevent automatic expiration.
See [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) for complete documentation.
### Cache-Memory Cleanup
[Section titled “Cache-Memory Cleanup”](#cache-memory-cleanup)
The maintenance workflow automatically cleans up outdated [cache-memory](/gh-aw/reference/cache-memory/) entries on every scheduled run. Cache keys follow the pattern `memory-{workflow}-{run-id}`, and the cleanup job groups caches by workflow prefix, keeps the latest run ID per group, and deletes older entries. This prevents cache storage from growing unboundedly as workflows run repeatedly.
The cleanup includes rate-limit awareness — it pauses early if the GitHub API rate limit is running low — and produces a job summary table showing how many caches were found, kept, and deleted.
You can also trigger cleanup manually using the `clean_cache_memories` operation (see [Manual maintenance operations](#manual-maintenance-operations) below).
### Manual Maintenance Operations
[Section titled “Manual Maintenance Operations”](#manual-maintenance-operations)
The generated `agentics-maintenance.yml` workflow supports manual bulk operations via `workflow_dispatch`. Admin or maintainer users can trigger operations from the GitHub Actions UI or the CLI. All operations are restricted to admin and maintainer roles and are not available on forks.
Available operations:
| Operation | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `disable` | Disable all agentic workflows in the repository |
| `enable` | Re-enable all agentic workflows in the repository |
| `update` | Recompile workflows and create a PR if files changed |
| `upgrade` | Upgrade agentic workflows to the latest version and create a PR if files changed |
| `safe_outputs` | Replay safe outputs from a specific workflow run (requires a run URL or run ID) |
| `create_labels` | Create any repository labels referenced in safe-outputs that do not yet exist |
| `clean_cache_memories` | Clean up outdated cache-memory entries (same as the automated scheduled cleanup) |
| `validate` | Run full workflow validation with all linters and file an issue if findings are detected |
| `activity_report` | Generate a repository activity report for the last 24 hours, week, and month, and create an issue with the results |
| `forecast` | Run a workflow token-usage forecast and create an issue with the JSON results |
**Details for select operations:**
* **`update` / `upgrade`**: Runs `gh aw update` or `gh aw upgrade`, stages changed files, and opens a pull request for review. After merging, recompile lock files with `gh aw compile`. See [Upgrading Agentic Workflows](/gh-aw/guides/upgrading/) for the manual upgrade process.
* **`safe_outputs`**: Replays safe output processing from a previous workflow run. Provide a run URL or numeric run ID in the `run_url` input field. Useful when safe outputs were not applied correctly on the original run.
* **`create_labels`**: Runs `gh aw compile --json --no-emit`, collects all unique label names across workflows, and creates missing ones with deterministic pastel colors. Requires `issues: write` permission.
* **`validate`**: Runs `gh aw compile --validate --no-emit --zizmor --actionlint --poutine --verbose`. If errors or warnings are found, creates or updates a GitHub issue titled `[aw] workflow validation findings` with the full output.
* **`activity_report`**: Runs `gh aw logs --format markdown` for the last 24 hours, 7 days, and 30 days (up to 1000 runs each), then creates an issue titled `[aw] agentic status report` with all three time-range sections as collapsible `` blocks. Downloaded logs are cached under `./.cache/gh-aw/activity-report-logs`. The job has a 2-hour timeout and skips the 30-day query when the GitHub API is rate-limited.
* **`forecast`**: Runs `gh aw forecast --repo --json`, writes the output to `./.cache/gh-aw/forecast/report.json`, then creates an issue titled `[aw] workflow forecast report` with the JSON payload embedded in a fenced block.
### Maintenance Configuration
[Section titled “Maintenance Configuration”](#maintenance-configuration)
You can customize the maintenance workflow runner or disable maintenance entirely using the `aw.json` configuration file at `.github/workflows/aw.json`.
**Customize the runner:**
```json
{
"maintenance": {
"runs_on": "ubuntu-latest",
"action_failure_issue_expires": 72
}
}
```
The `runs_on` field accepts a single string or an array of strings for multi-label runners (e.g., `["self-hosted", "linux"]`). The default runner is `ubuntu-slim`.
The `action_failure_issue_expires` field controls expiration (in hours) for failure issues opened by the conclusion job (including grouped parent issues when `group-reports: true`). The default is `168` (7 days).
See [Self-Hosted Runners](/gh-aw/guides/self-hosted-runners/#configuring-the-maintenance-workflow-runner) for more details.
**Disable maintenance entirely:**
```json
{
"maintenance": false
}
```
When maintenance is disabled, the compiler deletes any existing `agentics-maintenance.yml` file and emits a warning for workflows that use the `expires` field, since expiration depends on the maintenance workflow to run.
Caution
Disabling maintenance prevents automatic expiration of issues, discussions, and pull requests. Any `expires` configuration in your workflows will become a no-op until maintenance is re-enabled.
### Close Older Issues
[Section titled “Close Older Issues”](#close-older-issues)
Automatically close older issues with the same workflow-id marker when creating new ones. This keeps your issues focused on the latest information.
```yaml
safe-outputs:
create-issue:
close-older-issues: true # Close previous reports
```
When a new issue is created, up to 10 older issues with the same workflow-id marker are closed as “not planned” with a comment linking to the new issue. Requires `GH_AW_WORKFLOW_ID` to be set and appropriate repository permissions. Ideal for weekly reports and recurring analyses where only the latest result matters.
## Noise Reduction Features
[Section titled “Noise Reduction Features”](#noise-reduction-features)
### Hide Older Comments
[Section titled “Hide Older Comments”](#hide-older-comments)
Minimize previous comments from the same workflow before posting new ones. Useful for status update workflows where only the latest information matters.
```yaml
safe-outputs:
add-comment:
hide-older-comments: true
allowed-reasons: [outdated] # Optional: restrict hiding reasons
```
Before posting, the system finds and minimizes previous comments from the same workflow (identified by `GITHUB_WORKFLOW`). Comments are hidden, not deleted. Use `allowed-reasons` to restrict which minimization reason is applied: `spam`, `abuse`, `off_topic`, `outdated` (default), `resolved`, or `low_quality`.
See [Safe Outputs Reference](/gh-aw/reference/safe-outputs/#hide-older-comments) for complete documentation.
### Side Repository Pattern
[Section titled “Side Repository Pattern”](#side-repository-pattern)
Run agentic workflows from a separate “side” repository that targets your main codebase. This isolates AI-generated issues, comments, and workflow runs from your main repository, keeping automation infrastructure separate from production code.
See [MultiRepoOps — Side Repository](/gh-aw/patterns/multi-repo-ops/#the-side-repository-pattern-isolated-automation) for complete setup and usage documentation.
### Text Sanitization
[Section titled “Text Sanitization”](#text-sanitization)
Control which GitHub repository references (`#123`, `owner/repo#456`) are allowed in workflow output text. When configured, references to unlisted repositories are escaped with backticks to prevent GitHub from creating timeline items.
```yaml
safe-outputs:
allowed-github-references: [] # Escape all references
create-issue:
target-repo: "my-org/main-repo"
```
See [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) for complete documentation.
### Use Discussions Instead of Issues
[Section titled “Use Discussions Instead of Issues”](#use-discussions-instead-of-issues)
For ephemeral content, use discussions instead of issues. They have lower search weight and don’t clutter project boards, making them ideal for recurring reports and status updates.
```yaml
safe-outputs:
create-discussion:
category: "Status Updates"
expires: 14 # Close after 2 weeks
close-older-discussions: true # Replace previous reports
```
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Triggers Reference](/gh-aw/reference/triggers/) - Complete trigger configuration including `stop-after`
* [Safe Outputs Reference](/gh-aw/reference/safe-outputs/) - All safe output types and expiration options
* [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) — Complete setup for side repository operations
# Frequently Asked Questions
> Answers to common questions about GitHub Agentic Workflows, including security, costs, privacy, and configuration.
Note
GitHub Agentic Workflows is in early development and may change significantly. Using automated agentic workflows requires careful attention to security considerations and careful human supervision, and even then things can still go wrong. Use it with caution, and at your own risk.
## Determinism
[Section titled “Determinism”](#determinism)
### I like deterministic CI/CD. Isn’t this non-deterministic?
[Section titled “I like deterministic CI/CD. Isn’t this non-deterministic?”](#i-like-deterministic-cicd-isnt-this-non-deterministic)
Agentic workflows are **100% additive** to your existing CI/CD - they don’t replace your deterministic build, test, or release pipelines. Think of it as **Continuous AI** alongside Continuous Integration and Continuous Deployment: a new automation layer running in GitHub Actions where security, permissions, and repository context already exist.
Your deterministic pipelines stay unchanged. Agentic workflows handle tasks where exact reproducibility doesn’t matter - triaging issues, drafting documentation, researching dependencies, or proposing code improvements for human review.
## Capabilities
[Section titled “Capabilities”](#capabilities)
### What’s the difference between agentic workflows and regular GitHub Actions workflows?
[Section titled “What’s the difference between agentic workflows and regular GitHub Actions workflows?”](#whats-the-difference-between-agentic-workflows-and-regular-github-actions-workflows)
Agentic workflows use AI to interpret natural language instructions in markdown instead of complex YAML. The AI engine can call pre-approved tools to perform tasks while running with read-only default permissions, safe outputs, and sandboxed execution.
### What’s the difference between agentic workflows and just running a coding agent in GitHub Actions?
[Section titled “What’s the difference between agentic workflows and just running a coding agent in GitHub Actions?”](#whats-the-difference-between-agentic-workflows-and-just-running-a-coding-agent-in-github-actions)
While you could install and run a coding agent directly in a standard GitHub Actions workflow, agentic workflows provide a structured framework with simpler markdown format, built-in security controls, pre-defined tools for GitHub operations, and easy switching between AI engines.
### Can agentic workflows write code and create pull requests?
[Section titled “Can agentic workflows write code and create pull requests?”](#can-agentic-workflows-write-code-and-create-pull-requests)
Yes! Agentic workflows can create pull requests using the `create-pull-request` safe output. This allows the workflow to propose code changes, documentation updates, or other modifications as pull requests for human review and merging.
Some organizations may completely disable the creation of pull requests from GitHub Actions. In such cases, workflows can still generate diffs or suggestions in issues or comments for manual application.
### Can agentic workflows do more than code?
[Section titled “Can agentic workflows do more than code?”](#can-agentic-workflows-do-more-than-code)
Yes! Agentic workflows can analyze repositories, generate reports, triage issues, research information, create documentation, and coordinate work. The AI interprets natural language instructions and uses available [tools](/gh-aw/reference/tools/) to accomplish tasks.
### Can agentic workflows mix regular GitHub Actions steps with AI agentic steps?
[Section titled “Can agentic workflows mix regular GitHub Actions steps with AI agentic steps?”](#can-agentic-workflows-mix-regular-github-actions-steps-with-ai-agentic-steps)
Yes! Agentic workflows can include both AI agentic steps and traditional GitHub Actions steps. You can add custom steps before the agentic job using the [`steps:` configuration](/gh-aw/reference/steps-jobs/#custom-steps-steps). Additionally, [custom safe output jobs](/gh-aw/reference/safe-outputs/#custom-safe-output-jobs-jobs) can be used as consumers of agentic outputs. [MCP Scripts](/gh-aw/reference/mcp-scripts/) allow you to pass data between traditional steps and the AI agent with added checking.
### Can agentic workflows read other repositories?
[Section titled “Can agentic workflows read other repositories?”](#can-agentic-workflows-read-other-repositories)
Not by default, but yes with proper configuration. Cross-repository access requires:
1. A **Personal Access Token (PAT)** with access to target repositories
2. Configuring the token in your workflow
See [MultiRepoOps](/gh-aw/patterns/multi-repo-ops/) for coordinating across repositories, including running workflows from a separate side repository.
### Can I use agentic workflows in private repositories?
[Section titled “Can I use agentic workflows in private repositories?”](#can-i-use-agentic-workflows-in-private-repositories)
Yes, and in many cases we recommend it. Private repositories are ideal for proprietary code, creating a “sidecar” repository with limited access, testing workflows, and organization-internal automation. See [MultiRepoOps — Side Repository](/gh-aw/patterns/multi-repo-ops/#the-side-repository-pattern-isolated-automation) for patterns using private repositories.
### Can I edit workflows directly on GitHub.com without recompiling?
[Section titled “Can I edit workflows directly on GitHub.com without recompiling?”](#can-i-edit-workflows-directly-on-githubcom-without-recompiling)
Yes! The **markdown body** (AI instructions) is loaded at runtime and can be edited directly on GitHub.com or in any editor. Changes take effect on the next workflow run without recompilation.
However, **frontmatter configuration** (tools, permissions, triggers, network rules) is embedded in the compiled workflow and requires recompilation when changed. Run `gh aw compile my-workflow` after editing frontmatter.
See [Editing Workflows](/gh-aw/guides/editing-workflows/) for complete guidance on when recompilation is needed.
### Can workflows trigger other workflows?
[Section titled “Can workflows trigger other workflows?”](#can-workflows-trigger-other-workflows)
Yes, using the `dispatch-workflow` safe output:
```yaml
safe-outputs:
dispatch-workflow:
max: 1
```
This allows your workflow to trigger up to 1 other workflows with custom inputs. See [Safe Outputs](/gh-aw/reference/safe-outputs/#workflow-dispatch-dispatch-workflow) for details.
### Can I trigger an agentic workflow from an external system like Jira?
[Section titled “Can I trigger an agentic workflow from an external system like Jira?”](#can-i-trigger-an-agentic-workflow-from-an-external-system-like-jira)
Yes. GitHub Actions cannot listen to external events directly, but any external system that can make an HTTP request can trigger a workflow via the [`repository_dispatch`](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#repository_dispatch) API.
The two-step setup:
**1. Add a `repository_dispatch` trigger to your workflow frontmatter:**
```yaml
on:
repository_dispatch:
types: [jira-issue-created]
```
Access the caller’s payload in your workflow markdown via `${{ github.event.client_payload.* }}`.
**2. Send a `POST` request to the GitHub dispatch API from the external system:**
```http
POST https://api.github.com/repos///dispatches
Authorization: Bearer
Content-Type: application/json
{
"event_type": "jira-issue-created",
"client_payload": { "issue_key": "PROJ-123", "summary": "Fix the thing" }
}
```
For Jira specifically, use **Project → Automation → Issue created → Send web request** pointing at the dispatch API. Any system with webhook or outbound HTTP support—including Jira, PagerDuty, Slack, or a custom API—can trigger workflows this way.
The `repository_dispatch` token must have `repo` scope (classic PAT) or `contents: write` permission. Store it in the external system’s secret or credential store (e.g., Jira Automation secret text, a CI/CD vault), scoped to the single target repository.
See [Repository Dispatch Trigger](/gh-aw/reference/triggers/#repository-dispatch-trigger-repository_dispatch) for the full trigger reference. To control which branch the agent commits to based on content in the Jira issue, see [Can the agent use an existing branch specified at runtime?](#can-the-agent-use-an-existing-branch-specified-at-runtime-eg-from-a-jira-issue)
### Can I use MCP servers with agentic workflows?
[Section titled “Can I use MCP servers with agentic workflows?”](#can-i-use-mcp-servers-with-agentic-workflows)
Yes! [Model Context Protocol (MCP)](/gh-aw/reference/glossary/#mcp-model-context-protocol) servers extend workflow capabilities with custom tools and integrations. Configure them in your frontmatter:
```yaml
tools:
mcp-servers:
my-server:
image: "ghcr.io/org/my-mcp-server:latest"
network:
allowed: ["api.example.com"]
```
See [Using MCPs](/gh-aw/guides/mcps/) for configuration guides.
### If my agent can use a skill, can agentic workflows use it too?
[Section titled “If my agent can use a skill, can agentic workflows use it too?”](#if-my-agent-can-use-a-skill-can-agentic-workflows-use-it-too)
Usually, yes. If your agent can do it, agentic workflows can usually do it too, and that applies to skills as well.
For reusable packaging, start with [imports](/gh-aw/reference/imports/) and [APM (Agent Package Manager)](https://microsoft.github.io/apm/). Imports are a good fit for sharing workflow-level configuration and prompts, while APM is the recommended way to package and distribute skills and other agent primitives. See [APM Dependencies](/gh-aw/reference/dependencies/) for the gh-aw integration.
### The `plugins:` or `dependencies:` field I was using is gone - how do I install agent plugins now?
[Section titled “The plugins: or dependencies: field I was using is gone - how do I install agent plugins now?”](#the-plugins-or-dependencies-field-i-was-using-is-gone---how-do-i-install-agent-plugins-now)
The `plugins:` and `dependencies:` frontmatter fields have been removed in favour of the import-based approach backed by [Microsoft APM (Agent Package Manager)](https://microsoft.github.io/apm/). APM provides cross-agent support for all agent primitives – skills, prompts, instructions, hooks, and plugins (including the Copilot `plugin.json` format and the Claude `plugin.json` format).
Use `imports: - uses: shared/apm.md` with the `packages:` parameter to install plugins:
```yaml
imports:
- uses: shared/apm.md
with:
packages:
- microsoft/apm-sample-package
- github/awesome-copilot/skills/review-and-refactor
```
See [APM Dependencies](/gh-aw/reference/dependencies/) for full configuration options.
### Can I use Claude plugins with APM?
[Section titled “Can I use Claude plugins with APM?”](#can-i-use-claude-plugins-with-apm)
Yes! APM supports Claude plugins in the `plugin.json` format. When `engine: claude` is set, APM automatically infers the engine target and unpacks only Claude-compatible primitives. See [APM Dependencies](/gh-aw/reference/dependencies/) for details.
### Can workflows be broken up into shareable components?
[Section titled “Can workflows be broken up into shareable components?”](#can-workflows-be-broken-up-into-shareable-components)
Workflows can import shared configurations and components:
```yaml
imports:
- shared/github-tools.md
- githubnext/agentics/shared/common-tools.md
```
This enables reusable tool configurations, network settings, and permissions across workflows. See [Imports](/gh-aw/reference/imports/) and [Packaging Imports](/gh-aw/guides/packaging-imports/) for details.
### Can I run workflows on a schedule?
[Section titled “Can I run workflows on a schedule?”](#can-i-run-workflows-on-a-schedule)
Yes, use fuzzy schedule expressions in the `on:` trigger (recommended):
```yaml
on: weekly on monday # Automatically scattered to avoid load spikes
```
Or use standard cron syntax for fixed times:
```yaml
on:
schedule:
- cron: "0 9 * * MON" # Every Monday at 9am UTC
```
See [Schedule Syntax](/gh-aw/reference/schedule-syntax/) for all supported formats.
### Can I run workflows conditionally?
[Section titled “Can I run workflows conditionally?”](#can-i-run-workflows-conditionally)
Yes, use the `if:` expression at the workflow level:
```yaml
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
```
See [Conditional Execution](/gh-aw/reference/frontmatter/#conditional-execution-if) in the Frontmatter Reference for details.
## Guardrails
[Section titled “Guardrails”](#guardrails)
### Agentic workflows run in GitHub Actions. Can they access my repository secrets?
[Section titled “Agentic workflows run in GitHub Actions. Can they access my repository secrets?”](#agentic-workflows-run-in-github-actions-can-they-access-my-repository-secrets)
Repository secrets are not available to the agentic step by default. The AI agent runs with read-only permissions and cannot directly access your repository secrets unless explicitly configured. You should review workflows carefully, follow [GitHub Actions security guidelines](https://docs.github.com/en/actions/reference/security/secure-use), use least-privilege permissions, and inspect the compiled `.lock.yml` file. See the [Security Architecture](/gh-aw/introduction/architecture/) for details.
Some MCP tools may be configured using secrets, but these are only accessible to the specific tool steps, not the AI agent itself. Minimize the use of tools equipped with highly privileged secrets.
### Agentic workflows run in GitHub Actions. Can they write to the repository?
[Section titled “Agentic workflows run in GitHub Actions. Can they write to the repository?”](#agentic-workflows-run-in-github-actions-can-they-write-to-the-repository)
By default, the agentic “coding agent” step of agentic workflows runs with read-only permissions. Write operations require explicit approval through [safe outputs](/gh-aw/reference/safe-outputs/) or explicit general `write` permissions (not recommended). This ensures that AI agents cannot make arbitrary changes to your repository.
If safe outputs are configured, the workflow has limited, highly specific write operations that are then sanitized and executed securely.
### What sanitization is done on AI outputs before applying changes?
[Section titled “What sanitization is done on AI outputs before applying changes?”](#what-sanitization-is-done-on-ai-outputs-before-applying-changes)
All safe outputs from the AI agent are sanitized before being applied to your repository. Sanitization includes secret redaction, URL domain filtering, XML escaping, size limits, control character stripping, GitHub reference escaping and HTTPS enforcement.
Additionally, safe outputs enforce permission separation - write operations happen in separate jobs with scoped permissions, never in the agentic job itself.
See [Safe Outputs - Text Sanitization](/gh-aw/reference/safe-outputs/#text-sanitization-allowed-domains-allowed-github-references) for configuration options.
### How do I prevent workflow output from creating backlinks in referenced issues?
[Section titled “How do I prevent workflow output from creating backlinks in referenced issues?”](#how-do-i-prevent-workflow-output-from-creating-backlinks-in-referenced-issues)
When AI-generated content mentions issue or PR numbers (such as `#123` or `owner/repo#456`), GitHub automatically creates “mentioned in…” timeline entries in those issues. Set `allowed-github-references: []` to escape all such references before the content is posted:
```yaml
safe-outputs:
allowed-github-references: [] # Escape all GitHub references
create-issue:
```
With an empty list, every `#N` and `owner/repo#N` reference in the output is wrapped in backticks, which prevents GitHub from resolving them as cross-references and avoids cluttering other repositories’ timelines. This is especially useful for workflows that write content about issues in a main repository from a separate sidecar repository.
To allow references only from the current repository while still escaping all others:
```yaml
safe-outputs:
allowed-github-references: [repo]
add-comment:
```
When `allowed-github-references` is not configured at all, all references are left unescaped (default behavior).
See [Text Sanitization](/gh-aw/reference/safe-outputs/#text-sanitization-allowed-domains-allowed-github-references) for full configuration options.
### How are agent actions constrained — commenting, opening PRs, modifying files, and calling external tools?
[Section titled “How are agent actions constrained — commenting, opening PRs, modifying files, and calling external tools?”](#how-are-agent-actions-constrained--commenting-opening-prs-modifying-files-and-calling-external-tools)
gh-aw uses defense-in-depth rather than a single control. Four layers work together:
**1. Read-only agent by default.** The AI agent step has read-only GitHub permissions. It cannot comment, open PRs, or push files unless you explicitly configure [safe outputs](/gh-aw/reference/safe-outputs/).
**2. Safe outputs for all writes.** Commenting, creating PRs, and modifying files all go through safe outputs — separate GitHub Actions jobs with scoped write tokens. The agent produces a structured artifact; a downstream job applies the changes after sanitization (secret redaction, URL filtering, size limits). You declare which operations are permitted:
```yaml
safe-outputs:
add-comment:
```
**3. Threat detection before writes.** [Agentic threat detection](/gh-aw/reference/threat-detection/) runs automatically between the agent job and the safe output jobs. It scans the agent’s output for prompt injection attempts, secret leaks, and malicious code patches, blocking the write jobs if a threat is detected.
**4. Network allowlist for external calls.** The [Agent Workflow Firewall](/gh-aw/reference/sandbox/) blocks all outbound network access by default. You must explicitly allow each domain an agent may reach:
```yaml
network:
allowed:
- defaults
```
For sensitive operations, you can layer on a [GitHub Environment protection rule](/gh-aw/reference/faq/#can-i-require-external-human-approval-before-safe-outputs-are-applied) so a designated reviewer must approve before any write jobs run.
### Tell me more about guardrails
[Section titled “Tell me more about guardrails”](#tell-me-more-about-guardrails)
Guardrails are foundational to the design. Agentic workflows implement defense-in-depth through compilation-time validation (schema checks, expression safety, action SHA pinning), runtime isolation (sandboxed containers with network controls), permission separation (read-only defaults with [safe outputs](/gh-aw/reference/safe-outputs/) for writes), tool allowlisting, and output sanitization. See the [Security Architecture](/gh-aw/introduction/architecture/).
### Can I require external human approval before safe outputs are applied?
[Section titled “Can I require external human approval before safe outputs are applied?”](#can-i-require-external-human-approval-before-safe-outputs-are-applied)
Yes. The distinction here is between *guardrail validation* (does the agent output look acceptable?) and *external admission* (is this execution intent authorized to proceed?). gh-aw addresses both.
The safe outputs architecture already enforces permission separation: the agent job runs read-only and never holds write credentials; it only produces a structured artifact. Separate jobs, with scoped write tokens, apply the changes. This boundary is real — a compromised agent cannot directly write to GitHub.
For a fail-closed **external admission gate** before sensitive operations like deployments or credential use, apply **[GitHub Environment protection rules](https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment#required-reviewers)** to a [custom safe output job](/gh-aw/reference/custom-safe-outputs/). The job pauses until a designated reviewer outside the workflow system explicitly approves. No approval means no execution.
```yaml
jobs:
approval-gate:
runs-on: ubuntu-latest
needs: detection # waits for automated threat scanning to complete
environment: production-deploy # configure required reviewers in Settings → Environments
steps:
- name: Approved
run: echo "Execution approved by reviewer"
safe-outputs:
needs: [approval-gate] # built-in safe_outputs job waits for manual approval
```
This approval is enforced by GitHub’s infrastructure, not by workflow logic the agent could influence. Threat detection still runs before the gate, so the reviewer sees output that has already passed automated scanning.
Note that the *policy* — which environments require approval, what safe outputs are configured — is defined by whoever controls the repository. The admission decision for each run can be external; the admission policy itself is internal to repository owners.
**Fully off-platform admission control**
If your threat model requires an authority completely outside GitHub’s control plane — such as an external policy engine, a PAM/PIM system, or a compliance approval workflow — call that system from your gate job before it proceeds:
```yaml
jobs:
external-admission:
runs-on: ubuntu-latest
needs: [agent, detection] # waits for agent output and threat scanning to complete
environment: production-deploy # optional: also adds GitHub-native reviewer gate
steps:
- name: Request admission from external authority
run: |
curl --fail -X POST https://YOUR_POLICY_ENGINE/v1/admit \
-H "Authorization: Bearer $POLICY_TOKEN" \
-d '{"workflow_run": "${{ github.run_id }}"}'
env:
POLICY_TOKEN: ${{ secrets.POLICY_TOKEN }}
safe-outputs:
needs: [external-admission] # write jobs don't run until external admission is granted
```
If the external call fails or is denied, the safe output jobs never run. This places the final admission decision in a system entirely independent of GitHub.
### How is my code and data processed?
[Section titled “How is my code and data processed?”](#how-is-my-code-and-data-processed)
By default, your workflow is run on GitHub Actions, like any other GitHub Actions workflow, and as one if its jobs it invokes your nominated [AI Engine (coding agent)](/gh-aw/reference/engines/), run in a container. This engine may in turn make tool calls and MCP calls. When using the default **GitHub Copilot CLI**, the workflow is processed by the `copilot` CLI tool which uses GitHub Copilot’s services and related AI models. The specifics depend on your engine choice:
* **GitHub Copilot CLI**: See [GitHub Copilot documentation](https://docs.github.com/en/copilot) for details.
* **Claude/Codex**: Uses respective providers’ APIs with their data handling policies.
See the [Security Architecture](/gh-aw/introduction/architecture/) for details on the execution and data flow.
### Does the underlying AI engine run in a sandbox?
[Section titled “Does the underlying AI engine run in a sandbox?”](#does-the-underlying-ai-engine-run-in-a-sandbox)
Yes, the [AI engine](/gh-aw/reference/engines/) runs in a containerized sandbox with network egress control via the [Agent Workflow Firewall](/gh-aw/reference/sandbox/), container isolation, GitHub Actions resource constraints, and limited filesystem access to workspace and temporary directories. The sandbox container runs inside a GitHub Actions VM for additional isolation. See [Sandbox Configuration](/gh-aw/reference/sandbox/).
### Can an agentic workflow use outbound network requests?
[Section titled “Can an agentic workflow use outbound network requests?”](#can-an-agentic-workflow-use-outbound-network-requests)
Yes, but network access is restricted by the [Agent Workflow Firewall](/gh-aw/reference/sandbox/). You must explicitly declare which domains the workflow can access:
```yaml
network:
allowed:
- defaults # Basic infrastructure
- python # Python/PyPI ecosystem
- "api.example.com" # Custom domain
```
See [Network Permissions](/gh-aw/reference/network/) for complete configuration options.
### How does integrity filtering protect my workflow?
[Section titled “How does integrity filtering protect my workflow?”](#how-does-integrity-filtering-protect-my-workflow)
[Integrity filtering](/gh-aw/reference/integrity/) controls which GitHub content the agent can see, filtering by **author trust** and **merge status**. The MCP gateway silently removes content below the configured `min-integrity` threshold before the AI engine sees it.
For **public repositories**, `min-integrity: approved` is automatically applied at runtime — restricting content to owners, members, and collaborators — even without additional authentication.
For triage or spam-detection workflows that need to process content from all users, set `min-integrity: none` explicitly:
```yaml
tools:
github:
min-integrity: none
```
See [Integrity Filtering](/gh-aw/reference/integrity/) for available levels, user blocking, and approval labels.
## Configuration & Setup
[Section titled “Configuration & Setup”](#configuration--setup)
### Why do slash-command workflows show many “started then skipped” runs on comments?
[Section titled “Why do slash-command workflows show many “started then skipped” runs on comments?”](#why-do-slash-command-workflows-show-many-started-then-skipped-runs-on-comments)
This is expected behavior. A `slash_command` is compiled into multiple GitHub event listeners (issue/PR bodies, issue comments, PR comments, and review comments, depending on `events:`). GitHub first dispatches the event, then the activation logic checks whether the comment starts with a matching command (for example `/refresh`). If it does not match, the run exits early and appears as a quick skipped/no-op run in Actions.
To reduce this noise, narrow the trigger scope with `events:` so the workflow only listens where you actually use commands, and use [LabelOps](/gh-aw/patterns/label-ops/) for command-style operations that should not activate on every comment. LabelOps (`label_command`) triggers only when a specific label is applied, which produces fewer incidental runs than broad comment listeners.
```yaml
on:
slash_command:
name: refresh
events: [pull_request_comment] # only listen to PR comments
label_command:
name: refresh
events: [pull_request] # optional low-noise label trigger
```
### What is a workflow lock file?
[Section titled “What is a workflow lock file?”](#what-is-a-workflow-lock-file)
A **workflow lock file** (`.lock.yml`) is the compiled GitHub Actions workflow generated from your `.md` file by `gh aw compile`. It contains SHA-pinned actions, resolved imports, configured permissions, and all guardrail hardening - inspect it to see exactly what will run, with no hidden configuration.
Both files should be committed to version control:
* **`.md` file**: Your source - edit the prompt body freely; changes take effect at the next run without recompiling
* **`.lock.yml` file**: The compiled workflow GitHub Actions actually runs; must be regenerated after any frontmatter changes (permissions, tools, triggers)
### What is the actions-lock.json file?
[Section titled “What is the actions-lock.json file?”](#what-is-the-actions-lockjson-file)
The `.github/aw/actions-lock.json` file is a cache of resolved `action@version` → ref mappings. During compilation, the compiler **tries** to pin each action reference to an immutable commit SHA for security. Resolving a version tag to a SHA requires querying the GitHub API (scanning releases), which can fail when the available token has limited permissions — for example, when compiling via GitHub Copilot Coding Agent (CCA) where the token may not have access to external repositories. In those cases, the compiler may fall back to leaving a stable version tag ref (such as `@v0`) instead of a SHA.
The cache avoids this problem: if a ref (typically a SHA) was previously resolved (using a user PAT or a GitHub Actions token with broader access), the result is stored in `actions-lock.json` and reused on subsequent compilations, regardless of the current token’s capabilities. Without this cache, compilation is unstable — it succeeds with a permissive token but fails when token access is restricted.
Commit `actions-lock.json` to version control so that all contributors and automated tools (including CCA) use consistent action refs (SHAs or version tags) without needing to re-resolve them. Refresh the cache periodically with `gh aw update-actions`, or delete it and recompile to force a full re-resolution when you have an appropriate token. See [Action Pinning](/gh-aw/reference/compilation-process/#action-pinning) for details.
### What is `github/gh-aw-actions`?
[Section titled “What is github/gh-aw-actions?”](#what-is-githubgh-aw-actions)
`github/gh-aw-actions` is the GitHub Actions repository containing all reusable actions that power compiled agentic workflows. Compiled `.lock.yml` files reference these actions as `github/gh-aw-actions/setup@` (where `` is usually a commit SHA, but may be a stable version tag such as `v0`). These references are managed entirely by `gh aw compile` — never edit them manually. See [The gh-aw-actions Repository](/gh-aw/reference/compilation-process/#the-gh-aw-actions-repository) for details.
### Why is Dependabot opening PRs to update `github/gh-aw-actions`?
[Section titled “Why is Dependabot opening PRs to update github/gh-aw-actions?”](#why-is-dependabot-opening-prs-to-update-githubgh-aw-actions)
Dependabot scans `.lock.yml` files for action references and treats `github/gh-aw-actions` pins as regular dependencies to update. **Do not merge these PRs.** Action pins in compiled workflows should only be updated by running `gh aw compile` or `gh aw update-actions`.
Suppress these PRs by adding an `ignore` entry in `.github/dependabot.yml`:
```yaml
updates:
- package-ecosystem: github-actions
directory: "/.github/workflows"
ignore:
- dependency-name: "github/gh-aw-actions/**" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
```
See [Dependabot and gh-aw-actions](/gh-aw/reference/compilation-process/#dependabot-and-gh-aw-actions) for more details.
### How does `gh aw upgrade` resolve action versions when no GitHub Releases exist?
[Section titled “How does gh aw upgrade resolve action versions when no GitHub Releases exist?”](#how-does-gh-aw-upgrade-resolve-action-versions-when-no-github-releases-exist)
`gh aw upgrade` (and `gh aw update-actions`) resolves the latest version of each referenced action using a two-step process:
1. **GitHub Releases API** — queries `/repos/{owner}/{repo}/releases` via the `gh` CLI. If releases are found, the highest compatible semantic version is selected.
2. **Git tag fallback** — if the Releases API returns an empty list (which happens when a repository publishes tags without creating GitHub Releases), the command automatically falls back to scanning tags via `git ls-remote`. This fallback is **safe to ignore** — tags are a valid source for version pinning.
Only if *both* sources return no results does the upgrade produce a warning that cannot be resolved automatically.
> **Note:** `github/gh-aw-actions` intentionally publishes only tags (not GitHub Releases). The `gh aw upgrade` warning `github/gh-aw-actions/setup: no releases found` that appeared in earlier versions was caused by this two-step logic not falling back to tags. It has been fixed — the tag fallback now runs automatically.
### Why do I need a token or key?
[Section titled “Why do I need a token or key?”](#why-do-i-need-a-token-or-key)
When using **GitHub Copilot CLI**, a Personal Access Token (PAT) with “Copilot Requests” permission authenticates and associates automation work with your GitHub account. This ensures usage tracking against your subscription, appropriate AI permissions, and auditable actions. In the future, this may support organization-level association. See [Authentication](/gh-aw/reference/auth/).
### Can I use `CLAUDE_CODE_OAUTH_TOKEN` with the Claude engine?
[Section titled “Can I use CLAUDE\_CODE\_OAUTH\_TOKEN with the Claude engine?”](#can-i-use-claude_code_oauth_token-with-the-claude-engine)
No. `CLAUDE_CODE_OAUTH_TOKEN` is not supported by GitHub Agentic Workflows. The only supported authentication method for the Claude engine is [`ANTHROPIC_API_KEY`](/gh-aw/reference/auth/#anthropic_api_key), which must be configured as a GitHub Actions secret. Provider-based OAuth authentication for Claude (such as billing through a Claude Teams subscription) is not supported. See [Authentication](/gh-aw/reference/auth/) and [AI Engines](/gh-aw/reference/engines/#available-coding-agents) for setup instructions.
### What hidden runtime dependencies does this have?
[Section titled “What hidden runtime dependencies does this have?”](#what-hidden-runtime-dependencies-does-this-have)
The executing agentic workflow uses your nominated coding agent (defaulting to GitHub Copilot CLI), a GitHub Actions VM with NodeJS, pinned Actions from [github/gh-aw](https://github.com/github/gh-aw) releases, and an Agent Workflow Firewall container for network control (optional but default). The exact YAML workflow can be inspected in the compiled `.lock.yml` file - there’s no hidden configuration.
### Why are macOS runners not supported?
[Section titled “Why are macOS runners not supported?”](#why-are-macos-runners-not-supported)
macOS runners (`macos-*`) are not currently supported in agentic workflows. Agentic workflows rely on containers to build a secure execution sandbox - specifically the [Agent Workflow Firewall](/gh-aw/reference/sandbox/) that provides network egress control and process isolation. GitHub-hosted macOS runners do not support container jobs, which is a hard requirement for this security architecture.
Use `ubuntu-latest` (the default) or another Linux-based runner instead. For tasks that genuinely require macOS-specific tooling, consider running those steps in a regular GitHub Actions job that coordinates with your agentic workflow.
### Can I use agentic workflows on GitHub Enterprise Server (GHES)?
[Section titled “Can I use agentic workflows on GitHub Enterprise Server (GHES)?”](#can-i-use-agentic-workflows-on-github-enterprise-server-ghes)
Yes, but you may need to enable GHES compatibility mode to avoid artifact errors. GHES instances that predate `@actions/artifact` v2.0.0 support cannot run `actions/upload-artifact@v4+` or `actions/download-artifact@v4+`. On those instances, compiled workflows fail with a `GHESNotSupportedError` because the compiler emits v4+ artifact actions by default.
Enable GHES compatibility mode so the compiler emits `upload-artifact@v3.2.2` and `download-artifact@v3.1.0` instead:
**`aw.json` (recommended — applies to all workflows in the repository):**
```json
{
"ghes": true
}
```
**`--ghes` flag (one-off compilation):**
```bash
gh aw compile --ghes my-workflow.md
```
Running `gh aw init` inside a GHES repository automatically detects the deployment and writes `ghes: true` to `.github/workflows/aw.json` for you. For `gh` CLI host setup and Copilot prerequisites on GHES, see [Enterprise Configuration](/gh-aw/reference/enterprise-configuration/).
### I’m not using a supported AI Engine (coding agent). What should I do?
[Section titled “I’m not using a supported AI Engine (coding agent). What should I do?”](#im-not-using-a-supported-ai-engine-coding-agent-what-should-i-do)
If you want to use a coding agent that isn’t currently supported (Copilot, Claude, Codex, Gemini, or Crush), you can contribute support to the [gh-aw repository](https://github.com/github/gh-aw), or open an issue describing your use case. See [AI Engines](/gh-aw/reference/engines/).
### Can I test workflows without affecting my repository?
[Section titled “Can I test workflows without affecting my repository?”](#can-i-test-workflows-without-affecting-my-repository)
Yes! Use [TrialOps](/gh-aw/experimental/trial-ops/) to test workflows in isolated trial repositories. This lets you validate behavior and iterate on prompts without creating real issues, PRs, or comments in your actual repository.
### Where can I find help with common issues?
[Section titled “Where can I find help with common issues?”](#where-can-i-find-help-with-common-issues)
See [Common Issues](/gh-aw/troubleshooting/common-issues/) for detailed troubleshooting guidance including workflow failures, debugging strategies, permission issues, and network problems.
### Why is my create-discussion workflow failing?
[Section titled “Why is my create-discussion workflow failing?”](#why-is-my-create-discussion-workflow-failing)
Ensure discussions are enabled (**Settings → Features → Discussions**) and the workflow has `discussions: write` permission. For category matching failures, verify spelling (case-insensitive) and use lowercase slugs (e.g., `general`, `announcements`) rather than display names.
Use `fallback-to-issue: true` (the default) to automatically create an issue if discussions aren’t available. See [Discussion Creation](/gh-aw/reference/safe-outputs/#discussion-creation-create-discussion) for details.
### How do I turn off discussions in add-comment?
[Section titled “How do I turn off discussions in add-comment?”](#how-do-i-turn-off-discussions-in-add-comment)
By default, `add-comment` requests `discussions: write` permission. If your GitHub App lacks the Discussions permission (which can cause 422 errors during token generation), set `discussions: false`:
```yaml
safe-outputs:
add-comment:
discussions: false # exclude discussions:write permission
```
This removes the `discussions: write` permission requirement. Discussion targeting itself remains automatic — `discussions: false` only controls the permission scope, not which events trigger the workflow.
Similarly, you can opt out of `issues: write` or `pull-requests: write` using `issues: false` or `pull-requests: false`.
### Why is my create-pull-request workflow failing with “GitHub Actions is not permitted to create or approve pull requests”?
[Section titled “Why is my create-pull-request workflow failing with “GitHub Actions is not permitted to create or approve pull requests”?”](#why-is-my-create-pull-request-workflow-failing-with-github-actions-is-not-permitted-to-create-or-approve-pull-requests)
Some organizations block PR creation by GitHub Actions via **Settings → Actions → General → Workflow permissions**. If you can’t enable it, use one of these alternatives:
**Automatic issue fallback (default)** — `fallback-as-issue: true` is the default; when PR creation is blocked an issue with the branch link is created instead. Requires `contents: write`, `pull-requests: write`, and `issues: write`.
**Assign to Copilot** — create an issue assigned to `copilot` for automated implementation:
```yaml
safe-outputs:
create-issue:
assignees: [copilot]
labels: [automation, enhancement]
```
**Disable fallback** — set `fallback-as-issue: false` to skip the issue fallback and only attempt PR creation. Requires only `contents: write` and `pull-requests: write`, but the workflow will fail if PR creation is blocked.
See [Pull Request Creation](/gh-aw/reference/safe-outputs/#pull-request-creation-create-pull-request) for details.
### Why don’t pull requests created by agentic workflows trigger my CI checks?
[Section titled “Why don’t pull requests created by agentic workflows trigger my CI checks?”](#why-dont-pull-requests-created-by-agentic-workflows-trigger-my-ci-checks)
This is expected GitHub Actions security behavior. Pull requests created using the default `GITHUB_TOKEN` or by the GitHub Actions bot user **do not trigger workflow runs** on `pull_request`, `pull_request_target`, or `push` events. This is a [GitHub Actions security feature](https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow) designed to prevent accidental recursive workflow execution.
The easy way to fix this problem is to set a secret `GH_AW_CI_TRIGGER_TOKEN` with a Personal Access Token (PAT) with ‘Contents: Read & Write’ permission to your repo.
See [Triggering CI](/gh-aw/reference/triggering-ci/) for more details on how to configure workflows to run CI checks on PRs created by agentic workflows.
### How do I suppress the “Generated by…” text in workflow outputs?
[Section titled “How do I suppress the “Generated by…” text in workflow outputs?”](#how-do-i-suppress-the-generated-by-text-in-workflow-outputs)
When workflows create or update issues, pull requests, discussions, or post comments, they append a `> Generated by [Workflow Name](run_url) for issue #N` attribution line. Use `footer: false` to hide this visible text while preserving the hidden XML markers used for search and tracking.
**Hide footers globally** (all safe output types):
```yaml
safe-outputs:
footer: false
add-comment:
create-issue:
title-prefix: "[ai] "
```
**Hide footers for specific output types only:**
```yaml
safe-outputs:
footer: false # hide for all by default
create-pull-request:
footer: true # override: show footer for PRs only
```
Even with `footer: false`, the hidden `` XML marker is still included in the content for searchability - you can search GitHub for `"gh-aw-workflow-id: my-workflow" in:body` to find all items created by a workflow.
See [Footer Control](/gh-aw/reference/footers/) for complete documentation including per-handler overrides and PR review footer options.
### My workflow fails with “Runtime import file not found” when used in a repository ruleset
[Section titled “My workflow fails with “Runtime import file not found” when used in a repository ruleset”](#my-workflow-fails-with-runtime-import-file-not-found-when-used-in-a-repository-ruleset)
This happens because workflows configured as required status checks run in a restricted context without access to the repository file system, so runtime imports cannot be resolved.
The fix is to enable `inlined-imports: true` in your workflow frontmatter so the compiler bundles all imported content into the compiled `.lock.yml` at compile time. See [Self-Contained Lock Files](/gh-aw/reference/imports/#self-contained-lock-files-inlined-imports-true) for the full details.
### My cross-organization `workflow_call` fails with a repository checkout error
[Section titled “My cross-organization workflow\_call fails with a repository checkout error”](#my-cross-organization-workflow_call-fails-with-a-repository-checkout-error)
When a trigger file in one organization calls an agentic workflow in a **different organization**, the activation job attempts to check out the platform repo’s `.github` folder using the caller’s `GITHUB_TOKEN`. That token is scoped to the caller’s organization and cannot access a private repository in another organization, producing an error such as:
```plaintext
fatal: repository 'https://github.com/other-org/platform-repo/' not found
```
The fix is to enable `inlined-imports: true` on the **platform workflow** (the callee). This embeds all imported content into the compiled `.lock.yml` at compile time, eliminating the cross-organization checkout entirely:
```yaml
---
on:
workflow_call:
engine: copilot
inlined-imports: true
imports:
- shared/common-tools.md
---
```
See [Self-Contained Lock Files](/gh-aw/reference/imports/#self-contained-lock-files-inlined-imports-true) for the full details.
### My workflow checkout is very slow because my repository is a large monorepo. How can I speed it up?
[Section titled “My workflow checkout is very slow because my repository is a large monorepo. How can I speed it up?”](#my-workflow-checkout-is-very-slow-because-my-repository-is-a-large-monorepo-how-can-i-speed-it-up)
Use **sparse checkout** to only fetch the parts of the repository that your workflow actually needs. This can reduce checkout time from tens of minutes to seconds for large monorepos.
Configure `sparse-checkout` in your workflow frontmatter using the `checkout:` field:
```yaml
checkout:
sparse-checkout: |
node/my-package
.github
```
This generates a checkout step that only downloads the specified paths, dramatically reducing clone size and time.
For cases where you need multiple parts of a monorepo with different settings, you can combine checkouts:
```yaml
checkout:
- sparse-checkout: |
node/my-package
.github
- repository: org/shared-libs
path: ./libs/shared
sparse-checkout: |
defaults/
```
The `sparse-checkout` field accepts newline-separated path patterns compatible with `actions/checkout`. See [GitHub Repository Checkout](/gh-aw/reference/checkout/#configuration-options) for the full list of checkout configuration options.
## Workflow Design
[Section titled “Workflow Design”](#workflow-design)
### Should I focus on one workflow, or write many different ones?
[Section titled “Should I focus on one workflow, or write many different ones?”](#should-i-focus-on-one-workflow-or-write-many-different-ones)
One workflow is simpler to maintain and good for learning, while multiple workflows provide better separation of concerns, different triggers and permissions per task, and clearer audit trails. Start with one or two workflows, then expand as you understand the patterns. See [Peli’s Agent Factory](/gh-aw/blog/2026-01-12-welcome-to-pelis-agent-factory/) for examples.
### Should I create agentic workflows by hand editing or using AI?
[Section titled “Should I create agentic workflows by hand editing or using AI?”](#should-i-create-agentic-workflows-by-hand-editing-or-using-ai)
Either approach works well. AI-assisted authoring using `/agent agentic-workflows create` in GitHub Copilot Chat provides interactive guidance with automatic best practices, while manual editing gives full control and is essential for advanced customizations. See [Creating Workflows](/gh-aw/setup/creating-workflows/) for AI-assisted approach, or [Reference documentation](/gh-aw/reference/frontmatter/) for manual configuration.
### Can the agent use an existing branch specified at runtime (e.g., from a Jira issue)?
[Section titled “Can the agent use an existing branch specified at runtime (e.g., from a Jira issue)?”](#can-the-agent-use-an-existing-branch-specified-at-runtime-eg-from-a-jira-issue)
The `create-pull-request` safe output always creates a new branch, but you can control its name and make it reuse an existing remote branch. Set these two fields in your workflow frontmatter:
```yaml
safe-outputs:
create-pull-request:
preserve-branch-name: true # omit random salt suffix from agent-specified name
recreate-ref: true # force-reset remote branch if it already exists
```
With `preserve-branch-name: true`, the agent’s branch name (e.g., `feature/abc-123-my-change`) is used as-is instead of having a random hex suffix appended. With `recreate-ref: true`, if that branch already exists remotely, it is force-reset to the agent’s current HEAD rather than falling back to creating an issue.
To pass the branch name from a Jira issue body (or any issue body), instruct the agent in your workflow’s markdown:
```markdown
Read the issue body and extract the branch name from the line starting with
"Use existing branch:". Use that name when calling `create_pull_request`.
```
The agent reads the triggering issue body as part of its context, so no extra integration is needed when the branch name is embedded there. For richer Jira data (status, custom fields), use a [custom safe output](/gh-aw/reference/custom-safe-outputs/) or Jira MCP server.
Note
`recreate-ref` requires `preserve-branch-name: true` to take effect. The agent always starts from the configured base branch — it doesn’t literally check out the named branch before making changes.
See [Safe Outputs (Pull Requests)](/gh-aw/reference/safe-outputs-pull-requests/) for full configuration details.
### You use ‘agent’ and ‘agentic workflow’ interchangeably. Are they the same thing?
[Section titled “You use ‘agent’ and ‘agentic workflow’ interchangeably. Are they the same thing?”](#you-use-agent-and-agentic-workflow-interchangeably-are-they-the-same-thing)
Yes, for the purpose of this technology. An **“agent”** is an agentic workflow in a repository - an AI-powered automation that can reason, make decisions, and take actions. We use **“agentic workflow”** as it’s plainer and emphasizes the workflow nature of the automation, but the terms are synonymous in this context.
### How do I forward agent and detection artifacts to a third-party server after the workflow finishes?
[Section titled “How do I forward agent and detection artifacts to a third-party server after the workflow finishes?”](#how-do-i-forward-agent-and-detection-artifacts-to-a-third-party-server-after-the-workflow-finishes)
Add a custom job with `needs: [conclusion]` in the frontmatter `jobs:` block. The `conclusion` job is the last auto-generated job to run, so depending on it guarantees both the `agent` and `detection` artifacts are fully uploaded before your job starts.
```yaml
jobs:
forward-artifacts:
needs: [conclusion]
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: agent
path: artifacts/agent
- uses: actions/download-artifact@v4
with:
name: detection
path: artifacts/detection
continue-on-error: true
- name: Upload to third-party server
env:
INGEST_TOKEN: ${{ secrets.INGEST_TOKEN }}
run: |
tar -czf artifacts.tar.gz artifacts/
curl --fail --retry 3 -X POST https://ingest.example.com/artifacts \
-H "Authorization: ******" \
-F "file=@artifacts.tar.gz" \
-F "run_id=${{ github.run_id }}"
```
`if: always()` ensures the job runs even when the agent or safe-output jobs fail. The `detection` artifact is only present when [threat detection](/gh-aw/reference/threat-detection/) is enabled; `continue-on-error: true` on that step makes the job continue when the artifact doesn’t exist.
See [Artifacts](/gh-aw/reference/artifacts/) for a full list of artifact names and their contents.
## Costs & Usage
[Section titled “Costs & Usage”](#costs--usage)
### Who pays for the use of AI?
[Section titled “Who pays for the use of AI?”](#who-pays-for-the-use-of-ai)
This depends on the AI engine (coding agent) you use:
* **GitHub Copilot CLI** (default): Usage is currently associated with the individual GitHub account of the user supplying the [`COPILOT_GITHUB_TOKEN`](/gh-aw/reference/auth/#copilot_github_token), and is drawn from the monthly quota of premium requests for that account. See [GitHub Copilot billing](https://docs.github.com/en/copilot/about-github-copilot/subscription-plans-for-github-copilot).
* **Claude**: Usage is billed to the Anthropic account associated with [`ANTHROPIC_API_KEY`](/gh-aw/reference/auth/#anthropic_api_key) Actions secret in the repository.
* **Codex**: Usage is billed to your OpenAI account associated with [`OPENAI_API_KEY`](/gh-aw/reference/auth/#openai_api_key) Actions secret in the repository.
### What’s the approximate cost per workflow run?
[Section titled “What’s the approximate cost per workflow run?”](#whats-the-approximate-cost-per-workflow-run)
Costs vary depending on workflow complexity, AI model, and execution time. GitHub Copilot CLI uses 1-2 premium requests per workflow execution with agentic processing. Track usage with `gh aw logs` for runs and metrics, `gh aw audit ` for detailed token usage and costs, or check your AI provider’s usage portal. Consider creating separate PAT/API keys per repository for tracking.
Reduce costs by optimizing prompts, using smaller models, limiting tool calls, reducing run frequency, and caching results.
### Are GitHub Actions minutes charged in addition to AI costs?
[Section titled “Are GitHub Actions minutes charged in addition to AI costs?”](#are-github-actions-minutes-charged-in-addition-to-ai-costs)
Yes. Every agentic workflow run is a GitHub Actions workflow run, so it consumes Actions minutes alongside AI inference. These are billed separately:
* **Actions minutes**: Standard GitHub Actions billing applies — free for public repos, metered for private repos based on your plan. Set a [spending limit](https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-actions/managing-your-spending-limit-for-github-actions) at the org level to cap Actions spend.
* **AI inference**: Billed through your AI engine account (see [Who pays for the use of AI?](#who-pays-for-the-use-of-ai)).
### How do retries and agent loops affect costs?
[Section titled “How do retries and agent loops affect costs?”](#how-do-retries-and-agent-loops-affect-costs)
gh-aw has no automatic retry mechanism — each workflow trigger produces exactly one run. However, you can control reasoning depth and autopilot continuation, which directly affects how many tokens and how much wall-clock time (Actions minutes) a run consumes:
* `max-turns` (Claude only) — limits the number of AI chat iterations per run
* `max-continuations` (Copilot only) — enables autopilot mode with multiple consecutive triggered runs
```yaml
engine:
id: claude
max-turns: 5 # limit reasoning depth per run
```
Keep these values low for cost-sensitive workflows. For scheduled workflows, run frequency is the primary cost lever — an hourly schedule at 1–2 premium requests per run adds up quickly across many repositories.
### How do I control spend and set budgets?
[Section titled “How do I control spend and set budgets?”](#how-do-i-control-spend-and-set-budgets)
Spend controls live at the provider level, not inside gh-aw:
* **Actions minutes**: Set an org spending limit in GitHub Billing settings.
* **Claude / Codex / Gemini**: Configure spend limits in the Anthropic Console or OpenAI platform. These apply at the API key or project level.
* **Copilot**: Usage is quota-based (premium requests per month) rather than dollar-metered, so the natural cap is the plan’s monthly request quota.
For per-repository cost tracking, use a dedicated API key per repository so provider dashboards show usage broken down by key. You can also use `gh aw audit ` for per-run token and cost detail, and `gh aw logs` for run history and aggregate metrics.
### Can I change the model being used, e.g., use a cheaper or more advanced one?
[Section titled “Can I change the model being used, e.g., use a cheaper or more advanced one?”](#can-i-change-the-model-being-used-eg-use-a-cheaper-or-more-advanced-one)
Yes! You can configure the model in your workflow frontmatter:
```yaml
engine:
id: copilot
model: gpt-5 # or claude-sonnet-4
```
Or switch to a different engine entirely:
```yaml
engine: claude
```
See [AI Engines](/gh-aw/reference/engines/) for all configuration options.
# Feature Flags
> Enable experimental or optional compiler and runtime behaviors in GitHub Agentic Workflows using the features: frontmatter field
The `features:` frontmatter field enables experimental or optional compiler and runtime behaviors as key-value pairs.
```yaml
features:
my-experimental-feature: true
action-mode: "script"
```
## Action Mode (`features.action-mode`)
[Section titled “Action Mode (features.action-mode)”](#action-mode-featuresaction-mode)
Controls how the workflow compiler generates custom action references in compiled workflows. Can be set to `"dev"`, `"release"`, `"action"`, or `"script"`.
```yaml
features:
action-mode: "script"
```
**Available modes:**
* **`dev`** (default): References custom actions using local paths (e.g., `uses: ./actions/setup`). Best for development and testing workflows in the gh-aw repository.
* **`release`**: References custom actions using SHA-pinned remote paths within `github/gh-aw` (e.g., `uses: github/gh-aw/actions/setup@sha`). Used for production workflows with version pinning.
* **`action`**: References custom actions from the `github/gh-aw-actions` external repository at the same release version (e.g., `uses: github/gh-aw-actions/setup@sha`). Uses SHA pinning when available, with a version-tag fallback. Use this when deploying workflows from the `github/gh-aw-actions` distribution repository.
* **`script`**: Generates direct shell script calls instead of using GitHub Actions `uses:` syntax. The compiler:
1. Checks out the `github/gh-aw` repository’s `actions` folder to `/tmp/gh-aw/actions-source`
2. Runs the setup script directly: `bash /tmp/gh-aw/actions-source/actions/setup/setup.sh`
3. Uses shallow clone (`depth: 1`) for efficiency
**When to use script mode:**
* Testing custom action scripts during development
* Debugging action installation issues
* Environments where local action references are not available
* Advanced debugging scenarios requiring direct script execution
**Example:**
```yaml
---
name: Debug Workflow
on: workflow_dispatch
features:
action-mode: "script"
permissions:
contents: read
---
Debug workflow using script mode for custom actions.
```
**Note:** The `action-mode` can also be overridden via the CLI flag `--action-mode` or the environment variable `GH_AW_ACTION_MODE`. The precedence is: CLI flag > feature flag > environment variable > auto-detection.
## Copilot BYOK Mode (Default for `engine: copilot`)
[Section titled “Copilot BYOK Mode (Default for engine: copilot)”](#copilot-byok-mode-default-for-engine-copilot)
Copilot offline Bring Your Own Key (BYOK) behavior is now the default for `engine: copilot`, bundling four behaviors:
1. Injecting a dummy `COPILOT_API_KEY` to trigger the AWF BYOK runtime path.
2. Implicitly enabling `cli-proxy`.
3. Forcing the Copilot CLI to install at `latest` (ignoring any pinned `engine.version`).
4. Setting `COPILOT_MODEL` to `${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}` — Copilot BYOK providers require a non-empty model, so the compiler provides `claude-sonnet-4.6` as the fallback when `GH_AW_MODEL_AGENT_COPILOT` is not set.
No feature flag is required.
To use a different model, set the `GH_AW_MODEL_AGENT_COPILOT` repository variable. The compiled workflow uses `${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}` for `COPILOT_MODEL`.
Caution
`features.byok-copilot` is deprecated and no longer needed. Existing workflows may still include it, but it has no effect.
For Copilot BYOK setup and policy details, see [Using your LLM provider API keys with Copilot](https://docs.github.com/en/copilot/how-tos/administer-copilot/manage-for-enterprise/use-your-own-api-keys).
Note
Copilot BYOK defaults apply only to `engine: copilot` workflows. Other engines are unchanged.
## AWF Failure Diagnostics (`features.awf-diagnostic-logs`)
[Section titled “AWF Failure Diagnostics (features.awf-diagnostic-logs)”](#awf-failure-diagnostics-featuresawf-diagnostic-logs)
Enables AWF Docker operational diagnostics collection on failure by adding `--diagnostic-logs` to AWF runtime arguments.
When enabled, AWF includes failure diagnostics under the `diagnostics/` subdirectory in the `firewall-audit-logs` artifact (for example, container logs, exit codes, mount metadata, and sanitized compose configuration).
```yaml
features:
awf-diagnostic-logs: true
```
## Reaction-based Trust Signals (`features.integrity-reactions`)
[Section titled “Reaction-based Trust Signals (features.integrity-reactions)”](#reaction-based-trust-signals-featuresintegrity-reactions)
Enables maintainers to promote or demote content past the integrity filter using GitHub reactions (, , , ), without adding labels or modifying issue state. Available from gh-aw v0.68.2.
```yaml
features:
integrity-reactions: true
```
When set, the compiler automatically enables the CLI proxy (required to identify reaction authors) and injects default endorsement and disapproval reaction configuration. Only the `features.integrity-reactions` flag is required — the reaction fields under `tools.github` (`endorsement-reactions`, `disapproval-reactions`, `endorser-min-integrity`, `disapproval-integrity`) are optional overrides.
See [Promoting and demoting items via reactions](/gh-aw/reference/integrity/#promoting-and-demoting-items-via-reactions) in the Integrity Filtering Reference for complete configuration details.
## DIFC Proxy (`tools.github.integrity-proxy`)
[Section titled “DIFC Proxy (tools.github.integrity-proxy)”](#difc-proxy-toolsgithubintegrity-proxy)
Controls DIFC (Data Integrity and Flow Control) proxy injection. When `tools.github.min-integrity` is configured, the compiler inserts proxy steps around the agent that enforce integrity-level isolation at the network boundary. The proxy is **enabled by default** — set `integrity-proxy: false` to opt out.
```yaml
tools:
github:
min-integrity: approved
# integrity-proxy: false # uncomment to disable proxy injection
```
Without `min-integrity`, `integrity-proxy` has no effect. When both are configured, the proxy enforces network-boundary integrity filtering in addition to the MCP gateway-level filtering. Set `integrity-proxy: false` when you only need gateway-level filtering.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Frontmatter Reference](/gh-aw/reference/frontmatter/) — Complete frontmatter field reference
* [AI Engines](/gh-aw/reference/engines/) — Engine configuration including Copilot BYOK
* [Integrity Filtering](/gh-aw/reference/integrity/) — Integrity levels, reactions, and DIFC proxy
* [Network Permissions](/gh-aw/reference/network/) — Network access configuration
# Footer Control
> Learn how to control AI-generated footers in safe output operations and customize footer messages for GitHub issues, pull requests, discussions, and releases.
Control whether AI-generated footers are added to created and updated GitHub items (issues, pull requests, discussions, releases). Footers provide attribution and links to workflow runs, but you may want to omit them for cleaner content or when using custom branding.
## Global Footer Control
[Section titled “Global Footer Control”](#global-footer-control)
Set `footer: false` at the safe-outputs level to hide footers for all output types:
```yaml
safe-outputs:
footer: false # hide footers globally
create-issue:
title-prefix: "[ai] "
create-pull-request:
title-prefix: "[ai] "
```
When `footer: false` is set, visible attribution text is omitted from item bodies but hidden XML markers remain for searchability:
* `` — for search and tracking
* `` — for issue/discussion tracking
Applies to all output types: create-issue, create-pull-request, create-discussion, update-issue, update-pull-request, update-discussion, and update-release.
### Searching for Workflow-Created Items
[Section titled “Searching for Workflow-Created Items”](#searching-for-workflow-created-items)
Use the `gh-aw-workflow-id` marker (the workflow filename without `.md`) to find items in GitHub search:
```plaintext
repo:owner/repo is:issue is:open "gh-aw-workflow-id: daily-team-status" in:body
repo:owner/repo "gh-aw-workflow-id: bot-responder" in:comments
```
Combine with `is:open`, `created:>2024-01-01`, or `org:your-org` filters. See [GitHub advanced search](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests).
## Per-Handler Footer Control
[Section titled “Per-Handler Footer Control”](#per-handler-footer-control)
Override the global setting for specific output types by setting `footer` at the handler level:
```yaml
safe-outputs:
footer: false # global default: no footers
create-issue:
title-prefix: "[issue] "
# inherits footer: false
create-pull-request:
title-prefix: "[pr] "
footer: true # override: show footer for PRs only
```
Individual handler settings always take precedence over the global setting.
## PR Review Footer Control
[Section titled “PR Review Footer Control”](#pr-review-footer-control)
For PR reviews (`submit-pull-request-review`), the `footer` field supports conditional control over when the footer is added to the review body:
```yaml
safe-outputs:
create-pull-request-review-comment:
submit-pull-request-review:
footer: "if-body" # conditional footer based on review body
```
The `footer` field accepts `"always"` (default), `"none"`, or `"if-body"` (footer only when the review has body text). Booleans are accepted: `true` → `"always"`, `false` → `"none"`. Use `"if-body"` for clean approval reviews — approvals without body text appear without a footer, while reviews with comments include it.
## Backward Compatibility
[Section titled “Backward Compatibility”](#backward-compatibility)
The default value for `footer` is `true`. To hide footers, explicitly set `footer: false`.
## Customizing Footer Messages
[Section titled “Customizing Footer Messages”](#customizing-footer-messages)
Instead of hiding footers entirely, you can customize the footer message text using the `messages.footer` template. This allows you to maintain attribution while using custom branding:
```yaml
safe-outputs:
messages:
footer: "> Powered by [{workflow_name}]({agentic_workflow_url})"
create-issue:
title-prefix: "[bot] "
```
The `messages.footer` template supports variables like `{workflow_name}`, `{agentic_workflow_url}`, `{run_url}`, `{triggering_number}`, `{effective_tokens_suffix}`, and more. `{agentic_workflow_url}` links directly to the agentic workflow file view for the run (equivalent to `{run_url}/agentic_workflow`), while `{run_url}` links to the plain Actions run page. `{effective_tokens_suffix}` is a pre-formatted, always-safe suffix (e.g. `" · ● 1.2K"` or `""`) that you can place directly before `{history_link}` — the same `●` format the default footer uses. See [Custom Messages](/gh-aw/reference/safe-outputs/#custom-messages-messages) for complete documentation on message templates and available variables.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
* [Safe Outputs](/gh-aw/reference/safe-outputs/) - Complete safe outputs reference
* [Custom Messages](/gh-aw/reference/safe-outputs/#custom-messages-messages) - Message templates and variables
* [Frontmatter](/gh-aw/reference/frontmatter/) - All configuration options for workflows
# Forecast Command Specification
> Formal W3C-style specification for the gh aw forecast command — Monte Carlo token-usage projection, episode analysis, workflow discovery, and output formats for GitHub Agentic Workflows
# Forecast Command Specification
[Section titled “Forecast Command Specification”](#forecast-command-specification)
**Version**: 0.1.0\
**Status**: Experimental Draft\
**Latest Version**: [forecast-specification](/gh-aw/reference/forecast-specification/)\
**Editor**: GitHub Agentic Workflows Team
> ! **Experimental**: This specification describes a feature that is under active development. The command interface, output schema, and algorithmic parameters are subject to change without notice. Do not depend on this interface in production workflows.
***
## Abstract
[Section titled “Abstract”](#abstract)
This specification defines the `gh aw forecast` command for the GitHub Agentic Workflows (gh-aw) project. The command performs historical sampling of completed agentic workflow runs and applies a Monte Carlo simulation engine to project future Effective Token (ET) consumption over a configurable time horizon. The specification covers workflow discovery (local and remote modes), data sampling via the GitHub Actions API, the Poisson–bootstrap Monte Carlo projection algorithm, episode-level analysis, and both console-table and machine-readable JSON output formats. Implementations conforming to this specification provide operators with probabilistic token-consumption forecasts suitable for capacity planning, cost estimation, and budget governance.
***
## Status of This Document
[Section titled “Status of This Document”](#status-of-this-document)
This section describes the status of this document at the time of publication. This is an **Experimental Draft** specification and may be updated, replaced, or made obsolete by other documents at any time. The feature it describes is experimental and not yet subject to the stability guarantees that apply to other gh-aw commands.
This document is governed by the GitHub Agentic Workflows project specifications process.
Feedback should be filed as GitHub issues against the `github/gh-aw` repository.
***
## Table of Contents
[Section titled “Table of Contents”](#table-of-contents)
1. [Introduction](#1-introduction)
2. [Conformance](#2-conformance)
3. [Terminology](#3-terminology)
4. [Command Interface](#4-command-interface)
5. [Workflow Discovery](#5-workflow-discovery)
6. [Data Sampling](#6-data-sampling)
7. [Monte Carlo Projection Engine](#7-monte-carlo-projection-engine)
8. [Episode Analysis](#8-episode-analysis)
9. [Output Formats](#9-output-formats)
10. [Error Handling](#10-error-handling)
11. [Implementation Requirements](#11-implementation-requirements)
12. [Compliance Testing](#12-compliance-testing)
13. [Sync Notes](#13-sync-notes)
14. [Appendices](#14-appendices)
15. [References](#15-references)
16. [Change Log](#16-change-log)
***
## 1. Introduction
[Section titled “1. Introduction”](#1-introduction)
### 1.1 Purpose
[Section titled “1.1 Purpose”](#11-purpose)
The `gh aw forecast` command addresses the operational need to predict future Large Language Model (LLM) token expenditure for agentic workflows managed by gh-aw. Token consumption is a primary cost driver for agentic systems; the ability to project future usage from historical observations enables:
* **Capacity Planning**: Anticipating token demand before budget thresholds are reached.
* **Cost Governance**: Providing P10/P50/P90 confidence intervals for financial planning.
* **Workflow Comparison**: Ranking workflows by projected token cost across a shared time period.
* **Experiment Evaluation**: Measuring the token impact of A/B experiment variants.
The command combines empirical bootstrapping of historical token observations with a Poisson-distributed run-count model to produce statistically sound projections without requiring parametric distribution assumptions on token usage.
### 1.2 Scope
[Section titled “1.2 Scope”](#12-scope)
This specification covers:
* Command-line interface: flags, positional arguments, and invocation modes
* Workflow discovery in local (`.github/workflows/`) and remote (`--repo`) modes
* Historical run sampling and per-run metric derivation
* The Monte Carlo simulation algorithm producing P10, P50, P90 percentile estimates
* Episode grouping and episode-level metric computation
* Console table output format
* Machine-readable JSON output schema (`--json`)
* Error conditions and graceful-degradation behavior
This specification does NOT cover:
* The Effective Tokens (ET) computation algorithm (defined in the [Effective Tokens Specification](/gh-aw/reference/effective-tokens-specification/))
* The `aw_info.json` artifact schema
* A/B experiment frontmatter schema (defined in the [A/B Experiments Specification](/gh-aw/practices/experiments-specification/))
* Billing, pricing, or financial modeling beyond token projections
* Streaming or real-time token consumption reporting
### 1.3 Design Goals
[Section titled “1.3 Design Goals”](#13-design-goals)
A conforming `gh aw forecast` implementation MUST be designed for:
* **Empirical Accuracy**: Projections derived from observed historical data rather than assumed distributions.
* **Probabilistic Reporting**: P10/P50/P90 uncertainty bounds communicated to callers.
* **Graceful Degradation**: Missing data (no runs, no artifacts, no frontmatter) MUST produce partial results rather than failures.
* **Dual Modes**: Both local-repository and remote-repository operation without requiring a checkout.
* **Interoperability**: JSON output schema stable enough for machine consumption by downstream tooling.
***
## 2. Conformance
[Section titled “2. Conformance”](#2-conformance)
### 2.1 Conformance Classes
[Section titled “2.1 Conformance Classes”](#21-conformance-classes)
A **conforming forecast implementation** is one that satisfies all MUST, REQUIRED, and SHALL requirements in this specification.
A **partially conforming forecast implementation** is one that satisfies all MUST requirements in Sections 4, 5, 6, and 7 but MAY lack support for optional features such as episode analysis (Section 8), experiment variant reporting, or verbose diagnostics.
### 2.2 Requirements Notation
[Section titled “2.2 Requirements Notation”](#22-requirements-notation)
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
### 2.3 Compliance Levels
[Section titled “2.3 Compliance Levels”](#23-compliance-levels)
Implementations MUST support:
* **Level 1 (Required)**: Command invocation, workflow discovery, historical data sampling, and Monte Carlo projection with console output.
* **Level 2 (Standard)**: JSON output (`--json`), episode analysis, remote-repository mode (`--repo`), and experiment variant reporting.
* **Level 3 (Complete)**: All optional features including `--verbose` diagnostics, concurrency limit reporting, and frontmatter metadata enrichment.
***
## 3. Terminology
[Section titled “3. Terminology”](#3-terminology)
### 3.1 Effective Tokens (ET)
[Section titled “3.1 Effective Tokens (ET)”](#31-effective-tokens-et)
A normalized unit of LLM token consumption defined in the [Effective Tokens Specification](/gh-aw/reference/effective-tokens-specification/). ET accounts for token class weights and model multipliers to produce a single comparable scalar across heterogeneous LLM invocations.
### 3.2 Workflow Run
[Section titled “3.2 Workflow Run”](#32-workflow-run)
A single execution of a GitHub Actions workflow. A run has a unique numeric run ID, an event type, a status (`completed`, `in_progress`, `queued`), a conclusion (`success`, `failure`, `cancelled`, etc.), and a head commit SHA.
### 3.3 Historical Window
[Section titled “3.3 Historical Window”](#33-historical-window)
The time interval `[now − days, now]` used to bound the set of completed runs eligible for sampling. Controlled by the `--days` flag.
### 3.4 Sample
[Section titled “3.4 Sample”](#34-sample)
The subset of completed workflow runs within the historical window selected for metric derivation. The maximum sample size per workflow is controlled by the `--sample` flag.
### 3.5 Monte Carlo Trial
[Section titled “3.5 Monte Carlo Trial”](#35-monte-carlo-trial)
A single independent simulation that draws stochastic values for run count, per-run token usage, and per-run success, combining them to produce one projected Effective Token total for the projection period.
### 3.6 Projection Period
[Section titled “3.6 Projection Period”](#36-projection-period)
The future time interval for which token consumption is projected. Controlled by the `--period` flag; either one calendar week (`week`) or one calendar month (`month`).
### 3.7 Observed Runs Per Period
[Section titled “3.7 Observed Runs Per Period”](#37-observed-runs-per-period)
The rate of workflow runs observed in the historical window, extrapolated to the projection period length:
```plaintext
observed_runs_per_period = (sampled_run_count / history_days) × period_days
```
Where `period_days` is 7 for `week` and 30 for `month`.
### 3.8 Episode
[Section titled “3.8 Episode”](#38-episode)
A logical grouping of one or more workflow runs that collectively represent a single task attempt. Episodes are identified by grouping runs sharing the same `headSha` and `headBranch`, or by `workflow_dispatch`/`workflow_call` linkage where available.
### 3.9 Yield
[Section titled “3.9 Yield”](#39-yield)
The effective throughput rate: the expected number of successful runs per projection period, computed as the product of the observed run frequency and the historical success rate:
```plaintext
yield = observed_runs_per_period × success_rate
```
Where `success_rate = successful_run_count / total_sampled_run_count`.
### 3.10 Bootstrap Resampling
[Section titled “3.10 Bootstrap Resampling”](#310-bootstrap-resampling)
An empirical resampling technique where individual observations are drawn with replacement from the observed sample. Used in Section 7 to model per-run token usage without parametric distribution assumptions.
### 3.11 Lock File
[Section titled “3.11 Lock File”](#311-lock-file)
A `.lock.yml` file located in `.github/workflows/` that declares a compiled agentic workflow and its associated metadata. Lock files are the authoritative source of workflow identifiers in local mode.
***
## 4. Command Interface
[Section titled “4. Command Interface”](#4-command-interface)
### 4.1 Synopsis
[Section titled “4.1 Synopsis”](#41-synopsis)
```plaintext
gh aw forecast [workflow_id...] [flags]
```
### 4.2 Positional Arguments
[Section titled “4.2 Positional Arguments”](#42-positional-arguments)
| Argument | Type | Required | Description |
| ------------- | ------------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `workflow_id` | string (repeatable) | No | Zero or more workflow identifiers to forecast. If omitted, all discovered agentic workflows are forecasted. |
Workflow identifiers MUST be matched case-insensitively against:
1. The workflow display name
2. The workflow file-path basename (without extension)
If a provided `workflow_id` does not match any discovered workflow, the implementation MUST emit an error message identifying the unmatched identifier and MUST exit with a non-zero status code.
### 4.3 Flags
[Section titled “4.3 Flags”](#43-flags)
| Flag | Type | Default | Description |
| ----------- | ------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--days` | int | `30` | Length of the historical sampling window in days. Permitted values: `7`, `30`. |
| `--period` | string | `"month"` | Projection period length. Permitted values: `"week"`, `"month"`. |
| `--sample` | int | `100` | Maximum number of completed runs to sample per workflow. MUST be ≥ 1. |
| `--max-age` | int | `90` | Maximum age in days for historical runs eligible for sampling. Implementations SHOULD discard runs older than this bound unless the caller overrides it. MUST be ≥ 1. |
| `--repo` | string | (none) | Target a repository other than the current working directory, in `owner/repo` format. Enables remote mode. |
| `--json` | bool | `false` | Emit machine-readable JSON output instead of console tables. |
| `--verbose` | bool | `false` | Emit verbose diagnostic output to stderr during processing. |
### 4.4 Flag Validation
[Section titled “4.4 Flag Validation”](#44-flag-validation)
Implementations MUST validate all flag values before beginning any API calls or file system operations:
* **R-CLI-001**: If `--days` is not one of `{7, 30}`, the implementation MUST exit with a non-zero status and an error message specifying the permitted values.
* **R-CLI-002**: If `--period` is not one of `{"week", "month"}`, the implementation MUST exit with a non-zero status and an error message specifying the permitted values.
* **R-CLI-003**: If `--sample` is less than 1, the implementation MUST exit with a non-zero status.
* **R-CLI-004**: If `--repo` is provided, it MUST match the pattern `owner/repo` (two non-empty components separated by `/`). An invalid format MUST produce a non-zero exit with a descriptive error.
* **R-CLI-005**: If `--max-age` is provided and is less than 1, the implementation MUST exit with a non-zero status and a descriptive error.
### 4.5 Exit Codes
[Section titled “4.5 Exit Codes”](#45-exit-codes)
| Code | Meaning |
| ---- | ---------------------------------------------------- |
| `0` | Forecast completed successfully. |
| `1` | Usage error (invalid flags, unmatched workflow IDs). |
| `2` | GitHub API authentication failure. |
| `3` | No workflows discovered. |
### 4.6 Example Invocations
[Section titled “4.6 Example Invocations”](#46-example-invocations)
```sh
# Forecast all agentic workflows in the current repository for the next month
gh aw forecast
# Forecast two specific workflows and compare
gh aw forecast ci-doctor daily-planner
# Use a 7-day window and project over the next week
gh aw forecast --period week --days 7
# Emit machine-readable JSON
gh aw forecast --json
# Forecast workflows in a remote repository
gh aw forecast --repo owner/repo
# Forecast a specific workflow in a remote repository
gh aw forecast --repo owner/repo ci-doctor
# Ignore historical runs older than 90 days (default)
gh aw forecast --max-age 90
```
***
## 5. Workflow Discovery
[Section titled “5. Workflow Discovery”](#5-workflow-discovery)
### 5.1 Modes
[Section titled “5.1 Modes”](#51-modes)
The forecast command operates in one of two discovery modes, determined by the presence of the `--repo` flag:
* **Local Mode**: `--repo` is absent; workflows are discovered from the current repository’s `.github/workflows/` directory.
* **Remote Mode**: `--repo` is present; workflows are discovered via the GitHub Actions API.
### 5.2 Local Mode Discovery
[Section titled “5.2 Local Mode Discovery”](#52-local-mode-discovery)
In local mode, the implementation MUST:
1. **R-DISC-001**: Enumerate all files matching `*.lock.yml` within `.github/workflows/` of the current working repository.
2. **R-DISC-002**: Parse each lock file to extract the workflow identifier and display name.
3. **R-DISC-003**: If the `.github/workflows/` directory does not exist or contains no lock files, the implementation MUST emit an informational message and exit with code `3`.
The implementation MAY additionally read frontmatter metadata from corresponding workflow source files to enrich per-workflow records with:
* Active trigger types (`active_triggers`)
* Concurrency configuration (`concurrency_limit`)
* A/B experiment variant declarations (`experiment_variants`)
Frontmatter enrichment is OPTIONAL; absence of a corresponding source file MUST NOT prevent discovery or projection of the workflow.
### 5.3 Remote Mode Discovery
[Section titled “5.3 Remote Mode Discovery”](#53-remote-mode-discovery)
In remote mode (when `--repo owner/repo` is specified), the implementation MUST:
1. **R-DISC-010**: Call the GitHub Actions API (`GET /repos/{owner}/{repo}/actions/workflows`) to enumerate workflows in the target repository. If workflow discovery hits a primary or secondary GitHub API rate limit, the implementation SHOULD back off and retry before failing.
2. **R-DISC-011**: Filter the returned workflows to those identified as agentic (e.g., by inspecting file-path conventions, labels, or other implementation-defined heuristics).
3. **R-DISC-012**: Match any caller-supplied `workflow_id` positional arguments against workflow display names and file-path basenames using case-insensitive string comparison.
4. **R-DISC-013**: If rate-limit exhaustion occurs after at least one caller-supplied workflow identifier can still be attempted, the implementation MUST continue with that subset as a partial result set and MUST emit a warning identifying the degraded discovery mode.
5. **R-DISC-014**: Implementations MUST tolerate workflow discovery race conditions where a workflow is renamed, disabled, or deleted after enumeration but before run sampling. The affected workflow MUST be reported as a per-workflow partial failure without aborting the overall forecast.
Remote workflow discovery race-condition mitigation:
* Capture a stable snapshot of discovered workflow IDs from the initial listing call.
* During per-workflow run sampling, treat HTTP 404/410 for a previously listed workflow as a recoverable per-workflow partial failure.
* Emit a warning that identifies the workflow and the race condition class (renamed, removed, or inaccessible at sample time).
In remote mode, frontmatter metadata (triggers, concurrency, experiment variants) is UNAVAILABLE because the workflow source files are not accessible. The implementation MUST degrade gracefully: fields that depend on frontmatter MUST be omitted from output or reported as their zero/empty values rather than causing an error.
### 5.4 Workflow ID Matching
[Section titled “5.4 Workflow ID Matching”](#54-workflow-id-matching)
Workflow ID matching MUST be case-insensitive. A caller-supplied identifier matches a discovered workflow if and only if it equals (ignoring case) either:
* The workflow’s display name, OR
* The basename of the workflow’s file path (without file extension)
Matching MUST be performed after discovery is complete; partial prefix matches are NOT sufficient for conformance.
***
## 6. Data Sampling
[Section titled “6. Data Sampling”](#6-data-sampling)
### 6.1 Sampling Procedure
[Section titled “6.1 Sampling Procedure”](#61-sampling-procedure)
For each discovered workflow (or each workflow in the filtered set), the implementation MUST perform the following sampling procedure:
1. **R-SAMP-001**: Query completed workflow runs within the historical window using the equivalent of `gh run list --workflow --status completed --limit --created >=`.
2. **R-SAMP-002**: Limit the returned run set to at most `--sample` runs.
3. **R-SAMP-003**: Implementations SHOULD discard historical runs older than 90 days by default, even when a broader sampling window is requested, and SHOULD expose this bound through a `--max-age` flag so operators can opt in to older samples when needed.
4. **R-SAMP-004**: For each run in the sample, derive the per-run metrics defined in Section 6.2.
5. **R-SAMP-005**: Record the count of runs with a successful conclusion separately from the total sampled count.
If the historical window yields zero completed runs for a workflow, the implementation MUST:
* **R-SAMP-006**: Return `nil` (or a sentinel empty result) for that workflow’s Monte Carlo projection.
* **R-SAMP-007**: Include the workflow in output with `sampled_runs: 0` and all projection fields set to zero.
* **R-SAMP-008**: SHOULD emit a warning indicating that no historical data is available for the workflow.
### 6.2 Per-Run Metric Derivation
[Section titled “6.2 Per-Run Metric Derivation”](#62-per-run-metric-derivation)
For each sampled run, the implementation MUST derive:
| Metric | Source | Description |
| ------------------ | ------------------------ | ----------------------------------------------------------------------- |
| `effective_tokens` | `aw_info.json` artifact | Total ET for this run as defined in the Effective Tokens Specification. |
| `duration_seconds` | Run start/end timestamps | Wall-clock duration of the run in seconds. |
| `success` | Run conclusion field | `true` if conclusion is `"success"`, `false` otherwise. |
#### 6.2.1 Effective Token Retrieval
[Section titled “6.2.1 Effective Token Retrieval”](#621-effective-token-retrieval)
Effective token counts are obtained from locally-cached run summaries when available. The `gh aw logs` command stores a `run_summary.json` file for each processed run under `{output_dir}/run-{run_id}/`. During forecasting the implementation:
* **R-SAMP-010**: MUST attempt to load the cached `run_summary.json` for each sampled run using the default logs output directory (`.github/aw/logs`).
* **R-SAMP-011**: MUST extract the `TotalEffectiveTokens` field from the cached `TokenUsage` summary when present.
* **R-SAMP-012**: If no cached summary exists or the ET field is zero, the run’s ET contribution MUST be treated as zero and the run MUST still be counted in `sampled_runs`. The implementation SHOULD log a debug-level warning.
This lightweight approach avoids re-downloading artifacts while still providing accurate ET observations for runs that have already been processed locally by `gh aw logs`.
#### 6.2.2 Duration Derivation
[Section titled “6.2.2 Duration Derivation”](#622-duration-derivation)
Duration MUST be computed as:
```plaintext
duration_seconds = run.updated_at − run.started_at
```
Both timestamps MUST be sourced from the GitHub Actions API run object. If either timestamp is zero or unavailable, the run’s duration contribution SHOULD be treated as zero.
### 6.3 Observed Rate Computation
[Section titled “6.3 Observed Rate Computation”](#63-observed-rate-computation)
After sampling, the implementation MUST compute:
```plaintext
observed_runs_per_period = (sampled_run_count / history_days) × period_days
```
Where:
* `history_days` is the value of `--days`
* `period_days` is `7` for `"week"` and `30` for `"month"`
***
## 7. Monte Carlo Projection Engine
[Section titled “7. Monte Carlo Projection Engine”](#7-monte-carlo-projection-engine)
### 7.1 Overview
[Section titled “7.1 Overview”](#71-overview)
The Monte Carlo engine runs **10,000 independent simulation trials** per workflow to produce a probability distribution over projected Effective Token consumption in the next projection period. The engine models three independent sources of uncertainty per trial.
Implementations MUST use exactly 10,000 trials. The trial count is a normative requirement to ensure consistency of P10/P50/P90 estimates across implementations.
### 7.2 Uncertainty Sources
[Section titled “7.2 Uncertainty Sources”](#72-uncertainty-sources)
Each trial draws independently from three stochastic components:
#### 7.2.1 Run Count (Poisson Model)
[Section titled “7.2.1 Run Count (Poisson Model)”](#721-run-count-poisson-model)
The number of runs in the projection period is modeled as a Poisson random variable with rate parameter:
```plaintext
λ = observed_runs_per_period
```
The implementation MUST use:
* **Knuth’s exact algorithm** when `λ ≤ 15`:
```plaintext
L ← e^(−λ)
k ← 0; p ← 1
repeat:
k ← k + 1
p ← p × Uniform(0, 1)
until p ≤ L
return k − 1
```
* **Normal approximation** when `λ > 15`:
```plaintext
k ← round(Normal(μ=λ, σ=sqrt(λ)))
k ← max(0, k)
```
* **R-MC-001**: For `λ = 0`, the implementation MUST return a projected token total of 0 for that trial without invoking either algorithm.
* **R-FC-060**: Implementations MUST use `λ = 15` as the crossover threshold: Knuth’s exact algorithm for `λ ≤ 15`, and Normal approximation only for `λ > 15`. Implementations MUST NOT raise this threshold above 15 without a specification revision, because the documented error and comparability assumptions are calibrated to this crossover.
* **R-MC-002**: `λ` MUST be derived from `observed_runs_per_period` using the formula in §3.7 and MUST be reused unchanged for every trial of the same workflow forecast. Implementations MUST NOT recalculate or modify `λ` within a single forecast run.
* **R-MC-003**: `λ` MUST be treated as a real-valued rate parameter. Implementations MUST NOT round, floor, or ceil `λ` before selecting the Poisson branch or before drawing the projected run count.
* **R-MC-004**: If the computed `λ` is negative, `NaN`, or otherwise non-finite, implementations MUST replace it with `0`, emit a warning, and continue in the same zero-projection mode required by **R-MC-001**.
#### 7.2.2 Per-Run Token Usage (Bootstrap Resampling)
[Section titled “7.2.2 Per-Run Token Usage (Bootstrap Resampling)”](#722-per-run-token-usage-bootstrap-resampling)
Token usage per run is modeled empirically using bootstrap resampling:
* **R-MC-010**: For each run in a trial, the implementation MUST draw one observation uniformly at random **with replacement** from the set of historical ET observations in the sample.
* **R-MC-011**: If the sample contains zero ET observations (all runs had missing artifacts), the per-run token draw MUST return 0.
This non-parametric approach preserves the empirical distribution of token usage, including multi-modal distributions and heavy tails, without imposing a parametric form.
#### 7.2.3 Per-Run Success (Bernoulli Model)
[Section titled “7.2.3 Per-Run Success (Bernoulli Model)”](#723-per-run-success-bernoulli-model)
Whether a given run in the trial succeeds is modeled as a Bernoulli draw:
```plaintext
P(success) = success_rate = successful_run_count / total_sampled_run_count
```
* **R-MC-020**: Each run in a trial MUST independently draw from `Bernoulli(success_rate)`.
* **R-MC-021**: Only successful runs contribute their token draw to the trial’s projected total. Failed runs contribute zero tokens to the projection.
* **R-MC-022**: If `total_sampled_run_count = 0`, `success_rate` MUST be treated as 0. The implementation MUST return a zero projection for all trials.
### 7.3 Trial Aggregation
[Section titled “7.3 Trial Aggregation”](#73-trial-aggregation)
For a given trial with `k` drawn runs:
```plaintext
trial_tokens = Σ_{i=1}^{k} (success_i × token_draw_i)
```
Where:
* `success_i` is `1` if the Bernoulli draw for run `i` succeeds, `0` otherwise
* `token_draw_i` is the bootstrapped ET observation for run `i`
### 7.4 Output Statistics
[Section titled “7.4 Output Statistics”](#74-output-statistics)
After completing all 10,000 trials, the implementation MUST compute and report:
| Statistic | Definition |
| --------------------------------- | ----------------------------------------------------------- |
| `mean_projected_effective_tokens` | Arithmetic mean of all trial totals |
| `std_dev_effective_tokens` | Population or sample standard deviation of all trial totals |
| `p10_projected_effective_tokens` | 10th percentile of trial totals (lower bound of 80% CI) |
| `p50_projected_effective_tokens` | 50th percentile of trial totals (median projection) |
| `p90_projected_effective_tokens` | 90th percentile of trial totals (upper bound of 80% CI) |
Percentile computation MUST use the nearest-rank method or an equivalent method that produces results consistent with a 10,000-element sorted array.
The `projected_effective_tokens` top-level field MUST equal `p50_projected_effective_tokens`.
### 7.5 Nil Projection Condition
[Section titled “7.5 Nil Projection Condition”](#75-nil-projection-condition)
If no historical runs are available for a workflow, the implementation MUST return a nil (empty/zero) projection for that workflow. Nil projections MUST be represented in JSON output as zero values for all numeric Monte Carlo fields. The implementation MUST NOT run trials when the sample is empty.
### 7.6 Minimum Sample Size for Percentile Validity
[Section titled “7.6 Minimum Sample Size for Percentile Validity”](#76-minimum-sample-size-for-percentile-validity)
The P10 and P90 estimates produced by the Monte Carlo engine are only statistically reliable when the bootstrap sample contains a sufficient number of distinct ET observations.
* **R-MC-030**: Implementations SHOULD require a minimum of **10** ET observations (i.e., runs with non-zero `effective_tokens`) before treating P10 and P90 as reliable estimates. When `n < 10`, implementations SHOULD emit a warning to stderr indicating that the confidence interval may be unreliable due to insufficient sample size. *Rationale: Bootstrap resampling with fewer than 10 observations produces percentile estimates that are highly sensitive to individual outliers. With n < 10, the P10 and P90 bounds collapse toward the single minimum and maximum observations, making the 80% confidence interval misleadingly precise. The threshold of 10 is consistent with standard statistical practice for non-parametric bootstrapping.*
* **R-MC-031**: Implementations MUST still run the Monte Carlo simulation and return P10/P50/P90 values even when `n < 10`. The simulation MUST NOT be suppressed solely on the basis of sample size; the warning in **R-MC-030** is advisory only.
* **R-MC-032**: When `n = 0` (no ET observations in the sample), the **Nil Projection Condition** in §7.5 applies and the simulation MUST NOT run. This is a separate condition from the low-sample warning.
***
## 8. Episode Analysis
[Section titled “8. Episode Analysis”](#8-episode-analysis)
### 8.1 Purpose
[Section titled “8.1 Purpose”](#81-purpose)
An **episode** is a logical grouping of one or more workflow runs that collectively represent a single task attempt. Episode analysis computes per-episode metrics to reveal how many runs, on average, are required to complete a task successfully.
### 8.2 Episode Construction
[Section titled “8.2 Episode Construction”](#82-episode-construction)
The implementation MUST group sampled runs into episodes using the `buildEpisodeData` and `classifyEpisode` engine:
* **R-EP-001**: Runs sharing the same `headSha` and `headBranch` MUST be grouped into the same episode.
* **R-EP-002**: Runs linked by `workflow_dispatch` or `workflow_call` relationships (reconstructed from cached run summaries) SHOULD be merged into the triggering run’s episode.
#### 8.2.1 Limitations in Forecast Context
[Section titled “8.2.1 Limitations in Forecast Context”](#821-limitations-in-forecast-context)
During forecasting, full artifact data may not be available for all sampled runs. When cached summary data is unavailable:
* **R-EP-010**: `workflow_dispatch`/`workflow_call` linkage MUST be omitted from episode construction.
* **R-EP-011**: The resulting `sampled_episodes` count MUST be treated as a **lower-bound estimate**. Implementations MUST communicate this limitation in output (e.g., via a note in console output or a boolean `episode_count_is_lower_bound` field in JSON).
For orchestrator workflows that primarily receive `workflow_call` triggers, the episode count underestimate may be significant. Implementations SHOULD emit a warning when the dominant trigger type is `workflow_call` or `workflow_dispatch`.
### 8.3 Episode Metrics
[Section titled “8.3 Episode Metrics”](#83-episode-metrics)
For each workflow, the implementation MUST compute:
| Metric | Definition |
| ---------------------------------- | --------------------------------------------------- |
| `sampled_episodes` | Count of distinct episodes identified in the sample |
| `runs_per_episode` | `sampled_run_count / sampled_episodes` |
| `avg_effective_tokens_per_episode` | Mean ET summed across all runs within each episode |
| `observed_episodes_per_period` | `(sampled_episodes / history_days) × period_days` |
### 8.4 Episode Table Display
[Section titled “8.4 Episode Table Display”](#84-episode-table-display)
The implementation MUST display the episode analysis table in console output when any workflow in the result set has `runs_per_episode > 1.0`. The table SHOULD be omitted when all workflows have `runs_per_episode = 1.0` (one run per episode is the baseline and adds no additional information).
***
## 9. Output Formats
[Section titled “9. Output Formats”](#9-output-formats)
### 9.1 Console Table Output
[Section titled “9.1 Console Table Output”](#91-console-table-output)
When `--json` is not specified, the implementation MUST render a formatted console table to stderr with the following columns:
| Column | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------- |
| `Workflow` | Workflow display name or identifier |
| `Sampled Runs` | Count of completed runs included in the sample |
| `Success Rate` | Fraction of sampled runs concluding with `success`, formatted as a percentage; `N/A` when no runs were sampled |
| `Yield/Period` | Effective throughput rate (`success_rate × observed_runs_per_period`) formatted to one decimal place |
| `Avg ET` | `avg_effective_tokens` formatted as K/M abbreviations (e.g. `12.5K`, `1.20M`); `-` when zero |
| `Proj. ET (P50)` | Median projected effective tokens from Monte Carlo (P50), formatted as K/M abbreviations |
| `80% CI (P10–P90)` | Confidence interval range `p10–p90`, both formatted as K/M abbreviations |
| `Triggers` | Comma-separated list of active trigger event names from frontmatter (up to 3, remainder shown as `+N`) |
#### 9.1.1 Table Formatting Requirements
[Section titled “9.1.1 Table Formatting Requirements”](#911-table-formatting-requirements)
* **R-OUT-001**: Column widths MUST be auto-fitted to the widest value in each column.
* **R-OUT-002**: ET values MUST be formatted as K/M abbreviations (e.g. `12.5K`, `1.20M`); raw integer values of zero MUST be rendered as `-`.
* **R-OUT-003**: Rows MUST be sorted by Monte Carlo P50 projected effective tokens in descending order; when Monte Carlo data is unavailable, sort by `projected_effective_tokens`.
* **R-OUT-004**: A workflow with zero sampled runs MUST appear in the table with `-` in projection columns and `N/A` in rate columns.
* **R-OUT-005**: When episode analysis is applicable (Section 8.4), a second table with episode metrics MUST be printed below the main table, separated by a blank line.
#### 9.1.2 Example Console Output
[Section titled “9.1.2 Example Console Output”](#912-example-console-output)
```plaintext
Workflow Sampled Runs Success Rate Yield/Period Avg ET Proj. ET (P50) 80% CI (P10–P90) Triggers
ci-doctor 42 92% 35.4 12.5K 480.0K 430.0K–535.0K pull_request, workflow_dispatch
daily-planner 18 89% 14.4 8.2K 131.0K 105.0K–158.0K schedule
```
### 9.2 JSON Output Schema
[Section titled “9.2 JSON Output Schema”](#92-json-output-schema)
When `--json` is specified, the implementation MUST emit a single JSON object to stdout conforming to the following schema. No additional content (banners, progress indicators, or table output) MUST be emitted to stdout. Diagnostic messages MAY be emitted to stderr.
#### 9.2.1 Root Object
[Section titled “9.2.1 Root Object”](#921-root-object)
```json
{
"period": "",
"as_of": "",
"workflows": [ , ... ]
}
```
| Field | Type | Required | Description |
| ----------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------- |
| `period` | string | MUST | Projection period: `"week"` or `"month"`. |
| `as_of` | string | MUST | ISO 8601 / RFC 3339 UTC timestamp at which the forecast was computed. |
| `workflows` | array | MUST | Ordered array of per-workflow forecast objects. MUST be sorted by `projected_effective_tokens` (P50) descending. |
#### 9.2.2 WorkflowForecast Object
[Section titled “9.2.2 WorkflowForecast Object”](#922-workflowforecast-object)
```json
{
"workflow_id": "",
"period": "",
"sampled_runs": ,
"history_days": ,
"observed_runs_per_period": ,
"success_rate": ,
"yield": ,
"avg_effective_tokens": ,
"avg_duration_seconds": ,
"projected_effective_tokens": ,
"active_triggers": [ "", ... ],
"concurrency_limit": ,
"monte_carlo": { },
"episode_analysis": { },
"experiment_variants": [ , ... ]
}
```
| Field | Type | Required | Description |
| ---------------------------- | ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `workflow_id` | string | MUST | Workflow identifier as used in discovery. |
| `period` | string | MUST | Mirrors the root `period` field. |
| `sampled_runs` | integer | MUST | Number of runs included in the sample. |
| `history_days` | integer | MUST | Value of `--days` used for this forecast. |
| `observed_runs_per_period` | number | MUST | Extrapolated run rate for the projection period. |
| `success_rate` | number | MUST | Fraction of sampled runs that concluded successfully, in `[0.0, 1.0]`. |
| `yield` | number | MUST | Effective throughput rate: `success_rate × observed_runs_per_period`. |
| `avg_effective_tokens` | number | MUST | Mean ET per sampled run. `0` when no ET data is available. |
| `avg_duration_seconds` | number | MUST | Mean wall-clock duration per sampled run in seconds. |
| `projected_effective_tokens` | number | MUST | P50 Monte Carlo projection. Equals `monte_carlo.p50_projected_effective_tokens`. |
| `active_triggers` | array of strings | SHOULD | Trigger event types from workflow frontmatter. Empty array when frontmatter is unavailable. |
| `concurrency_limit` | integer | SHOULD | Concurrency group limit from frontmatter. `0` indicates unlimited or unavailable. |
| `monte_carlo` | object | MUST | Monte Carlo simulation results. See Section 9.2.3. |
| `episode_analysis` | object | SHOULD | Episode analysis results. See Section 9.2.4. |
| `experiment_variants` | array | MAY | A/B experiment variant breakdown. See Section 9.2.5. Empty array when frontmatter is unavailable or no experiments are configured. |
#### 9.2.3 MonteCarlo Object
[Section titled “9.2.3 MonteCarlo Object”](#923-montecarlo-object)
```json
{
"iterations": 10000,
"mean_projected_effective_tokens": ,
"std_dev_effective_tokens": ,
"p10_projected_effective_tokens": ,
"p50_projected_effective_tokens": ,
"p90_projected_effective_tokens":
}
```
| Field | Type | Required | Description |
| --------------------------------- | ------- | -------- | ----------------------------------------- |
| `iterations` | integer | MUST | Always `10000`. |
| `mean_projected_effective_tokens` | number | MUST | Arithmetic mean of trial totals. |
| `std_dev_effective_tokens` | number | MUST | Standard deviation of trial totals. |
| `p10_projected_effective_tokens` | number | MUST | 10th percentile of trial totals. |
| `p50_projected_effective_tokens` | number | MUST | 50th percentile (median) of trial totals. |
| `p90_projected_effective_tokens` | number | MUST | 90th percentile of trial totals. |
When `sampled_runs = 0`, all numeric fields in this object MUST be `0` and `iterations` MUST be `0`.
#### 9.2.4 EpisodeAnalysis Object
[Section titled “9.2.4 EpisodeAnalysis Object”](#924-episodeanalysis-object)
```json
{
"sampled_episodes": ,
"episode_count_is_lower_bound": ,
"runs_per_episode": ,
"avg_effective_tokens_per_episode": ,
"observed_episodes_per_period":
}
```
| Field | Type | Required | Description |
| ---------------------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `sampled_episodes` | integer | MUST | Distinct episode count. Lower-bound estimate when artifact linkage is unavailable. |
| `episode_count_is_lower_bound` | boolean | SHOULD | `true` when episode linkage data is incomplete (for example, remote mode without artifacts); otherwise `false`. |
| `runs_per_episode` | number | MUST | Mean runs per episode. |
| `avg_effective_tokens_per_episode` | number | MUST | Mean ET per episode. |
| `observed_episodes_per_period` | number | MUST | Extrapolated episode rate for the projection period. |
#### 9.2.5 ExperimentVariant Object
[Section titled “9.2.5 ExperimentVariant Object”](#925-experimentvariant-object)
```json
{
"experiment_name": "",
"variant": "",
"run_count": ,
"fraction":
}
```
| Field | Type | Required | Description |
| ----------------- | ------- | -------- | ----------------------------------------------------------------------- |
| `experiment_name` | string | MUST | Name of the A/B experiment from frontmatter. |
| `variant` | string | MUST | Variant identifier (e.g., `"control"`, `"treatment"`). |
| `run_count` | integer | MUST | Number of sampled runs assigned to this variant. |
| `fraction` | number | MUST | `run_count / sampled_runs` for this workflow; fraction in `[0.0, 1.0]`. |
#### 9.2.6 Complete JSON Example
[Section titled “9.2.6 Complete JSON Example”](#926-complete-json-example)
```json
{
"period": "month",
"as_of": "2026-05-10T22:00:00Z",
"workflows": [
{
"workflow_id": "ci-doctor",
"period": "month",
"sampled_runs": 42,
"history_days": 30,
"observed_runs_per_period": 38.5,
"success_rate": 0.92,
"yield": 0.92,
"avg_effective_tokens": 12500,
"avg_duration_seconds": 145.3,
"projected_effective_tokens": 480000,
"active_triggers": ["pull_request", "workflow_dispatch"],
"concurrency_limit": 0,
"monte_carlo": {
"iterations": 10000,
"mean_projected_effective_tokens": 481250,
"std_dev_effective_tokens": 32000.5,
"p10_projected_effective_tokens": 430000,
"p50_projected_effective_tokens": 480000,
"p90_projected_effective_tokens": 535000
},
"episode_analysis": {
"sampled_episodes": 40,
"episode_count_is_lower_bound": true,
"runs_per_episode": 1.05,
"avg_effective_tokens_per_episode": 13100,
"observed_episodes_per_period": 36.7
},
"experiment_variants": [
{
"experiment_name": "model-selection",
"variant": "control",
"run_count": 21,
"fraction": 0.5
},
{
"experiment_name": "model-selection",
"variant": "treatment",
"run_count": 21,
"fraction": 0.5
}
]
}
]
}
```
### 9.3 Output Ordering
[Section titled “9.3 Output Ordering”](#93-output-ordering)
* **R-OUT-010**: In both console and JSON output, workflows MUST be ordered by `projected_effective_tokens` (P50 value) in descending order.
* **R-OUT-011**: Workflows with zero projected tokens MUST appear after all workflows with non-zero projections.
* **R-OUT-012**: Among workflows with equal projected tokens, the ordering SHOULD be deterministic (e.g., alphabetical by workflow ID).
* **R-OUT-013**: JSON output SHOULD disclose episode lower-bound semantics by including `episode_analysis.episode_count_is_lower_bound` for each workflow. Console output SHOULD include a note when this field is `true`.
***
## 10. Error Handling
[Section titled “10. Error Handling”](#10-error-handling)
### 10.1 Authentication Errors
[Section titled “10.1 Authentication Errors”](#101-authentication-errors)
If the GitHub API returns an authentication error (HTTP 401 or 403):
* **R-ERR-001**: The implementation MUST emit a descriptive error message to stderr indicating the authentication failure and guidance on re-authenticating with `gh auth login`.
* **R-ERR-002**: The implementation MUST exit with code `2`.
### 10.2 API Rate Limiting
[Section titled “10.2 API Rate Limiting”](#102-api-rate-limiting)
If the GitHub API returns a rate-limit response (HTTP 429 or a `X-RateLimit-Remaining: 0` header):
* **R-ERR-010**: The implementation SHOULD retry the request after the period indicated by the `X-RateLimit-Reset` header.
* **R-ERR-011**: The implementation MUST emit a warning to stderr when entering a rate-limit wait state.
* **R-ERR-012**: If retry is not feasible, the implementation MUST exit with a non-zero status and a message indicating the rate limit condition.
### 10.3 Partial Failures
[Section titled “10.3 Partial Failures”](#103-partial-failures)
When one or more workflows in the discovery set encounter individual errors (e.g., artifact download failure, API timeout for a specific workflow):
* **R-ERR-020**: The implementation MUST continue processing the remaining workflows rather than aborting the entire forecast.
* **R-ERR-021**: Workflows that encountered individual errors MUST appear in output with `sampled_runs: 0` and all projection fields zeroed.
* **R-ERR-022**: The implementation MUST emit a warning to stderr for each workflow that encountered an individual error.
### 10.4 No Workflows Discovered
[Section titled “10.4 No Workflows Discovered”](#104-no-workflows-discovered)
If workflow discovery yields zero workflows:
* **R-ERR-030**: The implementation MUST emit a message to stderr indicating that no agentic workflows were found and describing the discovery mode used.
* **R-ERR-031**: The implementation MUST exit with code `3`.
### 10.5 Verbose Diagnostics
[Section titled “10.5 Verbose Diagnostics”](#105-verbose-diagnostics)
When `--verbose` is specified, the implementation SHOULD emit the following additional diagnostic information to stderr:
* The list of discovered workflows and their identifiers
* The number of runs fetched per workflow
* The number of runs with valid ET data versus missing artifacts
* The computed `λ` (Poisson rate) for each workflow
* Timing information for API calls and simulation execution
### 10.6 Safeguards for API Rate-Limit During Sampling
[Section titled “10.6 Safeguards for API Rate-Limit During Sampling”](#106-safeguards-for-api-rate-limit-during-sampling)
When the GitHub API returns HTTP 429 or HTTP 403 (with a `X-RateLimit-Remaining: 0` header) during `gh api` sampling calls (i.e., while fetching run lists or artifact data for individual workflows):
* **R-ERR-040**: The implementation MUST apply an exponential-backoff retry strategy: the first retry MUST wait at least the number of seconds indicated by the `Retry-After` or `X-RateLimit-Reset` header (whichever is present and non-zero). If neither header is present, the implementation MUST wait at least 60 seconds before the first retry attempt.
* **R-ERR-041**: The implementation MUST retry the failed request at least once before treating the workflow as a partial failure. Implementations SHOULD retry up to 3 times with increasing backoff intervals.
* **R-ERR-042**: The implementation MUST emit a warning to stderr before each backoff wait period, including the workflow identifier, the HTTP status code received, and the estimated wait duration.
* **R-ERR-043**: If all retry attempts are exhausted and the request still fails, the implementation SHOULD fall back to partial-result mode: the affected workflow MUST be included in output with `sampled_runs: 0` and all projection fields set to zero, consistent with **R-ERR-021**. The implementation MUST NOT abort the entire forecast run due to a single workflow’s rate-limit failure.
* **R-ERR-044**: When operating in partial-result mode due to rate-limit exhaustion, the implementation SHOULD include a `rate_limit_skipped` boolean field set to `true` in the workflow’s JSON output entry so that callers can distinguish rate-limit-induced zero projections from genuine zero-activity workflows. This field is an **additive optional extension** first defined in Section 10.6; callers MUST treat its absence as equivalent to `false` (per §11.5 / **R-IMPL-041**, unknown fields in JSON output MUST be treated as ignorable).
***
## 11. Implementation Requirements
[Section titled “11. Implementation Requirements”](#11-implementation-requirements)
### 11.1 Randomness
[Section titled “11.1 Randomness”](#111-randomness)
* **R-IMPL-001**: The Monte Carlo engine MUST use a cryptographically seeded pseudorandom number generator (PRNG). Implementations MUST NOT use a fixed seed unless in test mode.
* **R-IMPL-002**: The PRNG MUST be seeded independently per forecast invocation to ensure different results on repeated calls.
### 11.2 Performance
[Section titled “11.2 Performance”](#112-performance)
* **R-IMPL-010**: The 10,000-trial simulation for a single workflow MUST complete within 500 milliseconds on a single CPU core with a sample size of 100 runs.
* **R-IMPL-011**: Multiple workflows SHOULD be forecasted concurrently where the runtime environment supports parallelism.
* **R-IMPL-012**: API calls for data sampling SHOULD be made concurrently across workflows, subject to GitHub API rate limit constraints.
### 11.3 Deterministic Output
[Section titled “11.3 Deterministic Output”](#113-deterministic-output)
* **R-IMPL-020**: Given a fixed sample and fixed PRNG seed (in test mode), the Monte Carlo output MUST be reproducible. This requirement applies to test and validation scenarios only; production invocations MUST use random seeds (R-IMPL-001).
### 11.4 Numeric Precision
[Section titled “11.4 Numeric Precision”](#114-numeric-precision)
* **R-IMPL-030**: All intermediate ET computations MUST use 64-bit floating-point arithmetic (IEEE 754 double precision).
* **R-IMPL-031**: JSON serialization of numeric fields MUST NOT produce non-finite values (`NaN`, `+Inf`, `-Inf`). If a computation produces a non-finite value, it MUST be replaced with `0` and a warning MUST be emitted.
* **R-IMPL-032**: Implementations MUST NOT round projected ET values in intermediate computations; rounding for display purposes MUST occur only at serialization time.
### 11.5 Experimental Status Behavior
[Section titled “11.5 Experimental Status Behavior”](#115-experimental-status-behavior)
Because the forecast command is marked **Experimental**:
* **R-IMPL-040**: The implementation MUST emit a warning to stderr on every invocation indicating the experimental status of the command unless `--json` is specified (JSON callers are assumed to be automated pipelines that handle warnings separately).
* **R-IMPL-041**: The JSON output schema MAY have new fields added in minor versions without notice. Callers MUST treat unknown fields as ignorable.
***
## 12. Compliance Testing
[Section titled “12. Compliance Testing”](#12-compliance-testing)
### 12.1 Test Suite Requirements
[Section titled “12.1 Test Suite Requirements”](#121-test-suite-requirements)
Test fixtures for the compliance tests are located in `specs/forecast-compliance-fixtures/`. See `specs/forecast-compliance-fixtures/README.md` for instructions on running the test suite and adding new fixtures.
#### 12.1.1 Command Interface Tests
[Section titled “12.1.1 Command Interface Tests”](#1211-command-interface-tests)
* **T-FC-001**: Invocation with invalid `--days` value exits non-zero with descriptive error.
* **T-FC-002**: Invocation with invalid `--period` value exits non-zero with descriptive error.
* **T-FC-003**: Invocation with `--sample < 1` exits non-zero.
* **T-FC-004**: Invocation with invalid `--repo` format exits non-zero.
* **T-FC-005**: Unmatched `workflow_id` positional argument exits non-zero with identification of the unmatched value.
#### 12.1.2 Workflow Discovery Tests
[Section titled “12.1.2 Workflow Discovery Tests”](#1212-workflow-discovery-tests)
* **T-FC-010**: Local mode: discovers workflows from `.github/workflows/*.lock.yml`.
* **T-FC-011**: Local mode: no lock files found exits with code `3`.
* **T-FC-012**: Remote mode: calls GitHub Actions API and matches workflow IDs case-insensitively.
* **T-FC-013**: Remote mode: missing frontmatter fields default to zero/empty without error.
* **T-FC-030**: Remote mode: on GitHub API rate-limit exhaustion during workflow discovery, the implementation backs off and emits a warning before continuing with caller-supplied workflow IDs as partial results.
#### 12.1.3 Data Sampling Tests
[Section titled “12.1.3 Data Sampling Tests”](#1213-data-sampling-tests)
* **T-FC-020**: Sampling respects `--sample` limit.
* **T-FC-021**: Sampling respects `--days` historical window cutoff.
* **T-FC-022**: Run with missing `aw_info.json` artifact contributes zero ET and is still counted in `sampled_runs`.
* **T-FC-023**: Workflow with zero sampled runs produces nil projection with zero fields.
#### 12.1.4 Monte Carlo Engine Tests
[Section titled “12.1.4 Monte Carlo Engine Tests”](#1214-monte-carlo-engine-tests)
* **T-FC-031**: With `λ ≤ 15`, Knuth’s algorithm is used for Poisson draw (verifiable by seeded PRNG in test mode).
* **T-FC-032**: With `λ > 15`, Normal approximation is used; drawn value is non-negative.
* **T-FC-033**: With `λ = 0`, projected tokens is exactly `0` for all trials.
* **T-FC-034**: Bootstrap resampling draws with replacement from historical ET observations.
* **T-FC-035**: Only successful Bernoulli draws contribute ET to the trial total.
* **T-FC-036**: 10,000 trials are executed per workflow.
* **T-FC-037**: P10 ≤ P50 ≤ P90 for all non-zero projections.
* **T-FC-038**: `projected_effective_tokens` equals `p50_projected_effective_tokens`.
* **T-FC-039**: Boundary crossover: `λ = 15` uses Knuth’s exact branch.
* **T-FC-040**: Boundary crossover: `λ > 15` uses Normal approximation branch.
#### 12.1.5 Episode Analysis Tests
[Section titled “12.1.5 Episode Analysis Tests”](#1215-episode-analysis-tests)
* **T-FC-041**: Runs sharing `headSha` and `headBranch` are grouped into the same episode.
* **T-FC-042**: `runs_per_episode` equals `sampled_run_count / sampled_episodes`.
* **T-FC-043**: Episode table is printed in console output when any workflow has `runs_per_episode > 1`.
* **T-FC-044**: Episode table is suppressed when all workflows have `runs_per_episode = 1.0`.
#### 12.1.6 Output Format Tests
[Section titled “12.1.6 Output Format Tests”](#1216-output-format-tests)
* **T-FC-050**: Console output contains all required columns.
* **T-FC-051**: JSON output is valid JSON conforming to the schema in Section 9.2.
* **T-FC-052**: JSON `as_of` field is a valid RFC 3339 UTC timestamp.
* **T-FC-053**: JSON `workflows` array is sorted by `projected_effective_tokens` descending.
* **T-FC-054**: No stdout output (other than JSON) when `--json` is specified.
* **T-FC-055**: Experimental warning emitted to stderr unless `--json` is specified.
### 12.2 Compliance Checklist
[Section titled “12.2 Compliance Checklist”](#122-compliance-checklist)
| Requirement | Test ID | Level | Status |
| ------------------------------------------------------- | ------------ | ----- | -------- |
| Flag validation | T-FC-001–005 | 1 | Required |
| Local workflow discovery | T-FC-010–011 | 1 | Required |
| Remote workflow discovery | T-FC-012–013 | 2 | Required |
| Remote discovery rate-limit backoff and partial results | T-FC-030 | 2 | Required |
| Data sampling with limit and window | T-FC-020–021 | 1 | Required |
| Missing artifact graceful handling | T-FC-022 | 1 | Required |
| Nil projection for empty sample | T-FC-023 | 1 | Required |
| Knuth Poisson algorithm (λ ≤ 15) | T-FC-031 | 1 | Required |
| Normal approximation (λ > 15) | T-FC-032 | 1 | Required |
| Zero-λ projection | T-FC-033 | 1 | Required |
| Bootstrap resampling | T-FC-034 | 1 | Required |
| Bernoulli success filtering | T-FC-035 | 1 | Required |
| 10,000 trial count | T-FC-036 | 1 | Required |
| Percentile ordering | T-FC-037 | 1 | Required |
| P50 field consistency | T-FC-038 | 1 | Required |
| λ crossover threshold enforcement | T-FC-039–040 | 1 | Required |
| Episode grouping | T-FC-041–042 | 2 | Required |
| Episode table display logic | T-FC-043–044 | 2 | Required |
| Console output columns | T-FC-050 | 1 | Required |
| JSON schema conformance | T-FC-051–054 | 2 | Required |
| Experimental status warning | T-FC-055 | 1 | Required |
***
## 13. Sync Notes
[Section titled “13. Sync Notes”](#13-sync-notes)
This section maps normative forecast requirements to implementation files.
| Normative Area | Implementation File(s) |
| ------------------------------------------------------------------------- | ---------------------------------------------------- |
| Monte Carlo engine (Poisson/Bootstrap/Bernoulli) | `pkg/cli/forecast_montecarlo.go` |
| Forecast command orchestration and output fields | `pkg/cli/forecast.go`, `pkg/cli/forecast_command.go` |
| Workflow discovery, rate-limit backoff, and run sampling | `pkg/cli/forecast.go` |
| Forecast compliance tests (including rate-limit backoff and λ thresholds) | `pkg/cli/forecast_montecarlo_test.go` |
Sync procedure:
1. Update this specification when changing projection algorithms or thresholds.
2. Update corresponding Go implementation/tests in the files above in the same change.
3. Re-run forecast tests to verify normative parity.
Sync follow-up tasks:
* Add an implementation-level assertion that verbose diagnostics and JSON output are derived from the same `λ` value used by the Monte Carlo engine.
* Expand forecast fixtures to cover invalid/non-finite `λ` derivation paths and zero-projection fallback behavior.
* Re-review Appendix B whenever the Poisson branch threshold or `observed_runs_per_period` calculation changes.
***
## 14. Appendices
[Section titled “14. Appendices”](#14-appendices)
### Appendix A: Worked Example
[Section titled “Appendix A: Worked Example”](#appendix-a-worked-example)
#### A.1 Scenario
[Section titled “A.1 Scenario”](#a1-scenario)
A workflow named `ci-doctor` has the following historical sample over 30 days:
* 42 completed runs
* 5 runs missing `aw_info.json` (treated as 0 ET)
* ET observations (for the 37 runs with artifacts): range from 8,000 to 18,000, mean ≈ 12,500
* 38 successful runs (yield = 38/42 ≈ 0.905)
* Projection period: `month` (30 days)
#### A.2 Observed Rate
[Section titled “A.2 Observed Rate”](#a2-observed-rate)
```plaintext
observed_runs_per_period = (42 / 30) × 30 = 42.0
λ = 42.0
```
Since λ > 15, Normal approximation is used: `Normal(μ=42, σ=√42 ≈ 6.48)`.
#### A.3 Single Trial
[Section titled “A.3 Single Trial”](#a3-single-trial)
Draw `k ~ round(Normal(42, 6.48)) = 44` (example).
For each of the 44 runs:
1. Draw success: `Bernoulli(0.905)` → say 40 succeed.
2. For each of the 40 successful runs, draw one ET observation from the 37-item historical pool (bootstrap).
3. Sum the 40 ET draws.
One trial might yield: 40 × 12,200 (average draw) ≈ 488,000 ET.
#### A.4 After 10,000 Trials
[Section titled “A.4 After 10,000 Trials”](#a4-after-10000-trials)
Sorted trial totals (example summary):
```plaintext
P10 ≈ 415,000 (10th percentile — lower bound of 80% CI)
P50 ≈ 479,000 (median — headline projection)
P90 ≈ 545,000 (90th percentile — upper bound of 80% CI)
mean ≈ 481,000
std_dev ≈ 40,000
```
### Appendix B: Poisson Algorithm Selection Rationale
[Section titled “Appendix B: Poisson Algorithm Selection Rationale”](#appendix-b-poisson-algorithm-selection-rationale)
Knuth’s exact Poisson algorithm is used for small λ (≤ 15) because it produces exact integer draws from the Poisson distribution without bias. For large λ, the Poisson distribution converges to a Normal distribution (`N(λ, λ)`), making the Normal approximation computationally efficient and sufficiently accurate.
The threshold of λ = 15 is chosen as the crossover point where Normal approximation error is below 1% for the tails relevant to P10/P90 computation. This fixed crossover is mandated by **R-FC-060** and MUST NOT be changed without a specification revision.
### Appendix C: Bootstrap Resampling Rationale
[Section titled “Appendix C: Bootstrap Resampling Rationale”](#appendix-c-bootstrap-resampling-rationale)
Traditional projection models assume a parametric distribution (e.g., log-normal) for per-run token usage. Agentic workflow token usage is frequently multi-modal (e.g., simple tasks versus complex multi-step tasks) and exhibits heavy tails due to recursive sub-agent chains. Bootstrap resampling avoids distributional misspecification by directly sampling from the empirical distribution, preserving these characteristics faithfully. The tradeoff is that projections are bounded by observed extremes; extrapolation beyond observed maximum ET requires explicit assumption and is out of scope for this specification.
### Appendix D: Episode Count Lower-Bound Semantics
[Section titled “Appendix D: Episode Count Lower-Bound Semantics”](#appendix-d-episode-count-lower-bound-semantics)
For orchestrator workflows that primarily use `workflow_call` or `workflow_dispatch` triggers, episodes are initiated by calls from another workflow rather than directly by GitHub events. These cross-workflow links are embedded in `aw_info.json` artifacts and are unavailable during forecasting when artifacts cannot be retrieved. As a result, each received `workflow_call` is counted as a separate episode, causing the episode count to overcount episodes and undercount the linkage. This means `runs_per_episode` may appear closer to `1.0` than its true value. Callers MUST treat `sampled_episodes` as a lower-bound estimate in this scenario and SHOULD note this limitation in any capacity planning documents.
### Appendix E: Workflow Discovery Race Conditions
[Section titled “Appendix E: Workflow Discovery Race Conditions”](#appendix-e-workflow-discovery-race-conditions)
Remote discovery is inherently eventually consistent. Between the workflow listing call and subsequent run/artifact sampling calls, any workflow may be renamed, disabled, or deleted.
Conforming implementations SHOULD:
1. Use workflow IDs from the listing response as the stable key for subsequent requests.
2. Treat per-workflow 404/410 responses as recoverable partial failures.
3. Continue processing unaffected workflows and emit a warning for each raced workflow.
### Appendix F: Safeguards
[Section titled “Appendix F: Safeguards”](#appendix-f-safeguards)
#### F.1 Threat Model
[Section titled “F.1 Threat Model”](#f1-threat-model)
* **Credential scope abuse**: Over-scoped credentials could allow unauthorized repository access.
* **Artifact privacy leakage**: `aw_info.json` artifacts may contain operationally sensitive ET metadata and prompt-adjacent context.
* **Rate-limit abuse**: Aggressive polling or unrestricted retries can amplify API pressure and trigger organizational throttling.
#### F.2 Required Mitigations
[Section titled “F.2 Required Mitigations”](#f2-required-mitigations)
* **Credential scope**: The forecast command accesses the GitHub Actions API using `gh` CLI credentials. Token permissions MUST include only the minimum required scope (`actions:read` for target repositories).
* **Artifact privacy**: Implementations MUST NOT log raw artifact payloads at default verbosity and SHOULD redact prompt-adjacent fields in diagnostic output.
* **Rate-limit abuse controls**: Implementations MUST implement bounded retry/backoff behavior and MUST stop retrying when the retry budget is exhausted.
* **Remote repository access**: When `--repo` targets a repository the caller does not own, the caller MUST have explicit read access. Implementations MUST NOT bypass repository access controls.
* **JSON output handling**: The JSON schema can expose model and usage topology; operators SHOULD treat it as internal data and apply least-privilege access controls.
#### F.3 Residual Risk
[Section titled “F.3 Residual Risk”](#f3-residual-risk)
Even with these safeguards, operators with valid read access can still infer workload intensity from forecast outputs. This residual risk is accepted and MUST be managed through repository visibility and access-governance controls.
***
## 15. References
[Section titled “15. References”](#15-references)
### Normative References
[Section titled “Normative References”](#normative-references)
* **\[RFC 2119]** Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, March 1997.
* **\[RFC 3339]** Klyne, G. and Newman, C., “Date and Time on the Internet: Timestamps”, RFC 3339, July 2002.
* **\[ET-SPEC]** GitHub Agentic Workflows Team, “Effective Tokens Specification”. [effective-tokens-specification](/gh-aw/reference/effective-tokens-specification/)
* **\[EXP-SPEC]** GitHub Agentic Workflows Team, “A/B Experiments Specification”. [experiments-specification](/gh-aw/practices/experiments-specification/)
### Informative References
[Section titled “Informative References”](#informative-references)
* **\[KNUTH-TAOCP]** Knuth, D.E., “The Art of Computer Programming, Volume 2: Seminumerical Algorithms”, 3rd edition. Section 3.4.1 (Poisson distribution generation algorithm).
* **\[BOOTSTRAP]** Efron, B. and Tibshirani, R., “An Introduction to the Bootstrap”, Chapman & Hall, 1993.
* **\[GH-ACTIONS-API]** GitHub, “GitHub Actions REST API Reference”.
***
## 16. Change Log
[Section titled “16. Change Log”](#16-change-log)
### Version 0.1.0 (Experimental Draft)
[Section titled “Version 0.1.0 (Experimental Draft)”](#version-010-experimental-draft)
* Updated remote discovery requirements with workflow-race mitigation guidance (R-DISC-014)
* Added optional JSON lower-bound disclosure field `episode_count_is_lower_bound` and recommendation R-OUT-013 (without reassigning existing R-OUT-010..012 semantics)
* Added Appendix F safeguards format (threat model, mitigations, residual risk)
* Initial specification for `gh aw forecast` command
* Defined command interface: flags `--days`, `--period`, `--sample`, `--repo`, `--json`, `--verbose`
* Defined local and remote workflow discovery modes
* Defined data sampling procedure and per-run metric derivation
* Defined Monte Carlo projection engine with Poisson + bootstrap algorithm
* Defined episode analysis with lower-bound semantics for orchestrator workflows
* Defined console table output format
* Defined JSON output schema (Sections 9.2.1–9.2.6)
* Defined error handling and exit codes
* Defined compliance test suite (T-FC-001 through T-FC-055)
* Added appendices: worked example, algorithm rationale, and safeguards
***
*Copyright 2026 GitHub Agentic Workflows Team. All rights reserved.*
# Fork Support
> How GitHub Agentic Workflows behaves in forked repositories and how to allow PRs from trusted forks.
GitHub Agentic Workflows has two distinct fork scenarios with different behaviors: **inbound pull requests from forks** and **running workflows inside a forked repository**.
## Running workflows in a fork
[Section titled “Running workflows in a fork”](#running-workflows-in-a-fork)
Agentic workflows do **not** run in forked repositories. When a workflow runs in a fork, all jobs skip automatically using the `if: ${{ !github.event.repository.fork }}` condition injected at compile time.
This means:
* Agent jobs are skipped — no AI execution occurs
* Maintenance and self-update jobs do not run
* No secrets from the upstream repository are available
This is intentional. Forks lack the secrets and context required for agentic workflows to function correctly, and there is no safe way to run agents with partial configuration.
Note
To run agentic workflows in your own repository, fork the upstream repo and configure your own secrets — the workflows will then run in your copy of the repository, which is not a fork from GitHub Actions’ perspective.
## Inbound pull requests from forks
[Section titled “Inbound pull requests from forks”](#inbound-pull-requests-from-forks)
When a pull request is opened from a fork to your repository, the default behavior is to **block the workflow from running** — the `pull_request` trigger includes a repository ID check that verifies the PR head branch comes from the same repository.
To allow workflows to run for PRs from trusted fork repositories, use the `forks:` field:
```aw
---
on:
pull_request:
types: [opened, synchronize]
forks: ["trusted-org/*"]
---
```
### Fork patterns
[Section titled “Fork patterns”](#fork-patterns)
The `forks:` field accepts a string or a list of repository patterns:
| Pattern | Matches |
| -------------- | ---------------------------------------------- |
| `"*"` | All forks (use with caution) |
| `"owner/*"` | All forks from a specific user or organization |
| `"owner/repo"` | A specific fork repository |
```aw
---
on:
pull_request:
types: [opened, synchronize]
forks:
- "trusted-org/*"
- "partner/specific-fork"
---
```
Caution
Allowing all forks (`"*"`) means any user who forks your repository can trigger agent execution. Workflows triggered from fork PRs run with the permissions configured in the workflow — review those permissions carefully before allowing untrusted forks.
# Frontmatter
> Complete guide to all available frontmatter configuration options for GitHub Agentic Workflows, including triggers, permissions, AI engines, and workflow settings.
The [frontmatter](/gh-aw/reference/glossary/#frontmatter) (YAML configuration section between `---` markers) of GitHub Agentic Workflows includes the triggers, permissions, AI [engines](/gh-aw/reference/glossary/#engine) (which AI model/provider to use), and workflow settings. For example:
```yaml
---
on:
issues:
types: [opened]
tools:
edit:
bash: ["gh issue comment"]
---
...markdown instructions...
```
## Frontmatter Elements
[Section titled “Frontmatter Elements”](#frontmatter-elements)
Below is a comprehensive reference to all available frontmatter fields for GitHub Agentic Workflows.
### Description (`description:`)
[Section titled “Description (description:)”](#description-description)
Provides a human-readable description of the workflow rendered as a comment in the generated lock file.
```yaml
description: "Workflow that analyzes pull requests and provides feedback"
```
### Emoji (`emoji:`)
[Section titled “Emoji (emoji:)”](#emoji-emoji)
An optional emoji to represent the workflow visually, for example in listings and UI surfaces.
```yaml
emoji: ""
```
### Labels (`labels:`)
[Section titled “Labels (labels:)”](#labels-labels)
Optional array of strings for categorizing and organizing workflows. Labels are displayed in `gh aw status` command output and can be filtered using the `--label` flag.
```yaml
labels: ["automation", "ci", "diagnostics"]
```
Labels help organize workflows by purpose, team, or functionality. They appear in status command table output as `[automation ci diagnostics]` and as a JSON array in `--json` mode. Filter workflows by label using `gh aw status --label automation`.
### Metadata (`metadata:`)
[Section titled “Metadata (metadata:)”](#metadata-metadata)
Optional key-value pairs for storing custom metadata compatible with the [GitHub Copilot custom agent spec](https://docs.github.com/en/copilot/reference/custom-agents-configuration).
```yaml
metadata:
author: John Doe
version: 1.0.0
category: automation
```
**Constraints:**
* Keys: 1-64 characters
* Values: Maximum 1024 characters
* Only string values are supported
Metadata provides a flexible way to add descriptive information to workflows without affecting execution.
### Trigger Events (`on:`)
[Section titled “Trigger Events (on:)”](#trigger-events-on)
The `on:` section uses standard GitHub Actions syntax to define workflow triggers, with additional fields for security and approval controls:
* Standard GitHub Actions triggers (push, pull\_request, issues, schedule, etc.)
* `reaction:` - Add emoji reactions to triggering items
* `status-comment:` - Post a started/completed comment with a workflow run link (automatically enabled for `slash_command` and `label_command` triggers; must be explicitly set to `true` for other trigger types). Accepts a boolean or an object with optional `issues`, `pull-requests`, and `discussions` toggle fields to selectively disable status comments for specific target types.
* `stop-after:` - Automatically disable triggers after a deadline
* `manual-approval:` - Require manual approval using environment protection rules
* `forks:` - Configure fork filtering for pull\_request triggers
* `skip-roles:` - Skip workflow execution for specific repository roles
* `skip-bots:` - Skip workflow execution for specific GitHub actors
* `skip-author-associations:` - Skip execution for configured event + `author_association` combinations
* `roles:` - Restrict which repository roles can trigger the workflow (default: `[admin, maintainer, write]`)
* `bots:` - Allow specific bot accounts to trigger the workflow
* `skip-if-match:` - Skip execution when a search query has matches (supports `scope: none`; use top-level `on.github-token` / `on.github-app` for custom auth)
* `skip-if-no-match:` - Skip execution when a search query has no matches (supports `scope: none`; use top-level `on.github-token` / `on.github-app` for custom auth)
* `steps:` - Inject custom deterministic steps into the pre-activation job (saves one workflow job vs. multi-job pattern)
* `permissions:` - Grant additional GitHub token scopes to the pre-activation job (for use with `on.steps:` API calls)
* `needs:` - Add custom job dependencies that both `pre_activation` and `activation` must wait for
* `github-token:` - Custom token for activation job reactions, status comments, and skip-if search queries
* `github-app:` - GitHub App for minting a short-lived token used by the activation job and all skip-if search steps
See [Trigger Events](/gh-aw/reference/triggers/) for complete documentation.
### Conditional Execution (`if:`)
[Section titled “Conditional Execution (if:)”](#conditional-execution-if)
Standard GitHub Actions `if:` syntax:
```yaml
if: github.event_name == 'push'
```
### Imports (`imports:`)
[Section titled “Imports (imports:)”](#imports-imports)
Share and reuse workflow components across multiple workflows. The `imports:` field in frontmatter (or `{{#import ...}}` in markdown) composes shared tools, steps, MCP servers, and prompts from other workflow files.
```yaml
imports:
- shared/common-tools.md
- shared/mcp/tavily.md
```
See [Imports](/gh-aw/reference/imports/) for complete documentation on syntax, shared components, APM package dependencies, and composition patterns.
### Custom Steps and Jobs (`steps:`, `pre-agent-steps:`, `post-steps:`, `jobs:`)
[Section titled “Custom Steps and Jobs (steps:, pre-agent-steps:, post-steps:, jobs:)”](#custom-steps-and-jobs-steps-pre-agent-steps-post-steps-jobs)
Add deterministic steps before or after agentic execution, or define full custom GitHub Actions jobs that run before the agent. See [Custom Steps and Jobs](/gh-aw/reference/steps-jobs/) for complete documentation.
### Cache Configuration (`cache:`)
[Section titled “Cache Configuration (cache:)”](#cache-configuration-cache)
Cache configuration using standard GitHub Actions `actions/cache` syntax:
Single cache:
```yaml
cache:
key: node-modules-${{ hashFiles('package-lock.json') }}
path: node_modules
restore-keys: |
node-modules-
```
### Repository Checkout (`checkout:`)
[Section titled “Repository Checkout (checkout:)”](#repository-checkout-checkout)
Configure how `actions/checkout` is invoked in the agent job. Override default checkout settings or check out multiple repositories for cross-repository workflows.
Set `checkout: false` to disable the default repository checkout entirely — useful for workflows that access repositories through MCP servers or other mechanisms that do not require a local clone:
```yaml
checkout: false
```
See [Cross-Repository Operations](/gh-aw/reference/cross-repository/) for complete documentation on checkout configuration options (including `fetch:`, `checkout: false`), merging behavior, and cross-repo examples.
### Permissions (`permissions:`)
[Section titled “Permissions (permissions:)”](#permissions-permissions)
The `permissions:` section uses a syntax similar to standard GitHub Actions permissions syntax to specify the GitHub read permissions relevant to the agentic (natural language) part of the execution of the workflow. See [GitHub Tools Read Permissions](/gh-aw/reference/permissions/).
### AI Engine (`engine:`)
[Section titled “AI Engine (engine:)”](#ai-engine-engine)
Specifies which AI engine interprets the markdown section. See [AI Engines](/gh-aw/reference/engines/) for details.
```yaml
engine: copilot
```
### Network Permissions (`network:`)
[Section titled “Network Permissions (network:)”](#network-permissions-network)
Controls network access using ecosystem identifiers and domain allowlists. See [Network Permissions](/gh-aw/reference/network/) for full documentation.
```yaml
network:
allowed:
- defaults # Basic infrastructure
- python # Python/PyPI ecosystem
- "api.example.com" # Custom domain
```
### Tools (`tools:`)
[Section titled “Tools (tools:)”](#tools-tools)
Specifies which GitHub API calls, bash commands, browser automation, and MCP servers are available to the AI agent.
```yaml
tools:
edit:
bash: ["gh issue comment"]
github:
toolsets: [default]
```
See [Tools](/gh-aw/reference/tools/) for complete documentation on built-in tools, GitHub toolsets, and MCP server configuration.
### MCP Scripts (`mcp-scripts:`)
[Section titled “MCP Scripts (mcp-scripts:)”](#mcp-scripts-mcp-scripts)
Enables defining custom MCP tools inline using JavaScript or shell scripts. See [MCP Scripts](/gh-aw/reference/mcp-scripts/) for complete documentation on creating custom tools with controlled secret access.
### Safe Outputs (`safe-outputs:`)
[Section titled “Safe Outputs (safe-outputs:)”](#safe-outputs-safe-outputs)
Enables automatic issue creation, comment posting, and other safe outputs. See [Safe Outputs Processing](/gh-aw/reference/safe-outputs/).
### Run Configuration (`run-name:`, `runs-on:`, `runs-on-slim:`, `timeout-minutes:`)
[Section titled “Run Configuration (run-name:, runs-on:, runs-on-slim:, timeout-minutes:)”](#run-configuration-run-name-runs-on-runs-on-slim-timeout-minutes)
Standard GitHub Actions properties:
```yaml
run-name: "Custom workflow run name" # Defaults to workflow name
runs-on: ubuntu-latest # Defaults to ubuntu-latest (main job only)
runs-on-slim: ubuntu-slim # Defaults to ubuntu-slim (framework jobs only)
timeout-minutes: 30 # Defaults to 20 minutes
```
`runs-on` applies to the main agent job only. `runs-on-slim` applies to all framework/generated jobs (activation, safe-outputs, unlock, etc.) and defaults to `ubuntu-slim`. `safe-outputs.runs-on` takes precedence over `runs-on-slim` for safe-output jobs specifically.
`timeout-minutes` accepts either an integer or a GitHub Actions expression string. This allows `workflow_call` reusable workflows to parameterize the timeout via caller inputs:
```yaml
# Literal integer
timeout-minutes: 30
# Expression — useful in reusable (workflow_call) workflows
timeout-minutes: ${{ inputs.timeout }}
```
**Supported runners for `runs-on:`**
| Runner | Status |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `ubuntu-latest` | ✓ Default. Recommended for most workflows. |
| `ubuntu-24.04` / `ubuntu-22.04` | ✓ Supported. |
| `ubuntu-24.04-arm` | ✓ Supported. Linux ARM64 runner. |
| `macos-*` | ✗ Not supported. Docker is unavailable on macOS runners (no nested virtualization). See [FAQ](/gh-aw/reference/faq/). |
| `windows-*` | ✗ Not supported. AWF requires Linux. |
### Workflow Concurrency Control (`concurrency:`)
[Section titled “Workflow Concurrency Control (concurrency:)”](#workflow-concurrency-control-concurrency)
Automatically generates concurrency policies for the agent job. See [Concurrency Control](/gh-aw/reference/concurrency/).
### Environment Variables (`env:`)
[Section titled “Environment Variables (env:)”](#environment-variables-env)
Standard GitHub Actions `env:` syntax for workflow-level environment variables:
```yaml
env:
CUSTOM_VAR: "value"
```
Environment variables can be defined at multiple scopes (workflow, job, step, engine, safe-outputs, etc.) with clear precedence rules. See [Environment Variables](/gh-aw/reference/environment-variables/) for complete documentation on all 13 env scopes and precedence order.
Caution
Do not use `${{ secrets.* }}` expressions in the workflow-level `env:` section. Environment variables defined here are passed directly to the agent container, which means secret values would be visible to the AI model. In strict mode, this is a compilation error. In non-strict mode, it emits a warning.
Use engine-specific secret configuration instead of the `env:` section to pass secrets securely.
### Effective Token Budget (`max-effective-tokens:`)
[Section titled “Effective Token Budget (max-effective-tokens:)”](#effective-token-budget-max-effective-tokens)
Sets the AWF effective-token budget used for cost enforcement. Defaults to `25000000` when omitted. Token steering (budget-warning messages at 80%, 90%, 95%, and 99% of the budget) is enabled by default. Set to a negative value to disable both budget enforcement and token steering.
```yaml
max-effective-tokens: 5000000
```
```yaml
# Disable budget enforcement and token steering
max-effective-tokens: -1
```
### Secrets (`secrets:`)
[Section titled “Secrets (secrets:)”](#secrets-secrets)
Defines secret values passed to workflow execution. Secrets are typically used to provide sensitive configuration to MCP servers or workflow components. Values must be GitHub Actions expressions that reference secrets (e.g., `${{ secrets.API_KEY }}`).
```yaml
secrets:
API_TOKEN: ${{ secrets.API_TOKEN }}
DATABASE_URL: ${{ secrets.DB_URL }}
```
Secrets can also include descriptions for documentation:
```yaml
secrets:
API_TOKEN:
value: ${{ secrets.API_TOKEN }}
description: "API token for external service"
DATABASE_URL:
value: ${{ secrets.DB_URL }}
description: "Production database connection string"
```
**Security best practices:**
* Always use GitHub Actions secret expressions (`${{ secrets.NAME }}`)
* Never commit plaintext secrets to workflow files
* Use environment-specific secrets when possible (via `environment:` field)
* Limit secret access to only the components that need them
**Note:** For passing secrets to reusable workflows, use the `jobs..secrets` field instead. The top-level `secrets:` field is for workflow-level secret configuration.
### Environment Protection (`environment:`)
[Section titled “Environment Protection (environment:)”](#environment-protection-environment)
Specifies the environment for deployment protection rules and environment-specific secrets. Standard GitHub Actions syntax.
```yaml
environment: production
```
See [GitHub Actions environment docs](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment).
### Container Configuration (`container:`)
[Section titled “Container Configuration (container:)”](#container-configuration-container)
Specifies a container to run job steps in.
```yaml
container: node:18
```
See [GitHub Actions container docs](https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/run-jobs-in-a-container).
### Service Containers (`services:`)
[Section titled “Service Containers (services:)”](#service-containers-services)
Defines service containers that run alongside your job (databases, caches, etc.).
```yaml
services:
postgres:
image: postgres:13
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
```
Note
The AWF agent runs inside an isolated Docker container. Service containers expose ports on the runner host, not within the agent’s network namespace. To connect to a service from the agent, use `host.docker.internal` as the hostname instead of `localhost`. For example, a Postgres service configured with port `5432:5432` is accessible at `host.docker.internal:5432`.
See [GitHub Actions service docs](https://docs.github.com/en/actions/using-containerized-services).
### Observability (`observability:`)
[Section titled “Observability (observability:)”](#observability-observability)
Use `observability.otlp` to export distributed traces from workflow runs to an OpenTelemetry Protocol (OTLP) compatible backend.
```yaml
observability:
otlp:
endpoint: ${{ secrets.OTLP_ENDPOINT }}
headers:
Authorization: ${{ secrets.OTLP_TOKEN }}
X-Tenant: my-org
```
`endpoint` accepts a string, a `{url, headers}` object, or an array of endpoint objects for fan-out. `headers` accepts a map or comma-separated `key=value` string. `if-missing` supports `error` (default), `warn`, and `ignore`.
For full OpenTelemetry reference details, including runtime variables, endpoint forms, span attributes, and artifact files, see [OpenTelemetry](/gh-aw/reference/open-telemetry/).
### Resources (`resources:`)
[Section titled “Resources (resources:)”](#resources-resources)
Declares additional workflow or action files to fetch alongside this workflow when running `gh aw add`. Use this field when the workflow depends on companion workflows or custom actions stored in the same directory.
```yaml
resources:
- triage-issue.md # companion workflow
- label-issue.md # companion workflow
- shared/helper-action.yml # supporting GitHub Action
```
Entries are relative paths from the workflow’s location in the source repository. GitHub Actions expression syntax (`${{`) is not allowed in resource paths.
When a user runs `gh aw add` to install this workflow, each listed file is also downloaded and placed alongside the main workflow in the target repository. This ensures companion workflows and custom actions the main workflow depends on are available after installation.
In addition to files explicitly listed in `resources:`, `gh aw add` automatically fetches workflows referenced in the [`dispatch-workflow`](/gh-aw/reference/safe-outputs/#workflow-dispatch-dispatch-workflow) safe output.
### Runtimes (`runtimes:`)
[Section titled “Runtimes (runtimes:)”](#runtimes-runtimes)
Override default runtime versions for languages and tools used in workflows. The compiler automatically detects runtime requirements from tool configurations and workflow steps, then installs the specified versions.
**Format**: Object with runtime name as key and configuration as value
**Fields per runtime**:
* `version`: Runtime version string (required)
* `action-repo`: Custom GitHub Actions setup action (optional, overrides default)
* `action-version`: Version of the setup action (optional, overrides default)
**Supported runtimes**:
| Runtime | Default Version | Default Setup Action |
| --------- | --------------- | -------------------------- |
| `node` | 24 | `actions/setup-node@v6` |
| `python` | 3.12 | `actions/setup-python@v5` |
| `go` | 1.25 | `actions/setup-go@v5` |
| `uv` | latest | `astral-sh/setup-uv@v5` |
| `bun` | 1.1 | `oven-sh/setup-bun@v2` |
| `deno` | 2.x | `denoland/setup-deno@v2` |
| `ruby` | 3.3 | `ruby/setup-ruby@v1` |
| `java` | 21 | `actions/setup-java@v4` |
| `dotnet` | 8.0 | `actions/setup-dotnet@v4` |
| `elixir` | 1.17 | `erlef/setup-beam@v1` |
| `haskell` | 9.10 | `haskell-actions/setup@v2` |
**Examples**:
Override Node.js version:
```yaml
runtimes:
node:
version: "22"
```
Use specific Python version with custom setup action:
```yaml
runtimes:
python:
version: "3.12"
action-repo: "actions/setup-python"
action-version: "v5"
```
Multiple runtime overrides:
```yaml
runtimes:
node:
version: "20"
python:
version: "3.11"
go:
version: "1.22"
```
**Default Behavior**: If not specified, workflows use default runtime versions as defined in the system. The compiler automatically detects which runtimes are needed based on tool configurations (e.g., `bash: ["node"]`, `bash: ["python"]`) and workflow steps.
**Use Cases**:
* Pin specific runtime versions for reproducibility
* Use preview/beta runtime versions for testing
* Use custom setup actions (forks, enterprise mirrors)
* Override system defaults for compatibility requirements
**Note**: Runtimes from imported shared workflows are automatically merged with your workflow’s runtime configuration.
### Source Tracking (`source:`)
[Section titled “Source Tracking (source:)”](#source-tracking-source)
Tracks workflow origin in format `owner/repo/path@ref`. Automatically populated when using `gh aw add` to install workflows from external repositories. Optional for manually created workflows.
```yaml
source: "githubnext/agentics/workflows/ci-doctor.md@v1.0.0"
```
### Redirect (`redirect:`)
[Section titled “Redirect (redirect:)”](#redirect-redirect)
Specifies a new canonical location when a workflow has been moved or renamed. `gh aw add`, `gh aw add-wizard`, and `gh aw update` follow redirect chains to the resolved location for remote workflows. During add/update flows, the local `source` field is written (or rewritten) to the resolved location, and redirect loops are detected and reported as errors.
```yaml
redirect: "githubnext/agentics/workflows/new-workflow-name.md@main"
```
Use `gh aw update --no-redirect` to refuse updates when the source workflow has a `redirect` field — the update fails rather than following the redirect. This is useful for auditing or when you want to explicitly control when redirects are followed.
`gh aw compile` emits an informational message when a workflow has a `redirect` field configured, so the redirect is visible during local development.
The `redirect` field uses the same `owner/repo/path@ref` format as `source:`. Redirect chains are followed transitively (up to a depth limit).
Note
The `redirect` field is set by workflow *authors* to signal that a workflow has moved. It is not typically set by end-users. If you see a redirect when running `gh aw update`, it means the upstream workflow has been relocated.
### Private Workflows (`private:`)
[Section titled “Private Workflows (private:)”](#private-workflows-private)
Mark a workflow as private to prevent it from being installed into other repositories via `gh aw add`.
```yaml
private: true
```
When `private: true` is set, attempting to add the workflow from another repository will fail with an error:
```plaintext
workflow 'owner/repo/internal-tooling' is private and cannot be added to other repositories
```
Use this field for internal tooling, sensitive automation, or workflows that depend on repository-specific context and are not intended for external reuse.
The `private:` field only blocks installation via `gh aw add`. It does not affect the visibility of the workflow file itself — that is controlled by your repository’s access settings.
### Feature Flags (`features:`)
[Section titled “Feature Flags (features:)”](#feature-flags-features)
Enable experimental or optional compiler and runtime behaviors as key-value pairs. See [Feature Flags](/gh-aw/reference/feature-flags/) for complete documentation.
### Strict Mode (`strict:`)
[Section titled “Strict Mode (strict:)”](#strict-mode-strict)
Disables enhanced security validation for production workflows.
```yaml
strict: false # Disable for development/testing
```
Workflows compiled with `strict: false` cannot run on public repositories. The workflow fails at runtime with an error message prompting recompilation with strict mode.
See [Network Permissions - Strict Mode Validation](/gh-aw/reference/network/#strict-mode-validation) for details on network validation and [CLI Commands](/gh-aw/setup/cli/#compile) for compilation options.
## Related Documentation
[Section titled “Related Documentation”](#related-documentation)
See also: [Trigger Events](/gh-aw/reference/triggers/), [AI Engines](/gh-aw/reference/engines/), [CLI Commands](/gh-aw/setup/cli/), [Workflow Structure](/gh-aw/reference/workflow-structure/), [Network Permissions](/gh-aw/reference/network/), [Feature Flags](/gh-aw/reference/feature-flags/), [Custom Steps and Jobs](/gh-aw/reference/steps-jobs/), [OpenTelemetry](/gh-aw/reference/open-telemetry/), [Command Triggers](/gh-aw/reference/command-triggers/), [MCPs](/gh-aw/guides/mcps/), [Tools](/gh-aw/reference/tools/), [Imports](/gh-aw/reference/imports/)
# Frontmatter Reference
> Complete JSON Schema-based reference for all GitHub Agentic Workflows frontmatter configuration options with YAML examples.
This document provides a comprehensive reference for all available frontmatter configuration options in GitHub Agentic Workflows. The examples below are generated from the JSON Schema and include inline comments describing each field.
Note
This documentation is automatically generated from the JSON Schema. For a more user-friendly guide, see [Frontmatter](/gh-aw/reference/frontmatter/).
## Schema Description
[Section titled “Schema Description”](#schema-description)
JSON Schema for validating agentic workflow frontmatter configuration
## Complete Frontmatter Reference
[Section titled “Complete Frontmatter Reference”](#complete-frontmatter-reference)
```yaml
---
# Workflow name that appears in the GitHub Actions interface. If not specified,
# defaults to the filename without extension.
# (optional)
name: "My Workflow"
# Optional workflow description that is rendered as a comment in the generated
# GitHub Actions YAML file (.lock.yml)
# (optional)
description: "Description of the workflow"
# Optional emoji to represent the workflow visually in listings and UI surfaces.
# (optional)
emoji: "example-value"
# Optional source reference indicating where this workflow was added from. Format:
# owner/repo/path@ref (e.g., githubnext/agentics/workflows/ci-doctor.md@v1.0.0).
# Rendered as a comment in the generated lock file.
# (optional)
source: "example-value"
# Optional workflow location redirect for updates. Format: workflow spec or GitHub
# URL (e.g., owner/repo/path@ref or
# https://github.com/owner/repo/blob/main/path.md). When present, update follows
# this location and rewrites source.
# (optional)
redirect: "example-value"
# Optional tracker identifier to tag all created assets (issues, discussions,
# comments, pull requests). Must be at least 8 characters and contain only
# alphanumeric characters, hyphens, and underscores. This identifier will be
# inserted in the body/description of all created assets to enable searching and
# retrieving assets associated with this workflow.
# (optional)
tracker-id: "example-value"
# Optional array of labels to categorize and organize workflows. Labels can be
# used to filter workflows in status/list commands.
# (optional)
labels: []
# Array of strings
# Optional metadata field for storing custom key-value pairs compatible with the
# custom agent spec. Key names are limited to 64 characters, and values are
# limited to 1024 characters.
# (optional)
metadata:
{}
# Workflow specifications to import. Supports array form (list of paths) or object
# form with 'aw' (agentic workflow paths) subfield. Path resolution: (1) relative
# paths (e.g., 'shared/file.md') are resolved relative to the workflow's
# directory; (2) paths starting with '.github/' or '/' are resolved from the
# repository root (repo-root-relative); (3) paths matching 'owner/repo/path@ref'
# are fetched from GitHub at compile time (cross-repo).
# (optional)
# Accepted formats:
# Format 1: Array of workflow specifications to import. Three path formats are
# supported: relative paths ('shared/file.md'), repo-root-relative paths
# ('.github/agents/my-agent.md'), and cross-repo paths ('owner/repo/path@ref').
# Any markdown files under .github/agents directory are treated as custom agent
# files and only one agent file is allowed per workflow.
imports: []
# Array items: undefined
# Format 2: Object form of imports with 'aw' subfield for shared agentic workflow
# paths.
imports:
# Array of shared agentic workflow specifications to import. Format:
# owner/repo/path@ref or relative paths.
# (optional)
aw: []
# Optional list of additional workflow or action files that should be fetched
# alongside this workflow when running 'gh aw add'. Entries are relative paths
# (from the same directory as this workflow in the source repository) to agentic
# workflow .md files or GitHub Actions .yml/.yaml files. GitHub Actions expression
# syntax (${{) is not allowed in resource paths.
# (optional)
resources: []
# Array of Relative path to a workflow .md file or action .yml/.yaml file. Must be
# a static path; GitHub Actions expression syntax (${{) is not allowed.
# If true, inline all imports (including those without inputs) at compilation time
# in the generated lock.yml instead of using runtime-import macros. When enabled,
# the frontmatter hash covers the entire markdown body so any change to the
# content will invalidate the hash.
# (optional)
inlined-imports: true
# Workflow triggers that define when the agentic workflow should run. Supports
# standard GitHub Actions trigger events plus special command triggers for
# /commands (required)
# Accepted formats:
# Format 1: Simple trigger event name (e.g., 'push', 'issues', 'pull_request',
# 'discussion', 'schedule', 'fork', 'create', 'delete', 'public', 'watch',
# 'workflow_call'), schedule shorthand (e.g., 'daily', 'weekly'), or slash command
# shorthand (e.g., '/my-bot' expands to slash_command + workflow_dispatch)
on: "example-value"
# Format 2: Complex trigger configuration with event-specific filters and options
on:
# Special slash command trigger for /command workflows (e.g., '/my-bot' in issue
# comments). Creates conditions to match slash commands automatically. Note: Can
# be combined with issues/pull_request events if those events only use 'labeled'
# or 'unlabeled' types.
# (optional)
# Accepted formats:
# Format 1: Null command configuration - defaults to using the workflow filename
# (without .md extension) as the command name
slash_command: null
# Format 2: Command name as a string (shorthand format, e.g., 'customname' for
# '/customname' triggers). Command names must not start with '/' as the slash is
# automatically added when matching commands.
slash_command: "example-value"
# Format 3: Command configuration object with custom command name
slash_command:
# Name of the slash command that triggers the workflow (e.g., '/help',
# '/analyze'). Used for comment-based workflow activation.
# (optional)
# Accepted formats:
# Format 1: Single command name for slash commands (e.g., 'helper-bot' for
# '/helper-bot' triggers). Command names must not start with '/' as the slash is
# automatically added when matching commands. Defaults to workflow filename
# without .md extension if not specified.
name: "My Workflow"
# Format 2: Array of command names that trigger this workflow (e.g., ['cmd.add',
# 'cmd.remove'] for '/cmd.add' and '/cmd.remove' triggers). Each command name must
# not start with '/'.
name: []
# Array items: Command name without leading slash
# Events where the command should be active. Default is all comment-related events
# ('*'). Use GitHub Actions event names.
# (optional)
# Accepted formats:
# Format 1: Single event name or '*' for all events. Use GitHub Actions event
# names: 'issues', 'issue_comment', 'pull_request_comment', 'pull_request',
# 'pull_request_review_comment', 'discussion', 'discussion_comment'.
events: "*"
# Format 2: Array of event names where the command should be active (requires at
# least one). Use GitHub Actions event names.
events: []
# Array items: GitHub Actions event name.
# Slash command trigger compilation strategy. 'inline' (default) compiles direct
# comment listeners in this workflow. 'centralized' compiles this workflow as
# workflow_dispatch-centric and routes slash events via the generated central
# trigger workflow.
# (optional)
strategy: "inline"
# DEPRECATED: Use 'slash_command' instead. Special command trigger for /command
# workflows (e.g., '/my-bot' in issue comments). Creates conditions to match slash
# commands automatically.
# (optional)
# Accepted formats:
# Format 1: Null command configuration - defaults to using the workflow filename
# (without .md extension) as the command name
command: null
# Format 2: Command name as a string (shorthand format, e.g., 'customname' for
# '/customname' triggers). Command names must not start with '/' as the slash is
# automatically added when matching commands.
command: "example-value"
# Format 3: Command configuration object with custom command name
command:
# Name of the slash command that triggers the workflow (e.g., '/deploy', '/test').
# Used for command-based workflow activation.
# (optional)
# Accepted formats:
# Format 1: Custom command name for slash commands (e.g., 'helper-bot' for
# '/helper-bot' triggers). Command names must not start with '/' as the slash is
# automatically added when matching commands. Defaults to workflow filename
# without .md extension if not specified.
name: "My Workflow"
# Format 2: Array of command names that trigger this workflow (e.g., ['cmd.add',
# 'cmd.remove'] for '/cmd.add' and '/cmd.remove' triggers). Each command name must
# not start with '/'.
name: []
# Array items: Command name without leading slash
# Events where the command should be active. Default is all comment-related events
# ('*'). Use GitHub Actions event names.
# (optional)
# Accepted formats:
# Format 1: Single event name or '*' for all events. Use GitHub Actions event
# names: 'issues', 'issue_comment', 'pull_request_comment', 'pull_request',
# 'pull_request_review_comment', 'discussion', 'discussion_comment'.
events: "*"
# Format 2: Array of event names where the command should be active (requires at
# least one). Use GitHub Actions event names.
events: []
# Array items: GitHub Actions event name.
# On Label Command trigger: fires when a specific label is added to an issue, pull
# request, or discussion. The triggering label is automatically removed at
# workflow start so it can be applied again to re-trigger. Use the 'events' field
# to restrict which item types (issues, pull_request, discussion) activate the
# trigger.
# (optional)
# Accepted formats:
# Format 1: Label name as a string (shorthand format). The workflow fires when
# this label is added to any supported item type (issue, pull request, or
# discussion).
label_command: "example-value"
# Format 2: Label command configuration object with label name(s) and optional
# event filtering.
label_command:
# Label name(s) that trigger the workflow when added to an issue, pull request, or
# discussion.
# (optional)
# Accepted formats:
# Format 1: Single label name that acts as a command (e.g., 'deploy' triggers the
# workflow when the 'deploy' label is added).
name: "My Workflow"
# Format 2: Array of label names — any of these labels will trigger the workflow.
name: []
# Array items: A label name
# Alternative to 'name': label name(s) that trigger the workflow.
# (optional)
# Accepted formats:
# Format 1: Single label name.
names: "example-value"
# Format 2: Array of label names — any of these labels will trigger the workflow.
names: []
# Array items: A label name
# Item types where the label-command trigger should be active. Default is all
# supported types: issues, pull_request, discussion.
# (optional)
# Accepted formats:
# Format 1: Single item type or '*' for all types.
events: "*"
# Format 2: Array of item types where the trigger is active.
events: []
# Array items: Item type.
# Whether to automatically remove the triggering label after the workflow starts.
# Defaults to true. Set to false to keep the label on the item and skip the
# label-removal step. When false, the issues:write and discussions:write
# permissions required for label removal are also omitted.
# (optional)
remove_label: true
# Label command trigger compilation strategy. 'inline' (default) compiles direct
# labeled listeners in this workflow. 'decentralized' compiles this workflow as
# workflow_dispatch-centric and routes labeled events via the generated
# agentic_commands.yml workflow.
# (optional)
strategy: "inline"
# Push event trigger that runs the workflow when code is pushed to the repository
# (optional)
push:
# Branches to filter on
# (optional)
branches: []
# Array of strings
# Branches to ignore
# (optional)
branches-ignore: []
# Array of strings
# Paths to filter on
# (optional)
paths: []
# Array of strings
# Paths to ignore
# (optional)
paths-ignore: []
# Array of strings
# List of git tag names or patterns to include for push events (supports
# wildcards)
# (optional)
tags: []
# Array of strings
# List of git tag names or patterns to exclude from push events (supports
# wildcards)
# (optional)
tags-ignore: []
# Array of strings
# Pull request event trigger that runs the workflow when pull requests are
# created, updated, or closed
# (optional)
pull_request:
# Pull request event types to trigger on. Note: 'converted_to_draft' and
# 'ready_for_review' represent state transitions (events) rather than states.
# While technically valid to listen for both, consider if you need to handle both
# transitions or just one.
# (optional)
types: []
# Array of strings
# Branches to filter on
# (optional)
branches: []
# Array of strings
# Branches to ignore
# (optional)
branches-ignore: []
# Array of strings
# Paths to filter on
# (optional)
paths: []
# Array of strings
# Paths to ignore
# (optional)
paths-ignore: []
# Array of strings
# Filter by draft pull request state. Set to false to exclude draft PRs, true to
# include only drafts, or omit to include both
# (optional)
draft: true
# When true, allows workflow to run on pull requests from forked repositories.
# Security consideration: fork PRs have limited permissions.
# (optional)
# Accepted formats:
# Format 1: Single fork pattern (e.g., '*' for all forks, 'org/*' for org glob,
# 'org/repo' for exact match)
forks: "example-value"
# Format 2: List of allowed fork repositories with glob support (e.g., 'org/repo',
# 'org/*', '*' for all forks)
forks: []
# Array items: Repository pattern with optional glob support
# Array of pull request type names that trigger the workflow. Filters workflow
# execution to specific PR categories.
# (optional)
# Accepted formats:
# Format 1: Single label name to filter labeled/unlabeled events (e.g., 'bug')
names: "example-value"
# Format 2: List of label names to filter labeled/unlabeled events. Only applies
# when 'labeled' or 'unlabeled' is in the types array
names: []
# Array items: Label name
# Issues event trigger that runs when repository issues are created, updated, or
# managed
# (optional)
issues:
# Types of issue events
# (optional)
types: []
# Array of strings
# Array of issue type names that trigger the workflow. Filters workflow execution
# to specific issue categories.
# (optional)
# Accepted formats:
# Format 1: Single label name to filter labeled/unlabeled events (e.g., 'bug')
names: "example-value"
# Format 2: List of label names to filter labeled/unlabeled events. Only applies
# when 'labeled' or 'unlabeled' is in the types array
names: []
# Array items: Label name
# Whether to lock the issue for the agent when the workflow runs (prevents
# concurrent modifications)
# (optional)
lock-for-agent: true
# Issue comment event trigger
# (optional)
issue_comment:
# Types of issue comment events
# (optional)
types: []
# Array of strings
# Whether to lock the parent issue for the agent when the workflow runs (prevents
# concurrent modifications)
# (optional)
lock-for-agent: true
# Discussion event trigger that runs the workflow when repository discussions are
# created, updated, or managed
# (optional)
discussion:
# Types of discussion events
# (optional)
types: []
# Array of strings
# Discussion comment event trigger that runs the workflow when comments on
# discussions are created, updated, or deleted
# (optional)
discussion_comment:
# Types of discussion comment events
# (optional)
types: []
# Array of strings
# Scheduled trigger events using fuzzy schedules or standard cron expressions.
# Supports shorthand string notation (e.g., 'daily', 'daily around 2pm') or array
# of schedule objects. Fuzzy schedules automatically distribute execution times to
# prevent load spikes.
# (optional)
# Accepted formats:
# Format 1: Shorthand schedule string using fuzzy or cron format. Examples:
# 'daily', 'daily around 14:00', 'daily between 9:00 and 17:00', 'weekly', 'weekly
# on monday', 'weekly on friday around 5pm', 'hourly', 'every 2h', 'every 10
# minutes', '0 9 * * 1'. Fuzzy schedules distribute execution times to prevent
# load spikes. For fixed times, use standard cron syntax. Minimum interval is 5
# minutes.
schedule: "example-value"
# Format 2: Array of schedule objects with cron expressions (standard cron or
# fuzzy format)
schedule: []
# Array items: object
# Manual workflow dispatch trigger
# (optional)
# Accepted formats:
# Format 1: Simple workflow dispatch trigger
workflow_dispatch: null
# Format 2: object
workflow_dispatch:
# Input parameters for manual dispatch
# (optional)
inputs:
{}
# Workflow run trigger
# (optional)
workflow_run:
# List of workflows to trigger on
# (optional)
workflows: []
# Array of strings
# Types of workflow run events
# (optional)
types: []
# Array of strings
# Branches to filter on
# (optional)
branches: []
# Array of strings
# Branches to ignore
# (optional)
branches-ignore: []
# Array of strings
# Release event trigger
# (optional)
release:
# Types of release events
# (optional)
types: []
# Array of strings
# Pull request review comment event trigger
# (optional)
pull_request_review_comment:
# Types of pull request review comment events
# (optional)
types: []
# Array of strings
# Branch protection rule event trigger that runs when branch protection rules are
# changed
# (optional)
branch_protection_rule:
# Types of branch protection rule events
# (optional)
types: []
# Array of strings
# Check run event trigger that runs when a check run is created, rerequested,
# completed, or has a requested action
# (optional)
check_run:
# Types of check run events
# (optional)
types: []
# Array of strings
# Check suite event trigger that runs when check suite activity occurs
# (optional)
check_suite:
# Types of check suite events
# (optional)
types: []
# Array of strings
# Create event trigger that runs when a Git reference (branch or tag) is created
# (optional)
# Accepted formats:
# Format 1: Simple create event trigger
create: null
# Format 2: object
create:
{}
# Delete event trigger that runs when a Git reference (branch or tag) is deleted
# (optional)
# Accepted formats:
# Format 1: Simple delete event trigger
delete: null
# Format 2: object
delete:
{}
# Deployment event trigger that runs when a deployment is created
# (optional)
# Accepted formats:
# Format 1: Simple deployment event trigger
deployment: null
# Format 2: object
deployment:
{}
# Deployment status event trigger that runs when a deployment status is updated
# (optional)
# Accepted formats:
# Format 1: Simple deployment status event trigger
deployment_status: null
# Format 2: object
deployment_status:
# Filter to specific deployment states (compiled into if condition). Use a string
# for one state or an array for multiple states.
# (optional)
# Accepted formats:
# Format 1: string
state: "error"
# Format 2: array
state: []
# Array items: string
# Fork event trigger that runs when someone forks the repository
# (optional)
# Accepted formats:
# Format 1: Simple fork event trigger
fork: null
# Format 2: object
fork:
{}
# Gollum event trigger that runs when someone creates or updates a Wiki page
# (optional)
# Accepted formats:
# Format 1: Simple gollum event trigger
gollum: null
# Format 2: object
gollum:
{}
# Label event trigger that runs when a label is created, edited, or deleted
# (optional)
label:
# Types of label events
# (optional)
types: []
# Array of strings
# Merge group event trigger that runs when a pull request is added to a merge
# queue
# (optional)
merge_group:
# Types of merge group events
# (optional)
types: []
# Array of strings
# Milestone event trigger that runs when a milestone is created, closed, opened,
# edited, or deleted
# (optional)
milestone:
# Types of milestone events
# (optional)
types: []
# Array of strings
# Page build event trigger that runs when someone pushes to a GitHub Pages
# publishing source branch
# (optional)
# Accepted formats:
# Format 1: Simple page build event trigger
page_build: null
# Format 2: object
page_build:
{}
# Public event trigger that runs when a repository changes from private to public
# (optional)
# Accepted formats:
# Format 1: Simple public event trigger
public: null
# Format 2: object
public:
{}
# Pull request target event trigger that runs in the context of the base
# repository (secure for fork PRs)
# (optional)
pull_request_target:
# List of pull request target event types to trigger on
# (optional)
types: []
# Array of strings
# Branches to filter on
# (optional)
branches: []
# Array of strings
# Branches to ignore
# (optional)
branches-ignore: []
# Array of strings
# Paths to filter on
# (optional)
paths: []
# Array of strings
# Paths to ignore
# (optional)
paths-ignore: []
# Array of strings
# Filter by draft pull request state
# (optional)
draft: true
# When true, allows workflow to run on pull requests from forked repositories with
# write permissions. Security consideration: use cautiously as fork PRs run with
# base repository permissions.
# (optional)
# Accepted formats:
# Format 1: Single fork pattern
forks: "example-value"
# Format 2: List of allowed fork repositories with glob support
forks: []
# Array items: string
# Pull request review event trigger that runs when a pull request review is
# submitted, edited, or dismissed
# (optional)
pull_request_review:
# Types of pull request review events
# (optional)
types: []
# Array of strings
# Registry package event trigger that runs when a package is published or updated
# (optional)
registry_package:
# Types of registry package events
# (optional)
types: []
# Array of strings
# Repository dispatch event trigger for custom webhook events
# (optional)
repository_dispatch:
# Custom event types to trigger on
# (optional)
types: []
# Array of strings
# Status event trigger that runs when the status of a Git commit changes
# (optional)
# Accepted formats:
# Format 1: Simple status event trigger
status: null
# Format 2: object
status:
{}
# Watch event trigger that runs when someone stars the repository
# (optional)
watch:
# Types of watch events
# (optional)
types: []
# Array of strings
# Workflow call event trigger that allows this workflow to be called by another
# workflow
# (optional)
# Accepted formats:
# Format 1: Simple workflow call event trigger
workflow_call: null
# Format 2: object
workflow_call:
# Input parameters that can be passed to the workflow when it is called
# (optional)
inputs:
{}
# Secrets that can be passed to the workflow when it is called
# (optional)
secrets:
{}
# Time when workflow should stop running. Supports multiple formats: absolute
# dates (YYYY-MM-DD HH:MM:SS, June 1 2025, 1st June 2025, 06/01/2025, etc.) or
# relative time deltas (+25h, +3d, +1d12h30m). Maximum values for time deltas:
# 12mo, 52w, 365d, 8760h (365 days). Note: Minute unit 'm' is not allowed for
# stop-after; minimum unit is hours 'h'.
# (optional)
stop-after: "example-value"
# Conditionally skip workflow execution when a GitHub search query has matches.
# Can be a string (query only, implies max=1) or an object with 'query', optional
# 'max', and 'scope' fields. Use top-level on.github-token or on.github-app for
# custom authentication.
# (optional)
# Accepted formats:
# Format 1: GitHub search query string to check before running workflow (implies
# max=1). If the search returns any results, the workflow will be skipped. Query
# is automatically scoped to the current repository. Example: 'is:issue is:open
# label:bug'
skip-if-match: "example-value"
# Format 2: Skip-if-match configuration object with query, maximum match count,
# and optional scope. For custom authentication use the top-level on.github-token
# or on.github-app fields.
skip-if-match:
# GitHub search query string to check before running workflow. Query is
# automatically scoped to the current repository.
query: "example-value"
# Maximum number of items that must be matched for the workflow to be skipped.
# Defaults to 1 if not specified. Supports integer or GitHub Actions expression
# (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Scope for the search query. Set to 'none' to disable the automatic
# 'repo:owner/repo' scoping, enabling org-wide or cross-repo queries.
# (optional)
scope: "none"
# Conditionally skip workflow execution when a GitHub search query has no matches
# (or fewer than minimum). Can be a string (query only, implies min=1) or an
# object with 'query', optional 'min', and 'scope' fields. Use top-level
# on.github-token or on.github-app for custom authentication.
# (optional)
# Accepted formats:
# Format 1: GitHub search query string to check before running workflow (implies
# min=1). If the search returns no results, the workflow will be skipped. Query is
# automatically scoped to the current repository. Example: 'is:pr is:open
# label:ready-to-deploy'
skip-if-no-match: "example-value"
# Format 2: Skip-if-no-match configuration object with query, minimum match count,
# and optional scope. For custom authentication use the top-level on.github-token
# or on.github-app fields.
skip-if-no-match:
# GitHub search query string to check before running workflow. Query is
# automatically scoped to the current repository.
query: "example-value"
# Minimum number of items that must be matched for the workflow to proceed.
# Defaults to 1 if not specified.
# (optional)
min: 1
# Scope for the search query. Set to 'none' to disable the automatic
# 'repo:owner/repo' scoping, enabling org-wide or cross-repo queries.
# (optional)
scope: "none"
# Skip workflow execution if any CI checks on the target branch are failing or
# pending. Accepts true (check all) or an object to filter specific checks by name
# and optionally specify a branch or allow pending checks.
# (optional)
# Accepted formats:
# Format 1: Bare key with no value — equivalent to true. Skips workflow execution
# if any CI checks on the target branch are currently failing.
skip-if-check-failing: null
# Format 2: Skip workflow execution if any CI checks on the target branch are
# currently failing. For pull_request events, checks the base branch. For other
# events, checks the current ref.
skip-if-check-failing: true
# Format 3: Skip-if-check-failing configuration object with optional
# include/exclude filter lists, an optional branch name, and an allow-pending
# flag.
skip-if-check-failing:
# List of check names to evaluate. When specified, only these named checks are
# considered. If omitted, all checks are evaluated.
# (optional)
include: []
# Array of strings
# List of check names to ignore. Checks in this list are not considered when
# determining whether to skip the workflow.
# (optional)
exclude: []
# Array of strings
# Branch name to check for failing CI checks. When omitted, defaults to the base
# branch of a pull_request event or the current ref for other events.
# (optional)
branch: "example-value"
# When true, pending or in-progress checks are not treated as failing. By default
# (false), any check that has not yet completed is treated as failing and will
# block the workflow.
# (optional)
allow-pending: true
# Skip workflow execution for users with specific repository roles. Useful for
# workflows that should only run for external contributors or specific permission
# levels.
# (optional)
# Accepted formats:
# Format 1: Single role to skip workflow for (e.g., 'admin'). If the triggering
# user has this role, the workflow will be skipped.
skip-roles: "example-value"
# Format 2: List of roles to skip workflow for (e.g., ['admin', 'maintainer',
# 'write']). If the triggering user has any of these roles, the workflow will be
# skipped.
skip-roles: []
# Array items: string
# Skip workflow execution for specific GitHub users. Useful for preventing
# workflows from running for specific accounts (e.g., bots, specific team
# members).
# (optional)
# Accepted formats:
# Format 1: Single GitHub username to skip workflow for (e.g., 'user1'). If the
# triggering user matches, the workflow will be skipped.
skip-bots: "example-value"
# Format 2: List of GitHub usernames to skip workflow for (e.g., ['user1',
# 'user2']). If the triggering user is in this list, the workflow will be skipped.
skip-bots: []
# Array items: string
# Skip workflow execution when an event-specific payload author_association field
# (for example: github.event.comment.author_association,
# github.event.issue.author_association,
# github.event.pull_request.author_association) matches configured associations
# for specific events. Keys are event names (for example: issue_comment,
# pull_request_review_comment, issues, pull_request). Values accept a single
# string or an array of strings. Association values are case-insensitive in
# frontmatter.
# (optional)
skip-author-associations:
{}
# Repository access roles required to trigger agentic workflows. Defaults to
# ['admin', 'maintainer', 'write'] for security. Use 'all' to allow any
# authenticated user (! security consideration).
# (optional)
# Accepted formats:
# Format 1: Single repository permission level that can trigger the workflow. Use
# 'all' to allow any authenticated user (! disables permission checking entirely
# - use with caution)
roles: "admin"
# Format 2: List of repository permission levels that can trigger the workflow.
# Permission checks are automatically applied to potentially unsafe triggers.
roles: []
# Array items: Repository permission level: 'admin' (full access),
# 'maintainer'/'maintain' (repository management), 'write' (push access), 'triage'
# (issue management), 'read' (read-only access)
# Allow list of bot identifiers that can trigger the workflow even if they don't
# meet the required role permissions. When the actor is in this list, the bot must
# be active (installed) on the repository to trigger the workflow.
# (optional)
bots: []
# Array of Bot identifier/name (e.g., 'dependabot[bot]', 'renovate[bot]',
# 'github-actions[bot]')
# Filter workflows triggered by pull_request_target (or other labeled events) to
# only fire when the triggering label matches one of these names. Generates a
# job-level if: condition on the pre-activation job so unmatched label events show
# as Skipped (⊘) rather than Failed (✗).
# (optional)
# Accepted formats:
# Format 1: Single label name that must match the triggering label (e.g.,
# 'panel-review')
labels: "example-value"
# Format 2: List of label names; the workflow fires when the triggering label
# matches any entry.
labels: []
# Array items: undefined
# Allow the bot-posted-menu / user-checks-box pattern: when a workflow posts a
# checkbox-menu comment as a GitHub App bot and a human maintainer edits it to
# tick a box (issue_comment:edited where actor ≠ comment.user.login), treat this
# as safe and skip the confused-deputy check. When false (default), the check
# applies to all issue_comment events. The Dependabot confused-deputy attack
# vector (issue_comment:created) is unaffected.
# (optional)
allow-bot-authored-trigger-comment: true
# Environment name that requires manual approval before the workflow can run. Must
# match a valid environment configured in the repository settings.
# (optional)
manual-approval: "example-value"
# AI reaction to add/remove on triggering item. Scalar form accepts one of: +1,
# -1, laugh, confused, heart, hooray, rocket, eyes, none. Object form implies
# enabled reactions and supports optional `issues`, `pull-requests`, and
# `discussions` fields to control trigger groups independently; use `type` to
# choose the reaction emoji (defaults to `eyes` when omitted). Use 'none' to
# disable reactions.
# (optional)
# Accepted formats:
# Format 1: string
reaction: "+1"
# Format 2: YAML parses +1 and -1 without quotes as integers. These are converted
# to +1 and -1 strings respectively.
reaction: 1
# Format 3: object
reaction:
# Reaction type. Defaults to 'eyes' when omitted.
# (optional)
# Accepted formats:
# Format 1: string
type: "+1"
# Format 2: YAML parses +1 and -1 without quotes as integers. These are converted
# to +1 and -1 strings respectively.
type: 1
# Whether reactions are allowed for issue triggers (issues, issue_comment).
# (optional)
issues: true
# Whether reactions are allowed for pull request triggers (pull_request,
# pull_request_review_comment).
# (optional)
pull-requests: true
# Whether reactions are allowed for discussion and discussion_comment triggers.
# (optional)
discussions: true
# Whether to post status comments (started/completed) on the triggering item.
# Boolean form enables/disables status comments globally. Object form implies
# enabled status comments and supports optional `issues`, `pull-requests`, and
# `discussions` fields to control trigger groups independently. Automatically
# enabled for slash_command and label_command triggers when not explicitly
# configured.
# (optional)
# Accepted formats:
# Format 1: boolean
status-comment: true
# Format 2: object
status-comment:
# Whether status comments are allowed for issue triggers (issues, issue_comment).
# (optional)
issues: true
# Whether status comments are allowed for pull request triggers (pull_request,
# pull_request_review_comment).
# (optional)
pull-requests: true
# Whether status comments are allowed for discussion and discussion_comment
# triggers.
# (optional)
discussions: true
# Custom GitHub token for pre-activation reactions, activation status comments,
# and skip-if search queries. When specified, overrides the default GITHUB_TOKEN
# for these operations.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# GitHub App configuration for minting a token used in pre-activation reactions,
# activation status comments, and skip-if search queries. When configured, a
# single GitHub App installation access token is minted and shared across all
# these operations instead of using the default GITHUB_TOKEN. Can be defined in a
# shared agentic workflow and inherited by importing workflows.
# (optional)
github-app:
# Deprecated alias for client-id. GitHub App ID/client ID (e.g., '${{ vars.APP_ID
# }}').
# (optional)
app-id: "example-value"
# GitHub App client ID (e.g., '${{ vars.APP_ID }}'). Required to mint a GitHub App
# token.
# (optional)
client-id: "example-value"
# GitHub App private key (e.g., '${{ secrets.APP_PRIVATE_KEY }}'). Required to
# mint a GitHub App token.
# (optional)
private-key: "example-value"
# If true, skip token minting when client-id/private-key resolve to empty strings
# at runtime. Defaults to false.
# (optional)
ignore-if-missing: true
# Optional owner of the GitHub App installation (defaults to current repository
# owner if not specified)
# (optional)
owner: "example-value"
# Optional list of repositories to grant access to (defaults to current repository
# if not specified)
# (optional)
repositories: []
# Array of strings
# Optional extra GitHub App-only permissions to merge into the minted token. Takes
# effect for tools.github.github-app and safe-outputs.github-app; ignored in
# on.github-app and the top-level github-app fallback. Use to add GitHub App-only
# scopes (e.g. members, organization-administration) not expressible via standard
# handler declarations.
# (optional)
permissions:
# Permission level for repository administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission for repository administration.
# (optional)
administration: "read"
# Permission level for Codespaces (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces: "read"
# Permission level for Codespaces lifecycle administration (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
codespaces-lifecycle-admin: "read"
# Permission level for Codespaces metadata (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces-metadata: "read"
# Permission level for user email addresses (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
email-addresses: "read"
# Permission level for repository environments (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
environments: "read"
# Permission level for git signing (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
git-signing: "read"
# Permission level for organization members (read/none; "write" is rejected by the
# compiler). Required for org team membership API calls.
# (optional)
members: "read"
# Permission level for organization administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-administration: "read"
# Permission level for organization announcement banners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-announcement-banners: "read"
# Permission level for organization Codespaces (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-codespaces: "read"
# Permission level for organization Copilot (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-copilot: "read"
# Permission level for organization custom org roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-org-roles: "read"
# Permission level for organization custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-properties: "read"
# Permission level for organization custom repository roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-repository-roles: "read"
# Permission level for organization events (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-events: "read"
# Permission level for organization webhooks (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-hooks: "read"
# Permission level for organization members management (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-members: "read"
# Permission level for organization packages (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-packages: "read"
# Permission level for organization personal access token requests (read/none;
# "write" is rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-token-requests: "read"
# Permission level for organization personal access tokens (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-tokens: "read"
# Permission level for organization plan (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-plan: "read"
# Permission level for organization self-hosted runners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-self-hosted-runners: "read"
# Permission level for organization user blocking (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-user-blocking: "read"
# Permission level for repository custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
repository-custom-properties: "read"
# Permission level for repository webhooks (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
repository-hooks: "read"
# Permission level for single file access (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
single-file: "read"
# Permission level for team discussions (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
team-discussions: "read"
# Permission level for Dependabot vulnerability alerts (read/none; "write" is
# rejected by the compiler). Also available as a GITHUB_TOKEN scope. When used
# with a GitHub App, forwarded as permission-vulnerability-alerts input.
# (optional)
vulnerability-alerts: "read"
# Permission level for GitHub Actions workflow files (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
workflows: "read"
# Explicit additional custom workflow jobs that pre_activation and activation
# should depend on.
# (optional)
needs: []
# Array of strings
# Steps to inject into the pre-activation job. These steps run after all built-in
# checks (membership, stop-time, skip-if, etc.) and their results are exposed as
# pre-activation outputs. Use 'id' on steps to reference their results via
# needs.pre_activation.outputs._result.
# (optional)
steps: []
# Array items:
# Optional name for the step
# (optional)
name: "My Workflow"
# Optional step ID. When set, the step result is exposed as
# needs.pre_activation.outputs._result
# (optional)
id: "example-value"
# Shell command to run
# (optional)
run: "example-value"
# Action to use (e.g., 'actions/checkout@v4')
# (optional)
uses: "example-value"
# Input parameters for the action
# (optional)
with:
{}
# Environment variables for the step
# (optional)
env:
{}
# Conditional expression for the step
# (optional)
if: "example-value"
# Whether to continue if the step fails
# (optional)
continue-on-error: true
# Additional permissions for the pre-activation job. Use to declare extra scopes
# required by on.steps (e.g., issues: read for GitHub API calls in steps).
# (optional)
# Map of permission scope to level
# (optional)
permissions:
# (optional)
actions: "read"
# (optional)
checks: "read"
# (optional)
contents: "read"
# (optional)
deployments: "read"
# (optional)
discussions: "read"
# (optional)
issues: "read"
# (optional)
packages: "read"
# (optional)
pages: "read"
# (optional)
pull-requests: "read"
# (optional)
repository-projects: "read"
# (optional)
security-events: "read"
# (optional)
statuses: "read"
# When set to false, disables the frontmatter hash check step in the activation
# job. Default is true (check is enabled). Useful when the workflow source files
# are managed outside the default GitHub repo context (e.g. cross-repo org
# rulesets) and the stale check is not needed.
# (optional)
stale-check: true
# GitHub token permissions for the workflow. Controls what the GITHUB_TOKEN can
# access during execution. Use the principle of least privilege - only grant the
# minimum permissions needed.
# (optional)
# Accepted formats:
# Format 1: Simple permissions string: 'read-all' (all read permissions) or
# 'write-all' (all write permissions)
permissions: "read-all"
# Format 2: Detailed permissions object with granular control over specific GitHub
# API scopes
permissions:
# Permission for GitHub Actions workflows and runs (read: view workflows, write:
# manage workflows, none: no access)
# (optional)
actions: "read"
# Permission for artifact attestations (read: view attestations, write: create
# attestations, none: no access)
# (optional)
attestations: "read"
# Permission for repository checks and status checks (read: view checks, write:
# create/update checks, none: no access)
# (optional)
checks: "read"
# Permission for repository contents (read: view files, write: modify
# files/branches, none: no access)
# (optional)
contents: "read"
# Permission for repository deployments (read: view deployments, write:
# create/update deployments, none: no access)
# (optional)
deployments: "read"
# Permission for repository discussions (read: view discussions, write:
# create/update discussions, none: no access)
# (optional)
discussions: "read"
# Permission level for OIDC token requests (write/none only - read is not
# supported). Allows workflows to request JWT tokens for cloud provider
# authentication.
# (optional)
id-token: "write"
# Permission for repository issues (read: view issues, write: create/update/close
# issues, none: no access)
# (optional)
issues: "read"
# Permission for GitHub Copilot models (read: access AI models for agentic
# workflows, none: no access)
# (optional)
models: "read"
# Permission for repository metadata (read: view repository information, write:
# update repository metadata, none: no access)
# (optional)
metadata: "read"
# Permission level for GitHub Packages (read/write/none). Controls access to
# publish, modify, or delete packages.
# (optional)
packages: "read"
# Permission level for GitHub Pages (read/write/none). Controls access to deploy
# and manage GitHub Pages sites.
# (optional)
pages: "read"
# Permission level for pull requests (read/write/none). Controls access to create,
# edit, review, and manage pull requests.
# (optional)
pull-requests: "read"
# Permission level for repository projects (read/write/none). Controls access to
# manage repository-level GitHub Projects boards.
# (optional)
repository-projects: "read"
# Permission level for organization projects (read/write/none). Controls access to
# manage organization-level GitHub Projects boards.
# (optional)
organization-projects: "read"
# Permission level for security events (read/write/none). Controls access to view
# and manage code scanning alerts and security findings.
# (optional)
security-events: "read"
# Permission level for commit statuses (read/write/none). Controls access to
# create and update commit status checks.
# (optional)
statuses: "read"
# Permission level for Dependabot vulnerability alerts (read/write/none). Allows
# workflows to access the Dependabot alerts API via GITHUB_TOKEN instead of
# requiring a PAT or GitHub App.
# (optional)
vulnerability-alerts: "read"
# Permission shorthand that applies read access to all permission scopes. Can be
# combined with specific write permissions to override individual scopes. 'write'
# is not allowed for all.
# (optional)
all: "read"
# Custom name for workflow runs that appears in the GitHub Actions interface
# (supports GitHub expressions like ${{ github.event.issue.title }})
# (optional)
run-name: "example-value"
# Groups together all the jobs that run in the workflow
# (optional)
jobs:
{}
# Runner type for workflow execution (GitHub Actions standard field). Supports
# multiple forms: simple string for single runner label (e.g., 'ubuntu-latest'),
# array for runner selection with fallbacks, or object for GitHub-hosted runner
# groups with specific labels. For agentic workflows, runner selection matters
# when AI workloads require specific compute resources or when using self-hosted
# runners with specialized capabilities. Typically configured at the job level
# instead. See
# https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job
# (optional)
# Accepted formats:
# Format 1: Simple runner label string. Use for standard GitHub-hosted runners
# (e.g., 'ubuntu-latest', 'windows-latest', 'macos-latest') or self-hosted runner
# labels. Most common form for agentic workflows.
runs-on: "example-value"
# Format 2: Array of runner labels for selection with fallbacks. GitHub Actions
# will use the first available runner that matches any label in the array. Useful
# for high-availability setups or when multiple runner types are acceptable.
runs-on: []
# Array items: string
# Format 3: Runner group configuration for GitHub-hosted runners. Use this form to
# target specific runner groups (e.g., larger runners with more CPU/memory) or
# self-hosted runner pools with specific label requirements. Agentic workflows may
# benefit from larger runners for complex AI processing tasks.
runs-on:
# Runner group name for self-hosted runners or GitHub-hosted runner groups
# (optional)
group: "example-value"
# List of runner labels for self-hosted runners or GitHub-hosted runner selection
# (optional)
labels: []
# Array of strings
# Runner for all framework/generated jobs (activation, pre-activation,
# safe-outputs, unlock, APM, etc.). Provides a compile-stable override for
# generated job runners without requiring a safe-outputs section. Overridden by
# safe-outputs.runs-on when both are set. Defaults to 'ubuntu-slim'. Use this when
# your infrastructure does not provide the default runner or when you need
# consistent runner selection across all jobs.
# (optional)
runs-on-slim: "example-value"
# Workflow timeout in minutes (GitHub Actions standard field). Defaults to 20
# minutes for agentic workflows. Has sensible defaults and can typically be
# omitted. Custom runners support longer timeouts beyond the GitHub-hosted runner
# limit. Supports GitHub Actions expressions (e.g. '${{ inputs.timeout }}') for
# reusable workflow_call workflows.
# (optional)
# Accepted formats:
# Format 1: integer
timeout-minutes: 1
# Format 2: GitHub Actions expression that resolves to an integer (e.g. '${{
# inputs.timeout }}')
timeout-minutes: "example-value"
# Concurrency control to limit concurrent workflow runs (GitHub Actions standard
# field). Supports two forms: simple string for basic group isolation, or object
# with cancel-in-progress option for advanced control. Agentic workflows enhance
# this with automatic per-engine concurrency policies (defaults to single job per
# engine across all workflows) and token-based rate limiting. Default behavior:
# workflows in the same group queue sequentially unless cancel-in-progress is
# true. See https://docs.github.com/en/actions/using-jobs/using-concurrency
# (optional)
# Accepted formats:
# Format 1: Simple concurrency group name to prevent multiple runs in the same
# group. Use expressions like '${{ github.workflow }}' for per-workflow isolation
# or '${{ github.ref }}' for per-branch isolation. Agentic workflows automatically
# generate enhanced concurrency policies using 'gh-aw-{engine-id}' as the default
# group to limit concurrent AI workloads across all workflows using the same
# engine.
concurrency: "example-value"
# Format 2: Concurrency configuration object with group isolation and cancellation
# control. Use object form when you need fine-grained control over whether to
# cancel in-progress runs. For agentic workflows, this is useful to prevent
# multiple AI agents from running simultaneously and consuming excessive resources
# or API quotas.
concurrency:
# Concurrency group name. Workflows in the same group cannot run simultaneously.
# Supports GitHub Actions expressions for dynamic group names based on branch,
# workflow, or other context.
# (optional)
group: "example-value"
# Whether to cancel in-progress workflows in the same concurrency group when a new
# one starts. Default: false (queue new runs). Set to true for agentic workflows
# where only the latest run matters (e.g., PR analysis that becomes stale when new
# commits are pushed).
# (optional)
cancel-in-progress: true
# Pending run queue behavior for this concurrency group. 'single' (default) allows
# one pending run and replaces older pending runs. 'max' allows up to 100 pending
# runs in FIFO order.
# (optional)
queue: "single"
# Additional discriminator expression appended to compiler-generated job-level
# concurrency groups (agent, output jobs). Use this when multiple workflow
# instances are dispatched concurrently with different inputs (fan-out pattern) to
# prevent job-level concurrency groups from colliding. For example, '${{
# inputs.finding_id }}' ensures each dispatched run gets a unique job-level group.
# Supports GitHub Actions expressions. This field is stripped from the compiled
# lock file (it is a gh-aw extension, not a GitHub Actions field).
# (optional)
job-discriminator: "example-value"
# Environment variables for the workflow
# (optional)
# Accepted formats:
# Format 1: object
env:
{}
# Format 2: string
env: "example-value"
# Deprecated switch for inline sub-agent support. Inline sub-agents are enabled by
# default. Setting this to false is not supported and causes a compilation error.
# (optional)
inline-sub-agents: true
# Feature flags and configuration options for experimental or optional features in
# the workflow. Each feature can be a boolean flag or a string value. The
# 'action-tag' feature (string) specifies the tag or SHA to use when referencing
# actions/setup in compiled workflows (for testing purposes only).
# (optional)
features:
{}
# Named model alias definitions with ordered fallback lists, resolved recursively
# by AWF. Each key is an alias name (use empty string "" for the default policy).
# Each value is an ordered list of vendor/modelid glob patterns or other alias
# names to try in sequence. Entries defined here are merged on top of the builtin
# aliases; the main workflow file always wins over imported aliases. Builtin
# aliases include: sonnet, sonnet-6x, haiku, opus, gpt-5, gpt-5-mini, gpt-5-codex,
# gemini-flash, gemini-pro, small, mini, large, auto, any, agent, copilot, claude,
# codex, gemini.
# (optional)
models:
{}
# A/B testing experiments. Each key is an experiment name; the value is either an
# array of two or more variant strings (bare-array form) or an object with a
# 'variants' field plus optional metadata fields (description, metric, weight,
# issue, start_date, end_date, hypothesis, secondary_metrics, guardrail_metrics,
# min_samples). The reserved 'storage' key controls how experiment state is
# persisted: 'repo' (default) commits state to a git branch named
# 'experiments/{sanitizedWorkflowID}' (workflow ID lowercased with hyphens
# removed) for durability; 'cache' uses GitHub Actions cache. At runtime the
# activation job picks a variant and persists the updated counters. Use ${{
# experiments. }} in the workflow prompt to reference the selected variant.
# When multiple experiments are declared, assignments are statistically balanced
# using a least-used counter that round-robins across variants (or weighted when
# 'weight' is provided); ties are broken randomly so no variant is systematically
# favoured on the first run.
# (optional)
experiments:
# Storage backend for experiment state. 'repo' (default) persists state to a git
# branch named 'experiments/{sanitizedWorkflowID}' (workflow ID lowercased with
# hyphens removed, e.g. 'my-workflow' -> 'experiments/myworkflow') for durability
# across cache evictions. 'cache' uses GitHub Actions cache (legacy behaviour).
# Repo storage is recommended because experiment data is valuable and more durable
# than cache.
# (optional)
storage: "cache"
# Controls whether the custom agent should disable model invocation. When set to
# true, the agent will not make additional model calls.
# (optional)
disable-model-invocation: true
# Secret values passed to workflow execution. Secrets can be defined as simple
# strings (GitHub Actions expressions) or objects with 'value' and 'description'
# properties. Typically used to provide secrets to MCP servers or custom engines.
# Note: For passing secrets to reusable workflows, use the jobs..secrets
# field instead.
# (optional)
secrets:
{}
# Environment that the job references (for protected environments and deployments)
# (optional)
# Accepted formats:
# Format 1: Environment name as a string
environment: "example-value"
# Format 2: Environment object with name and optional URL
environment:
# The name of the environment configured in the repo
name: "My Workflow"
# A deployment URL
# (optional)
url: "example-value"
# Container to run the job steps in
# (optional)
# Accepted formats:
# Format 1: Docker image name (e.g., 'node:18', 'ubuntu:latest')
container: "example-value"
# Format 2: Container configuration object
container:
# The Docker image to use as the container
image: "example-value"
# Credentials for private registries
# (optional)
credentials:
# Username for Docker registry authentication when pulling private container
# images.
# (optional)
username: "example-value"
# Password or access token for Docker registry authentication. Should use secrets
# syntax: ${{ secrets.DOCKER_PASSWORD }}
# (optional)
password: "example-value"
# Environment variables for the container
# (optional)
env:
{}
# Ports to expose on the container
# (optional)
ports: []
# Volumes for the container
# (optional)
volumes: []
# Array of strings
# Additional Docker container options
# (optional)
options: "example-value"
# Service containers for the job
# (optional)
services:
{}
# Network access control for AI engines using ecosystem identifiers and domain
# allowlists. Supports wildcard patterns like '*.example.com' to match any
# subdomain. Controls web fetch and search capabilities. IMPORTANT: For workflows
# that build/install/test code, always include the language ecosystem identifier
# alongside 'defaults' — 'defaults' alone only covers basic infrastructure, not
# package registries. Key ecosystem identifiers by runtime: 'dotnet' (.NET/NuGet),
# 'python' (pip/PyPI), 'node' (npm/yarn), 'go' (go modules), 'java'
# (Maven/Gradle), 'ruby' (Bundler), 'rust' (Cargo), 'swift' (Swift PM). Example: a
# .NET project needs network: { allowed: [defaults, dotnet] }.
# (optional)
# Accepted formats:
# Format 1: Use default network permissions (basic infrastructure: certificates,
# JSON schema, Ubuntu, etc.)
network: "defaults"
# Format 2: Custom network access configuration with ecosystem identifiers and
# specific domains
network:
# List of allowed domains or ecosystem identifiers (e.g., 'defaults', 'python',
# 'node', '*.example.com'). Wildcard patterns match any subdomain AND the base
# domain.
# (optional)
allowed: []
# Array of Domain name or ecosystem identifier. Supports wildcards like
# '*.example.com' (matches sub.example.com, deep.nested.example.com, and
# example.com itself). Ecosystem identifiers by runtime: 'dotnet' (.NET/NuGet),
# 'python' (pip/PyPI), 'node' (npm/yarn), 'go' (go modules), 'java'
# (Maven/Gradle), 'ruby' (RubyGems), 'rust' (Cargo), 'swift' (Swift PM), 'php'
# (Composer), 'dart' (pub.dev), 'haskell' (Hackage), 'perl' (CPAN), 'containers'
# (Docker/GHCR), 'github' (GitHub domains), 'terraform' (HashiCorp),
# 'linux-distros' (apt/yum), 'playwright' (browser testing), 'defaults' (basic
# infrastructure).
# When true and the workflow uses workflow_call, expose a network_allowed string
# input on the compiled lock file. The caller-supplied value is unioned with
# network.allowed at runtime, supporting ecosystem identifiers (for example
# 'rust') or comma-separated domains.
# (optional)
allowed-input: true
# List of blocked domains or ecosystem identifiers (e.g., 'python', 'node',
# 'tracker.example.com'). Blocked domains take precedence over allowed domains.
# (optional)
blocked: []
# Array of Domain name or ecosystem identifier to block. Supports wildcards like
# '*.example.com' (matches sub.example.com, deep.nested.example.com, and
# example.com itself) and ecosystem names like 'python', 'node'.
# Sandbox configuration for AI engines. Controls agent sandbox (AWF) and MCP
# gateway. The MCP gateway is always enabled and cannot be disabled.
# (optional)
# Accepted formats:
# Format 1: String format for sandbox type: 'default' for no sandbox, 'awf' for
# Agent Workflow Firewall. Note: Legacy 'srt' and 'sandbox-runtime' values are
# automatically migrated to 'awf'
sandbox: "default"
# Format 2: Object format for full sandbox configuration with agent and mcp
# options
sandbox:
# Legacy sandbox type field (use agent instead). Note: Legacy 'srt' and
# 'sandbox-runtime' values are automatically migrated to 'awf'
# (optional)
type: "default"
# Agent sandbox type: 'awf' uses AWF (Agent Workflow Firewall), or false to
# disable agent sandbox. Defaults to 'awf' if not specified. Note: Disabling the
# agent sandbox (false) removes firewall protection but keeps the MCP gateway
# enabled.
# (optional)
# Accepted formats:
# Format 1: Set to false to disable the agent sandbox (firewall). Warning: This
# removes firewall protection but keeps the MCP gateway enabled. Not allowed in
# strict mode.
agent: true
# Format 2: Sandbox type: 'awf' for Agent Workflow Firewall
agent: "awf"
# Format 3: Custom sandbox runtime configuration
agent:
# Agent identifier (replaces 'type' field in new format): 'awf' for Agent Workflow
# Firewall
# (optional)
id: "awf"
# Legacy: Sandbox type to use (use 'id' instead)
# (optional)
type: "awf"
# AWF version override used to install and run the matching firewall version.
# (optional)
version: "example-value"
# Container mounts to add when using AWF. Each mount is specified using Docker
# mount syntax: 'source:destination:mode' where mode can be 'ro' (read-only) or
# 'rw' (read-write). Example: '/host/path:/container/path:ro'
# (optional)
mounts: []
# Array of Mount specification in format 'source:destination:mode'
# Memory limit for the AWF container (e.g., '4g', '8g'). Passed as --memory-limit
# to AWF. If not specified, AWF's default memory limit is used.
# (optional)
memory: "example-value"
# Custom sandbox runtime configuration. Note: Network configuration is controlled
# by the top-level 'network' field, not here.
# (optional)
config:
# Filesystem access control configuration for the agent within the sandbox.
# Controls read/write permissions and path restrictions.
# (optional)
filesystem:
# List of paths to deny read access
# (optional)
denyRead: []
# Array of strings
# List of paths to allow write access
# (optional)
allowWrite: []
# Array of strings
# List of paths to deny write access
# (optional)
denyWrite: []
# Array of strings
# Map of command patterns to paths that should ignore violations
# (optional)
ignoreViolations:
{}
# Enable weaker nested sandbox mode (recommended: true for Docker access)
# (optional)
enableWeakerNestedSandbox: true
# Legacy custom Sandbox Runtime configuration (use agent.config instead). Note:
# Network configuration is controlled by the top-level 'network' field, not here.
# (optional)
config:
# Filesystem access control configuration for sandboxed workflows. Controls
# read/write permissions and path restrictions for file operations.
# (optional)
filesystem:
# Array of path patterns that deny read access in the sandboxed environment. Takes
# precedence over other read permissions.
# (optional)
denyRead: []
# Array of strings
# Array of path patterns that allow write access in the sandboxed environment.
# Paths outside these patterns are read-only.
# (optional)
allowWrite: []
# Array of strings
# Array of path patterns that deny write access in the sandboxed environment.
# Takes precedence over other write permissions.
# (optional)
denyWrite: []
# Array of strings
# When true, log sandbox violations without blocking execution. Useful for
# debugging and gradual enforcement of sandbox policies.
# (optional)
ignoreViolations:
{}
# When true, allows nested sandbox processes to run with relaxed restrictions.
# Required for certain containerized tools that spawn subprocesses.
# (optional)
enableWeakerNestedSandbox: true
# MCP Gateway configuration for routing MCP server calls through a unified HTTP
# gateway. Requires the 'mcp-gateway' feature flag to be enabled. Per MCP Gateway
# Specification v1.0.0: Only container-based execution is supported.
# (optional)
mcp:
# Volume mounts for the MCP gateway container. Each mount is specified using
# Docker mount syntax: 'source:destination:mode' where mode can be 'ro'
# (read-only) or 'rw' (read-write). Example: '/host/data:/container/data:ro'
# (optional)
mounts: []
# Array of Mount specification in format 'source:destination:mode'
# Environment variables for MCP gateway
# (optional)
env:
{}
# Port number for the MCP gateway HTTP server (default: 8080)
# (optional)
port: 1
# API key for authenticating with the MCP gateway (supports ${{ secrets.* }}
# syntax)
# (optional)
api-key: "example-value"
# Gateway domain for URL generation (default: 'host.docker.internal' when agent is
# enabled, 'localhost' when disabled)
# (optional)
domain: "localhost"
# Keepalive ping interval in seconds for HTTP MCP backends. Sends periodic pings
# to prevent session expiry during long-running agent tasks. Set to -1 to disable
# keepalive pings. Unset or 0 uses the gateway default (1500 seconds = 25
# minutes).
# (optional)
keepalive-interval: 1
# Conditional execution expression
# (optional)
if: "example-value"
# Custom workflow steps
# (optional)
# Accepted formats:
# Format 1: object
steps:
{}
# Format 2: array
steps: []
# Array items: undefined
# Custom workflow steps to run at the very beginning of the agent job, before
# checkout and any other built-in steps. Use pre-steps to mint short-lived tokens
# or perform any setup that must happen before the repository is checked out. Step
# outputs are available via ${{ steps..outputs. }} and can be referenced
# in checkout.token to avoid masked-value cross-job-boundary issues.
# (optional)
# Accepted formats:
# Format 1: object
pre-steps:
{}
# Format 2: array
pre-steps: []
# Array items: undefined
# Custom workflow steps to run immediately before AI execution, after all
# initialization and setup steps in the agent job.
# (optional)
# Accepted formats:
# Format 1: object
pre-agent-steps:
{}
# Format 2: array
pre-agent-steps: []
# Array items: undefined
# Custom workflow steps to run after AI execution
# (optional)
# Accepted formats:
# Format 1: object
post-steps:
{}
# Format 2: array
post-steps: []
# Array items: undefined
# AI engine configuration that specifies which AI processor interprets and
# executes the markdown content of the workflow. Defaults to 'copilot'.
# (optional)
# Accepted formats:
# Format 1: Engine name: built-in ('claude', 'codex', 'copilot', 'gemini',
# 'opencode', 'crush', 'pi') or a named catalog entry
engine: "example-value"
# Format 2: Extended engine configuration object with advanced options for model
# selection, turn limiting, environment variables, and custom steps
engine:
# AI engine identifier: built-in ('claude', 'codex', 'copilot', 'gemini',
# 'opencode', 'crush', 'pi') or a named catalog entry
id: "example-value"
# Optional version of the AI engine action (e.g., 'beta', 'stable', 20). Has
# sensible defaults and can typically be omitted. Numeric values are automatically
# converted to strings at runtime. GitHub Actions expressions (e.g., '${{
# inputs.engine-version }}') are accepted and compiled with injection-safe env var
# handling.
# (optional)
version: null
# Optional specific LLM model to use (e.g., 'claude-3-5-sonnet-20241022',
# 'gpt-4'). Has sensible defaults and can typically be omitted.
# (optional)
model: "example-value"
# Maximum number of chat iterations per run. Helps prevent runaway loops and
# control costs. Has sensible defaults and can typically be omitted. Note: Only
# supported by the claude engine.
# (optional)
# Accepted formats:
# Format 1: Maximum number of chat iterations per run as an integer value
max-turns: 1
# Format 2: Maximum number of chat iterations per run as a string value
max-turns: "example-value"
# Maximum number of continuations for multi-run autopilot mode. Default is 1
# (single run, no autopilot). Values greater than 1 enable --autopilot mode for
# the copilot engine with --max-autopilot-continues set to this value. Note: Only
# supported by the copilot engine.
# (optional)
max-continuations: 1
# Agent job concurrency configuration. Defaults to single job per engine across
# all workflows (group: 'gh-aw-{engine-id}'). Supports full GitHub Actions
# concurrency syntax.
# (optional)
# Accepted formats:
# Format 1: Simple concurrency group name. Gets converted to GitHub Actions
# concurrency format with the specified group.
concurrency: "example-value"
# Format 2: GitHub Actions concurrency configuration for the agent job. Controls
# how many agentic workflow runs can run concurrently.
concurrency:
# Concurrency group identifier. Use GitHub Actions expressions like ${{
# github.workflow }} or ${{ github.ref }}. Defaults to 'gh-aw-{engine-id}' if not
# specified.
group: "example-value"
# Whether to cancel in-progress runs of the same concurrency group. Defaults to
# false for agentic workflow runs.
# (optional)
cancel-in-progress: true
# Pending run queue behavior for this concurrency group. 'single' (default) allows
# one pending run and replaces older pending runs. 'max' allows up to 100 pending
# runs in FIFO order.
# (optional)
queue: "single"
# Custom user agent string for GitHub MCP server configuration (codex engine only)
# (optional)
user-agent: "example-value"
# Custom executable path for the AI engine CLI. When specified, the workflow will
# skip the standard installation steps and use this command instead. The command
# should be the full path to the executable or a command available in PATH.
# (optional)
command: "example-value"
# Custom Node.js harness script filename for an agentic engine. This replaces the
# engine's built-in harness wrapper (when the engine supports one) and must end
# with .js, .cjs, or .mjs.
# (optional)
harness: "example-value"
# Custom environment variables to pass to the AI engine, including secret
# overrides (e.g., OPENAI_API_KEY: ${{ secrets.CUSTOM_KEY }})
# (optional)
env:
{}
# Engine-level authentication configuration for AWF API proxy sidecar integration
# (for example, Azure OpenAI via GitHub OIDC). Values are mapped to AWF_AUTH_*
# environment variables.
# (optional)
auth:
# Authentication type. Currently only 'github-oidc' is supported.
type: "github-oidc"
# OIDC audience to request from GitHub Actions for token exchange.
# (optional)
audience: "example-value"
# Optional Azure tenant ID for token exchange.
# (optional)
azure-tenant-id: "example-value"
# Optional Azure client ID for token exchange.
# (optional)
azure-client-id: "example-value"
# Optional Azure OAuth scope (defaults to
# https://cognitiveservices.azure.com/.default in AWF sidecar).
# (optional)
azure-scope: "example-value"
# Optional Azure cloud name (for example, public, usgovernment, china).
# (optional)
azure-cloud: "example-value"
# Additional TOML configuration text that will be appended to the generated
# config.toml in the action (codex engine only)
# (optional)
config: "example-value"
# Agent identifier to pass to copilot --agent flag (copilot engine only).
# Specifies which custom agent to use for the workflow.
# (optional)
agent: "example-value"
# Custom API endpoint hostname for the agentic engine. Used for GitHub Enterprise
# Cloud (GHEC), GitHub Enterprise Server (GHES), or custom AI endpoints. Example:
# 'api.acme.ghe.com' for GHEC, 'api.enterprise.githubcopilot.com' for GHES, or
# custom endpoint hostnames.
# (optional)
api-target: "example-value"
# Custom model token weights for effective token computation. Overrides or extends
# the built-in model multipliers from model_multipliers.json. Useful for custom
# models or adjusted cost ratios.
# (optional)
token-weights:
# Per-model cost multipliers relative to the reference model (claude-sonnet-4.5 =
# 1.0). Keys are model names (case-insensitive, prefix matching supported). Values
# are numeric multipliers.
# (optional)
multipliers:
{}
# Per-token-class weights applied before the model multiplier. Any specified
# weight overrides the corresponding default.
# (optional)
token-class-weights:
# Weight for input tokens (default: 1.0)
# (optional)
input: 1
# Weight for cached input tokens (default: 0.1)
# (optional)
cached-input: 1
# Weight for output tokens (default: 4.0)
# (optional)
output: 1
# Weight for reasoning tokens (default: 4.0)
# (optional)
reasoning: 1
# Weight for cache write tokens (default: 1.0)
# (optional)
cache-write: 1
# Optional array of command-line arguments to pass to the AI engine CLI. These
# arguments are injected after all other args but before the prompt.
# (optional)
args: []
# Array of strings
# When true, disables automatic loading of context and custom instructions by the
# AI engine. The engine-specific flag depends on the engine: copilot uses
# --no-custom-instructions (suppresses .github/AGENTS.md and user-level custom
# instructions), claude uses --bare (suppresses CLAUDE.md memory files), codex
# uses --no-system-prompt (suppresses the default system prompt), gemini sets
# GEMINI_SYSTEM_MD=/dev/null (overrides the built-in system prompt with an empty
# one). Defaults to false.
# (optional)
bare: true
# Engine-level MCP gateway configuration. Settings here apply to the MCP gateway
# used by this engine.
# (optional)
mcp:
# Session timeout for MCP gateway sessions as a Go duration string (e.g. "30m",
# "4h", "24h"). Must be at least 5m (no upper bound). Omitted or empty uses the
# effective gateway default (precedence: this field > MCP_GATEWAY_SESSION_TIMEOUT
# env var > built-in default 6h). Longer timeouts benefit multi-hour workflows
# such as large-scale migrations; shorter values free gateway resources sooner.
# (optional)
session-timeout: "example-value"
# Timeout for individual MCP tool calls as a Go duration string (e.g. "30s", "2m",
# "10m"). Must be between 10s and 600s inclusive. Omitted or empty uses the
# gateway built-in default (60s). Use a higher value for slow MCP backends such as
# full-text search over large indexes.
# (optional)
tool-timeout: "example-value"
# Format 3: Inline engine definition: specifies a runtime adapter and optional
# provider settings directly in the workflow frontmatter, without requiring a
# named catalog entry
engine:
# Runtime adapter reference for the inline engine definition
runtime:
# Runtime adapter identifier (e.g. 'codex', 'claude', 'copilot', 'gemini',
# 'opencode', 'crush', 'pi')
id: "example-value"
# Optional version of the runtime adapter (e.g. '0.105.0', 'beta')
# (optional)
version: null
# Optional provider configuration for the inline engine definition
# (optional)
provider:
# Provider identifier (e.g. 'openai', 'anthropic', 'github', 'google')
# (optional)
id: "example-value"
# Optional specific LLM model to use (e.g. 'gpt-5', 'claude-3-5-sonnet-20241022')
# (optional)
model: "example-value"
# Authentication configuration for the provider
# (optional)
auth:
# Name of the GitHub Actions secret that contains the API key for this provider
# (optional)
secret: "example-value"
# Authentication strategy for the provider (default: api-key when secret is set)
# (optional)
strategy: "api-key"
# OAuth 2.0 token endpoint URL. Required when strategy is
# 'oauth-client-credentials'.
# (optional)
token-url: "example-value"
# GitHub Actions secret name that holds the OAuth client ID. Required when
# strategy is 'oauth-client-credentials'.
# (optional)
client-id: "example-value"
# GitHub Actions secret name that holds the OAuth client secret. Required when
# strategy is 'oauth-client-credentials'.
# (optional)
client-secret: "example-value"
# JSON field name in the token response that contains the access token. Defaults
# to 'access_token'.
# (optional)
token-field: "example-value"
# HTTP header name to inject the API key or token into (e.g. 'api-key',
# 'x-api-key'). Required when strategy is not 'bearer'.
# (optional)
header-name: "example-value"
# Request shaping configuration for non-standard provider URL and body
# transformations
# (optional)
request:
# URL path template with {model} and other variable placeholders (e.g.
# '/openai/deployments/{model}/chat/completions')
# (optional)
path-template: "example-value"
# Static or template query-parameter values appended to every request
# (optional)
query:
{}
# Key/value pairs injected into the JSON request body before sending
# (optional)
body-inject:
{}
# When true, disables automatic loading of context and custom instructions by the
# AI engine. The engine-specific flag depends on the engine: copilot uses
# --no-custom-instructions, claude uses --bare, codex uses --no-system-prompt,
# gemini sets GEMINI_SYSTEM_MD=/dev/null. Defaults to false.
# (optional)
bare: true
# Format 4: Engine definition: full declarative metadata for a named engine entry
# (used in builtin engine shared workflow files such as @builtin:engines/*.md)
engine:
# Unique engine identifier (e.g. 'copilot', 'claude', 'codex', 'gemini',
# 'opencode', 'crush', 'pi')
id: "example-value"
# Human-readable display name for the engine
display-name: "example-value"
# Human-readable description of the engine
# (optional)
description: "Description of the workflow"
# Runtime adapter identifier. Maps to the CodingAgentEngine registered in the
# engine registry. Defaults to id when omitted.
# (optional)
runtime-id: "example-value"
# Provider metadata for the engine
# (optional)
provider:
# Provider name (e.g. 'anthropic', 'github', 'google', 'openai')
# (optional)
name: "My Workflow"
# Default authentication configuration for the provider
# (optional)
auth:
# Name of the GitHub Actions secret that contains the API key
# (optional)
secret: "example-value"
# Authentication strategy
# (optional)
strategy: "api-key"
# OAuth 2.0 token endpoint URL
# (optional)
token-url: "example-value"
# GitHub Actions secret name for the OAuth client ID
# (optional)
client-id: "example-value"
# GitHub Actions secret name for the OAuth client secret
# (optional)
client-secret: "example-value"
# JSON field name in the token response containing the access token
# (optional)
token-field: "example-value"
# HTTP header name to inject the API key or token into
# (optional)
header-name: "example-value"
# Request shaping configuration
# (optional)
request:
# URL path template with variable placeholders
# (optional)
path-template: "example-value"
# Static query parameters
# (optional)
query:
{}
# Key/value pairs injected into the JSON request body
# (optional)
body-inject:
{}
# Model selection configuration for the engine
# (optional)
models:
# Default model identifier
# (optional)
default: "example-value"
# List of supported model identifiers
# (optional)
supported: []
# Array of strings
# Authentication bindings — maps logical roles (e.g. 'api-key') to GitHub Actions
# secret names
# (optional)
auth: []
# Array items:
# Logical authentication role (e.g. 'api-key', 'token')
role: "example-value"
# Name of the GitHub Actions secret that provides credentials for this role
secret: "example-value"
# Additional engine-specific options
# (optional)
options:
{}
# Format 5: MCP gateway configuration for shared workflows. Declares engine.mcp
# settings (tool-timeout, session-timeout) that consumers inherit during import
# without specifying an engine identifier. The engine is always inherited from the
# importing workflow.
engine:
# Engine-level MCP gateway configuration. Settings here apply to the MCP gateway
# used by this engine.
mcp:
# Session timeout for MCP gateway sessions as a Go duration string (e.g. "30m",
# "4h", "24h"). Must be at least 5m (no upper bound). Omitted or empty uses the
# effective gateway default (precedence: this field > MCP_GATEWAY_SESSION_TIMEOUT
# env var > built-in default 6h).
# (optional)
session-timeout: "example-value"
# Timeout for individual MCP tool calls as a Go duration string (e.g. "30s", "2m",
# "10m"). Must be between 10s and 600s inclusive. Omitted or empty uses the
# gateway built-in default (60s). Use a higher value for slow MCP backends such as
# full-text search over large indexes.
# (optional)
tool-timeout: "example-value"
# Format 6: Engine object with only a model preference (no engine.id). Allows
# workflow authors to express a model-size hint (e.g. 'small', 'large') without
# committing to a specific engine. The runtime selects an appropriate engine using
# its default, and the model preference is applied to it.
engine:
# Model preference or size category (e.g. 'small', 'large', 'gpt-4.1'). Applied to
# the default engine when engine.id is not specified.
model: "example-value"
# Explicit ET budget control for firewall cost enforcement. Defaults to 25000000
# when omitted. Set to a negative value to disable budget enforcement and token
# steering.
# (optional)
# Accepted formats:
# Format 1: Maximum effective-token (ET) budget for AWF API proxy enforcement. Use
# a negative value to disable budget enforcement and token steering.
max-effective-tokens: 1
# Format 2: Maximum effective-token (ET) budget as a numeric string or GitHub
# Actions expression.
max-effective-tokens: "example-value"
# AWF invocation cap (`apiProxy.maxRuns`) applied consistently across all engines.
# Defaults to 500 when omitted.
# (optional)
# Accepted formats:
# Format 1: Maximum number of LLM invocations allowed per run.
max-runs: 1
# Format 2: Maximum number of LLM invocations allowed per run as a numeric string
# or GitHub Actions expression.
max-runs: "example-value"
# MCP server definitions
# (optional)
mcp-servers:
{}
# Tools and MCP (Model Context Protocol) servers available to the AI engine for
# GitHub API access, browser automation, file editing, and more
# (optional)
tools:
# GitHub API tools for repository operations (issues, pull requests, content
# management)
# (optional)
# Accepted formats:
# Format 1: Empty GitHub tool configuration (enables all read-only GitHub API
# functions)
github: null
# Format 2: Boolean to explicitly enable (true) or disable (false) the GitHub MCP
# server. When set to false, the GitHub MCP server is not mounted.
github: true
# Format 3: Simple GitHub tool configuration (enables all GitHub API functions)
github: "example-value"
# Format 4: GitHub tools object configuration with restricted function access
github:
# List of allowed GitHub API functions (e.g., 'create_issue', 'update_issue',
# 'add_comment')
# (optional)
allowed: []
# Array of strings
# GitHub access mode. Prefer 'gh-proxy' for better performance (uses
# pre-authenticated gh CLI prompt guidance). Legacy MCP transport values 'local'
# and 'remote' are accepted for backward compatibility and use GitHub MCP server
# prompt guidance.
# (optional)
mode: "gh-proxy"
# GitHub MCP transport type: 'local' (Docker-based, default) or 'remote' (hosted
# at api.githubcopilot.com)
# (optional)
type: "local"
# Optional version specification for the GitHub MCP server (used with 'local'
# type). Can be a string (e.g., 'v1.0.0', 'latest') or number (e.g., 20, 3.11).
# Numeric values are automatically converted to strings at runtime.
# (optional)
version: null
# Optional additional arguments to append to the generated MCP server command
# (used with 'local' type)
# (optional)
args: []
# Array of strings
# Enable read-only mode to restrict GitHub MCP server to read-only operations only
# (optional)
read-only: true
# Enable lockdown mode to limit content surfaced from public repositories (only
# items authored by users with push access). Default: false
# (optional)
lockdown: true
# Controls DIFC proxy injection for pre-agent gh CLI steps when guard policies
# (min-integrity) are configured. Default: true (enabled). Set to false to disable
# proxy injection and rely solely on MCP gateway-level filtering.
# (optional)
integrity-proxy: true
# Optional custom GitHub token (e.g., '${{ secrets.CUSTOM_PAT }}'). For 'remote'
# type, defaults to GH_AW_GITHUB_TOKEN if not specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# GitHub MCP server toolset name(s) to enable. Accepts a single toolset name
# (string) or an array of toolset names.
# (optional)
# Accepted formats:
# Format 1: A single GitHub MCP server toolset name (shorthand for a one-element
# array)
toolsets: "all"
# Format 2: Array of GitHub MCP server toolset names to enable specific groups of
# GitHub API functionalities
toolsets: []
# Array items: undefined
# Volume mounts for the containerized GitHub MCP server (format:
# 'host:container:mode' where mode is 'ro' for read-only or 'rw' for read-write).
# Applies to local mode only. Example: '/data:/data:ro'
# (optional)
mounts: []
# Array of Mount specification in format 'host:container:mode'
# Guard policy: repository access configuration. Restricts which repositories the
# agent can access. Use 'all' to allow all repos, 'public' for public repositories
# only, '${{ github.repository }}' for the current repository, or an array of
# repository patterns (e.g., 'owner/repo', 'owner/*', 'owner/prefix*').
# (optional)
# Accepted formats:
# Format 1: Allow access to all repositories ('all'), only public repositories
# ('public'), or the current repository ('${{ github.repository }}')
allowed-repos: "all"
# Format 2: Allow access to specific repositories using patterns (e.g.,
# 'owner/repo', 'owner/*', 'owner/prefix*')
allowed-repos: []
# Array items: Repository pattern in the format 'owner/repo', 'owner/*' (all repos
# under owner), or 'owner/prefix*' (repos with name prefix)
# Guard policy: minimum required integrity level for repository access. Restricts
# the agent to users with at least the specified permission level.
# (optional)
min-integrity: "none"
# Guard policy: GitHub usernames whose content is unconditionally blocked. Items
# from these users receive 'blocked' integrity (below 'none') and are always
# denied, even when 'min-integrity' is 'none'. Cannot be overridden by
# 'approval-labels'. Requires 'min-integrity' to be set. Accepts an array of
# usernames, a comma-separated string, a newline-separated string, or a GitHub
# Actions expression (e.g. '${{ vars.BLOCKED_USERS }}').
# (optional)
# Accepted formats:
# Format 1: Array of GitHub usernames to block
blocked-users: []
# Array items: GitHub username to block
# Format 2: Comma- or newline-separated list of usernames, or a GitHub Actions
# expression resolving to such a list (e.g. '${{ vars.BLOCKED_USERS }}')
blocked-users: "example-value"
# Guard policy: GitHub usernames whose content is elevated to 'approved' integrity
# regardless of author_association. Allows specific external contributors to
# bypass 'min-integrity' checks without lowering the global policy. Precedence:
# blocked-users > trusted-users > approval-labels > author_association. Requires
# 'min-integrity' to be set. Accepts an array of usernames, a comma-separated
# string, a newline-separated string, or a GitHub Actions expression (e.g. '${{
# vars.TRUSTED_USERS }}').
# (optional)
# Accepted formats:
# Format 1: Array of GitHub usernames to trust
trusted-users: []
# Array items: GitHub username to elevate to approved integrity
# Format 2: Comma- or newline-separated list of usernames, or a GitHub Actions
# expression resolving to such a list (e.g. '${{ vars.TRUSTED_USERS }}')
trusted-users: "example-value"
# Guard policy: GitHub label names that promote a content item's effective
# integrity to 'approved' when present. Enables human-review gates where a
# maintainer labels an item to allow it through. Uses max(base, approved) so it
# never lowers integrity. Does not override 'blocked-users'. Requires
# 'min-integrity' to be set. Accepts an array of label names, a comma-separated
# string, a newline-separated string, or a GitHub Actions expression (e.g. '${{
# vars.APPROVAL_LABELS }}').
# (optional)
# Accepted formats:
# Format 1: Array of GitHub label names
approval-labels: []
# Array items: GitHub label name
# Format 2: Comma- or newline-separated list of label names, or a GitHub Actions
# expression resolving to such a list (e.g. '${{ vars.APPROVAL_LABELS }}')
approval-labels: "example-value"
# Guard policy: GitHub reaction types that promote a content item's integrity to
# 'approved' when added by maintainers. Only enforced in proxy mode (DIFC/CLI
# proxy); ignored in MCP gateway mode because reaction authors cannot be
# identified. Optional; defaults to ["THUMBS_UP", "HEART"] when the
# integrity-reactions feature flag is enabled. Requires 'min-integrity' to be set
# and MCPG >= v0.2.18.
# (optional)
endorsement-reactions: []
# Array of GitHub ReactionContent enum value
# Guard policy: GitHub reaction types that demote content integrity when added by
# maintainers. Only enforced in proxy mode (DIFC/CLI proxy); ignored in MCP
# gateway mode because reaction authors cannot be identified. Optional; defaults
# to ["THUMBS_DOWN", "CONFUSED"] when the integrity-reactions feature flag is
# enabled. Disapproval overrides endorsement (safe default). Requires
# 'min-integrity' to be set and MCPG >= v0.2.18.
# (optional)
disapproval-reactions: []
# Array of GitHub ReactionContent enum value
# Guard policy: integrity level assigned when a disapproval reaction is present.
# Optional, defaults to 'none'. Requires the 'integrity-reactions' feature flag
# and MCPG >= v0.2.18.
# (optional)
disapproval-integrity: "none"
# Guard policy: minimum integrity level required for an endorser (reactor) to
# promote content. Optional, defaults to 'approved'. Requires the
# 'integrity-reactions' feature flag and MCPG >= v0.2.18.
# (optional)
endorser-min-integrity: "unapproved"
# GitHub App configuration for token minting. When configured, a GitHub App
# installation access token is minted at workflow start and used instead of the
# default token. This token overrides any custom github-token setting and provides
# fine-grained permissions matching the agent job requirements.
# (optional)
github-app:
# Deprecated alias for client-id. GitHub App ID/client ID (e.g., '${{ vars.APP_ID
# }}').
# (optional)
app-id: "example-value"
# GitHub App client ID (e.g., '${{ vars.APP_ID }}'). Required to mint a GitHub App
# token.
# (optional)
client-id: "example-value"
# GitHub App private key (e.g., '${{ secrets.APP_PRIVATE_KEY }}'). Required to
# mint a GitHub App token.
# (optional)
private-key: "example-value"
# If true, skip token minting when client-id/private-key resolve to empty strings
# at runtime. Defaults to false.
# (optional)
ignore-if-missing: true
# Optional owner of the GitHub App installation (defaults to current repository
# owner if not specified)
# (optional)
owner: "example-value"
# Optional list of repositories to grant access to (defaults to current repository
# if not specified)
# (optional)
repositories: []
# Array of strings
# Optional extra GitHub App-only permissions to merge into the minted token. Takes
# effect for tools.github.github-app and safe-outputs.github-app; ignored in
# on.github-app and the top-level github-app fallback. Use to add GitHub App-only
# scopes (e.g. members, organization-administration) not expressible via standard
# handler declarations.
# (optional)
permissions:
# Permission level for repository administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission for repository administration.
# (optional)
administration: "read"
# Permission level for Codespaces (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces: "read"
# Permission level for Codespaces lifecycle administration (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
codespaces-lifecycle-admin: "read"
# Permission level for Codespaces metadata (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces-metadata: "read"
# Permission level for user email addresses (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
email-addresses: "read"
# Permission level for repository environments (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
environments: "read"
# Permission level for git signing (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
git-signing: "read"
# Permission level for organization members (read/none; "write" is rejected by the
# compiler). Required for org team membership API calls.
# (optional)
members: "read"
# Permission level for organization administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-administration: "read"
# Permission level for organization announcement banners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-announcement-banners: "read"
# Permission level for organization Codespaces (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-codespaces: "read"
# Permission level for organization Copilot (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-copilot: "read"
# Permission level for organization custom org roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-org-roles: "read"
# Permission level for organization custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-properties: "read"
# Permission level for organization custom repository roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-repository-roles: "read"
# Permission level for organization events (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-events: "read"
# Permission level for organization webhooks (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-hooks: "read"
# Permission level for organization members management (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-members: "read"
# Permission level for organization packages (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-packages: "read"
# Permission level for organization personal access token requests (read/none;
# "write" is rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-token-requests: "read"
# Permission level for organization personal access tokens (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-tokens: "read"
# Permission level for organization plan (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-plan: "read"
# Permission level for organization self-hosted runners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-self-hosted-runners: "read"
# Permission level for organization user blocking (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-user-blocking: "read"
# Permission level for repository custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
repository-custom-properties: "read"
# Permission level for repository webhooks (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
repository-hooks: "read"
# Permission level for single file access (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
single-file: "read"
# Permission level for team discussions (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
team-discussions: "read"
# Permission level for Dependabot vulnerability alerts (read/none; "write" is
# rejected by the compiler). Also available as a GITHUB_TOKEN scope. When used
# with a GitHub App, forwarded as permission-vulnerability-alerts input.
# (optional)
vulnerability-alerts: "read"
# Permission level for GitHub Actions workflow files (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
workflows: "read"
# Bash shell command execution tool. Supports wildcards: '*' (all commands),
# 'command *' (command with any args, e.g., 'date *', 'echo *'). Default safe
# commands: echo, ls, pwd, cat, head, tail, grep, wc, sort, uniq, date.
# (optional)
# Accepted formats:
# Format 1: Enable bash tool with all shell commands allowed (security
# consideration: use restricted list in production)
bash: null
# Format 2: Enable bash tool - true allows all commands (equivalent to ['*']),
# false disables the tool
bash: true
# Format 3: List of allowed commands and patterns. Wildcards: '*' allows all
# commands, 'command *' allows command with any args (e.g., 'date *', 'echo *').
bash: []
# Array items: Command or pattern: 'echo' (exact match), 'echo *' (command with
# any args)
# Web content fetching tool for downloading web pages and API responses (subject
# to network permissions)
# (optional)
# Accepted formats:
# Format 1: Enable web fetch tool with default configuration
web-fetch: null
# Format 2: Web fetch tool configuration object
web-fetch:
{}
# Web search tool for performing internet searches and retrieving search results
# (subject to network permissions)
# (optional)
# Accepted formats:
# Format 1: Enable web search tool with default configuration
web-search: null
# Format 2: Web search tool configuration object
web-search:
{}
# File editing tool for reading, creating, and modifying files in the repository
# (optional)
# Accepted formats:
# Format 1: Enable edit tool
edit: null
# Format 2: Edit tool configuration object
edit:
{}
# Playwright browser automation tool for web scraping, testing, and UI
# interactions in containerized browsers
# (optional)
# Accepted formats:
# Format 1: Enable Playwright tool with default settings
playwright: null
# Format 2: Playwright tool configuration with custom version and arguments
playwright:
# Optional version pin. In CLI mode (recommended): the @playwright/cli npm package
# version (e.g., '0.1.11'). In MCP mode (deprecated): the Playwright browser
# Docker image version (e.g., 'v1.56.1'). Omit to use the default version.
# (optional)
version: null
# Optional additional arguments to append to the generated MCP server command (MCP
# mode only)
# (optional)
args: []
# Array of strings
# Integration mode: 'cli' (recommended) installs @playwright/cli via npm for
# token-efficient CLI invocations — use playwright-cli commands in bash and
# localhost to reach local servers; 'mcp' (deprecated) runs a Docker-based MCP
# server.
# (optional)
mode: "cli"
# GitHub Agentic Workflows MCP server for workflow introspection and analysis.
# Provides tools for checking status, compiling workflows, downloading logs, and
# auditing runs.
# (optional)
# Accepted formats:
# Format 1: Enable agentic-workflows tool with default settings
agentic-workflows: true
# Format 2: Enable agentic-workflows tool with default settings (same as true)
agentic-workflows: null
# Cache memory MCP configuration for persistent memory storage
# (optional)
# Accepted formats:
# Format 1: Enable cache-memory with default settings
cache-memory: true
# Format 2: Enable cache-memory with default settings (same as true)
cache-memory: null
# Format 3: Cache-memory configuration object
cache-memory:
# Custom cache key for memory MCP data (restore keys are auto-generated by
# splitting on '-')
# (optional)
key: "example-value"
# Optional description for the cache that will be shown in the agent prompt
# (optional)
description: "Description of the workflow"
# Number of days to retain uploaded artifacts (1-90 days, default: repository
# setting)
# (optional)
retention-days: 1
# If true, only restore the cache without saving it back. Uses
# actions/cache/restore instead of actions/cache. No artifact upload step will be
# generated.
# (optional)
restore-only: true
# Cache restore key scope: 'workflow' (default, only restores from same workflow)
# or 'repo' (restores from any workflow in the repository). Use 'repo' with
# caution as it allows cross-workflow cache sharing.
# (optional)
scope: "workflow"
# List of allowed file extensions (e.g., [".json", ".txt"]). Default: [".json",
# ".jsonl", ".txt", ".md", ".csv"]
# (optional)
allowed-extensions: []
# Array of strings
# Format 4: Array of cache-memory configurations for multiple caches
cache-memory: []
# Array items: object
# Comment memory configuration for managed comment persistence
# (optional)
# Accepted formats:
# Format 1: Configuration for persisting memory in a managed issue/PR comment.
# Memory is materialized to files for agent editing and synchronized back after
# execution.
comment-memory:
# Maximum number of comment_memory updates to process (default: 1). Supports
# integer or GitHub Actions expression.
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target for comment memory: 'triggering' (default), '*' (current issue/PR), or
# explicit issue/PR number
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository memory storage.
# (optional)
target-repo: "example-value"
# Additional repositories in format 'owner/repo' allowed for comment memory
# operations.
# (optional)
allowed-repos: []
# Array of strings
# Default memory identifier when output items omit memory_id.
# (optional)
memory-id: "example-value"
# Controls whether AI-generated footer is added to the managed comment. Defaults
# to true.
# (optional)
footer: true
# GitHub token to use for comment-memory operations. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable (true) or disable (false) comment-memory.
comment-memory: true
# Format 3: Explicitly disable comment-memory
comment-memory: null
# Timeout in seconds for tool/MCP server operations. Applies to all tools and MCP
# servers if supported by the engine. Default: 60 seconds (for both Claude and
# Codex). Supports GitHub Actions expressions for reusable workflow_call
# workflows.
# (optional)
# Accepted formats:
# Format 1: integer
timeout: 1
# Format 2: GitHub Actions expression (e.g. '${{ inputs.tool-timeout }}')
timeout: "example-value"
# Timeout in seconds for MCP server startup. Applies to MCP server initialization
# if supported by the engine. Default: 120 seconds. Supports GitHub Actions
# expressions for reusable workflow_call workflows.
# (optional)
# Accepted formats:
# Format 1: integer
startup-timeout: 1
# Format 2: GitHub Actions expression (e.g. '${{ inputs.startup-timeout }}')
startup-timeout: "example-value"
# When true, each user-facing MCP server is mounted as a standalone CLI tool on
# PATH. The agent can then call MCP servers via shell commands (e.g. 'github
# issue_read --method get ...'). CLI-mounted servers remain in the MCP gateway
# config so their containers can start, and are removed only from the agent's
# final config during convert_gateway_config_*.sh processing. Default: false.
# (optional)
cli-proxy: true
# Repo memory configuration for git-based persistent storage
# (optional)
# Accepted formats:
# Format 1: Enable repo-memory with default settings
repo-memory: true
# Format 2: Enable repo-memory with default settings (same as true)
repo-memory: null
# Format 3: Repo-memory configuration object
repo-memory:
# Branch prefix for memory storage (default: 'memory'). Must be 4-32 characters,
# alphanumeric with hyphens/underscores, and cannot be 'copilot'. Branch will be
# named {branch-prefix}/{id}
# (optional)
branch-prefix: "example-value"
# Target repository for memory storage (default: current repository). Format:
# owner/repo
# (optional)
target-repo: "example-value"
# Git branch name for memory storage (default: {branch-prefix}/default or
# memory/default if branch-prefix not set)
# (optional)
branch-name: "example-value"
# Glob patterns for files to include in repository memory. Supports wildcards
# (e.g., '**/*.md', 'docs/**/*.json') to filter cached files.
# (optional)
# Accepted formats:
# Format 1: Single file glob pattern for allowed files
file-glob: "example-value"
# Format 2: Array of file glob patterns for allowed files
file-glob: []
# Array items: string
# Maximum size per file in bytes (default: 102400 = 100KB)
# (optional)
max-file-size: 1
# Maximum file count per commit (default: 100)
# (optional)
max-file-count: 1
# Maximum total patch size in bytes (default: 10240 = 10KB, max: 1048576 = 1MB).
# The total size of the git diff must not exceed this value.
# (optional)
max-patch-size: 1
# Optional description for the memory that will be shown in the agent prompt
# (optional)
description: "Description of the workflow"
# Create orphaned branch if it doesn't exist (default: true)
# (optional)
create-orphan: true
# Use the GitHub Wiki git repository instead of the regular repository. When
# enabled, files are stored in and read from the wiki, and the agent will be
# instructed to follow GitHub Wiki markdown syntax (default: false)
# (optional)
wiki: true
# List of allowed file extensions (e.g., [".json", ".txt"]). Default: [".json",
# ".jsonl", ".txt", ".md", ".csv"]
# (optional)
allowed-extensions: []
# Array of strings
# Format 4: Array of repo-memory configurations for multiple memory locations
repo-memory: []
# Array items: object
# Cache configuration for workflow (uses actions/cache syntax)
# (optional)
# Accepted formats:
# Format 1: Single cache configuration
cache:
# An explicit key for restoring and saving the cache
key: "example-value"
# File path or directory to cache for faster workflow execution. Can be a single
# path or an array of paths to cache multiple locations.
# Accepted formats:
# Format 1: A single path to cache
path: "example-value"
# Format 2: Multiple paths to cache
path: []
# Array items: string
# Optional list of fallback cache key patterns to use if exact cache key is not
# found. Enables partial cache restoration for better performance.
# (optional)
# Accepted formats:
# Format 1: A single restore key
restore-keys: "example-value"
# Format 2: Multiple restore keys
restore-keys: []
# Array items: string
# The chunk size used to split up large files during upload, in bytes
# (optional)
upload-chunk-size: 1
# Fail the workflow if cache entry is not found
# (optional)
fail-on-cache-miss: true
# If true, only checks if cache entry exists and skips download
# (optional)
lookup-only: true
# Optional custom name for the cache step (overrides auto-generated name)
# (optional)
name: "My Workflow"
# Format 2: Multiple cache configurations
cache: []
# Array items: object
# Safe output processing configuration that automatically creates GitHub issues,
# comments, and pull requests from AI workflow output without requiring write
# permissions in the main job
# (optional)
safe-outputs:
# List of allowed domains for URL redaction in safe output handlers. Supports
# ecosystem identifiers (e.g., "python", "node", "default-safe-outputs") like
# network.allowed. These domains are unioned with the engine defaults and
# network.allowed when computing the final allowed domain set. localhost and
# github.com are always included.
# (optional)
allowed-domains: []
# Array of strings
# List of allowed repositories for GitHub references (e.g., #123 or
# owner/repo#456). Use 'repo' to allow current repository. References to other
# repositories will be escaped with backticks. If not specified, all references
# are allowed.
# (optional)
allowed-github-references: []
# Array of strings
# Enable AI agents to create GitHub issues from workflow output. Supports title
# prefixes, automatic labeling, assignees, and cross-repository creation. Does not
# require 'issues: write' permission.
# (optional)
# Accepted formats:
# Format 1: Configuration for automatically creating GitHub issues from AI
# workflow output. The main job does not need 'issues: write' permission.
create-issue:
# Optional prefix to add to the beginning of the issue title (e.g., '[ai] ' or
# '[analysis] ')
# (optional)
title-prefix: "example-value"
# Optional list of labels to automatically attach to created issues (e.g.,
# ['automation', 'ai-generated'])
# (optional)
labels: []
# Array of strings
# Optional list of allowed labels that can be used when creating issues. If
# omitted, any labels are allowed (including creating new ones). When specified,
# the agent can only use labels from this list.
# (optional)
allowed-labels: []
# Array of strings
# Optional list of issue field names that can be modified by create-issue field
# updates. If omitted or empty, any issue field may be set. Use ['*'] to
# explicitly allow all.
# (optional)
allowed-fields: []
# Array of strings
# GitHub usernames to assign the created issue to. Can be a single username string
# or array of usernames. Use 'copilot' to assign to GitHub Copilot.
# (optional)
# Accepted formats:
# Format 1: Single GitHub username to assign the created issue to (e.g., 'user1'
# or 'copilot'). Use 'copilot' to assign to GitHub Copilot using the @copilot
# special value.
assignees: "example-value"
# Format 2: List of GitHub usernames to assign the created issue to (e.g.,
# ['user1', 'user2', 'copilot']). Use 'copilot' to assign to GitHub Copilot using
# the @copilot special value.
assignees: []
# Array items: string
# Maximum number of issues to create (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Title-based deduplication for create-issue. Set to true for exact title
# matching, or provide a non-negative integer to deduplicate by Levenshtein edit
# distance (e.g., 1 allows one-character differences). Applies within-run and
# against open/recently-closed repository issues.
# (optional)
# Accepted formats:
# Format 1: boolean
deduplicate-by-title: true
# Format 2: integer
deduplicate-by-title: 1
# Target repository in format 'owner/repo' for cross-repository issue creation.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that issues can be
# created in. When specified, the agent can use a 'repo' field in the output to
# specify which repository to create the issue in. The target repository (current
# or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# Time until the issue expires and should be automatically closed. Supports
# integer (days), relative time format, or false to disable expiration. Minimum
# duration: 2 hours. When set, a maintenance workflow will be generated.
# (optional)
# Accepted formats:
# Format 1: Number of days until expires
expires: 1
# Format 2: Relative time (e.g., '2h', '7d', '2w', '1m', '1y'); minimum 2h for
# hour values
expires: "example-value"
# Format 3: Set to false to explicitly disable expiration
expires: false
# If true, group issues as sub-issues under a parent issue. The workflow ID is
# used as the group identifier. Parent issues are automatically created and
# managed, with a maximum of 64 sub-issues per parent.
# (optional)
group: true
# When true, automatically close older issues with the same workflow-id marker as
# 'not planned' with a comment linking to the new issue. Searches for issues
# containing the workflow-id marker in their body. Maximum 10 issues will be
# closed. Only runs if issue creation succeeds.
# (optional)
close-older-issues: true
# Optional explicit deduplication key for close-older matching. When set, a `` marker is embedded in the issue body and used as
# the primary key for searching and filtering older issues instead of the
# workflow-id markers. This gives deterministic isolation across caller workflows
# and is stable across workflow renames. The value is normalized to identifier
# style (lowercase alphanumeric, dashes, underscores).
# (optional)
close-older-key: "example-value"
# When true, if an open issue with the same close-older-key (or workflow-id marker
# when no key is set) was already created today (UTC), post the new content as a
# comment on that existing issue instead of creating a new one. Groups multiple
# same-day runs into a single issue. Works best when combined with
# close-older-issues: true.
# (optional)
group-by-day: true
# Controls whether AI-generated footer is added to the issue. When false, the
# visible footer content is omitted but XML markers (workflow-id, tracker-id,
# metadata) are still included for searchability. Defaults to true.
# (optional)
footer: true
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable issue creation with default configuration
create-issue: null
# Enable creation of GitHub Copilot coding agent tasks from workflow output.
# Allows workflows to spawn new agent sessions for follow-up work.
# (optional)
# Accepted formats:
# Format 1: DEPRECATED: Use 'create-agent-session' instead. Configuration for
# creating GitHub Copilot coding agent sessions from agentic workflow output using
# gh agent-task CLI. The main job does not need write permissions.
create-agent-task:
# Base branch for the agent session pull request. Defaults to the current branch
# or repository default branch.
# (optional)
base: "example-value"
# Maximum number of agent sessions to create (default: 1) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository agent session
# creation. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that agent sessions can
# be created in. When specified, the agent can use a 'repo' field in the output to
# specify which repository to create the agent session in. The target repository
# (current or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable agent session creation with default configuration
create-agent-task: null
# Enable creation of GitHub Copilot coding agent sessions from workflow output.
# Allows workflows to start interactive agent conversations.
# (optional)
# Accepted formats:
# Format 1: Configuration for creating GitHub Copilot coding agent sessions from
# agentic workflow output using gh agent-task CLI. The main job does not need
# write permissions.
create-agent-session:
# Base branch for the agent session pull request. Defaults to the current branch
# or repository default branch.
# (optional)
base: "example-value"
# Maximum number of agent sessions to create (default: 1) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository agent session
# creation. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that agent sessions can
# be created in. When specified, the agent can use a 'repo' field in the output to
# specify which repository to create the agent session in. The target repository
# (current or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable agent session creation with default configuration
create-agent-session: null
# Enable AI agents to add items to GitHub Projects, update custom fields, and
# manage project structure. Use this for organizing work into projects with status
# tracking, priority management, and custom metadata.
# (optional)
# Accepted formats:
# Format 1: Configuration for managing GitHub Projects boards. Enable agents to
# add issues and pull requests to projects, update custom field values (status,
# priority, effort, dates), create project fields and views. By default it is
# update-only: if the project does not exist, the job fails with instructions to
# create it. To allow workflows to create missing projects, explicitly opt in via
# agent output field create_if_missing=true. Requires a Personal Access Token
# (PAT) or GitHub App token with Projects permissions (default GITHUB_TOKEN cannot
# be used). Agent output includes: project (full URL or temporary project ID like
# aw_XXXXXXXXXXXX or #aw_XXXXXXXXXXXX from create_project), content_type
# (issue|pull_request|draft_issue), content_number, fields, create_if_missing. For
# specialized operations, agent can also provide: operation
# (create_fields|create_view), field_definitions (array of field configs when
# operation=create_fields), view (view config object when operation=create_view).
update-project:
# Maximum number of project operations to perform (default: 10). Each operation
# may add a project item, or update its fields. Supports integer or GitHub Actions
# expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Target project URL for update-project operations. This is required in the
# configuration for documentation purposes. Agent messages MUST explicitly include
# the project field in their output - the configured value is not used as a
# fallback. Must be a valid GitHub Projects v2 URL.
project: "example-value"
# Default repository in format 'owner/repo' for cross-repository content
# resolution. When specified, the agent can use 'target_repo' in agent output to
# resolve issues or PRs from this repository. Wildcards ('*') are not allowed.
# Supports GitHub Actions expression syntax (e.g., '${{ vars.TARGET_REPO }}').
# (optional)
# Accepted formats:
# Format 1: string
target-repo: "example-value"
# Format 2: GitHub Actions expression that resolves to owner/repo at runtime
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' allowed for
# cross-repository content resolution via 'target_repo'. The target-repo (or
# current repo) is always implicitly allowed. Supports wildcard patterns (e.g.,
# 'org/*', '*/repo', '*') and GitHub Actions expression syntax for individual
# entries.
# (optional)
allowed-repos: []
# Optional array of project views to create. Each view must have a name and
# layout. Views are created during project setup.
# (optional)
views: []
# Array items:
# The name of the view (e.g., 'Sprint Board', 'Roadmap')
name: "My Workflow"
# The layout type of the view
layout: "table"
# Optional filter query for the view (e.g., 'is:issue is:open', 'label:bug')
# (optional)
filter: "example-value"
# Optional array of field IDs that should be visible in the view (table/board
# only, not applicable to roadmap)
# (optional)
visible-fields: []
# Optional human description for the view. Not supported by the GitHub Views API
# and may be ignored.
# (optional)
description: "Description of the workflow"
# Optional array of project custom fields to create up-front.
# (optional)
field-definitions: []
# Array items:
# The field name to create (e.g., 'status', 'priority')
name: "My Workflow"
# The GitHub Projects v2 custom field type
data-type: "DATE"
# Options for SINGLE_SELECT fields. GitHub does not support adding options later.
# (optional)
options: []
# Array of strings
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable project management with default configuration (max=10)
update-project: null
# Enable AI agents to create new GitHub Projects for organizing and tracking work
# across issues and pull requests.
# (optional)
# Accepted formats:
# Format 1: Configuration for creating new GitHub Projects boards. Enables agents
# to create new project boards with optional custom fields, views, and an initial
# item. Requires a Personal Access Token (PAT) or GitHub App token with Projects
# write permission (default GITHUB_TOKEN cannot be used). Agent output includes:
# title (project name), owner (org/user login, uses default if omitted),
# owner_type ('org' or 'user'), optional item_url (issue to add as first item),
# and optional field_definitions. Returns a temporary project ID for use in
# subsequent update_project operations.
create-project:
# Maximum number of create operations to perform (default: 1). Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token to use for this specific output type. Must have Projects write
# permission. Overrides global github-token if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Optional default target owner (organization or user login, e.g., 'myorg' or
# 'username') for the new project. If specified, the agent can omit the owner
# field in the tool call and this default will be used. The agent can still
# override by providing an owner in the tool call.
# (optional)
target-owner: "example-value"
# Optional prefix for auto-generated project titles (default: 'Project'). When the
# agent doesn't provide a title, the project title is auto-generated as
# ': ' or ' #' based on the
# issue context.
# (optional)
title-prefix: "example-value"
# Optional array of project views to create automatically after project creation.
# Each view must have a name and layout. Views are created immediately after the
# project is created.
# (optional)
views: []
# Array items:
# The name of the view (e.g., 'Sprint Board', 'Roadmap')
name: "My Workflow"
# The layout type of the view
layout: "table"
# Optional filter query for the view (e.g., 'is:issue is:open', 'label:bug')
# (optional)
filter: "example-value"
# Optional array of field IDs that should be visible in the view (table/board
# only, not applicable to roadmap)
# (optional)
visible-fields: []
# Optional human description for the view. Not supported by the GitHub Views API
# and may be ignored.
# (optional)
description: "Description of the workflow"
# Optional array of project custom fields to create automatically after project
# creation.
# (optional)
field-definitions: []
# Array items:
# The field name to create (e.g., 'Priority', 'Classification')
name: "My Workflow"
# The GitHub Projects v2 custom field type
data-type: "DATE"
# Options for SINGLE_SELECT fields. GitHub does not support adding options later.
# (optional)
options: []
# Array of strings
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable project creation with default configuration (max=1)
create-project: null
# Enable AI agents to post status updates to GitHub Projects for progress tracking
# and stakeholder communication.
# (optional)
# Accepted formats:
# Format 1: Configuration for posting status updates to GitHub Projects. Status
# updates provide stakeholder communication about project progress, health, and
# timeline. Each update appears in the project's Updates tab and creates a
# historical record. Requires a Personal Access Token (PAT) or GitHub App token
# with Projects read & write permission (default GITHUB_TOKEN cannot be used).
# Typically used by scheduled workflows or orchestrators to post regular progress
# summaries with status indicators (on-track, at-risk, off-track, complete,
# inactive), dates, and progress details.
create-project-status-update:
# Maximum number of status updates to create (default: 1). Typically 1 per
# orchestrator run. Supports integer or GitHub Actions expression (e.g. '${{
# inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified. Must have Projects: Read+Write permission.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Target project URL for status update operations. This is required in the
# configuration for documentation purposes. Agent messages MUST explicitly include
# the project field in their output - the configured value is not used as a
# fallback. Must be a valid GitHub Projects v2 URL.
project: "example-value"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable project status updates with default configuration (max=1)
create-project-status-update: null
# Enable AI agents to create GitHub Discussions from workflow output. Supports
# categorization, labeling, and automatic closure of older discussions. Does not
# require 'discussions: write' permission.
# (optional)
# Accepted formats:
# Format 1: Configuration for creating GitHub discussions from agentic workflow
# output
create-discussion:
# Optional prefix for the discussion title
# (optional)
title-prefix: "example-value"
# Optional discussion category. Can be a category ID (string or numeric value),
# category name, or category slug/route. If not specified, uses the first
# available category. Matched first against category IDs, then against category
# names, then against category slugs. Numeric values are automatically converted
# to strings at runtime.
# (optional)
category: null
# Minimum required length of the discussion body content (before footer/metadata)
# in characters. If a create_discussion message body is shorter than this value,
# the safe-outputs job fails.
# (optional)
min-body-length: 1
# Optional list of labels to attach to created discussions. Also used for matching
# when close-older-discussions is enabled - discussions must have ALL specified
# labels (AND logic).
# (optional)
labels: []
# Array of strings
# Optional list of allowed labels that can be used when creating discussions. If
# omitted, any labels are allowed (including creating new ones). When specified,
# the agent can only use labels from this list.
# (optional)
allowed-labels: []
# Array of strings
# Maximum number of discussions to create (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository discussion
# creation. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that discussions can be
# created in. When specified, the agent can use a 'repo' field in the output to
# specify which repository to create the discussion in. The target repository
# (current or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# When true, automatically close older discussions matching the same title prefix
# or labels as 'outdated' with a comment linking to the new discussion. Requires
# title-prefix or labels to be set. Maximum 10 discussions will be closed. Only
# runs if discussion creation succeeds. When fallback-to-issue is enabled and
# discussion creation fails, older issues will be closed instead.
# (optional)
close-older-discussions: true
# Optional explicit deduplication key for close-older matching. When set, a `` marker is embedded in the discussion body and used
# as the primary key for searching and filtering older discussions instead of the
# workflow-id markers. This gives deterministic isolation across caller workflows
# and is stable across workflow renames. The value is normalized to identifier
# style (lowercase alphanumeric, dashes, underscores).
# (optional)
close-older-key: "example-value"
# When true (default), fallback to creating an issue if discussion creation fails
# due to permissions. The fallback issue will include a note indicating it was
# intended to be a discussion. If close-older-discussions is enabled, the
# close-older-issues logic will be applied to the fallback issue.
# (optional)
fallback-to-issue: true
# Controls whether AI-generated footer is added to the discussion. When false, the
# visible footer content is omitted but XML markers (workflow-id, tracker-id,
# metadata) are still included for searchability. Defaults to true.
# (optional)
footer: true
# Time until the discussion expires and should be automatically closed. Supports
# integer (days), relative time format like '2h' (2 hours), '7d' (7 days), '2w' (2
# weeks), '1m' (1 month), '1y' (1 year), or false to disable expiration. Minimum
# duration: 2 hours. When set, a maintenance workflow will be generated. Defaults
# to 7 days if not specified.
# (optional)
# Accepted formats:
# Format 1: Number of days until expires
expires: 1
# Format 2: Relative time (e.g., '2h', '7d', '2w', '1m', '1y'); minimum 2h for
# hour values
expires: "example-value"
# Format 3: Set to false to explicitly disable expiration
expires: false
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable discussion creation with default configuration
create-discussion: null
# Enable AI agents to close GitHub Discussions based on workflow analysis or
# conditions.
# (optional)
# Accepted formats:
# Format 1: Configuration for closing GitHub discussions with comment and
# resolution from agentic workflow output
close-discussion:
# Only close discussions that have all of these labels
# (optional)
required-labels: []
# Array of strings
# Only close discussions with this title prefix
# (optional)
required-title-prefix: "example-value"
# Only close discussions in this category
# (optional)
required-category: "example-value"
# Target for closing: 'triggering' (default, current discussion), or '*' (any
# discussion with discussion_number field)
# (optional)
target: "example-value"
# Maximum number of discussions to close (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository operations. Takes
# precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable discussion closing with default configuration
close-discussion: null
# Enable AI agents to edit and update existing GitHub Discussion content, titles,
# and metadata.
# (optional)
# Accepted formats:
# Format 1: Configuration for updating GitHub discussions from agentic workflow
# output
update-discussion:
# Target for updates: 'triggering' (default), '*' (any discussion), or explicit
# discussion number
# (optional)
target: "example-value"
# Allow updating discussion title - presence of key indicates field can be updated
# (optional)
title: null
# Allow updating discussion body - presence of key indicates field can be updated
# (optional)
body: null
# Allow updating discussion labels - presence of key indicates field can be
# updated
# (optional)
labels: null
# Optional list of allowed labels. If omitted, any labels are allowed (including
# creating new ones).
# (optional)
allowed-labels: []
# Array of strings
# Maximum number of discussions to update (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository discussion
# updates. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# Controls whether AI-generated footer is added when updating the discussion body.
# When false, the visible footer content is omitted. Defaults to true. Only
# applies when 'body' is enabled.
# (optional)
footer: true
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Format 2: Enable discussion updating with default configuration
update-discussion: null
# Enable AI agents to close GitHub issues based on workflow analysis, resolution
# detection, or automated triage.
# (optional)
# Accepted formats:
# Format 1: Configuration for closing GitHub issues with comment from agentic
# workflow output
close-issue:
# Only close issues that have all of these labels
# (optional)
required-labels: []
# Array of strings
# Only close issues with this title prefix
# (optional)
required-title-prefix: "example-value"
# Target for closing: 'triggering' (default, current issue), or '*' (any issue
# with issue_number field)
# (optional)
target: "example-value"
# Maximum number of issues to close (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository operations. Takes
# precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that issues can be closed
# in. When specified, the agent can use a 'repo' field in the output to specify
# which repository to close the issue in. The target repository (current or
# target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Reason for closing the issue (default: completed)
# (optional)
state-reason: "completed"
# Format 2: Enable issue closing with default configuration
close-issue: null
# Enable AI agents to close pull requests based on workflow analysis or automated
# review decisions.
# (optional)
# Accepted formats:
# Format 1: Configuration for closing GitHub pull requests without merging, with
# comment from agentic workflow output
close-pull-request:
# Only close pull requests that have any of these labels
# (optional)
required-labels: []
# Array of strings
# Only close pull requests with this title prefix
# (optional)
required-title-prefix: "example-value"
# Target for closing: 'triggering' (default, current PR), or '*' (any PR with
# pull_request_number field)
# (optional)
target: "example-value"
# Maximum number of pull requests to close (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository operations. Takes
# precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable pull request closing with default configuration
close-pull-request: null
# Enable AI agents to mark draft pull requests as ready for review when criteria
# are met.
# (optional)
# Accepted formats:
# Format 1: Configuration for marking draft pull requests as ready for review,
# with comment from agentic workflow output
mark-pull-request-as-ready-for-review:
# Only mark pull requests that have any of these labels
# (optional)
required-labels: []
# Array of strings
# Only mark pull requests with this title prefix
# (optional)
required-title-prefix: "example-value"
# Target for marking: 'triggering' (default, current PR), or '*' (any PR with
# pull_request_number field)
# (optional)
target: "example-value"
# Maximum number of pull requests to mark as ready (default: 1) Supports integer
# or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository operations. Takes
# precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable marking pull requests as ready for review with default
# configuration
mark-pull-request-as-ready-for-review: null
# Enable AI agents to add comments to GitHub issues, pull requests, or
# discussions. Supports templating, cross-repository commenting, and automatic
# mentions.
# (optional)
# Accepted formats:
# Format 1: Configuration for automatically creating GitHub issue or pull request
# comments from AI workflow output. The main job does not need write permissions.
add-comment:
# Maximum number of comments to create (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target for comments: 'triggering' (default), '*' (any issue), or explicit issue
# number
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository comments. Takes
# precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that comments can be
# created in. When specified, the agent can use a 'repo' field in the output to
# specify which repository to create the comment in. The target repository
# (current or target-repo) is always implicitly allowed. Accepts an array or a
# GitHub Actions expression resolving to a comma-separated list (e.g. '${{
# inputs[\'allowed-repos\'] }}').
# (optional)
# Accepted formats:
# Format 1: Array of repository slugs in 'owner/repo' format
allowed-repos: []
# Array items: string
# Format 2: GitHub Actions expression resolving to a comma-separated list of
# repository slugs (e.g. '${{ inputs[\'allowed-repos\'] }}')
allowed-repos: "example-value"
# When true, minimizes/hides all previous comments from the same agentic workflow
# (identified by tracker-id) before creating the new comment. Supports literal
# boolean or GitHub Actions expression (e.g. '${{ inputs.hide-older-comments }}').
# Default: false.
# (optional)
# Accepted formats:
# Format 1: boolean
hide-older-comments: true
# Format 2: GitHub Actions expression that resolves to a boolean at runtime
hide-older-comments: "example-value"
# List of allowed reasons for hiding older comments when hide-older-comments is
# enabled. Default: all reasons allowed (spam, abuse, off_topic, outdated,
# resolved, low_quality).
# (optional)
allowed-reasons: []
# Array of strings
# Controls whether the workflow requests discussions:write permission for
# add-comment. Default: true (includes discussions:write). Set to false if your
# GitHub App lacks Discussions permission to prevent 422 errors during token
# generation.
# (optional)
discussions: true
# Controls whether the workflow requests issues:write permission for add-comment.
# Default: true (includes issues:write). Set to false to disable issue commenting
# permissions.
# (optional)
issues: true
# Controls whether the workflow requests pull-requests:write permission for
# add-comment. Default: true (includes pull-requests:write). Set to false to
# disable pull request commenting permissions.
# (optional)
pull-requests: true
# Controls whether AI-generated footer is added to the comment. When false, the
# visible footer content is omitted but XML markers (workflow-id, metadata) are
# still included for searchability. Defaults to true.
# (optional)
footer: true
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Conjunctive label constraint: ALL of these labels must be present on the
# issue/PR for the operation to proceed.
# (optional)
required-labels: []
# Array of strings
# Title prefix constraint: the issue/PR title must start with this prefix for the
# operation to proceed.
# (optional)
required-title-prefix: "example-value"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable issue comment creation with default configuration
add-comment: null
# Enable AI agents to create GitHub pull requests from workflow-generated code
# changes, patches, or analysis results.
# (optional)
# Accepted formats:
# Format 1: Configuration for creating GitHub pull requests from agentic workflow
# output. Supports creating multiple PRs in a single run when max > 1.
create-pull-request:
# Maximum number of pull requests to create (default: 1). Each PR requires
# distinct changes on a separate branch. Supports integer or GitHub Actions
# expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Optional prefix to prepend to the pull request branch name (e.g. "signed/").
# Applied before the agent-specified or auto-generated branch name.
# (optional)
branch-prefix: "example-value"
# Optional prefix for the pull request title
# (optional)
title-prefix: "example-value"
# Optional list of labels to attach to the pull request. Accepts an array of label
# names or a GitHub Actions expression resolving to a comma-separated list (e.g.
# '${{ inputs.labels }}').
# (optional)
# Accepted formats:
# Format 1: Array of label names
labels: []
# Array items: string
# Format 2: GitHub Actions expression resolving to a comma-separated list of label
# names (e.g. '${{ inputs.labels }}')
labels: "example-value"
# Optional list of allowed labels that can be used when creating pull requests. If
# omitted, any labels are allowed (including creating new ones). When specified,
# the agent can only use labels from this list.
# (optional)
allowed-labels: []
# Array of strings
# Optional reviewer(s) to assign to the pull request. Accepts either a single
# string or an array of usernames. Use 'copilot' to request a code review from
# GitHub Copilot.
# (optional)
# Accepted formats:
# Format 1: Single reviewer username to assign to the pull request. Use 'copilot'
# to request a code review from GitHub Copilot using the
# copilot-pull-request-reviewer[bot].
reviewers: "example-value"
# Format 2: List of reviewer usernames to assign to the pull request. Use
# 'copilot' to request a code review from GitHub Copilot using the
# copilot-pull-request-reviewer[bot].
reviewers: []
# Array items: string
# Optional team reviewer(s) to assign to the pull request. Accepts either a single
# string or an array of team slugs.
# (optional)
# Accepted formats:
# Format 1: Single team slug to assign as a reviewer to the pull request.
team-reviewers: "example-value"
# Format 2: List of team slugs to assign as reviewers to the pull request.
team-reviewers: []
# Array items: string
# Optional assignee(s) for a fallback issue created when pull request creation
# cannot proceed, including protected-files fallback-to-issue and pull request
# creation or push failures. Accepts either a single string or an array of
# usernames.
# (optional)
# Accepted formats:
# Format 1: Single username to assign to a fallback issue created when pull
# request creation cannot proceed, including protected-files fallback-to-issue and
# pull request creation or push failures.
assignees: "example-value"
# Format 2: List of usernames to assign to a fallback issue created when pull
# request creation cannot proceed, including protected-files fallback-to-issue and
# pull request creation or push failures.
assignees: []
# Array items: string
# Optional labels to apply to fallback issues created when pull request creation
# cannot proceed. When omitted, fallback issues reuse pull request labels. A
# managed label is always added for triage.
# (optional)
fallback-labels: []
# Array of strings
# Whether to create pull request as draft (defaults to true). Accepts a boolean or
# a GitHub Actions expression.
# (optional)
draft: null
# Behavior when no changes to push: 'warn' (default - log warning but succeed),
# 'error' (fail the action), or 'ignore' (silent success)
# (optional)
if-no-changes: "warn"
# When true, allows creating a pull request without any initial changes or git
# patch. This is useful for preparing a feature branch that an agent can push
# changes to later. The branch will be created from the base branch without
# applying any patch. Defaults to false.
# (optional)
allow-empty: true
# Target repository in format 'owner/repo' for cross-repository pull request
# creation. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that pull requests can be
# created in. When specified, the agent can use a 'repo' field in the output to
# specify which repository to create the pull request in. The target repository
# (current or target-repo) is always implicitly allowed. Accepts an array or a
# GitHub Actions expression resolving to a comma-separated list (e.g. '${{
# inputs[\'allowed-repos\'] }}').
# (optional)
# Accepted formats:
# Format 1: Array of repository slugs in 'owner/repo' format
allowed-repos: []
# Array items: string
# Format 2: GitHub Actions expression resolving to a comma-separated list of
# repository slugs (e.g. '${{ inputs[\'allowed-repos\'] }}')
allowed-repos: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Time until the pull request expires and should be automatically closed (only for
# same-repo PRs without target-repo). Supports integer (days) or relative time
# format. Minimum duration: 2 hours.
# (optional)
# Accepted formats:
# Format 1: Number of days until expires
expires: 1
# Format 2: Relative time (e.g., '2h', '7d', '2w', '1m', '1y'); minimum 2h for
# hour values
expires: "example-value"
# Enable auto-merge for the pull request. When enabled, the PR will be
# automatically merged once all required checks pass and required approvals are
# met. Defaults to false.
# (optional)
auto-merge: true
# Base branch for the pull request. Defaults to the workflow's branch
# (github.ref_name) if not specified. Useful for cross-repository PRs targeting
# non-default branches (e.g., 'vnext', 'release/v1.0').
# (optional)
base-branch: "example-value"
# Optional list of allowed source branch patterns (glob syntax, e.g. 'feature/*',
# 'release/*'). When configured, the effective create_pull_request branch must
# match one of these patterns. Accepts an array or a GitHub Actions expression
# resolving to a comma-separated list (e.g. '${{ inputs[\'allowed-branches\']
# }}').
# (optional)
# Accepted formats:
# Format 1: Array of source branch patterns (glob syntax supported)
allowed-branches: []
# Array items: string
# Format 2: GitHub Actions expression resolving to a comma-separated list of
# source branch patterns (e.g. '${{ inputs[\'allowed-branches\'] }}')
allowed-branches: "example-value"
# Optional list of allowed base branch patterns (glob syntax, e.g. 'main',
# 'release/*'). When configured, the agent may provide a `base` field in
# create_pull_request output to override base-branch for a single run, but only if
# it matches one of these patterns. Accepts an array or a GitHub Actions
# expression resolving to a comma-separated list (e.g. '${{
# inputs[\'allowed-base-branches\'] }}').
# (optional)
# Accepted formats:
# Format 1: Array of base branch patterns (glob syntax supported)
allowed-base-branches: []
# Array items: string
# Format 2: GitHub Actions expression resolving to a comma-separated list of base
# branch patterns (e.g. '${{ inputs[\'allowed-base-branches\'] }}')
allowed-base-branches: "example-value"
# Maximum allowed size for git patches in kilobytes (KB) for create-pull-request
# only. Overrides safe-outputs max-patch-size for this output type. Defaults to
# 1024 KB (1 MB) when unset.
# (optional)
max-patch-size: 1
# Maximum allowed number of unique files in a create-pull-request patch. Overrides
# safe-outputs max-patch-files for this output type. Defaults to 100 when unset.
# (optional)
max-patch-files: 1
# Controls whether AI-generated footer is added to the pull request. When false,
# the visible footer content is omitted but XML markers (workflow-id, tracker-id,
# metadata) are still included for searchability. Defaults to true.
# (optional)
footer: true
# Controls the fallback behavior when pull request creation fails. When true
# (default), an issue is created as a fallback with the patch content. When false,
# no issue is created and the workflow fails with an error. Setting to false also
# removes the issues:write permission requirement.
# (optional)
fallback-as-issue: true
# When true (default), automatically appends a closing keyword ("Fixes #N") to the
# PR description when the workflow is triggered from an issue and no closing
# keyword is already present. This causes GitHub to auto-close the triggering
# issue when the PR is merged. Set to false to prevent this behavior, e.g., for
# partial-work PRs or multi-PR workflows. Accepts a boolean or a GitHub Actions
# expression.
# (optional)
auto-close-issue: null
# Token used to push an empty commit after PR creation to trigger CI events. Works
# around the GITHUB_TOKEN limitation where pushes don't trigger workflow runs.
# Defaults to the magic secret GH_AW_CI_TRIGGER_TOKEN if set in the repository.
# Use a secret expression (e.g. '${{ secrets.CI_TOKEN }}') for a custom token, or
# 'app' for GitHub App auth.
# (optional)
github-token-for-extra-empty-commit: "example-value"
# Controls protected-file protection. String form: blocked (default), allowed, or
# fallback-to-issue — or a GitHub Actions expression for reusable workflows.
# Object form: { policy, exclude } to customise the protected-file set.
# (optional)
# Accepted formats:
# Format 1: Controls protected-file protection. blocked (default): hard-block any
# patch that modifies package manifests (e.g. package.json, go.mod), engine
# instruction files (e.g. AGENTS.md, CLAUDE.md) or .github/ files. allowed: allow
# all changes. fallback-to-issue: push the branch but create a review issue
# instead of a PR, so a human can review the manifest changes before merging.
protected-files: "blocked"
# Format 2: GitHub Actions expression that resolves to 'blocked', 'allowed', or
# 'fallback-to-issue' at runtime. Use in reusable workflow_call workflows to
# parameterise the policy per caller.
protected-files: "example-value"
# Format 3: Object form for granular control over the protected-file set. Use the
# exclude list to remove specific files from the default protection while keeping
# the rest.
protected-files:
# (optional)
# Accepted formats:
# Format 1: Protection policy. blocked (default): hard-block any patch that
# modifies protected files. allowed: allow all changes. fallback-to-issue: push
# the branch but create a review issue instead of a PR.
policy: "blocked"
# Format 2: GitHub Actions expression that resolves to 'blocked', 'allowed', or
# 'fallback-to-issue' at runtime.
policy: "example-value"
# List of filenames or path prefixes to remove from the default protected-file
# set. Items are matched by basename (e.g. "AGENTS.md") or path prefix (e.g.
# ".agents/"). Use this to allow the agent to modify specific files that are
# otherwise blocked by default.
# (optional)
exclude: []
# Array of strings
# Exclusive allowlist of glob patterns. When set, every file in the patch must
# match at least one pattern — files outside the list are always refused,
# including normal source files. This is a restriction, not an exception: setting
# allowed-files: [".github/workflows/*"] blocks all other files. To allow multiple
# sets of files, list all patterns explicitly. Acts independently of the
# protected-files policy; both checks must pass. To modify a protected file, it
# must both match allowed-files and be permitted by protected-files (e.g.
# protected-files: allowed). Supports * (any characters except /) and ** (any
# characters including /).
# (optional)
allowed-files: []
# Array of strings
# When true, the random salt suffix is not appended to the agent-specified branch
# name. Invalid characters are still replaced for security, and casing is always
# preserved regardless of this setting. Useful when the target repository enforces
# branch naming conventions (e.g. Jira keys in uppercase such as
# 'bugfix/BR-329-red'). Defaults to false.
# (optional)
preserve-branch-name: true
# When true (and preserve-branch-name is true), allows the handler to force-delete
# an existing remote branch ref and recreate it from the agent's local HEAD. When
# false (default), if the agent-specified branch already exists on the remote with
# preserve-branch-name enabled, the handler falls back (e.g. opens an issue)
# rather than overwriting the remote ref. Useful for long-lived reusable branches
# whose previous PR was merged.
# (optional)
recreate-ref: true
# List of glob patterns for files to exclude from the patch. Each pattern is
# passed to `git format-patch` as a `:(exclude)` magic pathspec, so
# matching files are stripped by git at generation time and will not appear in the
# commit. Excluded files are also not subject to `allowed-files` or
# `protected-files` checks. Supports * (any characters except /) and ** (any
# characters including /).
# (optional)
excluded-files: []
# Array of strings
# Transport format for packaging changes. "bundle" (default) uses git bundle. "am"
# uses git format-patch/git am. Accepts a GitHub Actions expression for reusable
# workflows.
# (optional)
# Accepted formats:
# Format 1: Transport format for packaging changes. "bundle" (default) uses git
# bundle, which preserves merge commit topology, per-commit authorship, and
# merge-resolution-only content. "am" uses git format-patch/git am.
patch-format: "am"
# Format 2: GitHub Actions expression that resolves to 'am' or 'bundle' at
# runtime. Use in reusable workflow_call workflows to parameterise the transport
# format per caller.
patch-format: "example-value"
# When true (default), signed commits are required and pushes use GitHub's
# createCommitOnBranch GraphQL mutation so GitHub signs them. Set to false to use
# git push directly for repositories that do not require signed commits; this also
# allows pushing merge commits that GraphQL cannot represent.
# (optional)
signed-commits: true
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# When true, adds workflows: write to the GitHub App token permissions. Required
# when allowed-files targets .github/workflows/ paths. Requires
# safe-outputs.github-app to be configured because the workflows permission is a
# GitHub App-only permission and cannot be granted via GITHUB_TOKEN.
# (optional)
allow-workflows: true
# Format 2: Enable pull request creation with default configuration
create-pull-request: null
# Enable AI agents to add review comments to specific lines in pull request diffs
# during code review workflows.
# (optional)
# Accepted formats:
# Format 1: Configuration for creating GitHub pull request review comments from
# agentic workflow output
create-pull-request-review-comment:
# Maximum number of review comments to create (default: 10) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Side of the diff for comments: 'LEFT' or 'RIGHT' (default: 'RIGHT')
# (optional)
side: "LEFT"
# Target for review comments: 'triggering' (default, only on triggering PR), '*'
# (any PR, requires pull_request_number in agent output), or explicit PR number
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository PR review
# comments. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that PR review comments
# can be created in. When specified, the agent can use a 'repo' field in the
# output to specify which repository to create the review comment in. The target
# repository (current or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Format 2: Enable PR review comment creation with default configuration
create-pull-request-review-comment: null
# Enable AI agents to submit consolidated pull request reviews with a status
# decision. Works with create-pull-request-review-comment to batch inline comments
# into a single review.
# (optional)
# Accepted formats:
# Format 1: Configuration for submitting a consolidated PR review with a status
# decision (APPROVE, REQUEST_CHANGES, COMMENT). All
# create-pull-request-review-comment outputs are collected and submitted as part
# of this review.
submit-pull-request-review:
# Maximum number of reviews to submit (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Controls when AI-generated footer is added to the review body. Accepts boolean
# (true/false) or string ('always', 'none', 'if-body'). The 'if-body' mode is
# useful for clean approval reviews without body text. Defaults to 'always'.
# (optional)
# Accepted formats:
# Format 1: Controls whether AI-generated footer is added to the review body. true
# maps to 'always', false maps to 'none'.
footer: true
# Format 2: Controls when AI-generated footer is added to the review body:
# 'always' (default), 'none' (never), or 'if-body' (only when review has body
# text).
footer: "always"
# Target PR for the review: 'triggering' (default, current PR), '*' (any PR,
# requires pull_request_number in agent output), or explicit PR number (e.g. ${{
# github.event.inputs.pr_number }}). Required when workflow is not triggered by a
# pull request (e.g. workflow_dispatch).
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository PR review
# submission. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that PR reviews can be
# submitted in. When specified, the agent can use a 'repo' field in the output to
# specify which repository to submit the review in. The target repository (current
# or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# Optional list of allowed review event types. If omitted, all event types
# (APPROVE, COMMENT, REQUEST_CHANGES) are allowed. Use this to restrict the agent
# to specific event types, e.g. [COMMENT, REQUEST_CHANGES] to prevent approvals.
# (optional)
allowed-events: []
# Array of strings
# When true, after posting a replacement review this workflow dismisses older
# REQUEST_CHANGES reviews previously posted by the same workflow on the same pull
# request. This is best-effort and requires workflow markers in prior review
# bodies.
# (optional)
supersede-older-reviews: true
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Format 2: Enable PR review submission with default configuration
submit-pull-request-review: null
# Enable AI agents to reply to existing review comments on pull requests.
# (optional)
# Accepted formats:
# Format 1: Configuration for replying to existing pull request review comments
reply-to-pull-request-review-comment:
# Maximum number of replies to create (default: 10) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target for replies: 'triggering' (default), '*' (any PR), or explicit PR number
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository operations
# (optional)
target-repo: "example-value"
# List of additional repositories that replies can target
# (optional)
allowed-repos: []
# Array of strings
# Controls whether AI-generated footer is added to the reply body. When false, the
# footer is omitted. Defaults to true.
# (optional)
footer: true
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Format 2: Enable with default configuration
reply-to-pull-request-review-comment: null
# Enable AI agents to resolve review threads on the triggering pull request after
# addressing feedback.
# (optional)
# Accepted formats:
# Format 1: Configuration for resolving review threads on pull requests.
# Resolution is scoped to the triggering PR only — threads on other PRs cannot be
# resolved.
resolve-pull-request-review-thread:
# Maximum number of review threads to resolve (default: 10) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Format 2: Enable review thread resolution with default configuration
resolve-pull-request-review-thread: null
# Enable AI agents to create GitHub Advanced Security code scanning alerts for
# detected vulnerabilities or security issues.
# (optional)
# Accepted formats:
# Format 1: Configuration for creating repository security advisories (SARIF
# format) from agentic workflow output
create-code-scanning-alert:
# Maximum number of security findings to include (default: unlimited) Supports
# integer or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Driver name for SARIF tool.driver.name field (default: 'GitHub Agentic Workflows
# Security Scanner')
# (optional)
driver: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Target repository in format 'owner/repo' for cross-repository code scanning
# alert creation. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that code scanning alerts
# can be created in. When specified, the agent can use a 'repo' field in the
# output to specify which repository to create the alert in. The target repository
# (current or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable code scanning alert creation with default configuration
# (unlimited findings)
create-code-scanning-alert: null
# Enable AI agents to create autofixes for code scanning alerts using the GitHub
# REST API.
# (optional)
# Accepted formats:
# Format 1: Configuration for creating autofixes for code scanning alerts
autofix-code-scanning-alert:
# Maximum number of autofixes to create (default: 10) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable code scanning autofix creation with default configuration (max:
# 10)
autofix-code-scanning-alert: null
# Enable AI agents to add labels to GitHub issues or pull requests based on
# workflow analysis or classification.
# (optional)
# Accepted formats:
# Format 1: Null configuration allows any labels. Labels will be created if they
# don't already exist in the repository.
add-labels: null
# Format 2: Configuration for adding labels to issues/PRs from agentic workflow
# output. Labels will be created if they don't already exist in the repository.
add-labels:
# Optional list of allowed labels that can be added. Labels will be created if
# they don't already exist in the repository. If omitted, any labels are allowed
# (including creating new ones).
# (optional)
allowed: []
# Array of strings
# Optional list of blocked label patterns (supports glob patterns like '~*',
# '*[bot]'). Labels matching these patterns will be rejected. Applied before
# allowed list filtering for security.
# (optional)
blocked: []
# Array of strings
# Optional maximum number of labels to add (default: 3) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target for labels: 'triggering' (default), '*' (any issue/PR), or explicit
# issue/PR number
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository label addition.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# List of additional repositories in format 'owner/repo' that labels can be added
# to. When specified, the agent can use a 'repo' field in the output to specify
# which repository to add labels to. The target repository (current or
# target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# Conjunctive label constraint: ALL of these labels must be present on the
# issue/PR for the operation to proceed.
# (optional)
required-labels: []
# Array of strings
# Title prefix constraint: the issue/PR title must start with this prefix for the
# operation to proceed.
# (optional)
required-title-prefix: "example-value"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Enable AI agents to remove labels from GitHub issues or pull requests.
# (optional)
# Accepted formats:
# Format 1: Null configuration allows any labels to be removed.
remove-labels: null
# Format 2: Configuration for removing labels from issues/PRs from agentic
# workflow output.
remove-labels:
# Optional list of allowed labels that can be removed. If omitted, any labels can
# be removed.
# (optional)
allowed: []
# Array of strings
# Optional list of blocked label patterns (supports glob patterns like '~*',
# '*[bot]'). Labels matching these patterns will be rejected. Applied before
# allowed list filtering for security.
# (optional)
blocked: []
# Array of strings
# Optional maximum number of labels to remove (default: 3) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target for labels: 'triggering' (default), '*' (any issue/PR), or explicit
# issue/PR number
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository label removal.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# List of additional repositories in format 'owner/repo' that labels can be
# removed from. When specified, the agent can use a 'repo' field in the output to
# specify which repository to remove labels from. The target repository (current
# or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# Conjunctive label constraint: ALL of these labels must be present on the
# issue/PR for the operation to proceed.
# (optional)
required-labels: []
# Array of strings
# Title prefix constraint: the issue/PR title must start with this prefix for the
# operation to proceed.
# (optional)
required-title-prefix: "example-value"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Enable AI agents to request reviews from users or teams on pull requests based
# on code changes or expertise matching.
# (optional)
# Accepted formats:
# Format 1: Null configuration allows any reviewers
add-reviewer: null
# Format 2: Configuration for adding reviewers to pull requests from agentic
# workflow output
add-reviewer:
# Optional list of allowed reviewer usernames. If omitted, any reviewers are
# allowed.
# (optional)
allowed-reviewers: []
# Array of strings
# Optional allowed team reviewer or list of allowed team reviewers. If omitted,
# any team reviewers are allowed.
# (optional)
# Accepted formats:
# Format 1: string
allowed-team-reviewers: "example-value"
# Format 2: array
allowed-team-reviewers: []
# Array items: string
# Optional maximum number of reviewers to add (default: 3) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target for reviewers: 'triggering' (default), '*' (any PR), or explicit PR
# number
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository reviewer addition.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Enable AI agents to assign GitHub milestones to issues or pull requests based on
# workflow analysis or project planning.
# (optional)
# Accepted formats:
# Format 1: Null configuration allows assigning any milestones
assign-milestone: null
# Format 2: Configuration for assigning issues to milestones from agentic workflow
# output
assign-milestone:
# Optional list of allowed milestone titles that can be assigned. If omitted, any
# milestones are allowed.
# (optional)
allowed: []
# Array of strings
# Optional maximum number of milestone assignments (default: 1) Supports integer
# or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository milestone
# assignment. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Enable AI agents to assign issues or pull requests to GitHub Copilot (@copilot)
# for automated handling.
# (optional)
# Accepted formats:
# Format 1: Null configuration uses default agent (copilot)
assign-to-agent: null
# Format 2: Configuration for assigning GitHub Copilot coding agent to issues from
# agentic workflow output
assign-to-agent:
# Default agent name to assign (default: 'copilot')
# (optional)
name: "My Workflow"
# Default AI model to use for the agent (e.g., 'auto', 'claude-sonnet-4.5',
# 'claude-opus-4.5', 'claude-opus-4.6', 'gpt-5.1-codex-max', 'gpt-5.2-codex').
# Defaults to 'auto' if not specified.
# (optional)
model: "example-value"
# Default custom agent ID to use when assigning custom agents. This is used for
# specialized agent configurations beyond the standard Copilot agent.
# (optional)
custom-agent: "example-value"
# Default custom instructions to provide to the agent. These instructions will
# guide the agent's behavior when working on the task.
# (optional)
custom-instructions: "example-value"
# Optional list of allowed agent names. If specified, only these agents can be
# assigned. When configured, existing agent assignees not in the list are removed
# while regular user assignees are preserved.
# (optional)
allowed: []
# Array of strings
# Optional maximum number of agent assignments (default: 1) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target issue/PR to assign agents to. Use 'triggering' (default) for the
# triggering issue/PR, '*' to require explicit issue_number/pull_number, or a
# specific issue/PR number. With 'triggering', auto-resolves from
# github.event.issue.number or github.event.pull_request.number.
# (optional)
target: null
# Target repository in format 'owner/repo' for cross-repository agent assignment.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# Target repository where the pull request should be created, in format
# 'owner/repo'. If omitted, the PR will be created in the same repository as the
# issue (specified by target-repo or the workflow's repository). This allows
# issues and code to live in different repositories.
# (optional)
pull-request-repo: "example-value"
# List of additional repositories that pull requests can be created in beyond
# pull-request-repo. Each entry should be in 'owner/repo' format. The repository
# specified by pull-request-repo is automatically allowed without needing to be
# listed here.
# (optional)
allowed-pull-request-repos: []
# Array of strings
# If true, the workflow continues gracefully when agent assignment fails (e.g.,
# due to missing token or insufficient permissions), logging a warning instead of
# failing. Default is false. Useful for workflows that should not fail when agent
# assignment is optional.
# (optional)
ignore-if-error: true
# Base branch for pull request creation in the target repository. Defaults to the
# target repo's default branch. Only relevant when pull-request-repo is
# configured.
# (optional)
base-branch: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Enable AI agents to assign issues or pull requests to specific GitHub users
# based on workflow logic or expertise matching.
# (optional)
# Accepted formats:
# Format 1: Enable user assignment with default configuration
assign-to-user: null
# Format 2: Configuration for assigning users to issues from agentic workflow
# output
assign-to-user:
# Optional list of allowed usernames. If specified, only these users can be
# assigned.
# (optional)
allowed: []
# Array of strings
# Optional list of blocked usernames or patterns (e.g., 'copilot', '*[bot]').
# Users matching these patterns cannot be assigned. Supports exact matches and
# glob patterns.
# (optional)
blocked: []
# Array of strings
# Optional maximum number of user assignments (default: 1) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target issue to assign users to. Use 'triggering' (default) for the triggering
# issue, '*' to allow any issue, or a specific issue number.
# (optional)
target: null
# Target repository in format 'owner/repo' for cross-repository user assignment.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# If true, unassign all current assignees before assigning new ones. Useful for
# reassigning issues from one user to another (default: false).
# (optional)
unassign-first: true
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# List of allowed repositories in format 'owner/repo' for cross-repository user
# assignment operations. Use with 'repo' field in tool calls.
# (optional)
allowed-repos: []
# Array of strings
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Enable AI agents to unassign users from issues or pull requests. Useful for
# reassigning work or removing users from issues.
# (optional)
# Accepted formats:
# Format 1: Enable user unassignment with default configuration
unassign-from-user: null
# Format 2: Configuration for removing assignees from issues in agentic workflow
# output
unassign-from-user:
# Optional list of allowed usernames. If specified, only these users can be
# unassigned.
# (optional)
allowed: []
# Array of strings
# Optional list of blocked usernames or patterns (e.g., 'copilot', '*[bot]').
# Users matching these patterns cannot be unassigned. Supports exact matches and
# glob patterns.
# (optional)
blocked: []
# Array of strings
# Optional maximum number of unassignment operations (default: 1) Supports integer
# or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target issue to unassign users from. Use 'triggering' (default) for the
# triggering issue, '*' to allow any issue, or a specific issue number.
# (optional)
target: null
# Target repository in format 'owner/repo' for cross-repository user unassignment.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of allowed repositories in format 'owner/repo' for cross-repository
# unassignment operations. Use with 'repo' field in tool calls.
# (optional)
allowed-repos: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Enable AI agents to create hierarchical relationships between issues using
# GitHub's sub-issue (tasklist) feature.
# (optional)
# Accepted formats:
# Format 1: Enable sub-issue linking with default configuration
link-sub-issue: null
# Format 2: Configuration for linking issues as sub-issues from agentic workflow
# output
link-sub-issue:
# Maximum number of sub-issue links to create (default: 5) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Optional list of labels that parent issues must have to be eligible for linking
# (optional)
parent-required-labels: []
# Array of strings
# Optional title prefix that parent issues must have to be eligible for linking
# (optional)
parent-title-prefix: "example-value"
# Optional list of labels that sub-issues must have to be eligible for linking
# (optional)
sub-required-labels: []
# Array of strings
# Optional title prefix that sub-issues must have to be eligible for linking
# (optional)
sub-title-prefix: "example-value"
# Target repository in format 'owner/repo' for cross-repository sub-issue linking.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Enable AI agents to edit and update existing GitHub issue content, titles,
# labels, assignees, and metadata.
# (optional)
# Accepted formats:
# Format 1: Configuration for updating GitHub issues from agentic workflow output
update-issue:
# Allow updating issue status (open/closed) - presence of key indicates field can
# be updated
# (optional)
status: null
# Target for updates: 'triggering' (default), '*' (any issue), or explicit issue
# number
# (optional)
target: "example-value"
# Allow updating issue title - presence of key indicates field can be updated
# (optional)
title: null
# Allow updating issue body. Set to true to enable body updates, false to disable.
# For backward compatibility, null (body:) also enables body updates.
# (optional)
body: null
# Controls whether AI-generated footer is added when updating the issue body. When
# false, the visible footer content is omitted but XML markers are still included.
# Defaults to true. Only applies when 'body' is enabled.
# (optional)
footer: true
# Maximum number of issues to update (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository issue updates.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# Required prefix for issue title. Only issues with this title prefix can be
# updated.
# (optional)
title-prefix: "example-value"
# List of additional repositories in format 'owner/repo' that issues can be
# updated in. When specified, the agent can use a 'repo' field in the output to
# specify which repository to update the issue in. The target repository (current
# or target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Format 2: Enable issue updating with default configuration
update-issue: null
# Enable AI agents to edit and update existing pull request content, titles,
# labels, reviewers, and metadata.
# (optional)
# Accepted formats:
# Format 1: Configuration for updating GitHub pull requests from agentic workflow
# output. Both title and body updates are enabled by default.
update-pull-request:
# Target for updates: 'triggering' (default), '*' (any PR), or explicit PR number
# (optional)
target: "example-value"
# Allow updating pull request title - defaults to true, set to false to disable
# (optional)
title: true
# Allow updating pull request body - defaults to true, set to false to disable
# (optional)
body: true
# When true, update the pull request branch with the latest base branch changes
# before applying other updates. Defaults to false.
# (optional)
update-branch: true
# Default operation for body updates: 'append' (add to end), 'prepend' (add to
# start), or 'replace' (overwrite completely). Defaults to 'replace' if not
# specified.
# (optional)
operation: "append"
# Controls whether AI-generated footer is added when updating the pull request
# body. When false, the visible footer content is omitted but XML markers are
# still included. Defaults to true. Only applies when 'body' is enabled.
# (optional)
footer: true
# Maximum number of pull requests to update (default: 1) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository pull request
# updates. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Format 2: Enable pull request updating with default configuration (title and
# body updates enabled)
update-pull-request: null
# Enable AI agents to merge pull requests under configured policy gates.
# (optional)
# Accepted formats:
# Format 1: Enable pull request merge with default policy configuration
merge-pull-request: null
# Format 2: Configuration for controlled pull request merges. The merge is blocked
# unless all configured gates pass.
merge-pull-request:
# Maximum number of pull request merges to perform per run (default: 1). Supports
# integer or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# List of labels that must ALL be present on the pull request before merge is
# allowed.
# (optional)
required-labels: []
# Array of strings
# Glob patterns for allowed source branch names (pull request head ref). The PR's
# branch must match at least one pattern.
# (optional)
allowed-branches: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, evaluate merge gates and emit preview results without executing the
# merge API call.
# (optional)
staged: true
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Enable AI agents to push commits directly to pull request branches for automated
# fixes or improvements.
# (optional)
# Accepted formats:
# Format 1: Use default configuration (branch: 'triggering', if-no-changes:
# 'warn')
push-to-pull-request-branch: null
# Format 2: Configuration for pushing changes to a specific branch from agentic
# workflow output. Supports pushing to multiple PRs in a single run when max > 1.
push-to-pull-request-branch:
# Maximum number of push operations to perform (default: 1). Each push targets a
# different pull request branch. Supports integer or GitHub Actions expression
# (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# The branch to push changes to (defaults to 'triggering')
# (optional)
branch: "example-value"
# Target for push operations: 'triggering' (default), '*' (any pull request), or
# explicit pull request number
# (optional)
target: "example-value"
# Required prefix for pull request title. Only pull requests with this prefix will
# be accepted.
# (optional)
required-title-prefix: "example-value"
# Required labels for pull request validation. Only pull requests with all these
# labels will be accepted. Accepts an array of label names or a GitHub Actions
# expression resolving to a comma-separated list of labels (e.g. '${{
# inputs[\'required-labels\'] }}').
# (optional)
# Accepted formats:
# Format 1: Array of label names
required-labels: []
# Array items: string
# Format 2: GitHub Actions expression resolving to a comma-separated list of label
# names (e.g. '${{ inputs[\'required-labels\'] }}')
required-labels: "example-value"
# Behavior when no changes to push: 'warn' (default - log warning but succeed),
# 'error' (fail the action), or 'ignore' (silent success)
# (optional)
if-no-changes: "warn"
# When true, treat deleted/missing pull request branch errors as a skipped push
# instead of a hard failure. Useful when the PR branch may be deleted before safe
# outputs run.
# (optional)
ignore-missing-branch-failure: true
# Optional suffix to append to generated commit titles (e.g., ' [skip ci]' to
# prevent triggering CI on the commit)
# (optional)
commit-title-suffix: "example-value"
# Maximum allowed size for git patches in kilobytes (KB) for
# push-to-pull-request-branch only. Overrides safe-outputs max-patch-size for this
# output type. Defaults to 1024 KB (1 MB) when unset.
# (optional)
max-patch-size: 1
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Token used to push an empty commit after pushing changes to trigger CI events.
# Works around the GITHUB_TOKEN limitation where pushes don't trigger workflow
# runs. Defaults to the magic secret GH_AW_CI_TRIGGER_TOKEN if set in the
# repository. Use a secret expression (e.g. '${{ secrets.CI_TOKEN }}') for a
# custom token, or 'app' for GitHub App auth.
# (optional)
github-token-for-extra-empty-commit: "example-value"
# When true (default), if pushing to the PR branch fails due to a
# non-fast-forward/diverged branch, create a fallback pull request that targets
# the original PR branch. Set to false to disable this behavior and avoid
# requiring pull-requests: write permission.
# (optional)
fallback-as-pull-request: true
# When true (default), signed commits are required and pushes use GitHub's
# createCommitOnBranch GraphQL mutation so GitHub signs them. Set to false to use
# git push directly for repositories that do not require signed commits; this also
# allows pushing merge commits that GraphQL cannot represent.
# (optional)
signed-commits: true
# Target repository in format 'owner/repo' for cross-repository push to pull
# request branch. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' that push to pull request
# branch can target. When specified, the agent can use a 'repo' field in the
# output to specify which repository to push to. The target repository (current or
# target-repo) is always implicitly allowed. Accepts an array or a GitHub Actions
# expression resolving to a comma-separated list (e.g. '${{
# inputs[\'allowed-repos\'] }}').
# (optional)
# Accepted formats:
# Format 1: Array of repository slugs in 'owner/repo' format
allowed-repos: []
# Array items: string
# Format 2: GitHub Actions expression resolving to a comma-separated list of
# repository slugs (e.g. '${{ inputs[\'allowed-repos\'] }}')
allowed-repos: "example-value"
# Controls protected-file protection. String form: blocked (default), allowed, or
# fallback-to-issue — or a GitHub Actions expression for reusable workflows.
# Object form: { policy, exclude } to customise the protected-file set.
# (optional)
# Accepted formats:
# Format 1: Controls protected-file protection. blocked (default): hard-block any
# patch that modifies package manifests (e.g. package.json, go.mod), engine
# instruction files (e.g. AGENTS.md, CLAUDE.md) or .github/ files. allowed: allow
# all changes. fallback-to-issue: create a review issue instead of pushing to the
# PR branch, so a human can review the changes before applying.
protected-files: "blocked"
# Format 2: GitHub Actions expression that resolves to 'blocked', 'allowed', or
# 'fallback-to-issue' at runtime. Use in reusable workflow_call workflows to
# parameterise the policy per caller.
protected-files: "example-value"
# Format 3: Object form for granular control over the protected-file set. Use the
# exclude list to remove specific files from the default protection while keeping
# the rest.
protected-files:
# (optional)
# Accepted formats:
# Format 1: Protection policy. blocked (default): hard-block any patch that
# modifies protected files. allowed: allow all changes. fallback-to-issue: create
# a review issue instead of pushing.
policy: "blocked"
# Format 2: GitHub Actions expression that resolves to 'blocked', 'allowed', or
# 'fallback-to-issue' at runtime.
policy: "example-value"
# List of filenames or path prefixes to remove from the default protected-file
# set. Items are matched by basename (e.g. "AGENTS.md") or path prefix (e.g.
# ".agents/"). Use this to allow the agent to modify specific files that are
# otherwise blocked by default.
# (optional)
exclude: []
# Array of strings
# Exclusive allowlist of glob patterns. When set, every file in the patch must
# match at least one pattern — files outside the list are always refused,
# including normal source files. This is a restriction, not an exception: setting
# allowed-files: [".github/workflows/*"] blocks all other files. To allow multiple
# sets of files, list all patterns explicitly. Acts independently of the
# protected-files policy; both checks must pass. To modify a protected file, it
# must both match allowed-files and be permitted by protected-files (e.g.
# protected-files: allowed). Supports * (any characters except /) and ** (any
# characters including /).
# (optional)
allowed-files: []
# Array of strings
# List of glob patterns for files to exclude from the patch. Each pattern is
# passed to `git format-patch` as a `:(exclude)` magic pathspec, so
# matching files are stripped by git at generation time and will not appear in the
# commit. Excluded files are also not subject to `allowed-files` or
# `protected-files` checks. Supports * (any characters except /) and ** (any
# characters including /).
# (optional)
excluded-files: []
# Array of strings
# Transport format for packaging changes. "bundle" (default) uses git bundle. "am"
# uses git format-patch/git am. Accepts a GitHub Actions expression for reusable
# workflows.
# (optional)
# Accepted formats:
# Format 1: Transport format for packaging changes. "bundle" (default) uses git
# bundle, which preserves merge commit topology, per-commit authorship, and
# merge-resolution-only content. "am" uses git format-patch/git am.
patch-format: "am"
# Format 2: GitHub Actions expression that resolves to 'am' or 'bundle' at
# runtime. Use in reusable workflow_call workflows to parameterise the transport
# format per caller.
patch-format: "example-value"
# When true, adds workflows: write to the GitHub App token permissions. Required
# when allowed-files targets .github/workflows/ paths. Requires
# safe-outputs.github-app to be configured because the workflows permission is a
# GitHub App-only permission and cannot be granted via GITHUB_TOKEN.
# (optional)
allow-workflows: true
# When false, skips the branch protection API pre-flight check before pushing. Set
# to false to avoid requiring administration: read permission. The GitHub platform
# will still enforce branch protection at push time. Default is true (check
# enabled).
# (optional)
check-branch-protection: true
# Enable AI agents to minimize (hide) comments on issues or pull requests based on
# relevance, spam detection, or moderation rules.
# (optional)
# Accepted formats:
# Format 1: Enable comment hiding with default configuration
hide-comment: null
# Format 2: Configuration for hiding comments on GitHub issues, pull requests, or
# discussions from agentic workflow output
hide-comment:
# Maximum number of comments to hide (default: 5) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository in format 'owner/repo' for cross-repository comment hiding.
# Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of allowed reasons for hiding comments. Default: all reasons allowed (spam,
# abuse, off_topic, outdated, resolved, low_quality).
# (optional)
allowed-reasons: []
# Array of strings
# Controls whether the workflow requests discussions:write permission for
# hide-comment. Default: true (includes discussions:write). Set to false if your
# GitHub App lacks Discussions permission to prevent 422 errors during token
# generation.
# (optional)
discussions: true
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Enable AI agents to set or clear the type of GitHub issues. Use an empty string
# to clear the current type.
# (optional)
# Accepted formats:
# Format 1: Null configuration allows setting any issue type
set-issue-type: null
# Format 2: Configuration for setting the type of GitHub issues from agentic
# workflow output
set-issue-type:
# Optional list of allowed issue type names (e.g. 'Bug', 'Feature'). If omitted,
# any type is allowed. Empty string is always allowed to clear the type.
# (optional)
allowed: []
# Array of strings
# Optional maximum number of set-issue-type operations (default: 5). Supports
# integer or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target for issue type: 'triggering' (default), '*' (any issue), or explicit
# issue number
# (optional)
target: "example-value"
# Target repository in format 'owner/repo' for cross-repository issue type
# setting. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' where issue types can be
# set. When specified, the agent can use a 'repo' field in the output to specify
# which repository to target. The target repository (current or target-repo) is
# always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Enable AI agents to set one issue field value by field name and value.
# (optional)
# Accepted formats:
# Format 1: Null configuration enables set-issue-field with defaults.
set-issue-field: null
# Format 2: Configuration for setting one issue field value by field name and
# value.
set-issue-field:
# Optional maximum number of set-issue-field operations (default: 5). Supports
# integer or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target for issue field updates: 'triggering' (default), '*' (any issue), or
# explicit issue number
# (optional)
target: "example-value"
# Optional list of issue field names that can be modified by set-issue-field. If
# omitted or empty, any issue field may be set. Use ['*'] to explicitly allow all.
# (optional)
allowed-fields: []
# Array of strings
# Target repository in format 'owner/repo' for cross-repository issue field
# updates. Takes precedence over trial target repo settings.
# (optional)
target-repo: "example-value"
# List of additional repositories in format 'owner/repo' where issue fields can be
# updated. When specified, the agent can use a 'repo' field in the output to
# specify which repository to target. The target repository (current or
# target-repo) is always implicitly allowed.
# (optional)
allowed-repos: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# All of these labels must be present on the target item for this operation to
# proceed
# (optional)
required-labels: []
# Array of strings
# The target item's title must start with this prefix for this operation to
# proceed
# (optional)
required-title-prefix: "example-value"
# Dispatch workflow_dispatch events to other workflows. Used by orchestrators to
# delegate work to worker workflows with controlled maximum dispatch count.
# (optional)
# Accepted formats:
# Format 1: Configuration for dispatching workflow_dispatch events to other
# workflows. Orchestrators use this to delegate work to worker workflows.
dispatch-workflow:
# List of workflow names (without .md extension) to allow dispatching. Each
# workflow must exist in .github/workflows/.
workflows: []
# Array of strings
# Maximum number of workflow dispatch operations per run (default: 1, max: 50)
# Supports integer or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token to use for dispatching workflows. Overrides global github-token if
# specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Target repository in format 'owner/repo' for cross-repository workflow dispatch.
# When specified, the workflow will be dispatched to the target repository instead
# of the current one.
# (optional)
target-repo: "example-value"
# Git ref (branch, tag, or SHA) to use when dispatching the workflow. For
# workflow_call relay scenarios this is auto-injected by the compiler from
# needs.activation.outputs.target_ref. Overrides the caller's GITHUB_REF.
# (optional)
target-ref: "example-value"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Shorthand array format: list of workflow names (without .md extension)
# to allow dispatching
dispatch-workflow: []
# Array items: string
# Dispatch repository_dispatch events to external repositories. Each sub-key
# defines a named dispatch tool with its own event_type, target repository, input
# schema, and execution limits.
# (optional)
dispatch_repository:
{}
# Call reusable workflows via workflow_call fan-out. The compiler generates static
# conditional jobs; the agent selects which worker to activate. Use this for
# orchestrator/dispatcher patterns within the same repository.
# (optional)
# Accepted formats:
# Format 1: Configuration for calling reusable workflows via workflow_call
# fan-out. The compiler generates conditional `uses:` jobs at compile time; the
# agent selects which worker to activate at runtime.
call-workflow:
# List of workflow names (without .md extension) to allow calling. Each workflow
# must exist in .github/workflows/ and declare a workflow_call trigger.
workflows: []
# Array of strings
# Maximum number of workflow_call fan-out operations per run (default: 1, max:
# 50). Supports integer or GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token passed to called workflows. Overrides global github-token if
# specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Shorthand array format: list of workflow names (without .md extension)
# to allow calling
call-workflow: []
# Array items: string
# Enable AI agents to report when required MCP tools are unavailable. Used for
# workflow diagnostics and tool discovery.
# (optional)
# Accepted formats:
# Format 1: Configuration for reporting missing tools from agentic workflow output
missing-tool:
# Maximum number of missing tool reports (default: unlimited) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Whether to create or update GitHub issues when tools are missing (default:
# true). Supports literal boolean or GitHub Actions expression (e.g. '${{
# inputs.create-issue }}').
# (optional)
# Accepted formats:
# Format 1: boolean
create-issue: true
# Format 2: GitHub Actions expression that resolves to a boolean at runtime
create-issue: "example-value"
# Prefix for issue titles when creating issues for missing tools (default:
# '[missing tool]')
# (optional)
title-prefix: "example-value"
# Labels to add to created issues for missing tools
# (optional)
labels: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable missing tool reporting with default configuration
missing-tool: null
# Format 3: Explicitly disable missing tool reporting (false). Missing tool
# reporting is enabled by default when safe-outputs is configured.
missing-tool: true
# Enable AI agents to report when required data or context is missing. Used for
# workflow troubleshooting and data validation.
# (optional)
# Accepted formats:
# Format 1: Configuration for reporting missing data required to achieve workflow
# goals. Encourages AI agents to be truthful about data gaps instead of
# hallucinating information.
missing-data:
# Maximum number of missing data reports (default: unlimited) Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Whether to create or update GitHub issues when data is missing (default: true).
# Supports literal boolean or GitHub Actions expression (e.g. '${{
# inputs.create-missing-data-issue }}').
# (optional)
# Accepted formats:
# Format 1: boolean
create-issue: true
# Format 2: GitHub Actions expression that resolves to a boolean at runtime
create-issue: "example-value"
# Prefix for issue titles when creating issues for missing data (default:
# '[missing data]')
# (optional)
title-prefix: "example-value"
# Labels to add to created issues for missing data
# (optional)
labels: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable missing data reporting with default configuration
missing-data: null
# Format 3: Explicitly disable missing data reporting (false). Missing data
# reporting is enabled by default when safe-outputs is configured.
missing-data: true
# Enable AI agents to explicitly indicate no action is needed. Used for workflow
# control flow and conditional logic.
# (optional)
# Accepted formats:
# Format 1: Configuration for no-op safe output (logging only, no GitHub API
# calls). Always available as a fallback to ensure human-visible artifacts.
noop:
# Maximum number of noop messages (default: 1) Supports integer or GitHub Actions
# expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# Controls whether noop runs are reported as issue comments (default: true). Set
# to false to disable posting to the no-op runs issue.
# (optional)
report-as-issue: true
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable noop output with default configuration (max: 1)
noop: null
# Format 3: Explicitly disable noop output (false). Noop is enabled by default
# when safe-outputs is configured.
noop: true
# Enable AI agents to publish files (images, charts, reports) to an orphaned git
# branch for persistent storage and web access.
# (optional)
# Accepted formats:
# Format 1: Configuration for publishing assets to an orphaned git branch
upload-asset:
# Branch name (default: 'assets/${{ github.workflow }}')
# (optional)
branch: "example-value"
# Maximum file size in KB (default: 10240 = 10MB)
# (optional)
max-size: 1
# Allowed file extensions (default: common non-executable types)
# (optional)
allowed-exts: []
# Array of strings
# Maximum number of assets to upload (default: 10) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable asset publishing with default configuration
upload-asset: null
# Enable AI agents to upload files as run-scoped GitHub Actions artifacts. Returns
# a temporary artifact ID rather than a raw download URL, keeping authorization
# centralized.
# (optional)
# Accepted formats:
# Format 1: Configuration for uploading files as run-scoped GitHub Actions
# artifacts
upload-artifact:
# Maximum number of upload_artifact tool calls allowed per run (default: 1)
# (optional)
max-uploads: 1
# Artifact retention period in days (fixed; the agent cannot override this value).
# Supports integer or GitHub Actions expression (e.g. '${{ inputs.retention-days
# }}').
# (optional)
# Accepted formats:
# Format 1: integer
retention-days: 1
# Format 2: string
retention-days: "example-value"
# Upload files directly without zip archiving (fixed; the agent cannot override
# this value). Only valid for single-file uploads. Supports boolean or GitHub
# Actions expression (e.g. '${{ inputs.skip-archive }}').
# (optional)
# Accepted formats:
# Format 1: boolean
skip-archive: true
# Format 2: string
skip-archive: "example-value"
# Maximum total upload size in bytes per slot (default: 104857600 = 100 MB)
# (optional)
max-size-bytes: 1
# Glob patterns restricting which paths relative to the staging directory the
# model may upload
# (optional)
allowed-paths: []
# Array of strings
# Default include/exclude glob filters applied on top of allowed-paths
# (optional)
filters:
# Glob patterns for files to include
# (optional)
include: []
# Array of strings
# Glob patterns for files to exclude
# (optional)
exclude: []
# Array of strings
# Default values injected when the model omits a field
# (optional)
defaults:
# Behaviour when no files match: 'error' (default) or 'ignore'
# (optional)
if-no-files: "error"
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub Actions artifact
# uploads (preview mode)
# (optional)
staged: true
# Format 2: Enable artifact uploads with default configuration
upload-artifact: null
# Enable AI agents to edit and update GitHub release content, including release
# notes, assets, and metadata.
# (optional)
# Accepted formats:
# Format 1: Configuration for updating GitHub release descriptions
update-release:
# Maximum number of releases to update (default: 1) Supports integer or GitHub
# Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Target repository for cross-repo release updates (format: owner/repo). If not
# specified, updates releases in the workflow's repository.
# (optional)
target-repo: "example-value"
# Controls whether AI-generated footer is added when updating the release body.
# When false, the visible footer content is omitted. Defaults to true.
# (optional)
footer: true
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable release updates with default configuration
update-release: null
# If true, emit step summary messages instead of making GitHub API calls (preview
# mode)
# (optional)
staged: true
# Environment variables to pass to safe output jobs
# (optional)
env:
{}
# GitHub token to use for safe output jobs. Typically a secret reference like ${{
# secrets.GITHUB_TOKEN }} or ${{ secrets.CUSTOM_PAT }}
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# GitHub App credentials for minting installation access tokens. When configured,
# a token will be generated using the app credentials and used for all safe output
# operations.
# (optional)
github-app:
# Deprecated alias for client-id. GitHub App ID/client ID (e.g., '${{ vars.APP_ID
# }}').
# (optional)
app-id: "example-value"
# GitHub App client ID (e.g., '${{ vars.APP_ID }}'). Required to mint a GitHub App
# token.
# (optional)
client-id: "example-value"
# GitHub App private key (e.g., '${{ secrets.APP_PRIVATE_KEY }}'). Required to
# mint a GitHub App token.
# (optional)
private-key: "example-value"
# If true, skip token minting when client-id/private-key resolve to empty strings
# at runtime. Defaults to false.
# (optional)
ignore-if-missing: true
# Optional owner of the GitHub App installation (defaults to current repository
# owner if not specified)
# (optional)
owner: "example-value"
# Optional list of repositories to grant access to (defaults to current repository
# if not specified)
# (optional)
repositories: []
# Array of strings
# Optional extra GitHub App-only permissions to merge into the minted token. Takes
# effect for tools.github.github-app and safe-outputs.github-app; ignored in
# on.github-app and the top-level github-app fallback. Use to add GitHub App-only
# scopes (e.g. members, organization-administration) not expressible via standard
# handler declarations.
# (optional)
permissions:
# Permission level for repository administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission for repository administration.
# (optional)
administration: "read"
# Permission level for Codespaces (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces: "read"
# Permission level for Codespaces lifecycle administration (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
codespaces-lifecycle-admin: "read"
# Permission level for Codespaces metadata (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces-metadata: "read"
# Permission level for user email addresses (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
email-addresses: "read"
# Permission level for repository environments (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
environments: "read"
# Permission level for git signing (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
git-signing: "read"
# Permission level for organization members (read/none; "write" is rejected by the
# compiler). Required for org team membership API calls.
# (optional)
members: "read"
# Permission level for organization administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-administration: "read"
# Permission level for organization announcement banners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-announcement-banners: "read"
# Permission level for organization Codespaces (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-codespaces: "read"
# Permission level for organization Copilot (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-copilot: "read"
# Permission level for organization custom org roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-org-roles: "read"
# Permission level for organization custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-properties: "read"
# Permission level for organization custom repository roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-repository-roles: "read"
# Permission level for organization events (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-events: "read"
# Permission level for organization webhooks (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-hooks: "read"
# Permission level for organization members management (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-members: "read"
# Permission level for organization packages (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-packages: "read"
# Permission level for organization personal access token requests (read/none;
# "write" is rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-token-requests: "read"
# Permission level for organization personal access tokens (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-tokens: "read"
# Permission level for organization plan (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-plan: "read"
# Permission level for organization self-hosted runners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-self-hosted-runners: "read"
# Permission level for organization user blocking (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-user-blocking: "read"
# Permission level for repository custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
repository-custom-properties: "read"
# Permission level for repository webhooks (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
repository-hooks: "read"
# Permission level for single file access (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
single-file: "read"
# Permission level for team discussions (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
team-discussions: "read"
# Permission level for Dependabot vulnerability alerts (read/none; "write" is
# rejected by the compiler). Also available as a GITHUB_TOKEN scope. When used
# with a GitHub App, forwarded as permission-vulnerability-alerts input.
# (optional)
vulnerability-alerts: "read"
# Permission level for GitHub Actions workflow files (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
workflows: "read"
# Maximum allowed size for git patches in kilobytes (KB). Defaults to 1024 KB (1
# MB). If patch exceeds this size, the job will fail.
# (optional)
max-patch-size: 1
# Maximum allowed number of unique files in a create-pull-request patch. Defaults
# to 100. The check counts unique file paths (deduplicated across multi-commit
# patches), so it reflects how many distinct files the agent is pushing in this
# iteration.
# (optional)
max-patch-files: 1
# Enable AI agents to report detected security threats, policy violations, or
# suspicious patterns for security review.
# (optional)
# Accepted formats:
# Format 1: Enable or disable threat detection for safe outputs (defaults to true
# when safe-outputs are configured)
threat-detection: true
# Format 2: GitHub Actions expression that resolves to a boolean at runtime,
# enabling or disabling threat detection based on workflow inputs (e.g. '${{
# inputs.enable-threat-detection }}')
threat-detection: "example-value"
# Format 3: Threat detection configuration object
threat-detection:
# Whether threat detection is enabled. Accepts a boolean literal or a GitHub
# Actions expression (e.g. '${{ inputs.enable-threat-detection }}').
# (optional)
# Accepted formats:
# Format 1: boolean
enabled: true
# Format 2: GitHub Actions expression that resolves to a boolean at runtime
enabled: "example-value"
# Additional custom prompt instructions to append to threat detection analysis
# (optional)
prompt: "example-value"
# AI engine configuration specifically for threat detection (overrides main
# workflow engine). Set to false to disable AI-based threat detection. Supports
# same format as main engine field when not false.
# (optional)
# Accepted formats:
# Format 1: Disable AI engine for threat detection (only run custom steps)
engine: true
# Format 2: Configuration object
# Array of extra job steps to run before engine execution
# (optional)
steps: []
# Array of extra job steps to run after engine execution
# (optional)
post-steps: []
# Runner specification for the detection job. Overrides agent.runs-on for the
# detection job only. Defaults to agent.runs-on.
# (optional)
runs-on: "example-value"
# When true (default), detection failures produce warnings and allow safe outputs
# to proceed with a caution notice and 'needs-review' label. When false, detection
# failures block safe outputs entirely. Accepts a boolean literal or a GitHub
# Actions expression.
# (optional)
# Accepted formats:
# Format 1: boolean
continue-on-error: true
# Format 2: GitHub Actions expression that resolves to a boolean at runtime
continue-on-error: "example-value"
# Custom safe-output jobs that can be executed based on agentic workflow output.
# Job names containing dashes will be automatically normalized to underscores
# (e.g., 'send-notification' becomes 'send_notification').
# (optional)
jobs:
{}
# Inline JavaScript script handlers that run inside the consolidated safe-outputs
# job handler loop. Unlike 'jobs' (which create separate GitHub Actions jobs),
# scripts execute in-process alongside the built-in handlers. Users write only the
# body of the main function — the compiler wraps it with 'async function
# main(config = {}) { ... }' and 'module.exports = { main };' automatically.
# Script names containing dashes will be automatically normalized to underscores
# (e.g., 'post-slack-message' becomes 'post_slack_message').
# (optional)
scripts:
{}
# Custom message templates for safe-output footer and notification messages.
# Available placeholders: {workflow_name} (workflow name), {run_url} (GitHub
# Actions run URL), {triggering_number} (issue/PR/discussion number),
# {workflow_source} (owner/repo/path@ref), {workflow_source_url} (GitHub URL to
# source), {operation} (safe-output operation name for staged mode).
# (optional)
messages:
# Custom footer message template for AI-generated content. Available placeholders:
# {workflow_name}, {run_url}, {triggering_number}, {workflow_source},
# {workflow_source_url}. Example: '> Generated by [{workflow_name}]({run_url})'
# (optional)
footer: "example-value"
# Custom installation instructions template appended to the footer. Available
# placeholders: {workflow_source}, {workflow_source_url}. Example: '> Install: `gh
# aw add {workflow_source}`'
# (optional)
footer-install: "example-value"
# Custom footer message template for workflow recompile issues. Available
# placeholders: {workflow_name}, {run_url}, {repository}. Example: '> Workflow
# sync report by [{workflow_name}]({run_url}) for {repository}'
# (optional)
footer-workflow-recompile: "example-value"
# Custom footer message template for comments on workflow recompile issues.
# Available placeholders: {workflow_name}, {run_url}, {repository}. Example: '>
# Update from [{workflow_name}]({run_url}) for {repository}'
# (optional)
footer-workflow-recompile-comment: "example-value"
# Custom title template for staged mode preview. Available placeholders:
# {operation}. Example: ' Preview: {operation}'
# (optional)
staged-title: "example-value"
# Custom description template for staged mode preview. Available placeholders:
# {operation}. Example: 'The following {operation} would occur if staged mode was
# disabled:'
# (optional)
staged-description: "example-value"
# Custom message template for workflow activation comment. Available placeholders:
# {workflow_name}, {run_url}, {event_type}. Default: 'Agentic
# [{workflow_name}]({run_url}) triggered by this {event_type}.'
# (optional)
run-started: "example-value"
# Custom message template for successful workflow completion. Available
# placeholders: {workflow_name}, {run_url}. Default: '✓ Agentic
# [{workflow_name}]({run_url}) completed successfully.'
# (optional)
run-success: "example-value"
# Custom message template for failed workflow. Available placeholders:
# {workflow_name}, {run_url}, {status}. Default: '✗ Agentic
# [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.'
# (optional)
run-failure: "example-value"
# Custom message template for detection job failure. Available placeholders:
# {workflow_name}, {run_url}. Default: '! Security scanning failed for
# [{workflow_name}]({run_url}). Review the logs for details.'
# (optional)
detection-failure: "example-value"
# Custom footer template for agent failure tracking issues. Available
# placeholders: {workflow_name}, {run_url}. Default: '> Agent failure tracked by
# [{workflow_name}]({run_url})'
# (optional)
agent-failure-issue: "example-value"
# Custom footer template for comments on agent failure tracking issues. Available
# placeholders: {workflow_name}, {run_url}. Default: '> Agent failure update from
# [{workflow_name}]({run_url})'
# (optional)
agent-failure-comment: "example-value"
# Custom message template for pull request creation link appended to the
# activation comment. Available placeholders: {item_number}, {item_url}. Default:
# 'Pull request created: [#{item_number}]({item_url})'
# (optional)
pull-request-created: "example-value"
# Custom message template for issue creation link appended to the activation
# comment. Available placeholders: {item_number}, {item_url}. Default: 'Issue
# created: [#{item_number}]({item_url})'
# (optional)
issue-created: "example-value"
# Custom message template for commit push link appended to the activation comment.
# Available placeholders: {commit_sha}, {short_sha}, {commit_url}. Default:
# 'Commit pushed: [`{short_sha}`]({commit_url})'
# (optional)
commit-pushed: "example-value"
# Custom header text prepended to every message body generated by safe outputs
# (issues, comments, pull requests, discussions). Applied after any
# threat-detection caution alert and before the agent-generated content. Available
# placeholders: {workflow_name}, {run_url}.
# (optional)
body-header: "example-value"
# When enabled, workflow completion notifier creates a new comment instead of
# editing the activation comment. Creates an append-only timeline of workflow
# runs. Default: false
# (optional)
append-only-comments: true
# Configuration for @mention filtering in safe outputs. Controls whether and how
# @mentions in AI-generated content are allowed or escaped.
# (optional)
# Accepted formats:
# Format 1: Simple boolean mode: false = always escape mentions, true = always
# allow mentions (error in strict mode)
mentions: true
# Format 2: Advanced configuration for @mention filtering with fine-grained
# control
mentions:
# Allow mentions of repository team members (collaborators with any permission
# level, excluding bots). Default: true
# (optional)
allow-team-members: true
# Allow mentions inferred from event context (issue/PR authors, assignees,
# commenters). Default: true
# (optional)
allow-context: true
# List of user/bot names always allowed to be mentioned. Bots are not allowed by
# default unless listed here.
# (optional)
allowed: []
# Array of strings
# Maximum number of mentions allowed per message. Default: 50 Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Global footer control for all safe outputs. When false, omits visible
# AI-generated footer content from all created/updated entities (issues, PRs,
# discussions, releases) while still including XML markers for searchability.
# Individual safe-output types (create-issue, update-issue, etc.) can override
# this by specifying their own footer field. Defaults to true.
# (optional)
footer: true
# When set to false or "false", disables all activation and fallback comments
# entirely (run-started, run-success, run-failure, PR/issue creation links).
# Supports templatable boolean values including GitHub Actions expressions (e.g.
# ${{ inputs.activation-comments }}). Default: true
# (optional)
activation-comments: null
# When true, creates a parent '[aw] Failed runs' issue that tracks all workflow
# failures as sub-issues. Helps organize failure tracking but may be unnecessary
# in smaller repositories. Defaults to false.
# (optional)
group-reports: true
# When false, disables creating failure tracking issues when workflows fail.
# Useful for workflows where failures are expected or handled elsewhere. Defaults
# to true.
# (optional)
report-failure-as-issue: true
# Repository to create failure tracking issues in, in the format 'owner/repo'.
# Useful when the current repository has issues disabled. Defaults to the current
# repository.
# (optional)
failure-issue-repo: "example-value"
# Maximum number of bot trigger references (e.g. 'fixes #123', 'closes #456')
# allowed in output before all of them are neutralized. Default: 10. Supports
# integer or GitHub Actions expression (e.g. '${{ inputs.max-bot-mentions }}').
# (optional)
# Accepted formats:
# Format 1: integer
max-bot-mentions: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max-bot-mentions: "example-value"
# Override the id-token permission for the safe-outputs job. Use 'write' to
# force-enable the id-token: write permission (required for OIDC authentication
# with cloud providers). Use 'none' to suppress automatic detection and prevent
# adding id-token: write even when vault/OIDC actions are detected in steps. By
# default, the compiler auto-detects known OIDC/vault actions
# (aws-actions/configure-aws-credentials, azure/login, google-github-actions/auth,
# hashicorp/vault-action, cyberark/conjur-action) and adds id-token: write
# automatically.
# (optional)
id-token: "write"
# Concurrency group for the safe-outputs job. When set, the safe-outputs job will
# use this concurrency group with cancel-in-progress: false. Supports GitHub
# Actions expressions.
# (optional)
concurrency-group: "example-value"
# Explicit additional custom workflow jobs that the consolidated safe_outputs job
# should depend on.
# (optional)
needs: []
# Array of strings
# Override the GitHub deployment environment for the safe-outputs job. When set,
# this environment is used instead of the top-level environment: field. When not
# set, the top-level environment: field is propagated automatically so that
# environment-scoped secrets are accessible in the safe-outputs job.
# (optional)
# Accepted formats:
# Format 1: Environment name as a string
environment: "example-value"
# Format 2: Environment object with name and optional URL
environment:
# The name of the environment configured in the repo
name: "My Workflow"
# A deployment URL
# (optional)
url: "example-value"
# Runner specification for all safe-outputs jobs (activation, create-issue,
# add-comment, etc.). Single runner label (e.g., 'ubuntu-slim', 'ubuntu-latest',
# 'windows-latest', 'self-hosted'). Defaults to 'ubuntu-slim'. See
# https://github.blog/changelog/2025-10-28-1-vcpu-linux-runner-now-available-in-github-actions-in-public-preview/
# (optional)
runs-on: "example-value"
# Custom steps to inject into all safe-output jobs. These steps run after checking
# out the repository and setting up the action, and before any safe-output code
# executes.
# (optional)
steps: []
# Custom GitHub Actions to mount as once-callable MCP tools. Each action is
# resolved at compile time to derive its input schema from action.yml, and a
# guarded `uses:` step is injected in the safe_outputs job. Action names
# containing dashes will be automatically normalized to underscores (e.g.,
# 'add-smoked-label' becomes 'add_smoked_label').
# (optional)
actions:
{}
# Enable AI agents to signal that a task could not be completed due to
# infrastructure or tool failures (e.g., MCP crash, missing auth, inaccessible
# repository). Activates failure handling even when the agent exits 0.
# (optional)
# Accepted formats:
# Format 1: Configuration for report_incomplete safe output
report-incomplete:
# Maximum number of report_incomplete signals (default: 5). Supports integer or
# GitHub Actions expression (e.g. '${{ inputs.max }}').
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Whether to create or update GitHub issues when the task was incomplete (default:
# true). Supports literal boolean or GitHub Actions expression (e.g. '${{
# inputs.create-incomplete-issue }}').
# (optional)
# Accepted formats:
# Format 1: boolean
create-issue: true
# Format 2: GitHub Actions expression that resolves to a boolean at runtime
create-issue: "example-value"
# Prefix for issue titles when creating issues for incomplete runs (default:
# '[incomplete]')
# (optional)
title-prefix: "example-value"
# Labels to add to created issues for incomplete runs
# (optional)
labels: []
# Array of strings
# GitHub token to use for this specific output type. Overrides global github-token
# if specified.
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# If true, emit step summary messages instead of making GitHub API calls for this
# specific output type (preview mode)
# (optional)
staged: true
# Format 2: Enable report_incomplete with default configuration
report-incomplete: null
# Format 3: Explicitly disable report_incomplete (false). report_incomplete is
# enabled by default when safe-outputs is configured.
report-incomplete: true
# Configuration for secret redaction behavior in workflow outputs and artifacts
# (optional)
secret-masking:
# Additional secret redaction steps to inject after the built-in secret redaction.
# Use this to mask secrets in generated files using custom patterns.
# (optional)
steps: []
# Optional observability output settings for workflow runs.
# (optional)
observability:
# OTLP (OpenTelemetry Protocol) trace export configuration.
# (optional)
otlp:
# OTLP endpoint configuration. Accepts a plain URL string (backward-compat), a
# single {url, headers} object, or an array of {url, headers} objects for
# multi-endpoint concurrent fan-out. Encoded as GH_AW_OTLP_ENDPOINTS (JSON array).
# (optional)
# Accepted formats:
# Format 1: OTLP collector endpoint URL (e.g. 'https://traces.example.com:4317').
# Supports GitHub Actions expressions such as ${{ secrets.OTLP_ENDPOINT }}. When a
# static URL is provided, its hostname is automatically added to the network
# firewall allowlist.
endpoint: "example-value"
# Format 2: A single OTLP endpoint with a URL and optional per-endpoint headers.
endpoint:
# OTLP collector endpoint URL (e.g. 'https://traces.example.com:4317'). Supports
# GitHub Actions expressions such as ${{ secrets.OTLP_ENDPOINT }}. When a static
# URL is provided, its hostname is automatically added to the network firewall
# allowlist.
url: "example-value"
# (optional)
# Accepted formats:
# Format 1: Map of HTTP header names to values. Values support GitHub Actions
# expressions such as ${{ secrets.TOKEN }}.
headers:
{}
# Format 2: Deprecated: use the map form instead. Comma-separated list of
# key=value HTTP headers (e.g. 'Authorization=Bearer '). Supports GitHub
# Actions expressions such as ${{ secrets.OTLP_HEADERS }}.
headers: "example-value"
# Format 3: Multiple OTLP collector endpoints to export traces to concurrently.
# Each entry has its own URL and optional per-endpoint headers.
endpoint: []
# Array items: A single OTLP endpoint with a URL and optional per-endpoint
# headers.
# HTTP headers for the backward-compat string endpoint form. Only used when
# endpoint is a plain string; object/array endpoint entries carry their own
# per-endpoint headers.
# (optional)
# Accepted formats:
# Format 1: Map of HTTP header names to values to include with every OTLP export
# request. Values support GitHub Actions expressions such as ${{ secrets.TOKEN }}.
# Injected as the OTEL_EXPORTER_OTLP_HEADERS environment variable.
headers:
{}
# Format 2: Deprecated: use the map form instead. Comma-separated list of
# key=value HTTP headers to include with every OTLP export request (e.g.
# 'Authorization=Bearer '). Supports GitHub Actions expressions such as ${{
# secrets.OTLP_HEADERS }}. Injected as the OTEL_EXPORTER_OTLP_HEADERS environment
# variable.
headers: "example-value"
# How to handle missing OTLP endpoint/header values at runtime (for example from
# unset secrets). 'error' fails workflow startup (default), 'warn' logs a warning
# and skips MCP gateway OTLP configuration, and 'ignore' skips MCP gateway OTLP
# configuration without warning. This affects MCP gateway setup only;
# workflow-level OTEL_* environment variables are still injected.
# (optional)
if-missing: "error"
# Rate limiting configuration to restrict how frequently users can trigger the
# workflow. Helps prevent abuse and resource exhaustion from programmatically
# triggered events.
# (optional)
user-rate-limit:
# Maximum number of workflow runs allowed per user within the time window.
# Required field. Supports integer or GitHub Actions expression (e.g. '${{
# inputs.max }}').
# Accepted formats:
# Format 1: integer
max-runs-per-window: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max-runs-per-window: "example-value"
# Time window in minutes for rate limiting. Defaults to 60 (1 hour). Maximum: 180
# (3 hours).
# (optional)
window: 1
# Optional list of event types to apply rate limiting to. If not specified, rate
# limiting applies to all programmatically triggered events (e.g.,
# workflow_dispatch, issue_comment, pull_request_review).
# (optional)
events: []
# Array of strings
# Optional list of roles that are exempt from rate limiting. Defaults to ['admin',
# 'maintain', 'write'] if not specified. Users with any of these roles will not be
# subject to rate limiting checks. To apply rate limiting to all users, set to an
# empty array: []
# (optional)
ignored-roles: []
# Array of strings
# Legacy alias for 'user-rate-limit'. Prefer 'user-rate-limit' with
# 'max-runs-per-window'.
# (optional)
rate-limit:
# Legacy maximum runs key. Prefer 'max-runs-per-window'.
# (optional)
# Accepted formats:
# Format 1: integer
max-runs: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max-runs: "example-value"
# Legacy maximum runs key. Prefer 'max-runs-per-window'.
# (optional)
# Accepted formats:
# Format 1: integer
max: 1
# Format 2: GitHub Actions expression that resolves to an integer at runtime
max: "example-value"
# Time window in minutes for rate limiting. Defaults to 60 (1 hour). Maximum: 180
# (3 hours).
# (optional)
window: 1
# Optional list of event types to apply rate limiting to.
# (optional)
events: []
# Array of strings
# Optional list of roles that are exempt from rate limiting.
# (optional)
ignored-roles: []
# Array of strings
# Enable strict mode validation for enhanced security and compliance. Strict mode
# enforces: (1) Write Permissions - refuses contents:write, issues:write,
# pull-requests:write; requires safe-outputs instead, (2) Network Configuration -
# requires explicit network configuration with no standalone wildcard '*' in
# allowed domains (patterns like '*.example.com' are allowed), (3) Action Pinning
# - enforces actions pinned to commit SHAs instead of tags/branches, (4) MCP
# Network - requires network configuration for custom MCP servers with containers,
# (5) Deprecated Fields - refuses deprecated frontmatter fields. Can be enabled
# per-workflow via 'strict: true' in frontmatter, or disabled via 'strict: false'.
# CLI flag takes precedence over frontmatter (gh aw compile --strict enforces
# strict mode). Defaults to true. See:
# https://github.github.com/gh-aw/reference/frontmatter/#strict-mode-strict
# (optional)
strict: true
# Mark the workflow as private, preventing it from being added to other
# repositories via 'gh aw add'. A workflow with private: true is not meant to be
# shared outside its repository.
# (optional)
private: true
# Control whether the compile-agentic version update check runs in the activation
# job. When true (default), the activation job downloads config.json from the
# gh-aw repository and verifies the compiled version is not blocked and meets the
# minimum supported version. Set to false to disable the check (not allowed in
# strict mode). See:
# https://github.github.com/gh-aw/reference/frontmatter/#check-for-updates
# (optional)
check-for-updates: true
# Allow npm pre/post install scripts to execute during package installation. By
# default, --ignore-scripts is added to all generated npm install commands to
# prevent supply chain attacks via malicious install hooks. Setting
# run-install-scripts: true disables this protection globally (all runtimes). A
# supply chain security warning is emitted at compile time; in strict mode this is
# an error. Per-runtime control is also available via
# runtimes..run-install-scripts. See:
# https://github.github.com/gh-aw/reference/frontmatter/#run-install-scripts
# (optional)
run-install-scripts: true
# MCP Scripts configuration for defining custom lightweight MCP tools as
# JavaScript, shell scripts, or Python scripts. Tools are mounted in an MCP server
# and have access to secrets specified by the user. Only one of 'script'
# (JavaScript), 'run' (shell), or 'py' (Python) must be specified per tool.
# (optional)
mcp-scripts:
{}
# Runtime environment version overrides. Allows customizing runtime versions
# (e.g., Node.js, Python) or defining new runtimes. Runtimes from imported shared
# workflows are also merged.
# (optional)
runtimes:
{}
# Checkout configuration for the agent job. Controls how actions/checkout is
# invoked. Can be a single checkout configuration, an array for multiple
# checkouts, or false to disable the default checkout step entirely (dev-mode
# checkouts are unaffected).
# (optional)
# Accepted formats:
# Format 1: Single checkout configuration for the default workspace
checkout:
# Repository to checkout in owner/repo format. Defaults to the current repository.
# (optional)
repository: "example-value"
# Branch, tag, or SHA to checkout. Defaults to the ref that triggered the
# workflow.
# (optional)
ref: "example-value"
# Relative path within GITHUB_WORKSPACE to place the checkout. Defaults to the
# workspace root.
# (optional)
path: "example-value"
# Number of commits to fetch. 0 fetches all history. 1 (default) is a shallow
# clone. When multiple configs target the same path, the deepest value is used.
# (optional)
fetch-depth: 1
# Enable sparse-checkout with newline-separated patterns. When multiple configs
# target the same path, patterns are merged.
# (optional)
sparse-checkout: "example-value"
# Controls submodule checkout. Use "recursive" for all submodules, "true" for
# immediate submodules, or "false" to skip.
# (optional)
# Accepted formats:
# Format 1: string
submodules: "recursive"
# Format 2: boolean
submodules: true
# Whether to download Git LFS objects. Defaults to false.
# (optional)
lfs: true
# Deprecated: Use github-token instead. GitHub token for authentication.
# Credentials are always removed after checkout (persist-credentials: false is
# enforced).
# (optional)
token: "example-value"
# GitHub token for authentication. Use ${{ secrets.MY_TOKEN }} to reference a
# secret. Mutually exclusive with github-app (and deprecated app). Credentials are
# always removed after checkout (persist-credentials: false is enforced).
# (optional)
github-token: "${{ secrets.GITHUB_TOKEN }}"
# GitHub App authentication. Mints a short-lived installation access token via
# actions/create-github-app-token. Mutually exclusive with github-token.
# (optional)
github-app:
# Deprecated alias for client-id. GitHub App ID/client ID (e.g., '${{ vars.APP_ID
# }}').
# (optional)
app-id: "example-value"
# GitHub App client ID (e.g., '${{ vars.APP_ID }}'). Required to mint a GitHub App
# token.
# (optional)
client-id: "example-value"
# GitHub App private key (e.g., '${{ secrets.APP_PRIVATE_KEY }}'). Required to
# mint a GitHub App token.
# (optional)
private-key: "example-value"
# If true, skip token minting when client-id/private-key resolve to empty strings
# at runtime. Defaults to false.
# (optional)
ignore-if-missing: true
# Optional owner of the GitHub App installation (defaults to current repository
# owner if not specified)
# (optional)
owner: "example-value"
# Optional list of repositories to grant access to (defaults to current repository
# if not specified)
# (optional)
repositories: []
# Array of strings
# Optional extra GitHub App-only permissions to merge into the minted token. Takes
# effect for tools.github.github-app and safe-outputs.github-app; ignored in
# on.github-app and the top-level github-app fallback. Use to add GitHub App-only
# scopes (e.g. members, organization-administration) not expressible via standard
# handler declarations.
# (optional)
permissions:
# Permission level for repository administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission for repository administration.
# (optional)
administration: "read"
# Permission level for Codespaces (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces: "read"
# Permission level for Codespaces lifecycle administration (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
codespaces-lifecycle-admin: "read"
# Permission level for Codespaces metadata (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces-metadata: "read"
# Permission level for user email addresses (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
email-addresses: "read"
# Permission level for repository environments (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
environments: "read"
# Permission level for git signing (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
git-signing: "read"
# Permission level for organization members (read/none; "write" is rejected by the
# compiler). Required for org team membership API calls.
# (optional)
members: "read"
# Permission level for organization administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-administration: "read"
# Permission level for organization announcement banners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-announcement-banners: "read"
# Permission level for organization Codespaces (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-codespaces: "read"
# Permission level for organization Copilot (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-copilot: "read"
# Permission level for organization custom org roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-org-roles: "read"
# Permission level for organization custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-properties: "read"
# Permission level for organization custom repository roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-repository-roles: "read"
# Permission level for organization events (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-events: "read"
# Permission level for organization webhooks (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-hooks: "read"
# Permission level for organization members management (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-members: "read"
# Permission level for organization packages (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-packages: "read"
# Permission level for organization personal access token requests (read/none;
# "write" is rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-token-requests: "read"
# Permission level for organization personal access tokens (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-tokens: "read"
# Permission level for organization plan (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-plan: "read"
# Permission level for organization self-hosted runners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-self-hosted-runners: "read"
# Permission level for organization user blocking (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-user-blocking: "read"
# Permission level for repository custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
repository-custom-properties: "read"
# Permission level for repository webhooks (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
repository-hooks: "read"
# Permission level for single file access (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
single-file: "read"
# Permission level for team discussions (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
team-discussions: "read"
# Permission level for Dependabot vulnerability alerts (read/none; "write" is
# rejected by the compiler). Also available as a GITHUB_TOKEN scope. When used
# with a GitHub App, forwarded as permission-vulnerability-alerts input.
# (optional)
vulnerability-alerts: "read"
# Permission level for GitHub Actions workflow files (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
workflows: "read"
# Marks this checkout as the logical current repository for the workflow. When set
# to true, the AI agent will treat this repository as its primary working target.
# Only one checkout may have current set to true. Useful for central-repo
# workflows targeting a different repository.
# (optional)
current: true
# Additional Git refs to fetch after the checkout. Supported values: "*" (all
# branches), "refs/pulls/open/*" (all open pull-request refs), branch names (e.g.
# "main"), or glob patterns (e.g. "feature/*").
# (optional)
# Accepted formats:
# Format 1: A single additional ref pattern to fetch after checkout.
fetch: "example-value"
# Format 2: Additional Git refs to fetch after checkout. A git fetch step is
# emitted after the actions/checkout step.
fetch: []
# Array items: string
# When true, clones the repository's wiki git instead of the regular repository.
# The effective repository becomes "{repository}.wiki" (e.g. "owner/repo.wiki").
# Defaults to false.
# (optional)
wiki: true
# When true, persist credentials during checkout, then immediately run a
# post-checkout cleanup step that removes credentials from root and submodule git
# configs. Useful for submodule-safe cleanup behavior.
# (optional)
force-clean-git-credentials: true
# Format 2: Multiple checkout configurations
checkout: []
# Array items: undefined
# Format 3: Set to false to disable the default checkout step. The agent job will
# not check out any repository (dev-mode checkouts are unaffected).
checkout: false
# Top-level GitHub App configuration used as a fallback for all nested github-app
# token minting operations (on, safe-outputs, checkout, tools.github,
# dependencies). When a nested section does not define its own github-app, this
# top-level configuration is used automatically.
# (optional)
github-app:
# Deprecated alias for client-id. GitHub App ID/client ID (e.g., '${{ vars.APP_ID
# }}').
# (optional)
app-id: "example-value"
# GitHub App client ID (e.g., '${{ vars.APP_ID }}'). Required to mint a GitHub App
# token.
# (optional)
client-id: "example-value"
# GitHub App private key (e.g., '${{ secrets.APP_PRIVATE_KEY }}'). Required to
# mint a GitHub App token.
# (optional)
private-key: "example-value"
# If true, skip token minting when client-id/private-key resolve to empty strings
# at runtime. Defaults to false.
# (optional)
ignore-if-missing: true
# Optional owner of the GitHub App installation (defaults to current repository
# owner if not specified)
# (optional)
owner: "example-value"
# Optional list of repositories to grant access to (defaults to current repository
# if not specified)
# (optional)
repositories: []
# Array of strings
# Optional extra GitHub App-only permissions to merge into the minted token. Takes
# effect for tools.github.github-app and safe-outputs.github-app; ignored in
# on.github-app and the top-level github-app fallback. Use to add GitHub App-only
# scopes (e.g. members, organization-administration) not expressible via standard
# handler declarations.
# (optional)
permissions:
# Permission level for repository administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission for repository administration.
# (optional)
administration: "read"
# Permission level for Codespaces (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces: "read"
# Permission level for Codespaces lifecycle administration (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
codespaces-lifecycle-admin: "read"
# Permission level for Codespaces metadata (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
codespaces-metadata: "read"
# Permission level for user email addresses (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
email-addresses: "read"
# Permission level for repository environments (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
environments: "read"
# Permission level for git signing (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
git-signing: "read"
# Permission level for organization members (read/none; "write" is rejected by the
# compiler). Required for org team membership API calls.
# (optional)
members: "read"
# Permission level for organization administration (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-administration: "read"
# Permission level for organization announcement banners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-announcement-banners: "read"
# Permission level for organization Codespaces (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-codespaces: "read"
# Permission level for organization Copilot (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-copilot: "read"
# Permission level for organization custom org roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-org-roles: "read"
# Permission level for organization custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-properties: "read"
# Permission level for organization custom repository roles (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-custom-repository-roles: "read"
# Permission level for organization events (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-events: "read"
# Permission level for organization webhooks (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-hooks: "read"
# Permission level for organization members management (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-members: "read"
# Permission level for organization packages (read/none; "write" is rejected by
# the compiler). GitHub App-only permission.
# (optional)
organization-packages: "read"
# Permission level for organization personal access token requests (read/none;
# "write" is rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-token-requests: "read"
# Permission level for organization personal access tokens (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-personal-access-tokens: "read"
# Permission level for organization plan (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
organization-plan: "read"
# Permission level for organization self-hosted runners (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
organization-self-hosted-runners: "read"
# Permission level for organization user blocking (read/none; "write" is rejected
# by the compiler). GitHub App-only permission.
# (optional)
organization-user-blocking: "read"
# Permission level for repository custom properties (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
repository-custom-properties: "read"
# Permission level for repository webhooks (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
repository-hooks: "read"
# Permission level for single file access (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
single-file: "read"
# Permission level for team discussions (read/none; "write" is rejected by the
# compiler). GitHub App-only permission.
# (optional)
team-discussions: "read"
# Permission level for Dependabot vulnerability alerts (read/none; "write" is
# rejected by the compiler). Also available as a GITHUB_TOKEN scope. When used
# with a GitHub App, forwarded as permission-vulnerability-alerts input.
# (optional)
vulnerability-alerts: "read"
# Permission level for GitHub Actions workflow files (read/none; "write" is
# rejected by the compiler). GitHub App-only permission.
# (optional)
workflows: "read"
# Schema for validating 'with' input values when this workflow is imported by
# another workflow using the 'uses'/'with' syntax. Defines the expected
# parameters, their types, and whether they are required. Scalar inputs are
# accessible via '${{ github.aw.import-inputs. }}' expressions. Object
# inputs (type: object) allow one-level deep sub-fields accessible via '${{
# github.aw.import-inputs.. }}' expressions.
# (optional)
import-schema:
{}
---
```
## Additional Information
[Section titled “Additional Information”](#additional-information)
* Fields marked with `(optional)` are not required
* Fields with multiple options show all possible formats
* See the [Frontmatter guide](/gh-aw/reference/frontmatter/) for detailed explanations and examples
* See individual reference pages for specific topics like [Triggers](/gh-aw/reference/triggers/), [Tools](/gh-aw/reference/tools/), and [Safe Outputs](/gh-aw/reference/safe-outputs/)
# Frontmatter Hash Specification
> Specification for computing deterministic hashes of agentic workflow frontmatter
# Frontmatter Hash Specification
[Section titled “Frontmatter Hash Specification”](#frontmatter-hash-specification)
**Version**: 1.0.0\
**Status**: Draft\
**Publication Date**: 2026-05-07\
**Latest Version**: [frontmatter-hash-specification](/gh-aw/reference/frontmatter-hash-specification/)\
**Editor**: GitHub Agentic Workflows Team
***
This document specifies the algorithm for computing a deterministic hash of agentic workflow frontmatter, including contributions from imported workflows.
## Purpose
[Section titled “Purpose”](#purpose)
The frontmatter hash provides:
1. **Stale lock detection**: Identify when the compiled lock file is out of sync with the source workflow (e.g. after editing the `.md` file without recompiling)
2. **Reproducibility**: Ensure identical configurations produce identical hashes across languages (Go and JavaScript)
3. **Change detection**: Verify that workflow configuration has not changed between compilation and execution
## Conformance
[Section titled “Conformance”](#conformance)
### Conformance Classes
[Section titled “Conformance Classes”](#conformance-classes)
* **Basic Conformance**: An implementation MUST compute a deterministic SHA-256 hash from canonicalized frontmatter input and MUST produce the same output for identical input.
* **Full Conformance**: An implementation MUST satisfy Basic Conformance and MUST implement cross-language consistency checks between Go and JavaScript implementations.
### Requirements Notation
[Section titled “Requirements Notation”](#requirements-notation)
The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119).
## Hash Algorithm
[Section titled “Hash Algorithm”](#hash-algorithm)
### 1. Input Collection
[Section titled “1. Input Collection”](#1-input-collection)
Collect all frontmatter from the main workflow and all imported workflows in **breadth-first order** (BFS traversal):
1. **Main workflow frontmatter**: The frontmatter from the root workflow file
2. **Imported workflow frontmatter**: Frontmatter from each imported file in BFS processing order
* Includes transitively imported files (imports of imports)
* Agent files (`.github/agents/*.md`) only contribute markdown content, not frontmatter
#### BFS Traversal and Tie-Breaking Rules
[Section titled “BFS Traversal and Tie-Breaking Rules”](#bfs-traversal-and-tie-breaking-rules)
The BFS traversal processes imports level by level, starting from the root workflow. When a workflow imports multiple files, they are enqueued left-to-right in the order they appear in the `imports:` list. This ordering is preserved at every level.
**Diamond-import handling**: If a workflow file appears more than once in the import graph (a “diamond” dependency), the **first occurrence** in BFS order determines where that file’s frontmatter is merged; all subsequent occurrences of the same file **MUST be silently skipped**. Implementations MUST detect duplicate import paths using canonical path comparison (case-sensitive, no trailing-slash normalization) and discard duplicates without error.
**Example (diamond graph)**:
```plaintext
root.md → imports: [a.md, b.md]
a.md → imports: [shared.md]
b.md → imports: [shared.md]
```
BFS queue order: `[root.md, a.md, b.md, shared.md]`\
`shared.md` appears twice but is processed only once (after `a.md` in queue order).\
Canonical hash input order: root → a → b → shared.
If the root import list were reversed to `[b.md, a.md]`, the canonical order would be `root → b → a → shared`.
The first sibling encountered in BFS order always claims the shared dependency. Later duplicates are skipped. This rule ensures that the hash is deterministic regardless of which traversal path first discovers a shared dependency.
### 2. Field Selection
[Section titled “2. Field Selection”](#2-field-selection)
Include the following frontmatter fields in the hash computation:
**Core Configuration:**
* `engine` - AI engine specification
* `on` - Workflow triggers
* `permissions` - GitHub Actions permissions
* `tracker-id` - Workflow tracker identifier
**Tool and Integration:**
* `tools` - Tool configurations (GitHub, Playwright, etc.)
* `mcp-servers` - MCP server configurations
* `network` - Network access permissions
* `safe-outputs` - Safe output configurations
* `mcp-scripts` - Safe input configurations
**Runtime Configuration:**
* `runtimes` - Runtime version specifications (Node.js, Python, etc.)
* `services` - Container services
* `cache` - Caching configuration
**Workflow Structure:**
* `steps` - Custom workflow steps
* `post-steps` - Post-execution steps
* `jobs` - GitHub Actions job definitions
**Metadata:**
* `description` - Workflow description
* `labels` - Workflow labels
* `bots` - Authorized bot list
* `timeout-minutes` - Workflow timeout
* `secret-masking` - Secret masking configuration
**Import Metadata:**
* `imports` - List of imported workflow paths (for traceability)
* `inputs` - Input parameter definitions
**Excluded Fields:**
* Markdown body content (not part of frontmatter)
* Comments and whitespace variations
* Field ordering (normalized during processing)
### 3. Canonical JSON Serialization
[Section titled “3. Canonical JSON Serialization”](#3-canonical-json-serialization)
Transform the collected frontmatter into a canonical JSON representation:
#### 3.1 Merge Strategy
[Section titled “3.1 Merge Strategy”](#31-merge-strategy)
For each workflow in BFS order:
1. Parse frontmatter into a structured object
2. Merge with accumulated frontmatter using these rules:
* **Replace**: `engine`, `on`, `tracker-id`, `description`, `timeout-minutes`
* **Deep merge**: `tools`, `mcp-servers`, `network`, `permissions`, `runtimes`, `cache`, `services`
* **Append**: `steps`, `post-steps`, `safe-outputs`, `mcp-scripts`, `jobs`
* **Union**: `labels`, `bots` (deduplicated)
* **Track**: `imports` (list of all imported paths)
#### 3.2 Normalization Rules
[Section titled “3.2 Normalization Rules”](#32-normalization-rules)
Apply these normalization rules to ensure deterministic output:
1. **Key Sorting**: Sort all object keys alphabetically at every level
2. **Array Ordering**: Preserve array order as-is (no sorting of array elements)
3. **Whitespace**: Use minimal whitespace (no pretty-printing)
4. **Number Format**: Represent numbers without exponents (e.g., `120` not `1.2e2`)
5. **Boolean Values**: Use lowercase `true` and `false`
6. **Null Handling**: Include `null` values explicitly
7. **Empty Containers**: Include empty objects `{}` and empty arrays `[]`
8. **String Escaping**: Use JSON standard escaping (quotes, backslashes, control characters)
#### 3.3 Serialization Format
[Section titled “3.3 Serialization Format”](#33-serialization-format)
The canonical JSON includes all frontmatter fields plus version information:
```json
{
"bots": ["copilot"],
"cache": {},
"description": "Daily audit of workflow runs",
"engine": "claude",
"imports": ["shared/mcp/tavily.md", "shared/jqschema.md"],
"jobs": {},
"labels": ["audit", "automation"],
"mcp-servers": {},
"network": {"allowed": ["api.github.com"]},
"on": {"schedule": "daily"},
"permissions": {"actions": "read", "contents": "read"},
"post-steps": [],
"runtimes": {"node": {"version": "20"}},
"mcp-scripts": {},
"safe-outputs": {"create-discussion": {"category": "audits"}},
"services": {},
"steps": [],
"template-expressions": ["${{ env.MY_VAR }}"],
"timeout-minutes": 30,
"tools": {"repo-memory": {"branch-name": "memory/audit"}},
"tracker-id": "audit-workflows-daily",
"versions": {
"agents": "v0.0.84",
"awf": "v0.11.2",
"gh-aw": "dev"
}
}
```
### 4. Version Information
[Section titled “4. Version Information”](#4-version-information)
The hash includes version numbers to ensure hash changes when dependencies are upgraded:
* **gh-aw**: The compiler version (e.g., “0.1.0” or “dev”)
* **awf**: The firewall version (e.g., “v0.11.2”)
* **agents**: The MCP gateway version (e.g., “v0.0.84”)
This ensures that upgrading any component invalidates existing hashes.
1. **Serialize**: Convert the merged and normalized frontmatter to canonical JSON
2. **Add Versions**: Include version information for gh-aw, awf (firewall), and agents (MCP gateway)
3. **Hash**: Compute SHA-256 hash of the JSON string (UTF-8 encoded)
4. **Encode**: Represent the hash as a lowercase hexadecimal string (64 characters)
**Example:**
```plaintext
Input JSON: {"engine":"copilot","on":{"schedule":"daily"},"versions":{"agents":"v0.0.84","awf":"v0.11.2","gh-aw":"dev"}}
SHA-256: a1b2c3d4e5f6... (64 hex characters)
```
### 5. Cross-Language Consistency
[Section titled “5. Cross-Language Consistency”](#5-cross-language-consistency)
Both Go and JavaScript implementations MUST:
* Use the same field selection and merging rules
* Produce identical canonical JSON (byte-for-byte)
* Use SHA-256 hash function
* Encode output as lowercase hexadecimal
**Test cases** must verify identical hashes across both implementations for:
* Empty frontmatter
* Single-file workflows (no imports)
* Multi-level imports (2+ levels deep)
* All field types (strings, numbers, booleans, arrays, objects)
* Special characters and escaping
* All workflows in the repository
### 5.1 Cross-Language Validation Protocol
[Section titled “5.1 Cross-Language Validation Protocol”](#51-cross-language-validation-protocol)
The project maintains Go and JavaScript implementations of the frontmatter hash algorithm. A conforming change to either implementation MUST follow this validation protocol:
1. Update both implementations in the same change whenever the authoritative runtime algorithm or normalization behavior changes.
2. Execute the shared cross-language test vectors so each implementation validates the other implementation’s output, not just its own fixtures.
3. Treat any byte-level mismatch in canonical JSON or final SHA-256 output as a release-blocking failure until both implementations are aligned.
4. Recompile workflow lock files only after the cross-language checks pass, so newly generated hashes reflect a synchronized algorithm.
**R-XLANG-001**: The shared validation corpus **MUST** include at least one empty-frontmatter case, one single-file case, one multi-level import case, and one diamond-import case.
**R-XLANG-002**: A change that alters canonical JSON generation in either language **MUST** update the shared validation corpus in the same change.
**R-XLANG-003**: CI or pre-release validation **MUST** fail if Go and JavaScript produce different hashes for any corpus member.
## Implementation Notes
[Section titled “Implementation Notes”](#implementation-notes)
### Go Implementation
[Section titled “Go Implementation”](#go-implementation)
The current Go implementation (`pkg/parser/frontmatter_hash.go`) uses a **text-based approach** that diverges from the field-selection model described in Section 2 (“Field Selection”) of this specification:
* **Actual behavior**: The entire normalized frontmatter text is hashed as a single opaque string (`frontmatter-text` key in the canonical JSON), alongside a sorted list of imported file paths and their normalized texts. This means *all* frontmatter fields — including excluded ones such as comments — affect the hash value.
* **Specified behavior**: The specification calls for selecting individual named fields and merging them by type (replace, deep-merge, append, union).
**Implication**: The text-based approach is more conservative (any frontmatter change invalidates the hash, including whitespace-only changes after normalization) and simpler to implement cross-language. The trade-off is that it cannot support selective field exclusion without modifying the text normalization step.
**Sync status** (verified 2026-05-06): The Go implementation is consistent with the JavaScript implementation in `actions/setup/js/` for the text-based approach. Both produce identical hashes for the same input. The field-selection model in Section 2 documents the *logical* intent; the text-based implementation is the authoritative runtime behavior until a future revision aligns them.
**Resolution** (2026-05-08): The project officially adopts the **text-based approach** as the authoritative runtime behavior (option b). Section 2 (“Field Selection”) documents the intended logical model for future alignment, but is non-normative until a dedicated migration milestone is scheduled. No immediate changes to the Go or JavaScript implementations are required. A future v2.0.0 revision of this specification MAY align both implementations to the field-selection model if selective field exclusion becomes a concrete requirement; that revision MUST include updated cross-language test vectors and a migration guide. Until then, implementations MUST continue to use the text-based approach and MUST NOT selectively exclude fields from the hash input.
* Use `crypto/sha256` for hashing (`crypto/sha256.Sum256`)
* Use `hex.EncodeToString()` for hexadecimal encoding
### JavaScript Implementation
[Section titled “JavaScript Implementation”](#javascript-implementation)
* Uses the same text-based approach as the Go implementation
* Uses Node.js `crypto.createHash('sha256')` for hashing
* Uses `.digest('hex')` for hexadecimal encoding
* The JavaScript cross-language test suite in `pkg/parser/frontmatter_hash_cross_language_test.go` verifies identical output between the two implementations
### Hash Storage and Verification
[Section titled “Hash Storage and Verification”](#hash-storage-and-verification)
1. **Compilation**: The Go compiler computes the hash and writes it to the workflow log file
2. **Execution**: The JavaScript custom action:
* Reads the hash from the log file
* Recomputes the hash from the workflow file
* Compares the two hashes
* Creates a GitHub issue if they differ (indicating frontmatter modification)
## Safeguards
[Section titled “Safeguards”](#safeguards)
This section describes known risks associated with the frontmatter hash mechanism and the recommended mitigations.
### S-1: Hash Collision Risk
[Section titled “S-1: Hash Collision Risk”](#s-1-hash-collision-risk)
SHA-256 produces a 256-bit output, giving a collision probability of approximately 2⁻¹²⁸ for any two distinct inputs under the birthday paradox. For the expected number of compiled workflows in a repository (typically <10,000), the probability of an accidental collision is negligible and does not require mitigation at the application layer.
However, implementations MUST NOT rely on the hash as a cryptographic commitment or security boundary. The hash is an integrity check for stale-lock detection only.
**Mitigation**: If future use cases require stronger collision resistance (e.g., content-addressed storage), implementations SHOULD upgrade to SHA-512 or SHA3-256 and bump the specification version.
### S-2: Tamper Detection Limits
[Section titled “S-2: Tamper Detection Limits”](#s-2-tamper-detection-limits)
The frontmatter hash detects accidental drift between the `.md` source and the compiled `.lock.yml` file. It does **not** prevent intentional tampering. Any user with write access to the repository can modify both files simultaneously:
1. Edit the `.md` source.
2. Recompile to regenerate the `.lock.yml` with the new hash.
3. Commit both files in a single push.
This bypass is by design — the hash mechanism is intended to catch *accidental* stale locks, not to enforce a security boundary.
**Mitigation**: Enforce required code reviews via branch protection rules. Require signed commits for critical workflows. Use separate compilation and merge workflows with protected branches to prevent direct pushes to the default branch.
### S-3: Inclusion of Sensitive Configuration in Hash Input
[Section titled “S-3: Inclusion of Sensitive Configuration in Hash Input”](#s-3-inclusion-of-sensitive-configuration-in-hash-input)
The canonical JSON used for hash computation includes all frontmatter fields, some of which may encode sensitive topology information (e.g., MCP server addresses in `mcp-servers:`, secret names in `mcp-scripts:`, or branch names in `tools.repo-memory`). This information is embedded in the `.lock.yml` file at compile time and is visible to anyone who can read the repository.
**Mitigation**: Treat repository visibility as the primary access control boundary. Avoid storing secret *values* in frontmatter (use GitHub Actions secrets instead). Periodically audit lock files for inadvertently committed sensitive configuration.
### S-4: Version-Bump-Forced Recompilation
[Section titled “S-4: Version-Bump-Forced Recompilation”](#s-4-version-bump-forced-recompilation)
The hash includes `versions.gh-aw`, `versions.awf`, and `versions.agents`. Upgrading any of these components will invalidate all existing hashes, triggering stale-lock warnings on all workflows until they are recompiled. In a repository with many workflows, this can create a noisy wave of false-positive stale-lock issues.
**Mitigation**: Coordinate component upgrades with a bulk `make recompile` step. Automate recompilation in the upgrade PR so that lock files are always fresh after a version bump.
### S-5: Cross-Language Hash Divergence
[Section titled “S-5: Cross-Language Hash Divergence”](#s-5-cross-language-hash-divergence)
The Go and JavaScript implementations must produce byte-for-byte identical canonical JSON. Any divergence in key sorting, number representation, or null/undefined handling between the two implementations will cause the JavaScript runtime to report a false stale-lock mismatch for every workflow run.
**Mitigation**: Maintain a shared test-vector file (at minimum: empty frontmatter, single-field workflow, multi-level imports, all field types). Run cross-language hash tests in CI. Any change to the serialization algorithm in either language MUST be accompanied by updated test vectors verified against both implementations.
### S-6: Maximum Frontmatter Input Size
[Section titled “S-6: Maximum Frontmatter Input Size”](#s-6-maximum-frontmatter-input-size)
Very large frontmatter payloads can cause excessive memory use and hash-computation latency during compilation and runtime verification. This can degrade CI reliability and increase stale-lock false positives due to timeout or resource pressure.
**Mitigation**: Implementations SHOULD enforce a maximum cumulative frontmatter input size and MUST fail deterministically with a descriptive error when the limit is exceeded. A limit of 1 MiB for the combined normalized frontmatter input is RECOMMENDED unless repository-specific requirements justify a higher bound.
***
## Sync Notes
[Section titled “Sync Notes”](#sync-notes)
This section maps the frontmatter hash specification to the source files that implement it. Use this mapping to verify that specification changes are reflected in both implementations.
| Component | File(s) |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Go hash computation | `pkg/parser/frontmatter_hash.go` (`computeFrontmatterHashTextBased`, `computeFrontmatterHashTextBasedWithReader`) |
| JavaScript hash computation | `actions/setup/js/frontmatter_hash.cjs` |
| Cross-language test | `pkg/parser/frontmatter_hash_cross_language_test.go` |
| Text normalization | `pkg/parser/frontmatter_hash.go` (`normalizeFrontmatterText`) |
| Import processing | `pkg/parser/frontmatter_hash.go` (`processImportsTextBased`) |
**After any change to the hash algorithm:**
1. Update the Go implementation in `pkg/parser/frontmatter_hash.go`
2. Update the JavaScript implementation in `actions/setup/js/frontmatter_hash.cjs`
3. Run the cross-language test: `go test ./pkg/parser/ -run TestFrontmatterHash`
4. Run `make recompile` to regenerate all lock files with fresh hashes
5. Verify cross-language consistency for the test cases listed in Section 5
6. Verify BFS diamond-import tie-breaking remains deterministic: when the same imported file is reachable through multiple import paths at the same depth, the canonical traversal MUST prefer first-seen path order and MUST NOT duplicate imported content in hash input.
**Runtime behavior**: text-based approach is authoritative (see Implementation Notes § Resolution).
**Resolution log (2026-05-08, authoritative)**: Text-based canonicalization is the resolved, runtime-authoritative algorithm. Section 2 field-selection remains future-state design intent only until an explicit migration milestone is approved.
**Sync verification (2026-05-12)**: SPDD review reconfirmed that the 2026-05-08 text-based resolution remains in force.
***
## Security Considerations
[Section titled “Security Considerations”](#security-considerations)
* The hash is **not cryptographically secure** for authentication (no HMAC/signing)
* The hash is designed to **detect stale lock files** — it catches cases where the frontmatter has changed since the lock file was last compiled
* The hash **does not guarantee tamper protection**: anyone with write access to the repository can modify both the `.md` source and the `.lock.yml` file together, bypassing detection
* Always validate workflow sources through proper code review processes
## Versioning
[Section titled “Versioning”](#versioning)
This is version 1.0 of the frontmatter hash specification.
Future versions may:
* Add additional fields
* Change normalization rules
* Use different hash algorithms
Version changes will be documented and backward compatibility maintained where possible.
### Future Versions (v2.0.0 Planning)
[Section titled “Future Versions (v2.0.0 Planning)”](#future-versions-v200-planning)
Per the **Resolution (2026-05-08)** in Implementation Notes, the text-based algorithm remains authoritative until a dedicated migration milestone is approved.
Tracking issue: [#31983](https://github.com/github/gh-aw/issues/31983)
The project **MUST NOT** schedule a v2.0.0 migration to the field-selection model until all of the following tracked tasks are complete:
* [ ] Confirm and document a selective field-exclusion use case in [#31983](https://github.com/github/gh-aw/issues/31983).
* [ ] Draft a migration guide in [#31983](https://github.com/github/gh-aw/issues/31983), including lock-file invalidation and recompilation steps.
* [ ] Write candidate v2.0.0 cross-language test vectors in [#31983](https://github.com/github/gh-aw/issues/31983) and verify they pass in CI.
* [ ] Approve a rollout plan in [#31983](https://github.com/github/gh-aw/issues/31983), including backward-compatibility impact analysis.
Until these prerequisites are met, implementations **MUST** continue using the text-based algorithm and **MUST NOT** selectively exclude frontmatter fields from hash input.
## Appendix A: Cross-Language Test Vectors (Text-Based Algorithm)
[Section titled “Appendix A: Cross-Language Test Vectors (Text-Based Algorithm)”](#appendix-a-cross-language-test-vectors-text-based-algorithm)
The following vectors are normative for the current authoritative text-based algorithm.
Validation status: Each vector hash is verified to match in both implementations via automated cross-language tests in CI.
### FH-TV-001
[Section titled “FH-TV-001”](#fh-tv-001)
Expected hash: `4c8309afbcf816cd80c0824dce2b50047834b29e14b34b96953e88ae81048c46`
This vector represents an intentionally empty frontmatter block (`---` followed immediately by `---`) rather than a file with no frontmatter delimiter. These are treated as different input forms for conformance testing and MUST be validated independently; this vector defines only the explicit-empty-block form.
```yaml
---
---
# Empty Workflow
```
### FH-TV-002
[Section titled “FH-TV-002”](#fh-tv-002)
Expected hash: `b9def9907e3328e2e03e8c47c315723df39788f251627313b1a984bb61b9cbce`
```yaml
---
engine: copilot
description: Test workflow
on:
schedule: daily
---
# Test Workflow
```
### FH-TV-003
[Section titled “FH-TV-003”](#fh-tv-003)
Expected hash: `8c63a05ef42cbfaff9be87a06257282cb4dcb952f71481d9d65ec3037003dbe8`
```yaml
---
engine: claude
description: Complex workflow
tracker-id: complex-test
timeout-minutes: 30
on:
schedule: daily
workflow_dispatch: true
permissions:
contents: read
actions: read
tools:
playwright:
version: v1.41.0
labels:
- test
- complex
bots:
- copilot
---
# Complex Workflow
```
### FH-TV-004
[Section titled “FH-TV-004”](#fh-tv-004)
Expected hash: `701dc12776a417c6ce4c82b16d1fcc9de343130efb554fda27a701386b17d134`
This vector validates deterministic hash input when frontmatter includes agent file imports. It also exercises BFS diamond-import tie-breaking where multiple import branches reference the same transitive file.
```yaml
---
engine: copilot
imports:
- ./agents/router.agent.md
- ./agents/summarizer.agent.md
---
# Import-based Workflow
```
# Fuzzy Schedule Time Syntax Specification
> Formal specification for the fuzzy schedule time syntax following W3C conventions
**Version**: 1.2.0 **Status**: Draft Specification\
**Latest Version**: [fuzzy-schedule-specification](/gh-aw/reference/fuzzy-schedule-specification/)\
**Editor**: GitHub Agentic Workflows Team
***
## Abstract
[Section titled “Abstract”](#abstract)
This specification defines the Fuzzy Schedule Time Syntax, a human-friendly scheduling language for GitHub Agentic Workflows that automatically distributes workflow execution times to prevent server load spikes. The syntax supports daily, hourly, weekly, and interval-based schedules with optional time constraints and timezone conversions. The specification includes a deterministic scattering algorithm that uses hash functions to assign consistent execution times to workflows based on their identifiers, ensuring predictable behavior across multiple compilations while distributing load across an organization’s infrastructure.
## Status of This Document
[Section titled “Status of This Document”](#status-of-this-document)
This section describes the status of this document at the time of publication. This is a draft specification and may be updated, replaced, or made obsolete by other documents at any time.
This document is governed by the GitHub Agentic Workflows project specifications process.
## Table of Contents
[Section titled “Table of Contents”](#table-of-contents)
1. [Introduction](#1-introduction)
2. [Conformance](#2-conformance)
3. [Core Syntax](#3-core-syntax)
4. [Time Specifications](#4-time-specifications)
5. [Timezone Support](#5-timezone-support)
6. [Scattering Algorithm](#6-scattering-algorithm)
7. [Cron Expression Generation](#7-cron-expression-generation)
8. [Safeguards](#8-safeguards)
9. [Error Handling](#9-error-handling)
10. [Compliance Testing](#10-compliance-testing)
11. [Sync Notes](#11-sync-notes)
12. [Calendar Output Schema](#12-calendar-output-schema)
***
## 1. Introduction
[Section titled “1. Introduction”](#1-introduction)
### 1.1 Purpose
[Section titled “1.1 Purpose”](#11-purpose)
The Fuzzy Schedule Time Syntax addresses the problem of server load spikes that occur when multiple workflows execute simultaneously using fixed-time schedules. Traditional cron expressions require explicit time specifications, leading developers to commonly use convenient times (e.g., midnight, on-the-hour) that create load concentration. This specification defines a natural language syntax that automatically distributes execution times while preserving schedule semantics.
### 1.2 Scope
[Section titled “1.2 Scope”](#12-scope)
This specification covers:
* Natural language schedule expressions for daily, hourly, weekly, and interval-based schedules
* Time constraint syntax using `around` and `between` modifiers
* Timezone conversion syntax for local-to-UTC time translation
* Deterministic scattering algorithm for execution time distribution
* Cron expression generation from fuzzy syntax
* Validation requirements and error handling
This specification does NOT cover:
* Standard cron expression syntax (handled by GitHub Actions)
* Monthly or yearly schedule patterns
* Dynamic schedule adjustment based on load metrics
* Schedule conflict resolution between workflows
### 1.3 Design Goals
[Section titled “1.3 Design Goals”](#13-design-goals)
This specification prioritizes:
1. **Human readability**: Natural language expressions that clearly communicate intent
2. **Load distribution**: Automatic scattering prevents simultaneous workflow execution
3. **Determinism**: Same workflow identifier always produces same execution time
4. **Predictability**: Execution times remain consistent across recompilations
5. **Timezone awareness**: Support for local time specifications with UTC conversion
***
## 2. Conformance
[Section titled “2. Conformance”](#2-conformance)
### 2.1 Conformance Classes
[Section titled “2.1 Conformance Classes”](#21-conformance-classes)
A **conforming implementation** is a parser that satisfies all MUST, MUST NOT, REQUIRED, SHALL, and SHALL NOT requirements in this specification.
A **conforming fuzzy schedule expression** is a schedule string that conforms to the syntax grammar defined in Section 3 and produces a valid fuzzy cron placeholder.
A **conforming scattering implementation** is an implementation that satisfies all scattering algorithm requirements in Section 6.
### 2.2 Requirements Notation
[Section titled “2.2 Requirements Notation”](#22-requirements-notation)
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
### 2.3 Compliance Levels
[Section titled “2.3 Compliance Levels”](#23-compliance-levels)
**Level 1 (Basic)**: Supports daily and weekly schedules without time constraints
**Level 2 (Standard)**: Adds support for time constraints (`around`, `between`) and hourly schedules
**Level 3 (Complete)**: Includes timezone conversion, interval schedules, and bi-weekly/tri-weekly patterns
***
## 3. Core Syntax
[Section titled “3. Core Syntax”](#3-core-syntax)
### 3.1 Grammar Definition
[Section titled “3.1 Grammar Definition”](#31-grammar-definition)
A fuzzy schedule expression MUST conform to the following ABNF grammar:
```text
fuzzy-schedule = daily-schedule / hourly-schedule / weekly-schedule / interval-schedule
daily-schedule = "daily" [time-constraint]
weekly-schedule = "weekly" ["on" weekday] [time-constraint]
hourly-schedule = "hourly" / ("every" hour-interval)
interval-schedule = "every" (minute-interval / hour-interval / day-interval / week-interval)
time-constraint = around-constraint / between-constraint
around-constraint = "around" time-spec
between-constraint = "between" time-spec "and" time-spec
time-spec = (hour-24 ":" minute) [utc-offset]
/ (hour-12 am-pm) [utc-offset]
/ time-keyword [utc-offset]
time-keyword = "midnight" / "noon"
am-pm = "am" / "pm"
utc-offset = "utc" ("+" / "-") (hours / hours ":" minutes)
weekday = "sunday" / "monday" / "tuesday" / "wednesday"
/ "thursday" / "friday" / "saturday"
hour-24 = 1*2DIGIT ; 0-23
hour-12 = 1*2DIGIT ; 1-12
minute = 2DIGIT ; 00-59
hours = 1*2DIGIT
minutes = 2DIGIT
minute-interval = 1*DIGIT ("m" / "minutes" / "minute")
hour-interval = 1*DIGIT ("h" / "hours" / "hour")
day-interval = 1*DIGIT ("d" / "days" / "day")
week-interval = 1*DIGIT ("w" / "weeks" / "week")
```
### 3.2 Daily Schedules
[Section titled “3.2 Daily Schedules”](#32-daily-schedules)
#### 3.2.1 Basic Daily Schedule
[Section titled “3.2.1 Basic Daily Schedule”](#321-basic-daily-schedule)
A basic daily schedule expression SHALL take the form:
```yaml
daily
```
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:DAILY * * *`
The execution time SHALL be deterministically scattered across all 24 hours and 60 minutes of the day.
#### 3.2.2 Daily Around Time
[Section titled “3.2.2 Daily Around Time”](#322-daily-around-time)
A daily around schedule expression SHALL take the form:
```yaml
daily around
```
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:DAILY_AROUND:HH:MM * * *`
The execution time SHALL be scattered within a ±60 minute window around the specified time.
**Example**:
```yaml
daily around 14:00
# Generates: FUZZY:DAILY_AROUND:14:0 * * *
# Scatters within window: 13:00 to 15:00
```
#### 3.2.3 Daily Between Times
[Section titled “3.2.3 Daily Between Times”](#323-daily-between-times)
A daily between schedule expression SHALL take the form:
```yaml
daily between and
```
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:DAILY_BETWEEN:START_H:START_M:END_H:END_M * * *`
The execution time SHALL be scattered uniformly within the specified time range, including handling of midnight-crossing ranges.
**Example**:
```yaml
daily between 9:00 and 17:00
# Generates: FUZZY:DAILY_BETWEEN:9:0:17:0 * * *
# Scatters within window: 09:00 to 17:00
daily between 22:00 and 02:00
# Generates: FUZZY:DAILY_BETWEEN:22:0:2:0 * * *
# Scatters within window: 22:00 to 02:00 (crossing midnight)
```
### 3.3 Weekly Schedules
[Section titled “3.3 Weekly Schedules”](#33-weekly-schedules)
#### 3.3.1 Basic Weekly Schedule
[Section titled “3.3.1 Basic Weekly Schedule”](#331-basic-weekly-schedule)
A basic weekly schedule expression SHALL take the form:
```yaml
weekly
```
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:WEEKLY * * *`
The execution SHALL be scattered across all seven days of the week and all hours/minutes of each day.
#### 3.3.2 Weekly with Day Specification
[Section titled “3.3.2 Weekly with Day Specification”](#332-weekly-with-day-specification)
A weekly day schedule expression SHALL take the form:
```yaml
weekly on
```
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:WEEKLY:DOW * * DOW`
**Example**:
```yaml
weekly on monday
# Generates: FUZZY:WEEKLY:1 * * 1
# Scatters across all hours on Monday
```
#### 3.3.3 Weekly with Time Constraints
[Section titled “3.3.3 Weekly with Time Constraints”](#333-weekly-with-time-constraints)
A weekly schedule MAY include time constraints using `around` or `between`:
```yaml
weekly on around
weekly on between and
```
**Example**:
```yaml
weekly on friday around 17:00
# Generates: FUZZY:WEEKLY:5:AROUND:17:0 * * 5
# Scatters Friday 16:00-18:00
```
### 3.4 Hourly Schedules
[Section titled “3.4 Hourly Schedules”](#34-hourly-schedules)
#### 3.4.1 Basic Hourly Schedule
[Section titled “3.4.1 Basic Hourly Schedule”](#341-basic-hourly-schedule)
A basic hourly schedule expression SHALL take the form:
```yaml
hourly
```
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:HOURLY * * *`
The minute offset SHALL be scattered across 0-59 minutes but remain consistent for each hour.
**Example**:
```yaml
hourly
# Generates: FUZZY:HOURLY * * *
# Might scatter to: 43 * * * * (runs at minute 43 every hour)
```
#### 3.4.2 Hour Interval Schedules
[Section titled “3.4.2 Hour Interval Schedules”](#342-hour-interval-schedules)
An hour interval schedule expression SHALL take the form:
```yaml
every h
every hours
every hour
```
Where `` MUST be a positive integer.
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:HOURLY: * * *`
Valid hour intervals SHOULD be: 1, 2, 3, 4, 6, 8, 12 (factors of 24 for even distribution).
**Example**:
```yaml
every 2h
# Generates: FUZZY:HOURLY:2 * * *
# Might scatter to: 53 */2 * * * (runs at minute 53 every 2 hours)
```
### 3.5 Special Period Schedules
[Section titled “3.5 Special Period Schedules”](#35-special-period-schedules)
#### 3.5.1 Bi-weekly Schedule
[Section titled “3.5.1 Bi-weekly Schedule”](#351-bi-weekly-schedule)
A bi-weekly schedule expression SHALL take the form:
```yaml
bi-weekly
```
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:BI-WEEKLY * * *`
The schedule SHALL execute once every 14 days with scattered time.
#### 3.5.2 Tri-weekly Schedule
[Section titled “3.5.2 Tri-weekly Schedule”](#352-tri-weekly-schedule)
A tri-weekly schedule expression SHALL take the form:
```yaml
tri-weekly
```
An implementation MUST generate a fuzzy cron placeholder: `FUZZY:TRI-WEEKLY * * *`
The schedule SHALL execute once every 21 days with scattered time.
### 3.6 Interval Schedules
[Section titled “3.6 Interval Schedules”](#36-interval-schedules)
An interval schedule expression SHALL take the form:
```yaml
every
```
Where:
* `` MUST be a positive integer
* `` MUST be one of: `minutes`, `minute`, `m`, `hours`, `hour`, `h`, `days`, `day`, `d`, `weeks`, `week`, `w`
An implementation MUST generate appropriate cron expressions based on the unit:
* Minutes: `*/N * * * *` (minimum N=5 per GitHub Actions constraint)
* Hours: `FUZZY:HOURLY:N * * *` (scattered minute)
* Days: `0 0 */N * *` (fixed midnight)
* Weeks: `0 0 */N*7 * *` (fixed Sunday midnight)
**Example**:
```yaml
every 5 minutes
# Generates: */5 * * * *
every 6h
# Generates: FUZZY:HOURLY:6 * * *
every 2 days
# Generates: 0 0 */2 * *
```
### 3.7 Error Norms for Invalid Schedule Expressions
[Section titled “3.7 Error Norms for Invalid Schedule Expressions”](#37-error-norms-for-invalid-schedule-expressions)
The following table specifies normative behavior (MUST/SHALL requirements) for malformed or unrecognizable fuzzy schedule expressions encountered during compilation. These norms apply at parse time (when the compiler processes the workflow frontmatter) and at test time (when the compliance test suite exercises the parser with invalid inputs).
| # | Error Condition | Input Example | MUST/SHALL Behavior | Error Code |
| ---- | ----------------------------------------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
| E-01 | Unknown schedule keyword (not one of `daily`, `weekly`, `hourly`, `bi-weekly`, `tri-weekly`, `every`) | `monthly` | Implementation MUST reject with a descriptive error naming the unrecognized keyword and listing valid keywords | `UNKNOWN_KEYWORD` |
| E-02 | Out-of-range hour in 24-hour format | `daily around 25:00` | Implementation MUST reject; the error message MUST state the valid hour range (0–23) and the offending value | `HOUR_OUT_OF_RANGE` |
| E-03 | Out-of-range minute | `daily around 14:65` | Implementation MUST reject; the error message MUST state the valid minute range (0–59) and the offending value | `MINUTE_OUT_OF_RANGE` |
| E-04 | `around` keyword with no time specification | `daily around` | Implementation MUST reject; the error message MUST include an example of correct `around` usage | `MISSING_TIME_SPEC` |
| E-05 | `between` keyword with only one time argument | `daily between 9:00` | Implementation MUST reject; the error message MUST state that `between` requires both a start and an end time connected by `and` | `INCOMPLETE_RANGE` |
| E-06 | `between` range where start equals end | `daily between 14:00 and 14:00` | Implementation MUST reject; a zero-duration window cannot scatter execution times | `ZERO_DURATION_RANGE` |
| E-07 | Unknown weekday in `weekly on ` | `weekly on mondey` | Implementation MUST reject with a did-you-mean suggestion when the input differs from a valid weekday by one character | `UNKNOWN_WEEKDAY` |
| E-08 | Invalid interval unit | `every 5 fortnights` | Implementation MUST reject; the error message MUST list valid units (`minutes`, `hours`, `days`, `weeks` and their abbreviations) | `UNKNOWN_UNIT` |
| E-09 | Interval value below minimum allowed by GitHub Actions | `every 2 minutes` | Implementation MUST reject; the error message MUST state the minimum permitted interval (5 minutes for the `minutes` unit) and the GitHub Actions constraint source | `INTERVAL_TOO_SMALL` |
| E-10 | Non-integer interval value | `every 1.5 hours` | Implementation MUST reject; fractional interval values are not supported | `NON_INTEGER_INTERVAL` |
**Normative notes**:
* All error messages MUST be directed to the user’s console (stderr) and MUST be human-readable.
* Implementations MUST NOT silently fall back to a default schedule when the input is invalid; all errors in rows E-01 through E-10 MUST cause compilation to fail with a non-zero exit code.
* Implementations SHOULD NOT attempt automatic correction of the schedule expression. Actionable correction guidance in the error message is preferred over silent fixup.
***
## 4. Time Specifications
[Section titled “4. Time Specifications”](#4-time-specifications)
### 4.1 Time Format Requirements
[Section titled “4.1 Time Format Requirements”](#41-time-format-requirements)
An implementation MUST support the following time formats:
#### 4.1.1 24-Hour Format
[Section titled “4.1.1 24-Hour Format”](#411-24-hour-format)
The 24-hour format SHALL use the pattern `HH:MM`:
* Hours MUST be in range 0-23
* Minutes MUST be in range 0-59
* Leading zeros MAY be omitted for hours
* Minutes MUST use two digits with leading zero if necessary
**Valid examples**: `00:00`, `9:30`, `14:00`, `23:59`
#### 4.1.2 12-Hour Format
[Section titled “4.1.2 12-Hour Format”](#412-12-hour-format)
The 12-hour format SHALL use the pattern `H[H]am` or `H[H]pm`:
* Hours MUST be in range 1-12
* AM/PM indicator MUST be lowercase `am` or `pm`
* Minutes MAY be omitted (defaults to :00)
* Colon and minutes MAY be included (e.g., `3:30pm`)
**Valid examples**: `1am`, `12pm`, `11pm`, `9am`, `3:30pm`
**Conversion rules**:
* `12am` converts to 00:00 (midnight)
* `12pm` converts to 12:00 (noon)
* `1am-11am` converts to 01:00-11:00
* `1pm-11pm` converts to 13:00-23:00
#### 4.1.3 Time Keywords
[Section titled “4.1.3 Time Keywords”](#413-time-keywords)
An implementation MUST support the following time keywords:
* `midnight`: Represents 00:00 (start of day)
* `noon`: Represents 12:00 (middle of day)
Keywords MUST be case-insensitive.
### 4.2 Time Range Requirements
[Section titled “4.2 Time Range Requirements”](#42-time-range-requirements)
#### 4.2.1 Window Specification
[Section titled “4.2.1 Window Specification”](#421-window-specification)
When using `around