Re-plan a pull request from a comment
Optional: hand this page to your coding agentThe steps work by hand too.Show the whole prompt
Read https://intentius.io/terragucci/guides/re-plan-from-a-comment/.
Check that the default branch's pipeline has the comment trigger (on GitLab, the `comments` job and a pipeline schedule with TERRAGUCCI_SCHEDULE set to comments); if it does not, run `npx terragucci init` and open a pull request with the result.
You may comment `/terragucci plan` on a pull request and report what the note says.
Never comment `/terragucci apply`, `/terragucci lock` or `/terragucci unlock`.
Never apply, approve (a pull request review or `terragucci approve`), override a policy denial (`terragucci override`), use `--mode apply`, or merge; never touch `.chant/allowed_signers` or `chant/lifecycle`.Result
Section titled “Result”/terragucci plan on a pull request plans it again and updates its plan note and terragucci/plan status. /terragucci plan envs/dev/orders plans that root alone.
Prerequisites
Section titled “Prerequisites”| You need | Why |
|---|---|
| The pipeline with the comment trigger | comment workflows run from the default branch; to add it, run npx terragucci init again and merge the result |
| Write access to the repo, for whoever comments (on GitLab, the Developer role or above) | anyone else gets no answer and no plan |
Each new pull request comment starts the replan job.
GitLab starts no pipeline for a merge request note. A pipeline schedule polls for notes instead and answers each on its next run.
| Step | What to do |
|---|---|
| Turn it on | add comments: "*/5 * * * *" to terragucci.yml, run npx terragucci init, and merge the result; the pipeline gets a comments job |
| Add the schedule | under CI/CD, Schedules: the same cron, the default branch, and the variable TERRAGUCCI_SCHEDULE set to comments |
| The token | GITLAB_TOKEN (or the token_env variable): a project access token with the api scope and the Developer role, in a masked variable |
The comments job runs from the default branch and holds no cloud credentials. It reads notes and calls GitLab’s API: /terragucci plan starts a new merge request pipeline, whose plan job updates the plan note. Each reply carries a marker, so a note is answered once.
As on GitHub.
-
Comment on the pull request with this line alone:
/terragucci planThe optional word after
planis a root path from the repository root. -
Read the note. The
replanjob plans the head with the read-only role and no forge token, and thereplan-notejob updates the same note and status.A named root the change does not reach gets a “not affected” reply and no update; a name that is not a root is refused with the list of roots:


Re-plans by dispatch
Section titled “Re-plans by dispatch”A chat front end or a script can re-plan without a comment. On GitHub and Forgejo the workflow takes a workflow_dispatch:
| Input | Value |
|---|---|
pr |
the pull request’s number; left empty, the dispatch runs the drift job where drift: is set |
root |
optional: one root path, read by the comment grammar |
The re-plan runs the same checks as /terragucci plan [root] and updates the same note and status.
gh workflow run terragucci.yml -f pr=42 -f root=envs/dev/ordersThe decision step asks the API for the dispatcher’s permission and plans nothing without write access.
GitLab has no dispatch with inputs. Run a new merge request pipeline instead (Run pipeline on its Pipelines tab), or call the API:
curl -X POST -H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$CI_API_V4_URL/projects/<id>/merge_requests/<iid>/pipelines"It needs the Developer role or above, and plans the whole merge request.
curl -X POST -H "Authorization: token $FORGEJO_TOKEN" -H 'content-type: application/json' \
-d '{"ref":"main","inputs":{"pr":"42"}}' \
"https://forgejo.example.com/api/v1/repos/acme/infra/actions/workflows/terragucci.yml/dispatches"Forgejo answers 403 to a dispatcher without write access and starts no run. Its dispatch event carries no permission to read, so that refusal is the check.
Comment forms
Section titled “Comment forms”The text is untrusted and never reaches a script. terragucci comment reads it from the event file, or on GitLab from the API. Only one line in one of these forms is accepted; anything else is refused with the reason.
| Comment | Runs | Where | GitLab, with comments: set |
|---|---|---|---|
/terragucci plan |
a re-plan | every GitHub and Forgejo pipeline | a new merge request pipeline |
/terragucci plan <root> |
a re-plan of that root, which must be exactly a root the pipeline knows | every GitHub and Forgejo pipeline | the root is checked, and the whole merge request is planned |
/terragucci apply |
the approved waves | merged pull requests; open ones with apply.when: pull-request |
merged merge requests: a retry of the merge commit’s first apply job that did not succeed; with apply.when: pull-request, open ones: an mr-apply pipeline on the default branch |
/terragucci apply wave-<n> |
the approved waves up to wave n | as above | an open merge request with apply.when: pull-request; otherwise refused, since GitLab runs the waves after a retried job by itself |
/terragucci lock |
locks on the roots (in a Terragrunt repo, the units) this pull request reaches, with no apply | apply.when: pull-request or locks: plan |
apply.when: pull-request, through mr-apply; otherwise answered as unsupported |
/terragucci unlock |
a release of this pull request’s locks | apply.when: pull-request or locks: plan |
as lock |
atlantis plan [-d <dir>], atlantis apply |
the same as /terragucci plan [<root>] and /terragucci apply, with the same checks |
atlantis_comments: true |
the same, with atlantis_comments: true |
/terragucci agent <ask> |
a coding agent’s change | agent.comment set; see Have an agent change a pull request |
with agent.comment set, starts the agent’s pipeline on the default branch; otherwise answered that it is off |
No comment approves, and a re-plan cannot change what an approval binds. When a comment runs nothing lists every check each command must pass. A refusal is a reply that starts with terragucci:.
| Case | Job |
|---|---|
| a comment that asks for nothing | ends cleanly |
| a forge 403 or failure, or an unreadable event file | fails, with the cause in the log |
Apply a merged pull request
Section titled “Apply a merged pull request”On a merged pull request, /terragucci apply re-runs tf-apply for the waves approved as Approve a waiting wave shows.


| Part | What happens |
|---|---|
| Workflow | apply-comment runs the default branch’s workflow at the merge commit, never the head |
| Checks | terragucci comment-apply checks the comment before any credential, against the merged-PR column |
| Credentials | the job assumes oidc.apply_role, beside any push’s apply |
| Another run’s resources | with choudoufu, a wave that changes a resource another run is applying is refused; the reply names that run, and you comment again once it is done |
| Waves | from wave 1 until one does not apply; wave-<n> stops after wave n |
| A waiting wave | waits as on a push |
| A refused wave | prints its diff (respond.wave-refused) |
| A failed wave | prints its triage (respond.apply-failed) |
| Terragrunt | the same job and refusals; each wave runs tf-apply --terragrunt on its units from the merge commit |
| On GitLab | |
|---|---|
/terragucci apply |
retries the waiting wave’s job in the merge commit’s pipeline |
| The gate | stage tf-apply decides it again, so a wave with no approval waits again; the waves after it follow, each behind its own gate |
| A merge commit a later apply superseded | refused, as on GitHub |
Apply an open pull request
Section titled “Apply an open pull request”With apply.when: pull-request, /terragucci apply on an open pull request applies its head before merge. Apply a pull request before it merges has the steps.
Anyone with write access can lock or unlock, and the reply names what was locked or released. Locks lists everything that takes and releases one.
| Comment | Does | Refused when |
|---|---|---|
/terragucci lock |
locks the roots the pull request reaches, applying nothing, so no other pull request applies them first; needs no approval or checks | another open pull request holds one of those roots; the reply names it |
/terragucci unlock |
releases the pull request’s locks so another can apply those roots; under locks: plan it locks again on its next push or /terragucci plan |
|
/terragucci plan, under locks: plan |
takes the locks again once a holder is gone |
- Approve a waiting wave is the one place an approval is given.
- Stages lists what
tf-planreads and writes.
These docs count page views and clicks with PostHog. They set no cookies, store nothing in your browser, and send nothing when your browser asks not to be tracked.





