4.1.1. Reading Triggers and Effects from Config and Logs
💡 First Principle: Given a workflow file and a run, you should be able to work in both directions — predict from on: which events will produce runs, and infer from a run's metadata which subscription produced it — because most "unexpected behavior" questions are really mismatches between those two views.
Start with the run's own testimony. The header states the event name (push, pull_request, schedule, workflow_dispatch, workflow_run, repository_dispatch), the actor who triggered it, and the ref and SHA it's running against. Inside the workflow, the same facts are available as github.event_name, github.actor, github.ref, github.head_ref (PRs only), and the full payload at github.event.*. When a question asks "how would you conditionally deploy only on tag pushes," those context values are the answer: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/').
The most common surprises have crisp explanations. A workflow ran twice on one action usually means two subscriptions overlapped — for example push and pull_request both firing for a branch that has an open PR; the fix is a branches-ignore filter or a concurrency group. A workflow didn't run at all is nearly always a filter (branch, path, or activity type), a workflow file that isn't on the default branch for schedule/workflow_dispatch, a disabled workflow, or a fork PR awaiting maintainer approval. A workflow ran against unexpected code points at pull_request using the merge commit rather than the head, or a scheduled run using the default branch.
For PR runs specifically, distinguish github.ref (for pull_request, the merge ref refs/pull/N/merge) from github.head_ref (the source branch name) and github.base_ref (the target). Scenario questions about branch-name-derived logic — environment naming, image tagging — hinge on picking the right one.
⚠️ Exam Trap: For pull_request events, github.ref is not the PR's source branch — it's the merge ref. Using ${{ github.ref_name }} to build an image tag on a PR produces something like 42/merge, not feature-login. Use github.head_ref for the source branch.
Reflection Question: A single git push to a branch with an open PR produced two runs of the same workflow. Explain the mechanism, then give two different fixes — one that changes subscriptions and one that changes concurrency.