The pulumi member
The pulumi member kind is supplied by @intentius/chant-lexicon-pulumi
(workspace-kinds.json). It moved there from the terraform lexicon in
#4453 and kept its name.
What chant runs
Section titled “What chant runs”The ruling on #4476
(2026-10-25) widened the #4450
ruling’s preview and refresh --preview-only to the list below, and the
list is the whole of what chant runs for a Pulumi member:
| Read | Stock member | Member on plunge | Changes the stack |
|---|---|---|---|
| graph | none; the export in the index store, else the checked-in stack-export.json | the same | no |
| export after an apply | pulumi stack export --stack <name>, from the pulumi-up apply only | plunge stack export --stack <name>, the same | no |
| change set | pulumi preview --json | plunge preview --json | no |
| drift | pulumi refresh --preview-only --json --stack <name> --target <urn> | plunge drift --resource <urn> --stream --stack <name> | no |
| applied outputs | pulumi stack output --json, --show-secrets absent or =false | plunge stack output --json, the same | no |
| planned outputs | the change set’s preview | the same | no |
| engine facts | none | plunge graph --json --stack <name>, or the checked-in engine-graph.json (A member on plunge) | no |
| check stage | pulumi install, then pulumi about --json | plunge install, then plunge about --json | no |
runAllowedPulumi and runAllowedPlunge refuse any other argv before it
reaches the binary, and stack output with secrets shown is refused too.
With CHANT_PULUMI_APPLIED_OUTPUTS=export (or applied: "export" on the
source), the applied outputs are read from the checked-in export and nothing
runs. Each command runs in the member’s directory against its selected stack,
except the drift read, which names the stack with --stack (#4496):
pulumi.member.stack when the config sets it, else the stack the checked-in
export’s URNs name. A checked-in member with no selected stack can still be
refreshed.
The check stage
Section titled “The check stage”chant ci check runs three steps for each pulumi member after the ci.hooks.check hooks (#4633), so plywood check runs them too:
| Step | Runs | Fails when |
|---|---|---|
pulumi-install | pulumi install in the member’s directory (plunge install on plunge) | the install exits non-zero, or the member has no Pulumi.yaml |
stack-config | nothing; each Pulumi.<stack>.yaml is read against Pulumi.yaml’s config: block | a key with a type and no default is unset, a value does not read as its type, or a secret: true key is in plain text |
pulumi-about | pulumi about --json, with --stack when the member declares one | it prints no JSON |
The stacks are the project’s stack files, in stackConfigDir when Pulumi.yaml sets it, plus the member’s declared stack. The steps run with the check job’s environment, which holds no forge token and no cloud credential, so neither command can write a stack. runCheckPulumi refuses any other argv. Each step’s result is in check-hooks.json under steps, and a failed one names the member and stack and makes the check exit 1.
The member kind
Section titled “The member kind”| Field | Value |
|---|---|
| probe | a Pulumi.yaml in the directory |
| member file | Pulumi.yaml, checked for name and runtime |
| precedence | 310 |
| graph | the pulumi lexicon, with pulumi.member set to the member’s directory and name |
The graph
Section titled “The graph”The graph is the stack’s pulumi stack export. After each apply through the
pulumi-up capability, chant writes the export to its index store
(refs/chant/snapshots by default) with every secret blinded, and the graph
reads it from there. A stack applied outside chant checks the export in
beside Pulumi.yaml as stack-export.json instead. A node’s id is <member>/<qualified type>::<name>,
the URN after its stack and project, so urn:pulumi:dev::shop::aws:s3/bucketV2:BucketV2::assets
in member infra is infra/aws:s3/bucketV2:BucketV2::assets. The change set
and the drift records put a resource on the same node by the same rule
(@intentius/chant/pulumi-urn). Edges come from dependencies and parent.
Providers and the root stack have no node, and inputs and outputs are not
copied, since an export can hold secrets.
With no export, or one that is not JSON, the member has no nodes and the
graph’s meta.pulumiMember is { exported: false, reason }.
The change set
Section titled “The change set”The member’s part of the change set is pulumi preview --json, planner
pulumi, with the checked-in export as the last deployment. One entry per
URN: create, update (in place), delete, a replace as one replace
entry, same as no-op, read and refresh as read. An input that holds
Pulumi’s unknown value is unknown: true, and a secret is sensitive: true
with neither value. Resources a program registers inside an apply() the
preview could not run are marked mayAppear when they sit under a component,
or when the checked-in export holds them and the preview has no step for
them. A resource registered inside an apply() directly under the stack is
not marked: with no export holding it, it is not in the change set, and when
the export holds it and its input is unknown, the preview prints a delete
that the change set keeps as a plain delete. Nothing in a Pulumi preview tells
that apart from a real removal
(#4497). The full table is
under Pulumi plans.
Preview tips
Section titled “Preview tips”The pull request note gets a tips section for each pulumi member whose change set shows one of these patterns (#4634). Each row starts with the tip’s id, and a product’s note ends with its own command for the fix, such as plywood tips rename-without-aliases.
| Tip | Found when |
|---|---|
rename-without-aliases | the preview deletes a resource and creates one of the same type whose name has the same stem (assets and assets-v2), or the same name under another parent |
replace-on-name | a replace that creates before it deletes is forced by a name input (name, <x>Name, bucket) |
unknown-in-apply | an entry may register at up, because the preview could not list it |
A rename with aliases previews as same or update under the new URN, so a delete and a create of one type and stem in one preview is the rename without them. The stem is the name up to its first -, _, ., : or /. The tips are read from the change set alone, so stack config and protect give none.
chant workspace drift --member <member> [--resource <urn>] [--stream] runs
chant lifecycle drift in the member’s reader project, which calls the
lexicon’s readDrift. A stock member is read with one
pulumi refresh --preview-only --json --target <urn> per resource in its
export; with no export, one untargeted refresh. same is in-sync, update
is drifted with a path per detailedDiff key, delete is missing, and a
target the refresh did not reach is not-observed. See the
drift record reference.
A member on plunge
Section titled “A member on plunge”A member runs on plunge when its config says engine: "plunge", or when
Pulumi.yaml has a backend.url starting with plunge. Its drift is
plunge drift --resource <urn> --stream per resource. A member on plunge
covers the rule and the commands.
Outputs
Section titled “Outputs”Another member reads a Pulumi member’s outputs as parameters with core’s
member-outputs verb (see Wiring components).
The consumer lists pulumi among its lexicons.
| Outputs | Source |
|---|---|
| applied | pulumi stack output --json --show-secrets=false, or the checked-in export with CHANT_PULUMI_APPLIED_OUTPUTS=export |
| planned | pulumi preview --json --show-sames: the export’s outputs when it plans nothing to apply; when it plans a change, the outputs it leaves alone keep the export’s value and the rest are unknown |
A real preview prints the stack’s step with no outputs (recorded against the
plunge corpus, #4475), so chant cannot read a planned value off it. Instead
each output is traced by value to the export’s resource properties that hold
it (#4635). An output is
unknown when one of those resources is replaced or deleted, or updated with a
diff naming the property, or when it is held only by a property the provider
computes on an update. It keeps the export’s value when its resources are
unchanged, or when it is an id, arn or urn, or an input the diff leaves
alone. An output no property holds, such as one the program builds from a
template string, is unknown. In the recorded VPC replace, vpcId,
securityGroupId, vpcParameterName and workerQueueUrls are unknown and
the other eight outputs keep their values. pulumi-up’s plan lists each
verdict and the property behind it on its artifact under plannedOutputs.
A stack that reads another through StackReference (#4547)
plans now when every output it reads keeps its value. When one is unknown,
the reading stack is a hole naming the read, the note lists it under “Known
only after apply”, and the stack is previewed again after the producer’s
up. A planned output that is unknown leaves the consumer
planned without it, the change set has a hole on <member>#outputs naming
it, and on merge the consumer waits for the member’s apply and is planned
again. The member part’s pulumi-preview digest is the plan digest, so a
preview that moves after review moves the consumer’s digest. Secret outputs
are left out of both reads.
Reading the member into a build
Section titled “Reading the member into a build”A project can set pulumi.member itself to read a member’s graph into its
own chant build:
export default { lexicons: ["pulumi"], pulumi: { member: { dir: "./infra", label: "infra" } },} satisfies ChantConfig;dir is resolved against the project root. engine is pulumi or plunge
and defaults to what Pulumi.yaml says. stack is the stack the drift read
passes as --stack, and defaults to the one the export’s URNs name. The build runs these checks:
| Id | Kind | Reports |
|---|---|---|
| PUL101 | lint | pulumi.member.dir written as an absolute path |
| PUL001 | post-synth | a member read with no nodes, with the reason |
| PUL002 | post-synth | an export that records pending operations from an interrupted update |
| PUL003 to PUL015 | post-synth | what else the export says about the stack: plaintext secrets, half-finished replacements, the wrong stack or project, a damaged file |
The stack export lists every check with its fix, and
chant audit gives each a title and a remediation from the lexicon’s audit
catalog.
Watching a member
Section titled “Watching a member”PulumiWatchOp (#4494) previews one member and reads its drift, on a
schedule or with chant run. It runs the commands in the table above and
never applies. Watching a member has its fields, phases and
outcomes.