By the end of this chapter you can choose the right on: events so the Repo Assistant runs at exactly the right moments — and no others. You'll know the everyday triggers (issues, pull requests, comments, schedules, manual runs), the safe defaults gh-aw applies, and the cost-and-security guardrails that come attached to when a workflow fires.
Everything targets gh aw v0.81.6. We keep evolving the same Repo Assistant from Chapter 3 — this time teaching it to wake up both on a new issue and on a nightly sweep.
In Chapter 1 we framed gh-aw as automation for the repository's outer loop — the judgment work that happens around the edges of writing code. But an outer-loop teammate is only useful if it shows up at the right time. A triager who reads issues a week late is worse than none. The trigger is the workflow's clock: it decides the precise moment the agent is worth spending money and attention on.
Two shapes of “the right moment”
Almost every useful trigger is one of two shapes:
Reactive — something happened, respond now. An issue was opened, a PR was pushed, someone left a comment. The event carries a payload (the issue, the PR) that is the work.
Proactive — on a rhythm, go look for work. A nightly sweep for stale issues, a weekly docs audit. Nothing “happened”; the schedule itself is the prompt.
Great agentic teammates use both. A human maintainer answers issues as they arrive and does a Friday-afternoon cleanup; the Repo Assistant should too. The rest of this chapter is about expressing those two shapes precisely — and about the fact that choosing a trigger is also a security and cost decision, because it decides who and what can make your agent run.
Triggers live in the on: block of the frontmatter. gh-aw “supports all standard GitHub Actions triggers plus additional enhancements for reactions, cost control, and advanced filtering” (Triggers). The simplest form is pure Actions syntax:
The minimal reactive trigger — run when an issue is opened
on:
issues:
types: [opened]
The everyday events
You'll reach for a small set of triggers constantly. Each one hands the agent a different payload to reason about:
Trigger
Fires when…
Typical use
issues:
an issue is opened, edited, labeled, closed…
triage, auto-response
pull_request:
a PR is opened, synchronized, labeled…
review, CI-doctor
issue_comment:
someone comments on an issue or PR
ChatOps, follow-ups
schedule:
a recurring time arrives
sweeps, audits, reports
workflow_dispatch:
you run it manually (UI, API, or gh aw run)
testing, on-demand tasks
workflow_run:
another workflow (e.g. CI) completes
react to build failures
When a pull_request or comment event fires, “the coding agent has access to both the PR branch and the default branch” (Triggers) — the context it needs to actually review the change.
Human-friendly schedules
For proactive work, gh-aw improves on raw cron. You can write “human-friendly expressions” that compile to cron, and even use fuzzy scheduling, which “scatter[s] execution times to avoid load spikes” (Schedule Syntax):
Three ways to say “roughly every day”
on:
schedule: daily # compiler picks a scattered time
# schedule: daily around 14:00 # ±1 hour around 2pm UTC
# schedule: daily between 9:00 and 17:00 # scattered within business hours
# schedule:
# - cron: "30 6 * * 1" # or exact cron: Monday 06:30 UTC
The compiler “assigns each workflow a unique, deterministic execution time based on the file path, ensuring load distribution and consistency across recompiles” (Schedule Syntax). If a hundred repos all say daily, they won't all stampede at midnight.
Shorthands: the one-line trigger
Many triggers have a natural-language shorthand string that “expands into standard GitHub Actions trigger syntax and automatically includes workflow_dispatch” so you can always run the workflow by hand (Triggers):
Shorthands that read like English
on: issue opened # issues: [opened]
on: issue labeled bug # issues labeled "bug" only
on: pull_request opened affecting docs/** # PR touching docs paths
on: push to main # push to a branch
on: daily # a fuzzy daily schedule
Feedback and cost controls attached to the trigger
Two enhancements ride along in the same on: block and matter for every workflow you ship:
reaction: adds an emoji to the triggering item so a human sees the agent noticed — "eyes" when it starts, for instance. “The reaction is added to the triggering item” (Triggers).
stop-after: “automatically disable[s] workflow triggering after a deadline to control costs” — e.g. stop-after: "+30d". “Recompiling the workflow resets the stop time” (Triggers). It's a seatbelt for scheduled jobs that would otherwise run forever.
Choosing a trigger is mostly about matching the two shapes from the concept — but a few defaults exist specifically to stop a trigger from becoming an attack vector. These are the parts a reviewer should always check.
Safe defaults you get for free
Forks are blocked by default. “Pull request workflows block forks by default for security” — you opt specific forks in with the forks: field (Triggers). This is the front line against a malicious PR trying to run your agent with your secrets.
Who can trigger is an allowlist. Unsafe triggers (push, issues, pull_request) “automatically enforce permission checks.” The roles: filter defaults to [admin, maintainer, write], and “failed checks cancel the workflow with a warning” (Triggers). A drive-by issue from a stranger won't spend your credits unless you widen the allowlist.
workflow_run is hardened. The compiler “injects repository ID and fork checks to reject cross-repository or fork-triggered runs,” and warns (or errors in strict mode) if you don't scope branches: (Triggers).
A quick decision guide
You want to…
Reach for
respond to each new issue/PR
issues: / pull_request: with types:
do periodic maintenance
schedule: (prefer fuzzy daily/weekly)
let humans invoke on demand
workflow_dispatch:
answer a /command in a comment
slash_command:
react to CI results
workflow_run: with branches:
trigger from an external system (Jira, PagerDuty)
repository_dispatch:
When not to
Don't trigger on high-frequency events without a filter.on: push to a busy repo, or issue_comment on every comment, can fire constantly — each run costs AI credits. Filter by label (names:), path (affecting), or a slash_command so the agent runs only when it's genuinely wanted.
Don't open the fork gate casually.forks: ["*"] lets any fork trigger your workflow; use the narrowest pattern that meets the need, and pair it with the security model in Chapter 7.
Don't leave a scheduled workflow uncapped. A nightly job with no stop-after: and no budget will run indefinitely. Cost controls belong on the trigger, and we return to budgets in Chapter 13.
Let's give the Repo Assistant both shapes at once: it triages each new issue reactively and runs a nightly stale-issue sweep proactively. The whole thing is one file, and it compiles cleanly under strict mode with no engine key.
examples/ch04/repo-assistant-triggers.md — two triggers, one assistant (compiles: 0 errors, 0 warnings)
Read the on: block as the assistant's clock. It wakes up three ways — a new/reopened issue, a fuzzy daily schedule, or a manual workflow_dispatch — drops an :eyes: reaction on whatever triggered it, and stops firing 30 days after compilation unless you recompile. Everything else is the safe posture from earlier chapters: read-only permissions:, the default Copilot engine, curated network:, and writes routed through safe-outputs: (the subject of Chapter 6).
Because the same file now handles two kinds of run, the Markdown body branches on github.event_name:
The body decides its job from which trigger fired
# Repo Assistant — triage on open, sweep on a schedule
Check `${{ github.event_name }}` first.
## If an issue was just opened or reopened (`issues`)
Read the triggering issue, post one triage comment, apply the best label.
## If this is the daily schedule (`schedule`) or a manual run
No single issue triggered this run — do a **daily sweep**: find open issues with
no activity in 30 days and, for the clearly abandoned ones, add `stale` and a
gentle comment. Be conservative: when in doubt, leave the issue alone.
Compile it exactly as before — offline, no secrets:
You can now make a workflow wake up at exactly the right moments:
Triggers are the outer loop's clock, and they come in two shapes: reactive (issues, PRs, comments) and proactive (schedules).
The on: block is standard Actions syntax plus gh-aw enhancements: human-friendly and fuzzy schedules, one-line shorthands, reaction: feedback, and stop-after: cost control.
Choosing a trigger is a security and cost decision. Forks are blocked by default, roles: gates who can trigger, and workflow_run is hardened against cross-repo abuse — safe by default, widened deliberately.
Filter high-frequency events and cap scheduled ones, so the agent runs only when it's worth it.
What's next. The assistant now wakes at the right time — but which brain does it think with? In Chapter 5: Engines, we choose and configure the engine (Copilot, Claude, Codex, or Gemini) and see the portability that engine-neutral design buys you.