# Update Workflow Versions

> How to manually regenerate the compiled agentic workflow .lock.yml files when the gh-aw Version Check workflow reports version drift. All workflows track the newest github/gh-aw release or prerelease.

- Skill: `github/update-workflow-versions` (Agent Skill)
- Install (CLI): `npx skillmds@latest add github/update-workflow-versions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/github/update-workflow-versions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: GitHub (Microsoft) (https://skillmd.com/u/github)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/github/update-workflow-versions

---


# Updating Agentic Workflow Versions

Use this skill when the **gh-aw Version Check** workflow
(`.github/workflows/gh-aw-version-check.yml`) opens a *"Workflow version drift"*
issue, or whenever you need to bump the versions the compiled workflow
`.lock.yml` files are pinned to.

There is no automation for the regeneration itself — it is a deliberate,
human-reviewed step. Pushing changes under `.github/workflows/` requires a token
with the `workflows` permission, which the built-in `GITHUB_TOKEN` lacks, so a
maintainer (or an agent with suitably-permissioned credentials) performs the
recompile locally and opens a PR.

## Background

Each agentic workflow source (`.github/workflows/*.md`) is compiled by the
`gh-aw` compiler into a committed `*.lock.yml`. The version baked into a lock is
whatever compiler produced it, and **every** workflow tracks the same gh-aw
version: the newest `github/gh-aw` release **or prerelease**, whichever was
published most recently. There are no per-workflow categories — a bump
recompiles all the locks together.

**The detector version is baked into the locks.** `gh-aw`'s
`buildInstallThreatDetectStep` unconditionally emits
`constants.DefaultThreatDetectVersion` (a hardcoded tag, e.g. `v0.4.12`) as the
argument to `install_threat_detect_binary.sh`. There is no CLI flag, env var, or
frontmatter field that consults an alternate version at compile time. The
detector pin therefore only moves when either:

1. `github/gh-aw` bumps the constant upstream (typically after a detector
   promotion) and a normal drift-recompile picks up the new tag, or
2. a maintainer does a targeted post-compile edit of the lock (see
   [Testing an unpromoted detector prerelease in the smokes](#testing-an-unpromoted-detector-prerelease-in-the-smokes)
   below).

Promoting a detector release is **not** by itself enough to put it into the
smokes — it takes effect only once the upstream constant is bumped and this
repo's locks are recompiled against that gh-aw tag.

## 1. Determine the target gh-aw version

The drift issue lists it. To confirm or resolve it yourself (any method works —
`gh`, `curl`, or the GitHub Releases UI): newest `github/gh-aw` release **or**
prerelease, i.e. the most recent by publish date whose tag looks like
`v<digit>...`; ignore drafts.

For example, with `gh` available:

```bash
gh api repos/github/gh-aw/releases --paginate \
  --jq '.[] | select(.draft == false) | select(.tag_name | test("^v[0-9]")) | [.published_at, .tag_name] | @tsv' \
  | sort | tail -n1 | cut -f2
```

In the steps below, refer to this as `<TARGET_GH_AW>`.

## 2. Recompile the locks

Recompile every workflow with the same tag. The released `gh aw` extension is
the simplest route:

```bash
gh extension install github/gh-aw --pin <TARGET_GH_AW> --force
gh aw compile --action-mode action --action-tag <TARGET_GH_AW> --no-check-update \
  .github/workflows/*.md
```

### Building the compiler from source instead

If `gh extension install` is unavailable (no `gh` auth, offline, etc.), build
gh-aw from source — but you **must** set both version ldflags:

```bash
git clone --depth 1 --branch <TAG> https://github.com/github/gh-aw /tmp/gh-aw-src
( cd /tmp/gh-aw-src && go build -ldflags "-X main.version=<TAG> -X main.isRelease=true" -o /tmp/gh-aw ./cmd/gh-aw )
```

> [!IMPORTANT]
> `-X main.isRelease=true` is not optional. `cmd/gh-aw/main.go` defaults
> `isRelease` to `"false"` and passes it to `workflow.SetIsRelease()`, which
> normalizes the emitted `compiler_version` / `GH_AW_VERSION` to `dev` and skips
> release-only generation. Locks compiled without it look superficially fine but
> carry `dev` at runtime.

Verify before compiling:

```bash
/tmp/gh-aw version   # must print the target tag, not "dev"
```

## 3. Verify and open a PR

1. Sanity-check that only the intended version bumps changed:
   ```bash
   git status --short -- .github/workflows
   git diff -- .github/workflows
   ```
   Confirm each regenerated lock carries the target version in **both** places —
   they must not say `dev`:
   ```bash
   grep -o '"compiler_version":"[^"]*"' .github/workflows/*.lock.yml
   grep -n 'GH_AW_VERSION:' .github/workflows/*.lock.yml
   ```
   Re-running the **gh-aw Version Check** workflow after the PR merges should
   report no drift.
2. Commit only the regenerated `*.lock.yml` files (and any intended `.md`
   changes). The compiler also refreshes `.github/aw/actions-lock.json` and may
   touch `.gitattributes` — that churn is expected. Open a PR describing the
   version bumps.
3. **Pushing workflow-file changes requires a token with the `workflows`
   permission.** The built-in `GITHUB_TOKEN` (github-actions[bot]) is rejected
   for changes under `.github/workflows/`, so this regeneration is done by a
   maintainer / agent whose credentials carry that permission — not by an
   automated push in the version-check workflow.
4. After merging, dispatch the top-level **Smoke** workflow to confirm the new
   versions run green.

## Testing an unpromoted detector prerelease

There are two sanctioned paths, depending on what you want to exercise.

### Replay path (preferred for most prerelease iteration)

Dispatch `.github/workflows/replay-detection.yml` with
`detector_source=release`, `detector_ref=<prerelease tag>`, and `use_awf=true`.
It downloads that exact release asset and runs it under AWF against a prior
gh-aw run's artifacts. No lock edit needed.

### Smoke path (when you specifically need fresh AWF + fresh artifacts)

The smokes only ever see whatever tag gh-aw's constant currently points at
(promoted, stable). To point them at an unpromoted prerelease, do a targeted
post-compile edit of the smoke locks:

```bash
sed -i 's|install_threat_detect_binary.sh" v0\.[0-9.]*|install_threat_detect_binary.sh" <PRERELEASE_TAG>|' \
  .github/workflows/smoke-copilot-standalone.lock.yml \
  .github/workflows/smoke-claude-standalone.lock.yml \
  .github/workflows/smoke-codex-standalone.lock.yml
```

Commit those three files and open a PR. Do **not** patch
`constants.DefaultThreatDetectVersion` in a `gh-aw` fork to accomplish this:
that route corrupts the compiler-version story (locks would carry a fork tag or
lie about upstream), and it inverts the invariant that the constant tracks the
promoted detector.

Only touch the smoke locks. Leave `detection-stats-daily`,
`detection-failure-monitor`, `gh-aw-issue-digest`, and `gh-aw-parity-monitor` on
the compiler-emitted tag — they are not the test bed for detector prereleases.

The edit is deliberately ephemeral: the next `gh aw compile` run will regenerate
the locks from `constants.DefaultThreatDetectVersion` and revert the pin. That
is intentional — prerelease pins should not survive a routine recompile. Once
the detector is promoted and gh-aw bumps its constant, a normal drift-recompile
picks it up on the standard path.

