# plywood > Pulumi ops and governance. plywood runs Pulumi previews and updates from pull requests on GitHub, GitLab or Forgejo. It posts one preview note for every stack a change reaches, binds each approval to a preview digest, and rolls changes out in waves. It runs on chant's runtime, and plunge, the Pulumi fork from the team behind plywood, adds resource-level features. The source is at https://github.com/INTENTIUS/plywood. A page's prompt sits under "Optional: hand this page to your coding agent" and in llms.txt under the page. Every prompt ends with this rule, left out below: Never run `pulumi up`, `plunge up`, `pulumi destroy` or `plywood approve`, override a policy denial, use `--mode up`, or merge; never touch `.chant/allowed_signers` or `chant/lifecycle`. ## Pages - [plywood](https://intentius.io/plywood/): Pulumi ops and governance. - [Get your first preview note](https://intentius.io/plywood/getting-started/): Add plywood to a repository with a Pulumi program and get one preview note on your next pull request. - Optional agent prompt: Set up plywood in this repository. Read https://intentius.io/plywood/llms.txt first, then https://intentius.io/plywood/getting-started/agents/ and follow it. Ask me which stacks should wait for an approval before they update. Open a pull request with the result. - [Architecture](https://intentius.io/plywood/concepts/how-it-works/): The path of a change through plywood, and the parts that belong to Pulumi and to chant. - [Glossary](https://intentius.io/plywood/concepts/glossary/): Pulumi's words as plywood uses them, and plywood's few additions. - [Approvals are records](https://intentius.io/plywood/concepts/approvals-as-records/): An approval is a row in the chant workspace ledger in your repository, bound to one preview digest. - [The control repo](https://intentius.io/plywood/concepts/control-repo/): One home for the approvers and pins of many repositories. - [Locking and staleness](https://intentius.io/plywood/concepts/locking-and-staleness/): plywood locks per resource on plunge and per stack on stock Pulumi. Stock Pulumi orders updates to a stack with its stack lock, and plunge needs none. - [Waves and approvals](https://intentius.io/plywood/concepts/waves-and-approvals/): The way stacks form waves, and the digest each gate binds. - [Why previews are grouped](https://intentius.io/plywood/concepts/why-previews-are-grouped/): One note and one digest across stacks keep a reviewer's approval tied to the whole change. - [With a coding agent](https://intentius.io/plywood/for/agents/): Agents set plywood up and read stacks over MCP. A person approves every update. - [Self-managed backends](https://intentius.io/plywood/for/diy-backend/): A file, S3, GCS or Azure Blob backend works as it is, with a lock per stack. - [Many projects](https://intentius.io/plywood/for/many-projects/): Projects that read each other's outputs through StackReference update in order. - [Many repositories](https://intentius.io/plywood/for/many-repos/): A control repo holds the approvers and the engine pins for every repository's stacks. - [One program, many stacks](https://intentius.io/plywood/for/many-stacks/): One Pulumi project with a stack per environment or region gives waves over stacks. - [Stacks on plunge](https://intentius.io/plywood/for/plunge/): A plunge+ backend URL runs every stage with plunge, which updates per resource. - [Pulumi Cloud stacks](https://intentius.io/plywood/for/pulumi-cloud/): Pulumi Cloud keeps your state and history. plywood runs previews and updates from your forge. - [Security review](https://intentius.io/plywood/for/security/): The identities each job holds and the approvals that let an update run. - [Set it up with your agent](https://intentius.io/plywood/getting-started/agents/): A paste-in prompt has your coding agent add plywood to the repository and open the pull request. - [A job outside its environment](https://intentius.io/plywood/guides/a-job-outside-its-environment/): Exit code 9: the job ran somewhere plywood.yml does not give the stack. - [Add plywood to a repo](https://intentius.io/plywood/guides/add-to-a-repo/): plywood init finds each Pulumi.yaml and its stacks, writes plywood.yml and the waves spec, and renders the pipeline for your forge. - Optional agent prompt: Set up plywood in this repository. Read https://intentius.io/plywood/llms.txt first, then https://intentius.io/plywood/getting-started/agents/ and follow it. Ask me which stacks should wait for an approval before they update. Open a pull request with the result. - [Have an agent fix drift](https://intentius.io/plywood/guides/agent-fix-drift/): The agent opens a pull request that brings the program back in line with the drift found. A person approves the update. - [Read your stacks over MCP](https://intentius.io/plywood/guides/agent-read-over-mcp/): An agent reads your stacks and their runs with read-only tools. - [Have an agent review a pull request](https://intentius.io/plywood/guides/agent-review-a-pull-request/): A model compares the pull request's description with its previews and posts one note. - [Alongside Pulumi Deployments](https://intentius.io/plywood/guides/alongside-pulumi-deployments/): Some stacks keep deploying from Pulumi Deployments, and others run from your forge through plywood, on the same Pulumi Cloud organisation. - [Read why an update stopped](https://intentius.io/plywood/guides/answer-a-refused-or-failed-update/): A refused wave posts its change since the approval, and a failed update posts the failure. - [Approvals runbook](https://intentius.io/plywood/guides/approvals-runbook/): The people who approve each stack, and the reasons an approval stops counting. - [Approve a wave](https://intentius.io/plywood/guides/approve-a-wave/): plywood approve records an approval of one preview digest, and the wave's next job updates the stacks it covers. - [Choose pulumi or plunge](https://intentius.io/plywood/guides/choose-an-engine/): Every stage runs with stock Pulumi or with plunge. The engine table, and the features plunge adds. - [Drift checks with their own identities](https://intentius.io/plywood/guides/drift-identities-and-writers/): The drift job reads with the preview identity and names the writer of each drifted attribute. - [Fix a refused wave](https://intentius.io/plywood/guides/fix-a-refused-wave/): Each refusal names its cause and the command that clears it. - [From pulumi/actions in GitHub Actions](https://intentius.io/plywood/guides/from-pulumi-actions/): plywood init reads your existing pulumi/actions workflow and writes plywood.yml and the generated pipeline next to it. - [Govern many repositories](https://intentius.io/plywood/guides/govern-many-repos/): One control repo sets the approvers and pins for all of them. - [Merge once the update is done](https://intentius.io/plywood/guides/merge-after-the-update/): apply.merge: auto merges the pull request after /plywood up updates it. - [Notify a chat channel](https://intentius.io/plywood/guides/notify-a-chat-channel/): A chat channel or a signed webhook hears about waiting waves and refused or failed updates, and about drift. - [Override a denial](https://intentius.io/plywood/guides/override-a-denial/): plywood override records a person's reasoned approval of one denied preview. It is spent once the update runs. - [Query with SQL](https://intentius.io/plywood/guides/query-with-sql/): plywood query answers questions such as which updates ran this week, over the ledger. - [Read the audit trail](https://intentius.io/plywood/guides/read-the-audit-trail/): Every approval and update is a row in the chant workspace ledger. - [Resources registered inside apply()](https://intentius.io/plywood/guides/resources-registered-at-up/): A preview cannot run an apply() whose input is unknown. The note lists the component resources that may register more at up. - [A stack per pull request](https://intentius.io/plywood/guides/review-stacks/): Each open pull request gets its own stack from a template stack's config, destroyed and removed when it closes. - [Roll a stack back to a release](https://intentius.io/plywood/guides/roll-back-a-stack/): chant release cut records a stack's program tree, and a rollback previews and updates that tree. - [Run a day-2 action](https://intentius.io/plywood/guides/run-a-day-2-action/): Declare a command resource as an action, and run it on request once a second person approves its preview. - [Run in a prepared image](https://intentius.io/plywood/guides/run-in-a-prepared-image/): Use an image with your plugins and language runtime installed, so each job skips the install. - [Run without approvals](https://intentius.io/plywood/guides/run-without-approvals/): Sandbox stacks can update on merge with no approval. - [Scope state and cloud access](https://intentius.io/plywood/guides/scope-state-access/): Preview and update run as different identities, and on Pulumi Cloud they use different tokens. - [See the approval before merge](https://intentius.io/plywood/guides/see-the-approval-before-merge/): plywood/approval on the pull request says whether its approval covers every wave. - [Send traces and metrics](https://intentius.io/plywood/guides/send-traces-and-metrics/): Each run is an OpenTelemetry trace with a span per stack. - [Separate environments](https://intentius.io/plywood/guides/separate-environments/): A stack per environment, each environment in its own wave with its own gate. - [Order stacks by their references](https://intentius.io/plywood/guides/stack-references/): A stack that reads another's outputs updates after it. - [Turn on drift checks](https://intentius.io/plywood/guides/turn-on-drift-checks/): A scheduled job reads each stack against the cloud and keeps one issue per stack. - [Update a stack before it merges](https://intentius.io/plywood/guides/update-before-merge/): Opt in with apply.when, then a /plywood up comment updates the approved preview from the pull request. - [Use your CrossGuard policy packs](https://intentius.io/plywood/guides/use-policy-packs/): The preview job runs your CrossGuard policy packs, and their violations go into the note and the gate. - [Write a policy](https://intentius.io/plywood/guides/write-a-policy/): A Rego rule over the change set denies a preview or holds it for a person. - [Backends](https://intentius.io/plywood/reference/backends/): The backend URLs plywood reads, and the features each gives the pipeline. - [CLI](https://intentius.io/plywood/reference/cli/): Every plywood command and flag. - [Configuration](https://intentius.io/plywood/reference/config/): Each key of the config file, with its default value. - [Development](https://intentius.io/plywood/reference/development/): Links and notes for people changing plywood itself. - [Environment variables](https://intentius.io/plywood/reference/environment/): The environment variables plywood and its engines read. - [Exit codes](https://intentius.io/plywood/reference/exit-codes/): plywood uses chant's exit-code table, which terragucci shares. - [The report](https://intentius.io/plywood/reference/report/): The JSON each stage writes, with its schema version. - [Stages](https://intentius.io/plywood/reference/stages/): The four stages and the commands each one runs on each engine. - [Threat model](https://intentius.io/plywood/reference/threat-model/): The reach of each job, starting with the preview job that runs the pull request's program. - [Tips](https://intentius.io/plywood/reference/tips/): Each tip id the report can print, and the fix for it. - [Versions and pins](https://intentius.io/plywood/reference/versions-and-pins/): The pins for the Pulumi CLI and plunge, and the CI image digests. - [Changes](https://intentius.io/plywood/reference/whats-new/): Changes in each plywood release. - [Boot the example](https://intentius.io/plywood/tutorial/): One command starts a local forge and an AWS emulator with an example repository. Nothing reaches a real cloud account. - Optional agent prompt: Follow https://intentius.io/plywood/tutorial/ on this machine: build the images with `just images`, start the bench with `just tutorial-up` and report the three addresses it prints. - [A preview that changed after review](https://intentius.io/plywood/tutorial/changed-preview/): Push again after an approval, and the old approval no longer covers the waves. - [Find drift](https://intentius.io/plywood/tutorial/drift/): Change a queue by hand on floci; the scheduled drift job opens one issue naming the URN and the attribute. - [Your first pull request](https://intentius.io/plywood/tutorial/first-pull-request/): Change a queue setting and read the preview note on the pull request. - [One note for every stack](https://intentius.io/plywood/tutorial/one-note/): A change that reaches four stacks gives one note with a section per stack and one approval command. - [See your runs](https://intentius.io/plywood/tutorial/see-your-runs/): Read the tutorial's runs and approvals with plywood audit and plywood query. - [Canary, then waves](https://intentius.io/plywood/tutorial/waves/): The dev stack updates first, then staging, then both prod stacks, each wave under the approval of the pull request's preview. - [Now your repo](https://intentius.io/plywood/tutorial/your-repo/): Point the same setup at your own programs, stacks and backend. ## Full text - [llms-full.txt](https://intentius.io/plywood/llms-full.txt): every page above in one file