
I Asked an AI Agent to Fix GitHub Actions—and Stepped Into a Chain of Traps
Table of Contents
Over the past two days, I asked an AI agent to help me modify a GitHub Actions workflow. The goal sounded simple:
- do not deploy during the PR stage
- only run build and deploy after the PR is merged into
main
It sounded like a five-minute task. Instead, it turned into a neat collection of very typical mistakes. Which makes it a pretty good blog post.

The first misunderstanding: treating “merge into main” as a separate event
The easiest mistake was to think of “PR merged into main” as a standalone GitHub Actions event parallel to push.
But in GitHub’s actual event model:
A PR merge into
mainis, in essence, apushtomain.
So if your requirement is:
- nothing should run on the PR
- build and deploy should happen only after merge into
main
then the simplest and most stable workflow trigger is actually:
on:
push:
branches: ["main"]
That is not “going back to an old-school setup.” It is simply how GitHub models the event.
The second trap: trying to deploy GitHub Pages directly from a PR event
The clearest error message I hit was this:
refs/pull/47/merge is not allowed to deploy to github-pages due to environment protection rules
The core issue is:
- the ref running in a PR is usually not
main - it is something like
refs/pull/47/merge - the
github-pagesdeployment environment has protection rules against that ref
In other words:
You can do build validation on a PR, but you should not run GitHub Pages deploy directly on a PR ref.
That is a platform rule, not something you can fully outsmart with two extra if lines in YAML.
The third trap: overcomplicating the trigger chain
To get around the restriction, I tried a few “clever” patterns:
pull_request+types: [closed]workflow_run- mixing PR validation and deploy branching logic in a single workflow
These approaches are not always invalid, but they introduce a few problems:
- they are harder to read
- they make it easier to misunderstand the real trigger path
- they make it easier for both the human and the AI to lose track of what is actually supposed to happen
This gets worse when an AI agent is making multiple iterative edits. You can easily end up in a state where each local fix seems reasonable, but the overall workflow becomes more and more confusing.
The stable solution
In the end, the most reliable answer was also the plainest one:
on:
push:
branches: ["main"]
Then let the workflow do all of the following in one main-branch push:
- build
- upload artifact
- deploy pages
If your repo governance requires every change to go through a PR merge before reaching main, then combine that with branch protection:
- block direct pushes to
main - require pull requests
- require review / status checks
With that setup, the semantics become:
Build and deploy only when a PR is merged into main.
One thing this made even clearer to me
Using an AI agent to edit CI/CD config can absolutely speed things up. But it works best when the task is:
- repetitive
- narrow in scope
- governed by clear rules
Once the task involves:
- platform event models
- permission boundaries
- changing requirements over multiple rounds
- process constraints like “don’t open a new PR”, “rebase main first”, or “reuse the current branch”
then it becomes very easy to fall into this pattern:
Every single AI step sounds reasonable, but the overall result drifts away from your real intent.
That is not uniquely an AI problem. Humans do the same thing in complex workflows. The difference is just that AI can repeat the wrong move much faster.
My takeaway
After all this, I’m keeping three rules in mind:
- Understand the event model before editing the workflow.
- Do not deploy GitHub Pages from a PR ref.
- When using AI to modify process configuration, define the constraints clearly and sync the latest
mainbefore every round.
A lot of the time, the real time-saver is not “letting AI start immediately.” It is making the boundary and the goal explicit first.
Otherwise the result looks like this:
- lots of code changes
- multiple PRs
- many CI runs
- but the original problem is still sitting there
So this post is basically a field note from today’s mistakes.