A living book about agentic workflows, agent orchestration, and agentic scaffolding
This chapter explains how GH-AW compiles markdown into deterministic workflows that GitHub Actions can execute. It shows how to set up GH-AW with the supported setup actions, including both vendored and upstream approaches. Finally, it highlights the safety controls that make agentic workflows production-ready: permissions, safe outputs, and approval gates.
GitHub Agentic Workflows (GH-AW) (https://github.github.io/gh-aw/) turns natural language into automated repository agents that run inside GitHub Actions. Instead of writing large YAML pipelines by hand, you write markdown instructions that an AI agent executes with guardrails. The result is a workflow you can read like documentation but run like automation.
At a glance, GH-AW provides several key capabilities. Natural language workflows allow you to write markdown instructions that drive the agent’s behaviour, making automation readable to humans. Compile-time structure means your markdown is compiled into GitHub Actions workflows, ensuring reproducibility across runs. Security boundaries let you define permissions, tools, and safe outputs that constrain what the agent can and cannot do. Composable automation enables imports and shared components that you can reuse across repositories.
A GH-AW workflow is a markdown file with frontmatter and instructions:
---
on:
issues:
types: [opened]
permissions:
contents: read
tools:
edit:
github:
toolsets: [issues]
engine: copilot
---
# Triage this issue
Read issue #$ and summarize it.
Key parts:
The frontmatter section configures the workflow’s behaviour. The on field specifies GitHub Actions triggers such as issues, schedules, or dispatch events. The permissions field declares least-privilege access to GitHub APIs, ensuring the agent can only perform authorised operations. The tools field lists the capabilities your agent can invoke, such as edit, bash, web, or github. The engine field specifies the AI model or provider to use, such as Copilot, Claude Code, or Codex.
The markdown instructions section contains natural language steps for the agent to follow. You can include context variables from the event payload, such as issue number, PR number, or repository name, using template syntax.
GH-AW compiles markdown workflows into .lock.yml GitHub Actions workflows. The compiled file is what GitHub actually executes, but the markdown remains the authoritative source. This gives you readable automation with predictable execution.
Both the source markdown files and the compiled .lock.yml files live in the .github/workflows/ directory:
.github/workflows/
|-- triage.md # Source (human-editable)
|-- triage.lock.yml # Compiled (auto-generated, do not edit)
|-- docs-refresh.md
`-- docs-refresh.lock.yml
Use gh aw compile (from the GH-AW CLI at https://github.com/github/gh-aw) in your repository root to generate .lock.yml files from your markdown sources. Only edit the .md files; the .lock.yml files are regenerated on compile.
If you do not vendor the GH-AW actions/ directory in your repository, you can instead reference the upstream setup action directly (pin to a commit SHA for security):
- name: Setup GH-AW scripts
uses: github/gh-aw/actions/setup@5a4d651e3bd33de46b932d898c20c5619162332e
with:
destination: /opt/gh-aw/actions
There are three key behaviours to understand about the compilation model. First, frontmatter edits require recompile—any changes to triggers, permissions, tools, or engine settings must be followed by running gh aw compile to regenerate the lock file. Second, markdown instruction updates can often be edited directly because the runtime loads the markdown body at execution time; however, structural changes may still require recompilation. Third, shared components can be stored as markdown files without an on: trigger; these are imported rather than compiled, allowing reuse without duplication.
GH-AW compilation is predictable, but a few pitfalls are common in real repositories.
Only compile workflow markdown. The compiler expects frontmatter with an on: trigger. Non-workflow files like AGENTS.md or general docs should not be passed to gh aw compile. Use gh aw compile <workflow-id> to target specific workflows when the directory includes other markdown files.
Strict mode rejects direct write permissions. GH-AW runs in strict mode by default; you can opt out by adding strict: false to the workflow frontmatter, but the recommended path is to keep strict mode on. Workflows that request issues: write, pull-requests: write, or contents: write will fail validation in strict mode. Use read-only permissions plus safe-outputs for labels, comments, and PR creation instead.
GH-AW compilation is mostly a structural translation: frontmatter becomes the workflow header, the markdown body is packaged as a script or prompt payload, and imports are inlined or referenced. The compiled .lock.yml is the contract GitHub Actions executes. The examples below show how a markdown workflow turns into a compiled job.
Source markdown (.github/workflows/triage.md)
---
on:
issues:
types: [opened]
permissions:
contents: read
issues: read
tools:
github:
toolsets: [issues]
safe-outputs:
add-comment:
max: 1
add-labels:
allowed: [needs-triage, needs-owner]
max: 2
engine: copilot
---
# Triage this issue
Read issue #$ and summarize it.
Then suggest labels: needs-triage and needs-owner.
Compiled workflow (.github/workflows/triage.lock.yml)
name: GH-AW triage
on:
issues:
types: [opened]
permissions:
contents: read
issues: read
jobs:
agent:
runs-on: ubuntu-latest
steps:
- name: Checkout actions folder
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
sparse-checkout: |
actions
persist-credentials: false
- name: Setup GH-AW scripts
uses: ./actions/setup
with:
destination: /opt/gh-aw/actions
- name: Run GH-AW agent (generated)
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
// Generated execution script omitted for brevity.
What changed during compilation
on, permissions, jobs).safe-outputs declarations were compiled into guarded output steps.Component (.github/workflows/shared/common-tools.md)
---
tools:
bash:
edit:
engine: copilot
---
Workflow using an import (.github/workflows/docs-refresh.md)
---
on:
workflow_dispatch:
permissions:
contents: read
imports:
- shared/common-tools.md
safe-outputs:
create-pull-request:
max: 1
---
# Refresh docs
Update the changelog with the latest release notes.
Compiled workflow (.github/workflows/docs-refresh.lock.yml)
name: GH-AW docs refresh
on:
workflow_dispatch:
permissions:
contents: read
jobs:
agent:
runs-on: ubuntu-latest
steps:
- name: Checkout actions folder
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
sparse-checkout: |
actions
persist-credentials: false
- name: Setup GH-AW scripts
uses: ./actions/setup
with:
destination: /opt/gh-aw/actions
- name: Run GH-AW agent (generated)
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
// Generated execution script omitted for brevity.
What changed during compilation
imports were resolved and merged with the workflow frontmatter.tools and engine were applied to the final workflow.on: are compiled; components remain markdown-only.safe-outputs to stage changes safely.Source markdown (.github/workflows/release-notes.md)
---
on:
workflow_dispatch:
permissions:
contents: read
tools:
edit:
safe-outputs:
create-pull-request:
max: 1
engine: copilot
---
# Draft release notes
Summarize commits since the last tag and propose a PR with the notes.
Compiled workflow (.github/workflows/release-notes.lock.yml)
name: GH-AW release notes
on:
workflow_dispatch:
permissions:
contents: read
jobs:
agent:
runs-on: ubuntu-latest
steps:
- name: Checkout actions folder
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
sparse-checkout: |
actions
persist-credentials: false
- name: Setup GH-AW scripts
uses: ./actions/setup
with:
destination: /opt/gh-aw/actions
- name: Run GH-AW agent (generated)
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
// Generated execution script omitted for brevity.
What changed during compilation
safe-outputs was translated into the generated safe-output scripts invoked by the job.GH-AW workflows are designed for safety by default. Agents run with minimal access and must declare tools explicitly.
Warning: Treat CI secrets and tokens as production credentials. Use least-privilege permissions, require human approval for write actions, and keep all agent actions auditable.
Tools are capabilities the agent can use. The edit tool allows the agent to modify files in the workspace. The bash tool runs shell commands, with safe commands enabled by default. The web-fetch and web-search tools allow the agent to fetch or search web content. The github tool operates on issues, pull requests, discussions, and projects. The playwright tool provides browser automation for UI checks.
Write actions (creating issues, comments, commits) can be routed through safe outputs to sanitize what the agent writes. This keeps the core job read-only and limits accidental changes.
In strict mode, safe outputs are required for write operations. Declare them in frontmatter to specify what the agent can produce:
safe-outputs:
add-comment:
max: 1
add-labels:
allowed: [needs-triage, needs-owner]
max: 2
create-pull-request:
max: 1
The agent generates structured output that downstream steps apply, keeping repository writes explicit and auditable.
max caps how many outputs of a given type are accepted; extra outputs are rejected by the safe-output validator.
When using add-labels, keep the allowed list in sync with labels that already exist in the repository; missing labels cause runtime output failures when the safe-output job applies them.
You can define safe inputs to structure what the agent receives. This is a good place to validate schema-like data for tools or commands.
For terminology and trust-model definitions, see Discovery and Imports. This section focuses only on GH-AW-specific syntax and composition patterns.
GH-AW supports imports in two ways:
imports:
- shared/common-tools.md
- shared/research-library.md
{{#import shared/common-tools.md}}
In GH-AW, these imports are typically workflow-fragment artefacts: shared prompts, tool declarations, and policy snippets. Keep reusable fragments in files without on: so they can be imported as components rather than compiled as standalone workflows.
GH-AW documents a ResearchPlanAssign strategy: a scaffolded loop that keeps humans in control while delegating research and execution to agents.
Phase 1: Research. A scheduled agent scans the repository or ecosystem for updates such as new libraries, frameworks, or scaffolds. It produces a report in an issue or discussion, summarising findings and flagging items that may warrant attention.
Phase 2: Plan. Maintainers review the report and decide whether to proceed. If approved, a planning agent drafts the implementation steps, breaking the work into discrete tasks that can be assigned and tracked.
Phase 3: Assign and Implement. Agents are assigned to implement the approved changes. Updates are validated through tests and reviews, committed to the repository, and published to the appropriate outputs.
This pattern maps well to this book: use scheduled research to discover new agentic tooling, post a proposal issue, build consensus, then update the chapters and blog.
Here is how GH-AW can drive the book’s maintenance through a four-stage cycle.
Research (scheduled). The system uses web-search tooling to scan for new agentic workflow libraries and related developments. It produces a structured report in a GitHub issue, documenting what was found and why it may be relevant.
Consensus (issues/discussions). The community collects votes or comments to accept or reject the proposal. Maintainers label outcomes with tags like accepted, needs-revision, or rejected to track decisions.
Implementation (assigned agent). An agent updates or adds chapters as needed, refreshes the table of contents and homepage, and adds a blog post summarising the update. All changes go through the normal pull request review process.
Publish (automation). Pages and PDF outputs rebuild automatically after merge, ensuring the public site stays current without manual intervention.
This approach keeps the book aligned with the latest GH-AW practices while maintaining a transparent, auditable workflow.
GH-AW turns markdown instructions into reproducible GitHub Actions workflows, combining the readability of documentation with the reliability of automation. Frontmatter defines triggers, permissions, tools, and models, giving you fine-grained control over what the agent can do. Imports enable composable, reusable workflow building blocks that reduce duplication across repositories. Safe inputs and outputs combined with least-privilege permissions reduce the risk of unintended changes. The ResearchPlanAssign pattern provides a practical loop for continuous, agent-powered improvement with human oversight at key decision points.