By the end of this chapter you can open any agentic workflow and read it fluently — the YAML frontmatter and the Markdown body — run the compile-and-iterate loop with confidence, and understand what the generated .lock.yml actually contains and why it exists. In Chapter 2 you shipped a workflow; here you open the hood.
Everything targets gh aw v0.81.6. We reuse the same Repo Assistant from Chapter 2 — no new workflow — and read its compiled output side by side with its source.
The most important idea in this chapter is also the most quietly radical: in gh-aw, the prose is the source code. A workflow is a single Markdown file with two parts — a YAML frontmatter block between --- markers that carries configuration, and a Markdown body of natural-language instructions for the agent (Workflow Structure). That file lives in .github/workflows/, under version control, and is reviewed in a pull request exactly like any other source file.
Why “reviewable” is the whole point
Traditional automation buries its real intent in verbose, opaque configuration that few teammates can review well. Making the prose the artifact inverts that: instead of encoding logic as “if issue has label X, do Y,” “you write ‘analyze this issue and provide helpful context’, and the AI decides what's helpful based on the specific issue content” (How They Work). A reviewer reads the same English the agent will act on — the behavior is auditable by anyone who can read, not just those fluent in Actions YAML.
Two distinctions keep this precise:
Frontmatter vs. body. Frontmatter is machine-facing configuration (triggers, permissions, engine, tools); the body is human-facing intent. The file deliberately separates “technical configuration from natural language instructions.”
Reviewable is not the same as deterministic. Making the instruction inspectable does not make the agent's response reproducible. Holding those two ideas apart is what the rest of the chapter is about.
A prose file can't run on GitHub's infrastructure, and hand-writing the hardened Actions YAML it would need is verbose and easy to get insecurely wrong. So gh-aw inserts a compile step. gh aw compile “transforms a markdown workflow file into a complete GitHub Actions .lock.yml” — and the official mental model is exactly the one you'd expect: “Think of it like compiling code — you write human-friendly markdown, the compiler produces machine-ready YAML” (Compilation Process).
Compile the Repo Assistant (offline — no engine, no secrets)
The compile step earns its place by buying four things at once:
Portability. The output is ordinary GitHub Actions YAML that runs on infrastructure you already have — no hosted runtime, no black box.
Review & validation. Compilation “includ[es] validation, import resolution, tool configuration, and security hardening,” catching errors and unsafe choices before they ship (Compilation Process).
Determinism. The same source yields the same hardened artifact — the property the next section builds on.
Pinning & hardening. The compiler pins every referenced action to an immutable commit SHA (“tags can be moved, SHAs cannot”) and applies security hardening automatically.
It's fast enough to feel like a normal build: simple workflows “compile in ~100ms” (Compilation Process). Internally it runs five phases — parsing, validation, job construction, dependency resolution, and YAML generation — but you need only the idea of a build pipeline, not the internals. And crucially, compilation is offline: it never calls an engine or touches GitHub, which is exactly why every example in this book is compile-verified without secrets.
Two artifacts, one source of truth
One authored intent produces two committed files. “The .md file is the editable source of truth, while .lock.yml is the compiled GitHub Actions workflow with security hardening. Commit both files” (How They Work). You edit the Markdown; the lock runs. You never hand-edit the lock — it opens with a blunt DO NOT EDIT banner, and the next compile would overwrite your changes anyway. Committing both gives reviewers transparency: they can diff the human intent and the exact hardened artifact that will execute.
Authoring a workflow is a feedback cycle, not a one-shot: write → compile → (check status) → run → iterate. You rarely get the instructions right the first time, so the model is built for cheap iteration — with a fast path and a slow path that are worth internalizing.
The fast path and the slow path
Not every edit needs a recompile. The frontmatter is compiled into the lock and the body is loaded at run time, so:
Fast path — edit the body. Change the natural-language instructions and the update “takes effect on the next run” with no recompile. Iterate on wording freely.
Slow path — change the frontmatter. Triggers, permissions, engine, tools — these “always require recompilation because they affect security-sensitive configuration” (Editing Workflows).
The rule of thumb: edit the body freely; recompile after any frontmatter change. This is what keeps security-sensitive configuration behind the compiler while letting you tune the prompt at the speed of thought. gh aw compile --watch recompiles on save to tighten the loop further.
Observe, then run
Two commands let you see state before you spend anything. gh aw status reports each workflow's state — enabled or disabled, schedules, labels — and its quick sibling gh aw list shows name, engine, and compilation status without an API call. Only gh aw run actually triggers execution.
Underneath the loop sits the mental model that ties this chapter together — the determinism boundary. Almost everything the compiler emits is fixed and reproducible; exactly one step, where the engine reads context and decides, is not. The next section shows that boundary in the compiled file itself.
Let's read the Repo Assistant's compiled lock. It's ~100 KB of machine-generated YAML, so we'll look at the parts that teach the model: the header, the pinned dependencies, and the job graph. Every excerpt below is copied verbatim from the file gh aw compile produced on v0.81.6.
The header: provenance and a hash
Top of repo-assistant-triage.lock.yml — metadata, the DO NOT EDIT banner, and SHA-pinned actions
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a783ee73…",
# "body_hash":"8c11b173…","compiler_version":"v0.81.6","strict":true,"agent_id":"copilot"}
# This file was automatically generated by gh-aw (v0.81.6). DO NOT EDIT.
#
# Custom actions used:
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
#
# Secrets used:
# - COPILOT_GITHUB_TOKEN
# - GITHUB_TOKEN
Three things to notice. The frontmatter_hash and body_hash are the compile-determinism signal: identical source produces identical hashes, so a reviewer (or CI) can tell whether a lock is in sync with its .md. The compiler_version records exactly which gh-aw built it. And every action is pinned to a full commit SHA with the human-readable version in a trailing comment — the hardening the compiler applies for you. The lock even lists its Secrets used and Custom actions used up front, so the file audits itself.
The job graph: where the determinism boundary lives
Scroll past the header and the source's tidy five-key frontmatter has expanded into a full Actions job graph. Note the top-level permissions: {} — the compiler grants nothing globally and pushes least-privilege scopes down into individual jobs.
The compiled job graph — only one job is non-deterministic
permissions: {} # top-level: nothing; scopes pushed down per-job
jobs:
pre_activation: # ┐
activation: # │ deterministic, SHA-pinned scaffold
agent: # ← the ONE non-deterministic step: the engine runs here
detection: # │ threat / safe-output detection
safe_outputs: # │ validates + applies the agent's requested writes
conclusion: # ┘ finalize & report
This is the determinism boundary made concrete. Five of these six jobs are fixed infrastructure — they always run the same way, on SHA-pinned actions, and you can audit them like any workflow. Exactly one, the agent job, is model-driven: that's where the engine reads the issue and exercises judgment. Everything unpredictable is boxed in by predictable steps — including the write path, because the agent runs read-only and its proposed writes flow through the separate, deterministic safe_outputs job (the mechanism is Chapter 6; the threat model is Chapter 7).
You can now read a workflow and its compiled output with a clear model of each:
A workflow is natural language as reviewable source — YAML frontmatter (config) plus a Markdown body (intent), committed and reviewed like code.
gh aw compile is a real build step: offline, ~100 ms, five phases, producing a hardened .lock.yml with validation, security hardening, and SHA-pinned actions.
You commit two artifacts — edit the .md, never hand-edit the .lock.yml (it says DO NOT EDIT for a reason).
The authoring loop has a fast path (edit the body, no recompile) and a slow path (change frontmatter, recompile).
The determinism boundary is visible in the job graph: five fixed jobs around one non-deterministic agent job — predictable infrastructure boxing in the model's judgment.
What's next. You've read the whole anatomy except the piece that decides when a workflow wakes up. In Chapter 4: Triggers, we open the on: block and choose the events that make the Repo Assistant run at exactly the right moments — and no others.