A Pulumi stack that its files name and the backend lacks is created by the scan, when its entry asks
Decision record 0107
Amends 0022 (a stack the backend lacks stays a preview failure, unless the stack’s entry sets
createInBackend: true, and then the scan makes it before the preview) and 0074 (the check withbackend: truenames each such stack with the line that the first scan creates it, and keeps it out of theignoreblock). Built as slice 5.42, for issue 248.
A new Pulumi project was added to a real repo on 2026-09-24, for a service the dashboard should deploy. The stack file existed, the backend did not hold the stack, and the row was a preview failure until the owner ran a preview by hand on a laptop to create it. With strict on, that one row would have turned every scan red until then, so the stack was ignored with a reason, and the owner’s checklist grew by a local step and a config edit. Nothing in Sluiceway could create the stack, on purpose: record 0022 makes a stack the backend lacks a preview failure, and the check offers an ignore block for it (0074).
OpenTofu already has the other answer. Its init makes a missing workspace, so a root module’s first scan previews it as all creates (0053, 0074). Pulumi gets the same, opt in.
Decision
stacks[].createInBackend: true, a key per entry and not a top level switch. The person then names exactly which stacks a scan may create, and a stray stack file anywhere else still creates nothing. The key follows the shape ofenvFile,drift.enabledandvalueFingerprint: an entry without a name covers every stack in its path, an entry with a name wins key by key. Off by default, because a typo in a stack file must never create junk in a backend. An entry withtoolrefuses it, in the words discovery uses fordependsOn: auto: only a Pulumi stack has a stack to create.- One preparation per such stack, before its first preview (0053). The Pulumi adapter gives, for each stack the scan hands it to create, a preparation titled with the stack id:
pulumi stack ls --jsonin the stack’s directory, the same read-only question the check asks (0074), thenpulumi stack init <name>when the list lacks the stack. The init gets the name alone: no--secrets-provider, so the tool’s default stands, and the passphrase is the one the job already has in its environment, or in the stack’s env file (0103). A stack the list holds is left alone. Both commands get the stack’s environment minusINPUT_*(0013) and the stack’s preview time limit. The scan then previews the created stack in the same round, as all creates. - Once per stack per job, and each stack alone. The scan’s preparation step runs each preparation before the pool and remembers what it prepared across rounds, so a second round never asks again. Each stack is its own preparation, so a list or an init the tool refuses is a preview failure of that stack alone, with a reason picked from the exit code as a preview’s is (0022 as amended in slice 5.9), and the scan goes on. The tool’s words go to the preparation’s group in the job log and nowhere else.
- A preparation may give its own words.
PrepareResultgainsdetail, Sluiceway’s own lines for the group: that the backend did not hold the stack and it was created, or that the backend held it and nothing was created. Never a word of the tool’s. - Only the scan creates. The scan hands the preparation step the stacks whose entries ask;
applyhands it none, whatever the entries say, so a deploy never creates a stack. A stack gone at deploy time fails the fresh preview as it does today. - The check names each such stack. With
backend: true, a stack the backend lacks whose entry asks is one line in the log and one cell in the summary,No, the first scan creates it, not a warning, and it gets no entry in theignoreblock. Withoutbackend: truethe check cannot know what the backend holds and says nothing new. - The salt stays in the checkout.
stack initwrites anencryptionsaltinto the stack file of the checkout. Sluiceway never commits, so the salt lives and dies with the runner, as the.terraformdirectory of an init does. A repo that later sets config secrets with the tool commits the salt itself, as today. A stack whose config needs secrets still gets them from the environment or from a person; the docs say so.
Considered
- A top level
scan.createStacks: true. Rejected: it would create every stack any stack file names, which is exactly what a typo must not do. One key per entry is one decision per stack, and the check can name each. - Creating the stack on a deploy too. Rejected: a deploy is a tick on a row that a preview made, and a stack that vanished between the preview and the deploy is news the person should hear as a failure, not something to paper over.
- Running
stack initwithout the list first, and reading “already exists” from the tool’s words. Rejected: 0022 picks a reason from facts Sluiceway establishes, never from the tool’s message, and the tool documents no exit code for a stack that is there. The list is one quick read, and the check already trusts it. - One preparation per project directory. Rejected: a refused init would then fail the previews of every stack of the directory, and one broken stack must not stop the others (0012).
- A
secretsProvideroption, or the salt committed by Sluiceway. Left out (docs/later.md): nobody has asked, and Sluiceway commits nothing to a repo.
Consequences
- Record 0022 is amended: the list of preview reasons does not change, and a stack the backend lacks reaches the reason only when no entry asked the scan to create it.
- Record 0074 is amended: the check’s backend part has one more kind of line, and the
ignoreblock leaves out the stacks the first scan creates. - The adapter interface:
preparetakesPrepareOptionswith the stacks to create, andPrepareResultmay carrydetail.PrepareContextof the modes gainscreateInBackend, which the scan sets andapplydoes not.BackendCheckcarries the entry’s ask. CONTEXT.mdamends the preparation.docs/configuration.mdgains the key,docs/credentials.mdsays where the secrets of a created stack come from,docs/workflow.mdsays what the check prints, and the onboarding log has hurdle 29.- The recorded
create-stackscenario holds the list, the init, the preview of the new stack and a second init the tool refuses, on both supported CLI versions.