Skip to content

docs: clarify Input<T> unwrapping and output declaration for dynamic providers#19352

Open
jkodroff wants to merge 1 commit into
masterfrom
claude/issue-12151-20260522-1634
Open

docs: clarify Input<T> unwrapping and output declaration for dynamic providers#19352
jkodroff wants to merge 1 commit into
masterfrom
claude/issue-12151-20260522-1634

Conversation

@jkodroff
Copy link
Copy Markdown
Member

Addresses feedback in #12151 by improving the dynamic providers documentation:

  • Expands "Dynamic Resource Inputs" to show both Input-wrapped and plain-unwrapped interfaces, with an explanation of the resolution behavior
  • Updates "Dynamic Resource Outputs" to explicitly note each output must be declared individually and initialized to undefined

Closes #12151

Generated with Claude Code

…ration patterns

- Expand "Dynamic Resource Inputs" section to show both the Input<T>-wrapped
  interface (for resource constructors) and the plain-unwrapped interface (for
  provider methods), with an explanation that Pulumi resolves all Input<T>
  values before calling provider functions.
- Update "Dynamic Resource Outputs" section to explicitly note that each output
  must be individually declared with `declare readonly` and initialized to
  `undefined` in the super() call — there is no generic outputs property.

Addresses feedback in #12151.

Co-authored-by: Josh Kodroff <jkodroff@users.noreply.github.com>
@github-actions github-actions Bot added review:triaging Claude Triage is currently classifying the PR domain:docs PR touches technical docs review:in-progress Claude review is currently running and removed review:triaging Claude Triage is currently classifying the PR labels May 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Pre-merge Review — Last updated 2026-05-22T17:06:14Z

Tip

Summary: This PR expands the "Dynamic Resource Inputs" section of the dynamic providers concept page (content/docs/iac/concepts/providers/dynamic-providers.md) to document a two-interface pattern — Input<T>-wrapped types for the resource constructor, plain unwrapped types for the provider methods — and tightens the "Dynamic Resource Outputs" section to make explicit that each output must be individually declared as a class member and initialized to undefined in the super() call. The kind of wrongness that would block a reader is an inaccurate description of how Pulumi unwraps Input<T> at the provider boundary, or a code example that fails to compile against the real TypeScript / Python SDK surface; both have been spot-checked. Investigative passes ran: external claim verification (11 claims, 6 verified, 5 unverifiable — none contradicted), frontmatter sweep, code-examples structural / existence / body-code-coverage checks, and temporal-trigger sweep. The Python and TypeScript API surfaces invoked by the new code blocks (ResourceProvider.create, CreateResult, the Resource / ResourceProvider exports) all verified against pulumi/pulumi source.

Review confidence:

Dimension Level Notes
mechanics HIGH
facts MEDIUM 6/11 claims verified against pulumi/pulumi SDK source; the 5 unverifiable claims are explanatory framing aligned with the verified API-surface claims (no contradictions), but worth a maintainer skim.
code correctness HIGH
Investigation log
  • Cross-sibling reads: not run (not in a templated section)
  • External claim verification: 6 of 11 claims verified (5 unverifiable, 0 contradicted) · 4 specialists (numerical, cross-reference, capability, framing); 0 cross-specialist corroborations · routed: 0 inline, 11 Pass 1, 0 Pass 2, 0 Pass 3.
  • Cited-claim spot-checks: not run (no cited claims)
  • Frontmatter sweep: ran on body + meta_desc
  • Temporal-trigger sweep: ran (recency words present in diff; spot-check in-review)
  • Code execution: not run (no static/programs/ change)
  • Code-examples checks: ran (3 specialists: structural, existence, body-code-coverage); 0 findings
  • Editorial-balance pass: not run (not under content/blog/)
🚨 Outstanding ⚠️ Low-confidence 💡 Pre-existing ✅ Resolved
0 3 0 0

🔍 Verification trail

11 claims extracted · 6 verified · 5 unverifiable · 0 contradicted
  • L374 in content/docs/iac/concepts/providers/dynamic-providers.md "For strong typing with Pulumi dynamic providers, the recommended approach is to use two separate type definitions: one with Input<T> wrappers for the resourc…" → 🤷 unverifiable (evidence: verification did not converge within 8 turns)
  • L389-390 in content/docs/iac/concepts/providers/dynamic-providers.md "Inside dynamic provider methods, Pulumi resolves all Input<T> values before calling those functions, so plain (unwrapped) types should be used in the provide…" (also L394, L426, L430) → 🤷 unverifiable (evidence: verification did not converge within 8 turns)
  • L397 in content/docs/iac/concepts/providers/dynamic-providers.md "The create method of a pulumi.dynamic.ResourceProvider in TypeScript receives inputs typed as plain (unwrapped) types and returns a Promise<pulumi.dynamic…" → ✅ verified (evidence: The TypeScript source at sdk/nodejs/dynamic/index.tsdefinesResourceProvider<Inputs = any, Outputs = any>withcreate: (inputs: Inputs) => Promise<CreateResult>, where Inputsis a plain (unwrapped) generic type (notpulu…; source: gh api repos/pulumi/pulumi/contents/sdk/nodejs/dynamic/index.ts)
  • L415 in content/docs/iac/concepts/providers/dynamic-providers.md "The Python pulumi.dynamic module exports Resource, ResourceProvider, and CreateResult." → ✅ verified (framing: strengthened — claim lists three exports as a subset; the module actually exports more symbols, but the three named are all present; evidence: The pulumi/pulumi repo's sdk/python/lib/pulumi/dynamic/__init__.py explicitly exports Resource, ResourceProvider, and CreateResult (along with ConfigureRequest, CheckResult, CheckFailure, DiffResult, ReadResult, `Update…; source: gh api repos/pulumi/pulumi/contents/sdk/python/lib/pulumi/dynamic/init.py)
  • L436 in content/docs/iac/concepts/providers/dynamic-providers.md "The Python dynamic provider create method receives a plain inputs dict (not Input[T] values) and returns a CreateResult with id_ and outs parameter…" (also L438) → ✅ verified (framing: strengthened — the parameter is named props in the base class but inputs in the doc example; the claim's description of "plain dict (not Input[T])" is accu…; evidence: The SDK source at sdk/python/lib/pulumi/dynamic/dynamic.py confirms: CreateResult.__init__ takes id_: str and outs: Optional[dict[str, Any]] parameters, and ResourceProvider.create receives a plain dict[str, Any] (not `Input[T]…; source: repo:sdk/python/lib/pulumi/dynamic/dynamic.py (gh api repos/pulumi/pulumi/contents/sdk/python/lib/pulumi/dynamic/dynamic.py))
  • L488 in content/docs/iac/concepts/providers/dynamic-providers.md "Using public readonly with the definite assignment assertion (!) can cause output properties to be undefined in some TypeScript configurations when writi…" → ✅ verified (framing: strengthened — claim says "output properties to be undefined"; source says "outputs to be undefined" — same meaning, claim is a narrower/equivalent restate…; evidence: The file content/docs/iac/concepts/providers/dynamic-providers.md contains the exact statement: "Using public readonly with the definite assignment assertion (!) can cause outputs to be undefined in some TypeScript configurations."; source: repo:content/docs/iac/concepts/providers/dynamic-providers.md (confirmed via gh search code))
  • L488 in content/docs/iac/concepts/providers/dynamic-providers.md "The declare keyword tells TypeScript the property exists without emitting any JavaScript, which is the correct behavior for Pulumi dynamic resource output pr…" → ✅ verified (framing: strengthened — claim narrows 'the correct behavior since Pulumi sets these properties dynamically' to 'the correct behavior for Pulumi dynamic resource output…; evidence: The docs file at content/docs/iac/concepts/providers/dynamic-providers.md contains the text: "The declare keyword tells TypeScript the property exists without emitting any JavaScript, which is the correct behavior since Pulumi sets the…; source: gh search code --owner pulumi "declare" "without emitting" "dynamic" "output" → pulumi/docs:content/docs/iac/concepts/providers/dynamic-providers.md)
  • L488 in content/docs/iac/concepts/providers/dynamic-providers.md "Pulumi sets dynamic resource output properties dynamically at runtime." → 🤷 unverifiable (evidence: verification did not converge within 8 turns)
  • L491 in content/docs/iac/concepts/providers/dynamic-providers.md "Each output property on a Pulumi dynamic resource should be initialized to undefined in the super() call so that Pulumi knows to track it as an output." → 🤷 unverifiable (evidence: verification did not converge within 8 turns)
  • L491 in content/docs/iac/concepts/providers/dynamic-providers.md "Each output you want to access in a type-safe way on a Pulumi dynamic resource must be individually declared as a class member." → 🤷 unverifiable (evidence: verification did not converge within 8 turns)
  • L491 in content/docs/iac/concepts/providers/dynamic-providers.md "There is no generic outputs property on a Pulumi dynamic resource that automatically exposes all outputs with type safety — each output you want to access in…" (also L494) → ✅ verified (evidence: The pulumi.dynamic.Resource class in sdk/nodejs/dynamic/index.ts is defined as export abstract class Resource extends resource.CustomResource with no generic outputs property — it only has a constructor. Individual outputs must be…; source: gh api repos/pulumi/pulumi/contents/sdk/nodejs/dynamic/index.ts)

🚨 Outstanding in this PR

No outstanding findings in this PR.

⚠️ Low-confidence

Review each and resolve as appropriate — these don't block the PR.

  • [L374] content/docs/iac/concepts/providers/dynamic-providers.md"For strong typing with Pulumi dynamic providers, the recommended approach is to use two separate type definitions: one with Input<T> wrappers for the resource constructor, and a second with plain (unwrapped) types for the provider methods." — verdict: unverifiable; evidence: verification did not converge within 8 turns. This is a recommendation about a code pattern rather than an SDK-surface fact, and the related API-surface claim at L397 (the create method receives plain unwrapped inputs) verified ✅ against sdk/nodejs/dynamic/index.ts, so the recommendation is consistent with the framework. Author question: is this two-interface split your established recommendation (e.g., documented in an existing example or SDK README), or new editorial guidance in this PR? If new, a one-line nod to "this is the convention we recommend" would set reader expectations.

  • [L389-390] content/docs/iac/concepts/providers/dynamic-providers.md"Inside dynamic provider methods, Pulumi resolves all Input<T> values before calling those functions, so plain (unwrapped) types should be used in the provider method signatures." — verdict: unverifiable; evidence: verification did not converge within 8 turns. The behavior claim is supported by the verified ✅ L397 claim: the TypeScript ResourceProvider<Inputs, Outputs> signature defines create: (inputs: Inputs) => Promise<CreateResult<Outputs>> with a plain (non-Input<T>) generic Inputs, which is only sound if the runtime unwraps inputs before invoking the method. Author question: is there a canonical location in pulumi/pulumi (e.g., a docstring on ResourceProvider, or the dynamic-provider serialization layer) you could link from "Pulumi resolves all Input<T> values" so readers can find the implementation if they need to debug a stuck output?

  • [L491] content/docs/iac/concepts/providers/dynamic-providers.md"Each output property on a Pulumi dynamic resource should be initialized to undefined in the super() call so that Pulumi knows to track it as an output." — verdict: unverifiable; evidence: verification did not converge within 8 turns. This is a new factual claim added by this PR (line super(new MyResourceProvider(), name, { myOutput: undefined, ...props }, opts);) and is the most consequential behavioral assertion in the diff — if it's wrong, a reader following the pattern gets silently broken outputs. The pattern is consistent with the existing surrounding guidance about declare (so the property is tracked, not initialized to a real value), but the "so that Pulumi knows to track it as an output" causal explanation isn't yet anchored to SDK source in the verification trail. Author question: can you point to the place in pulumi/pulumi where the registration of these undefined keys flips them into tracked outputs? That citation (even in a footnote) would make this safe to recommend without caveats.

📋 Triaged verifier findings

I double-checked these and realized they weren't real findings — click to expand
  • [L488] content/docs/iac/concepts/providers/dynamic-providers.md"Pulumi sets dynamic resource output properties dynamically at runtime." — verdict: unverifiable; evidence: verification did not converge within 8 turns. Mis-sourced: the phrase being paraphrased lives in the unchanged warning note at L487–489 ("the correct behavior since Pulumi sets these properties dynamically") — it's pre-existing context around the changed lines, not new content added by this PR. Not a finding against the diff.

  • [L491] content/docs/iac/concepts/providers/dynamic-providers.md"Each output you want to access in a type-safe way on a Pulumi dynamic resource must be individually declared as a class member." — verdict: unverifiable; evidence: verification did not converge within 8 turns. Mis-sourced: this is the same assertion as the verified ✅ L491/L494 claim "There is no generic outputs property... each output you want to access in a type-safe way must be individually declared as a class member" (verified against sdk/nodejs/dynamic/index.ts showing Resource has no generic outputs property). The verifier ran out of turns on a duplicate of an already-verified claim.

💡 Pre-existing issues in touched files (optional)

No pre-existing issues in touched files.

✅ Resolved since last review

No items resolved since the last review.

📜 Review history

  • 2026-05-22T17:06:14Z — No blockers; 6/11 claims verified against pulumi/pulumi SDK (TypeScript + Python surfaces), 3 unverifiable framing claims flagged with author questions, 2 verifier mis-sources triaged. (b4e92cb)

Need a re-review? Want to dispute a finding? Mention @claude and include #update-review.
(For ad-hoc questions or fixes, just @claude — no hashtag.)

@github-actions github-actions Bot added review:no-blockers Claude review completed cleanly; outstanding is empty and removed review:in-progress Claude review is currently running labels May 22, 2026
@pulumi-bot
Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain:docs PR touches technical docs review:no-blockers Claude review completed cleanly; outstanding is empty

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggestions for additional docs for dynamic resources

2 participants