GitHub Actions now has a lockfile. It's the cheapest way to stop an agent's workflow edits pulling in a moved tag
Workflow lockfiles pin every action to a commit while your YAML keeps readable tags. What it does, what it doesn't, and the one line to add to your agent's instructions.

Ask an agent to add a deploy job and it will write uses: some-org/deploy-action@v3 without a second thought. That line means “run whatever commit the v3 tag points at when the job starts”. If the tag moves, perhaps because a maintainer’s account was compromised, your pipeline runs different code and nothing in your repo changed.
On 14 October 2026 GitHub put a fix into public preview: workflow dependency lockfiles
. A new file, .github/workflows/actions.lock, records the exact commit behind every action your workflows reference, and Actions runs the recorded commit. Your workflow YAML keeps its readable tags.
There’s a catch up front, and it’s a big one. The preview is limited to GitHub Enterprise Cloud and Enterprise Cloud with data residency. If you’re on a Free, Team or plain Enterprise Server plan, this is a story to watch rather than switch on.
What changes in your repo
The lockfile is generated by a GitHub CLI extension, gh actions-lock, from the public gh-actions-lock
repository (MIT licensed). GitHub’s docs say the file should never be edited by hand. Its format has two sections: which dependencies each onboarded workflow uses, and what each reference resolves to. This is the example from the docs:
# This file is machine-generated by `gh actions-lock`.
# Do not edit by hand; run `gh actions-lock` to update.
version: 'v0.0.2'
workflows:
'.github/workflows/ci.yml':
- 'octo-org/deploy-action@v3.1.0'
dependencies:
'octo-org/deploy-action@v3.1.0':
ref: 'v3.1.0'
commit: 'sha1-...'
owner_id: 44036562
repo_id: 197814629
The commit field is what runs. The owner_id and repo_id fields are the numeric IDs of the source repository, and Actions checks them at run time to confirm the name still points at the repository you locked. That guards against the repository behind a name changing hands, which a pinned commit alone doesn’t tell you.
The lock also reaches past your own uses lines. According to the docs it covers transitive dependencies, meaning the actions your actions call, with one exception: dependencies of reusable workflow calls. A reusable workflow is covered by the lockfile of the repository that defines it, so onboard that repository first.
Onboarding takes three commands
From the repository root:
gh extension install github/gh-actions-lock
gh actions-lock
git diff
gh actions-lock scans every workflow under .github/workflows/. Pass a path, such as gh actions-lock .github/workflows/ci.yml, to onboard one workflow at a time. The docs recommend enabling Dependabot for Actions first.
Read the diff before committing, because the tool edits your workflows too. Two rewrites matter:
- Loose references become precise ones: the docs’ example is
@v3becoming@v3.1.0. - Local references such as
uses: ./.github/actions/setupbecomeuses: $/.github/actions/setup. A$/reference resolves to the commit that’s running, so it needs no lockfile entry.
Once a workflow is onboarded, every action dependency it uses must appear in the lockfile. Those commands are documented behaviour, not something run for this piece, since the preview needs an Enterprise Cloud account.
Why this suits agent-written workflows
The usual advice is to pin every action to a full 40-character commit SHA. It works, and it’s miserable: unreadable diffs, no obvious way to tell which version you’re on, and nobody wants to hand-update forty of them. An agent will happily paste a SHA it half remembers, or fall back to a tag because the tag is what its training data shows.
A lockfile splits the job. The agent writes whatever readable reference it likes. The lockfile decides what executes. The docs say that for repositories that adopt one, pinning to full SHAs in the workflow files is no longer necessary.
Dependabot completes the loop. When it opens a version update for an action in an onboarded workflow, it regenerates the matching lockfile entry in the same pull request, with no extra configuration. One caveat from the docs: Dependabot won’t onboard a workflow for you. If it bumps a dependency in a workflow that has no lockfile entry, it reports that onboarding is required.
Reviewing a lockfile change
The commit in a lockfile diff is the code your pipeline will run, so treat it like any dependency version bump. An agent-authored PR that touches actions.lock deserves the same scrutiny as one that touches a package lockfile.
One field deserves a hard stop. A changed owner_id or repo_id is rare, and the docs say it means the repository behind the name isn’t the one you originally locked. Investigate before merging. Don’t ask an agent to explain it away.
Another failure mode is quieter. Branch and partial-version references such as main or v3 are trusted from the lockfile and aren’t re-resolved on a normal run. If a tag has genuinely moved, gh actions-lock --relock re-resolves it. If the locked commit has become unreachable, for example after a force-push upstream, --relock still errors, and you have to add --accept-moved to take the new commit. That second flag is the one to run last, and only after reading the upstream history.
What a lockfile doesn’t do
GitHub is plain about the limits. Locking doesn’t verify that a dependency is safe, it only freezes what you already trusted. Review an action before it goes in. If you onboard a malicious one, the lockfile will faithfully protect your pipeline’s right to run it.
It also protects only the workflows you onboard, so adoption is gradual. Administrators can switch on a Require lockfile policy at enterprise, organisation or repository level, which makes workflows use lockfiles. That policy is also in public preview, and the details of its run-time behaviour aren’t on the pages cited here, so check them before you rely on it.
Allowlists need attention too. The docs warn that once a workflow is onboarded, an allowed-actions policy is checked against the locked SHAs. Entries pinned to tags, such as actions/checkout@v6, stop matching. Switch them to a wildcard or the full commit SHA before you commit the lockfile, or the first run fails on a policy check.
And it’s a preview. The README warns that flags and the file format may change, and the example above is stamped version: 'v0.0.2'.
Two changes to make this week
Add a check so a stale lockfile fails CI rather than surprising someone. The README documents gh actions-lock --verify for that: it checks without writing anything. A sketch of the job follows. The --verify flag is documented, but the job itself is untested, and you may need to adapt the token setup for your runners.
name: lockfile
on: pull_request
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: gh extension install github/gh-actions-lock
env:
GH_TOKEN: ${{ github.token }}
- run: gh actions-lock --verify
Then tell the agent. The docs say to rerun gh actions-lock whenever a workflow gains or loses a uses dependency, which is exactly what an agent does without announcing it. Two lines in CLAUDE.md or AGENTS.md cover it:
After adding, removing or changing any `uses:` line in .github/workflows,
run `gh actions-lock` and include the actions.lock diff in the commit.
Never edit actions.lock by hand.
If you’re not on Enterprise Cloud yet, the same two lines of instructions still help once you are, and until then full-SHA pins remain the only protection against a moved tag.