Lock the Targets a Pull Request Reaches
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`.Optional: hand this page to your coding agent
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.
Turn locks on
Section titled “Turn locks on”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.
Add the lock job on GitHub
Section titled “Add the lock job on GitHub”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 githubRelease the locks when a pull request closes, in a workflow of its own so the plan does not run on close:
name: chant-pr-unlockon: 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.
See who holds what
Section titled “See who holds what”npx chant lock --listapp pull request 12 plan alice 2026-10-22T10:00:00.000Znet pull request 12 plan alice 2026-10-22T10:00:00.000ZRelease a stuck lock
Section titled “Release a stuck lock”A pull request that is abandoned but left open keeps its locks until someone releases them with chant unlock.
npx chant unlock --pr 12chant unlock --pr 12 net releases one target only. The next run of the other pull request’s lock job then takes it.
Lock by hand
Section titled “Lock by hand”chant lock also takes a lock with no plan behind it, for example to hold a component during an incident:
npx chant lock net --pr 31 --by aliceA later plan of pull request 31 keeps that lock as it is, so the plan job cannot drop it (source).