Story
Building-a-house world — same site and crew as every Phase 0 lesson.
In lesson 0.6 you met the automatic inspection rig — the machine that springs to life the moment work is dropped at the approval desk, runs every check, and lights up green or red.
But the rig isn't magic. It doesn't decide on its own when to run, what to do, or what it's allowed to touch. Someone taped a checklist card to its side. That card says:
"WHEN work is submitted at the approval desk → DO these steps → you may ONLY touch these tools."
Read the card top to bottom and you know everything: when the rig fires, what it does, and what access it has. That checklist card is a workflow YAML file.
YAML basics — three rules
YAML ("YAML Ain't Markup Language") is the plain-text format GitHub uses for workflow files. Three rules cover almost everything:
key: value— a setting and its value on one line.- Indentation = nesting. Two spaces (never tabs) show that something belongs inside something else.
- A list item starts with a dash:
- item.
That's it. The rest is just knowing which keys the workflow card uses.
The shape of a workflow — read this file
Every GitHub Actions workflow lives in .github/workflows/ inside your repo.
Read the comments in this example — they map each key to the checklist-card analogy:
name: CI # the label on the card (shows in the Actions tab)
on: # WHEN does the rig fire?
pull_request: # → when a PR opens or gets a new push
push:
branches: [ main ] # → when someone pushes to main
permissions: # what is the rig ALLOWED to touch?
contents: read # → can read code, but can't push or delete anything
jobs: # the WORK — one or more independent jobs
test: # this job is called "test" (you name it)
runs-on: ubuntu-latest # run on a fresh Linux machine
steps: # the ordered checklist of steps
- uses: actions/checkout@v4 # pull in someone else's action: grab the code
- name: Run tests # a step with a label
run: npm test # your own shell command
Key-by-key: what each line means
These are the keys you must recognize on the exam — with their official one-line meanings:
| Key | Where | What it does |
|---|---|---|
name |
top | "The name of the workflow. GitHub displays the names of your workflows under your repository's 'Actions' tab." |
on |
top | "Define which events can cause the workflow to run." |
permissions |
top or job | "Modify the default permissions granted to the GITHUB_TOKEN, adding or removing access as required, so that you only allow the minimum required access." |
env |
top or job | "A map of variables that are available to the steps of all jobs in the workflow." |
jobs |
top | "A workflow run is made up of one or more jobs, which run in parallel by default." |
runs-on |
job | Specifies the runner — the machine type where the job executes. |
steps |
job | The ordered list of tasks inside a job. |
uses |
step | References a pre-built reusable action (someone else wrote it). |
run |
step | "Executes a command-line program using the operating system's shell." |
with |
step | Passes input parameters into a uses action. |
source: docs.github.com — Workflow syntax for GitHub Actions · fetched 2026-05-29
uses vs run — one-look closure
These two step types look similar. They're not:
uses | run | |
|---|---|---|
| What it is | Someone else's pre-built action | Your own shell command |
| Example | uses: actions/checkout@v4 |
run: npm test |
| The catch | Third-party code — pin to a version/SHA you trust, or an update can silently change your workflow | Your code, so you control it |
| Think of it as | Calling in a specialist trade (electrician's standard process) | Writing your own checklist step |
One sentence: uses = borrow someone else's action and pin the version; run = write your own shell command.
The permissions key — least privilege, on the card
The rig automatically gets a temporary key called GITHUB_TOKEN
(referenced in workflows as ${{ secrets.GITHUB_TOKEN }}).
By default it has broad read and write access to the repo — more than most workflows need.
The permissions key narrows it down. The docs say:
"you should always grant the GITHUB_TOKEN the least required access."
permissions:
contents: read # can read code; cannot push, create releases, or delete anything
Why does this matter? If something goes wrong in a workflow — a bug, a hijacked third-party action — a narrowly scoped token limits the blast radius. The rig can't push malicious code to the repo if it was only given read access.
Least privilege is a phrase the exam returns to repeatedly. When you see
permissions: contents: read, that's least privilege in practice.
source: docs.github.com — Automatic token authentication · fetched 2026-05-29
Why this matters for GH-600
Workflow YAML → Exam: HIGH · Day-to-day: HIGH. Both axes are high — so we go deep enough to recognize the whole card, not write it from scratch.
-
ondecides when automation (and agent gating) fires.on: pull_request= "run every time a PR opens or updates" — the check that guards the agent's proposed work before it can merge. Big for GH-600. -
permissionsis the safety guardrail baked into the card. The exam will show you a workflow and ask what it's allowed to do — read thepermissionsblock first. Big for GH-600. -
usesvsrun—usesintroduces a supply-chain dependency (third-party code). Pin to a trusted version or commit SHA.runis your command, your control.
You won't write workflows from scratch on the exam. You will be shown one and asked:
"What triggers this?" or "What is this workflow allowed to do?" —
look at on and permissions. Those two blocks answer both questions.
Check-in — three quick questions
Open-ended. Think first, then peek at the suggested answer.
-
Look at this snippet — what triggers this workflow, and what is it allowed to do?
on: push: branches: [ main ] permissions: contents: read pull-requests: writeSuggested answer
It triggers whenever someone pushes a commit to the
mainbranch. It is allowed to read the repo's code (contents: read) and write to pull requests — for example, post a comment or add a label (pull-requests: write). It cannot push code, create releases, or take any other action beyond those two scopes.source: docs.github.com — Workflow syntax · fetched 2026-05-29
-
Why would you pin a
usesaction to a specific version (like@v4) rather than leaving it unpinned?Suggested answer
A
usesaction is someone else's code. If you don't pin the version, the action's author could push a new version tomorrow that behaves differently — or maliciously. Pinning to@v4(or a commit SHA) means your workflow always runs the exact code you reviewed and trusted. This is the supply-chain consideration:runis your code;usesis borrowed code. -
A workflow has no
permissionskey at all. Is that safer or less safe than one withpermissions: contents: read? Why?Suggested answer
Less safe. Without a
permissionskey, theGITHUB_TOKENgets broad default access — typically read and write across multiple repo scopes. Addingpermissions: contents: readexplicitly narrows the token to read-only. The docs say to always grant the least required access, so omittingpermissionsleaves the rig with more power than it probably needs — a bigger blast radius if something goes wrong.source: docs.github.com — Automatic token authentication · fetched 2026-05-29
Mentor marks this lesson verified after you compare answers and say "got it" in chat.
Ticks this lesson done on the home roadmap. Saved in this browser.
Sources: docs.github.com — Workflow syntax for GitHub Actions · docs.github.com — Automatic token authentication · fetched 2026-05-29.