Triggers and Events — Making Agents React

Lesson 4 · Safe Agentic Workflows · ~8 minutes

Until now, every workflow we've discussed is manually invoked — you type a command and the agent runs. That's powerful, but it's only half the picture. The real shift happens when agents react to events without you asking.

This lesson covers the on: frontmatter section — the declarative syntax that tells a workflow when to activate. If you've used GitHub Actions, this will feel familiar. The difference: instead of triggering a CI pipeline, you're triggering an AI agent.

The on: Section

Every agentic workflow file has YAML frontmatter at the top. The on: key declares the trigger — what event or condition causes the workflow to fire:

---
on:
  issues:
    types: [opened]

safe-outputs:
  add-comment:
---

# Auto-Triage New Issues

Read the issue title and body. Assign appropriate labels based on content.
Add a comment acknowledging receipt and estimated triage time.

This workflow fires every time a new issue is opened. No human invocation needed. The agent reads, labels, and responds — all from a single trigger declaration.

Four Types of Triggers

Triggers fall into four categories. Most workflows use one; complex workflows combine several.

Event Event Triggers — React to Repository Activity

Fire when something happens in your repository: an issue opens, a PR is created, a comment is posted, a label is applied.

These are the workhorses of agentic automation. They turn your agent into a team member that monitors and responds.

Schedule Schedule Triggers — Run on a Timer

Fire at regular intervals: daily, weekly, every 30 minutes, or at a specific cron time. Use for maintenance tasks, reports, and periodic sweeps.

Supports both standard cron syntax and human-friendly "fuzzy" expressions.

Manual Manual Triggers — On-Demand with Inputs

Fire when explicitly invoked via the GitHub UI, CLI (gh aw run), or API. Can accept typed input parameters (strings, booleans, dropdowns).

Use for workflows that need human judgment about when or what to run on.

Command Command Triggers — Slash Commands in Comments

Fire when someone types /command-name in an issue or PR comment. This is the ChatOps pattern — triggering agent work through conversation.

Enables team members to invoke complex workflows without leaving their issue thread.

Event Triggers in Detail

Event triggers use standard GitHub webhook event names with optional types: filtering:

# React to new and edited issues
on:
  issues:
    types: [opened, edited, labeled]

# React to PR creation and new commits
on:
  pull_request:
    types: [opened, synchronize]

# React to comments (ChatOps pattern)
on:
  issue_comment:
    types: [created]

# React to new discussions
on:
  discussion:
    types: [created]

The types: array lets you be precise. Without it, the workflow fires on every event type — which is usually too broad. Always specify types.

Note on issue_comment

GitHub fires issue_comment events for comments on both issues and pull requests. When triggered by a PR comment, the agent gets access to the PR branch. This is what makes ChatOps patterns work for code review.

Schedule Triggers

Schedule triggers support two syntaxes: human-friendly fuzzy expressions and standard cron.

Fuzzy Syntax (Recommended)

Fuzzy schedules are easier to read and automatically scatter execution times to avoid load spikes:

# Simple intervals
on:
  schedule: daily
  schedule: weekly on monday
  schedule: hourly
  schedule: every 30 minutes

# With time preferences
on:
  schedule: daily around 14:00              # ±1 hour around 2pm UTC
  schedule: daily between 9:00 and 17:00    # Scattered within business hours
  schedule: weekly on friday around 5pm

# With timezone offsets
on:
  schedule: daily between 9am and 5pm utc-5  # Eastern time business hours

The compiler converts these to deterministic cron expressions based on the workflow's file path — so the same workflow always runs at the same time, but different workflows scatter naturally.

Standard Cron

For exact timing, use cron directly:

on:
  schedule:
    - cron: "0 9 * * 1-5"      # 9:00 UTC weekdays
    - cron: "30 6 * * 1"       # Monday at 06:30 UTC
    - cron: "0 9 15 * *"       # 15th of month at 09:00 UTC
      timezone: "America/New_York"  # IANA timezone override

Manual Triggers (workflow_dispatch)

Manual triggers let you define input parameters the user provides when invoking the workflow:

on:
  workflow_dispatch:
    inputs:
      topic:
        description: 'Research topic'
        required: true
        type: string
      priority:
        description: 'Task priority'
        required: false
        type: choice
        options:
          - low
          - medium
          - high
        default: medium

Access inputs in your prompt with template syntax: ${{ github.event.inputs.topic }}

This is the bridge between "fully manual" and "fully automatic." You decide when to run it, but the workflow handles the how.

Command Triggers (slash_command)

Command triggers turn issue comments into agent invocations:

on:
  slash_command:
    name: investigate
    events: [issues, issue_comment]

Now, when anyone types /investigate in an issue comment, the workflow fires with the full issue context. This is the ChatOps pattern — your team interacts with the agent through normal GitHub conversations.

Use cases:

Combining Triggers

A single workflow can respond to multiple triggers. This is common — you want something to run both automatically and on-demand:

on:
  # Fire automatically on new issues
  issues:
    types: [opened]
  # Also allow manual invocation for testing
  workflow_dispatch:
  # And fire on a schedule for catch-up
  schedule: daily around 9:00

The workflow runs identically regardless of which trigger fired. This makes testing easy — use workflow_dispatch during development, then let the event triggers handle production.

Common Trigger Patterns

Pattern Trigger Config What It Enables
Auto-triage issues: [opened] Label, assign, and acknowledge new issues instantly
PR review pull_request: [opened, synchronize] Automated code review on every push
ChatOps slash_command: name: fix Team invokes agent via /fix in comments
Daily report schedule: daily around 9:00 Stale PR sweep, metrics digest, dependency check
CI follow-up workflow_run: [completed] Agent analyzes failures after CI finishes
Label-as-command label_command: deploy Apply "deploy" label → agent deploys, removes label
External webhook repository_dispatch: [jira-created] Jira/PagerDuty/Slack triggers agent work
On-demand + auto issues: [opened] + workflow_dispatch: Automatic in production, manual for testing

Practical Example: Auto-Triage New Issues

Let's build a complete auto-triage workflow. This is one of the most impactful first automations — it gives every issue reporter instant acknowledgment and applies initial labels:

---
on:
  issues:
    types: [opened]
  reaction: "eyes"

safe-outputs:
  add-comment:
  add-labels:

permissions:
  issues: write
---

# Auto-Triage New Issues

You are a triage agent for this repository.

## Context

Read the issue: "${{ github.event.issue.title }}"

Body:
${{ github.event.issue.body }}

## Task

1. Analyze the issue content and determine:
   - Is this a bug report, feature request, question, or documentation issue?
   - What area of the codebase does it relate to?
   - What priority level seems appropriate (critical, high, medium, low)?

2. Apply labels:
   - One type label: `bug`, `enhancement`, `question`, or `documentation`
   - One area label if identifiable: `area/api`, `area/ui`, `area/infra`, etc.
   - One priority label: `priority/critical`, `priority/high`, `priority/medium`, `priority/low`

3. Add a comment:
   - Acknowledge the issue
   - Summarize your understanding
   - Note any missing information needed for triage
   - Be friendly and concise

This workflow activates the moment an issue is created. The reaction: "eyes" immediately adds an 👀 emoji so the reporter knows something is happening. Within seconds, the issue has labels and a helpful comment.

Connection to Your Work

Think about your existing /comply workflow. Right now, it's Layer 2 — a manually invoked command. You type it, it runs. But what if it fired automatically?

Today (Manual)
---
on:
  workflow_dispatch:
---

# Compliance Check
Run compliance checks...
Tomorrow (Automatic)
---
on:
  pull_request:
    types: [opened, synchronize]
  workflow_dispatch:
---

# Compliance Check
Run compliance checks...

One line changes the trigger from "when I ask" to "when any PR is opened or updated." The workflow body stays identical. The agent's behavior doesn't change — only when it activates. This is the power of declarative triggers: you graduate workflows from manual to automatic by editing frontmatter, not rewriting logic.

The progression

Manual → Event-driven → Scheduled → Combined. Start with workflow_dispatch to build and test. Add event triggers once you trust the output. Add schedule triggers for catch-up sweeps. This is how you safely increase autonomy.

Trigger Shorthands

For common patterns, the gh-aw compiler accepts natural-language shorthand instead of full YAML:

# These are equivalent:
on: issue opened
on:
  issues:
    types: [opened]

# More shorthand examples:
on: pull_request opened
on: issue labeled bug
on: manual
on: manual with input version
on: comment created
on: "deployment failed"

Shorthands are useful for simple, single-trigger workflows. Use full YAML when combining triggers or adding filtering options.

Check Your Understanding

A new issue is created in your repository. Which trigger configuration would fire?
Correct — a new issue fires the issues event with type opened. The edited type fires when the issue body or title is changed later. issue_comment is for comments, not issue creation.
Not quite. Creating a new issue fires the issues event with type opened. The edited type fires on title/body changes. issue_comment fires when a comment is posted. pull_request is for PRs, not issues.
You want a workflow to run at 9:00 AM on weekdays (UTC), with some flexibility in exact timing. Which schedule syntax is most appropriate?
Right — fuzzy syntax like daily between 9:00 and 10:00 scatters execution within that window and is the recommended approach when you want "roughly around 9 AM" without load-spike concerns. Combine with an if: weekday condition or use cron: "0 9 * * 1-5" for exact-time weekday-only execution.
The cron syntax "0 9 * * 1-5" would give you exact 9:00 UTC weekdays — but the question asked for flexibility. Fuzzy syntax like daily between 9:00 and 10:00 scatters within the window, which is the "flexible timing" approach. every 24 hours runs every day including weekends. weekly on monday only runs once a week.
What do command triggers (slash_command:) enable that event triggers alone cannot?
Exactly — command triggers enable the ChatOps pattern. Team members type /command-name in a comment and the agent activates in that conversational context. This is uniquely interactive: the team works with the agent through normal GitHub conversation.
Command triggers specifically enable ChatOps — invoking agent workflows by typing /command-name in issue or PR comments. Schedule triggers handle timers. workflow_dispatch handles typed inputs. repository_dispatch handles external system integration.

What's Next

You now understand the four trigger types and how to declare them. The next lesson covers safe outputs — controlling what an event-triggered agent is allowed to do. Because once an agent activates autonomously, the guardrails matter more than ever.

Primary Source

Frontmatter (Triggers) — GitHub Agentic Workflows reference. Complete syntax for all trigger types, filtering options, and advanced features like lock-for-agent and skip-if-match.

Questions? Ask me about any trigger pattern — I can help you figure out the right on: configuration for a specific workflow you're imagining, or explain how triggers interact with the safety layers from Lesson 1.
← Back Next →