Skip to content

Lock the Targets a Pull Request Reaches

llms.txt lists every page for an agent
Optional: hand this page to your coding agentThe steps work by hand too.
Show the whole prompt
Read https://intentius.io/chant/guide/target-locks/.
Add `"locks": "plan"` to the `ci` block of `chant.workspace.json`, add the lock and unlock jobs this page shows to the pull-request workflow, and open a pull request with both.
Show me `npx chant lock --list` before and after.
Never deploy to a real cloud account or shared environment (with `chant run`, `chant components` or a platform's own CLI), approve (a pull request review, `chant approve` or `chant run approve`), override a policy denial (`chant approve policy-override`), or merge; never cut or publish a release (`chant release cut --yes` or `chant release publish`), push or delete a `ci/*` tag (`chant ci tick` pushes them), or change the value of an existing flag; never touch `.chant/allowed_signers` or `chant/lifecycle`.

Two pull requests that change the same component can each be approved against the estate as it is. Whichever applies second then applies a plan made before the first one landed. Target locks stop that (#4293). The first pull request to plan a component holds it, and the second gets a failing chant/lock status until the first merges or closes. chant lock is the reference for every flag below.

This page assumes the generated pull-request pipeline already runs.

Set ci.locks in chant.workspace.json:

"ci": {
"locks": "plan"
}

plan makes the plan take the locks, through the lock job below. apply leaves the plan alone. Then only chant lock takes locks, and an apply before merge will once it ships (#4301). Under either value, chant components pr-apply refuses with locked when another open pull request holds a component it reaches, and applies nothing (source). It reads the field at base (source), so the pull request that adds it does not lock yet. Set minReader to 0.124.41 or newer.

The plan job runs the pull request’s code, so it writes nothing to chant/lifecycle. The lock job runs the target branch’s code instead. It reads the components the plan selected from the plan job’s report and locks them.

Add this job to the generated workflow, beside the plan job. chant-pr-plan is the artifact the plan job keeps. Give it the same setup steps as the plan job, so chant is installed.

lock:
if: github.event_name == 'pull_request'
needs: plan
runs-on: ubuntu-latest
container: node:22
permissions:
contents: write
pull-requests: read
statuses: write
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.base.sha }}
fetch-depth: 0
- run: npm ci
- uses: actions/download-artifact@v4
with:
name: chant-pr-plan
path: .chant/pr
- name: Lock the components the plan selected
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
npx chant lock --from-report .chant/pr/pr-plan.json
--pr ${{ github.event.pull_request.number }}
--head ${{ github.event.pull_request.head.sha }}
--stage plan --forge github

Release the locks when a pull request closes, in a workflow of its own so the plan does not run on close:

.github/workflows/chant-pr-unlock.yml
name: chant-pr-unlock
on:
pull_request:
types: [closed]
jobs:
unlock:
runs-on: ubuntu-latest
container: node:22
permissions:
contents: write
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- run: npm ci
- name: Release the pull request's locks
run: npx chant unlock --pr ${{ github.event.pull_request.number }}

Each new push to the pull request plans again and locks again. A component the new head no longer reaches is released (source). The unlock workflow tidies the file. Without it, a lock whose pull request closed still reads as released, because --forge asks GitHub whether each holder is open (source).

A held pull request does not lock again on its own when the holder merges. Re-run its lock job, or push to it.

A fork’s pull request gets a read-only token, so its lock job cannot push chant/lifecycle and fails. Locks work for branches of the repository.

To block the merge of a pull request whose components another one holds, make chant/lock a required status in the branch protection rule.

On GitLab and Forgejo the commands are the same. Run chant lock in a job that checks out the target branch and has the plan job’s .chant/pr as an artifact, with --forge gitlab or --forge forgejo.

Terminal window
npx chant lock --list
app pull request 12 plan alice 2026-10-22T10:00:00.000Z
net pull request 12 plan alice 2026-10-22T10:00:00.000Z

A pull request that is abandoned but left open keeps its locks until someone releases them with chant unlock.

Terminal window
npx chant unlock --pr 12

chant unlock --pr 12 net releases one target only. The next run of the other pull request’s lock job then takes it.

chant lock also takes a lock with no plan behind it, for example to hold a component during an incident:

Terminal window
npx chant lock net --pr 31 --by alice

A later plan of pull request 31 keeps that lock as it is, so the plan job cannot drop it (source).

chant