Skip to content

fix(stainless): restore typed union variants under the V2 normalizer - #1129

Draft
pengying wants to merge 5 commits into
mainfrom
peng/stainless-fix-union-variants
Draft

pengying wants to merge 5 commits into
mainfrom
peng/stainless-fix-union-variants

Conversation

@pengying

@pengying pengying commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What this does

The SDK generators return a union type wherever the spec uses oneOf, with one typed variant per schema. Since #525 set edition: 2026-05-06, five of those unions have generated without most of their variants. That release turned on the V2 normalizer, and V2 handles allOf differently from the old normalizer.

Our config strips each variant's base $ref (BaseQuoteSource, BaseExternalAccountInfo, and others), which leaves an empty entry in the variant's allOf. The old normalizer tolerated that. V2 can't tell the variants apart and collapses the union.

Union Before After
ExternalAccountInfoOneOf 5 variants 49
PaymentInstructions.AccountOrWalletInfo 3 18
QuoteSourceOneOf none 2
QuoteDestinationOneOf none 2
TransactionSourceOneOf none 3
AuthCredentialVerifyRequestOneOf none 4

In the Kotlin SDK 1.10.0, externalAccount.accountInfo() can't return an MXN, USD, or EUR account as a typed value, and QuoteSourceOneOf has no Account variant. The TypeScript generator types QuoteSourceOneOf as unknown.

Changes

  • Remove the six transforms that strip a base $ref from account, quote, and transaction variants.

  • Keep the transform that strips accountType from BaseExternalAccountInfo and BasePaymentAccountInfo, and also drop their required lists. Without this, V2 gives each account variant its own AccountType enum, and the generated to*AccountInfo() converters don't compile.

  • Remove three model refs to card summary schemas that Remove unimplemented CardTransaction summary fields #947 deleted. Every build reported them as missing references.

  • Add staging_repo and a publish.release block to the Kotlin target for stlc. The release block makes publish-sonatype.yml run on each GitHub release.

  • Move the descriptions on ExchangeRate.destinationPaymentRail and AccountDestination.paymentRail next to their $ref instead of wrapping the $ref in an allOf. V2 turns that wrapper into an untyped field, so the Kotlin SDK returned these as JsonValue instead of the PaymentRail enum. 33 other fields already use the $ref plus description form.

  • Also strip sourceType and destinationType, and their required entries, from the four source and destination base schemas, as for accountType. The SDK builders then default the type for each variant, as they did in Kotlin SDK 1.7.1, instead of requiring callers to set sourceType(ACCOUNT).

  • Stop registering QuoteRequest as a quotes model. The registration, added in chore(stainless): bump edition, sync resources, fix webhook discrimination #525, made QuoteCreateParams take one quoteRequest(QuoteRequest) argument. Without it, the builder has the flat source, destination, and lockedCurrencyAmount setters that Kotlin SDK 1.7.1 had.

  • Stop registering Refund as a sandbox card simulate model. The registration moved IncomingTransaction.refund() and OutgoingTransaction.refund() onto sandbox.cards.simulate.Refund. Without it, each transaction type keeps its own Refund class, as in 1.7.1.

  • Replace the transform that strips the AuthCredentialVerifyRequest $ref from each verify variant with a strip of the auth base schemas' required lists, the same fix as the account schemas. Under V2 the old transform left PasskeyCredentialVerifyRequest with no fields, so Kotlin SDK 1.9.0 and later can't build a passkey verify request. The create and verify unions now dispatch on type, so SMS_OTP and EMAIL_OTP requests deserialize as the right variant.

Tests

  • Generated the Kotlin SDK with stlc. ./gradlew test passes: 1,846 tests, 0 failures, 114 skipped. ./gradlew lint passes.
  • Compiled a file of Kotlin SDK 1.7.1 quote and transaction calls against each build. Against 1.7.1 it compiles cleanly. Against the build before the QuoteRequest and Refund change it needs edits on 26 lines; after the change, 21. The remaining edits are the V2 normalizer's union variant names (ofAccountQuoteSource is now ofAccount) and OutgoingTransaction.status(), which returns OutgoingTransaction.Status instead of OutgoingTransactionStatus. Setting x-stainless-variantName on the variants did not change the names under V2.
  • Generated the Python and TypeScript SDKs before and after. Both generate without errors after this change. Before it, both reported the three missing card summary refs.
  • tsc --noEmit on the TypeScript SDK reports the same three errors before and after: one TS2307 and two TS2353 in tests. None is the TS2312 the removed transforms were written to avoid.
  • Migrated samples/kotlin to the regenerated SDK. All 8 of its end-to-end tests pass against the dev sandbox. A program written against Kotlin SDK 1.10.0 that doesn't create quotes or build auth unions by hand compiles unchanged. The SDK PR lists what does change.

The regenerated Kotlin SDK is lightsparkdev/grid-kotlin-sdk#28.

🤖 Generated with Claude Code

Since #525 set edition 2026-05-06, the V2 normalizer has merged allOf
entries that the old normalizer dropped. Transforms written for the old
normalizer strip each variant's base $ref, which leaves an empty allOf
entry. V2 then can't tell the variants apart and collapses these unions
in every generated SDK:

- ExternalAccountInfoOneOf: 5 of 49 account variants
- PaymentInstructions.AccountOrWalletInfo: 3 of 18 variants
- QuoteSourceOneOf, QuoteDestinationOneOf, TransactionSourceOneOf: none

Remove the six base-$ref strips and the sourceType and destinationType
strips. Keep the accountType strip, and also drop accountType from the
base schemas' required lists, so each account variant reuses its shared
*AccountInfo enum and the generated converters compile.

Also drop three model refs to card summary schemas that #947 removed,
and configure the Kotlin target for stlc: a staging repo and a
release-please block so Maven publishing runs on each GitHub release.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

This PR does not match any of the 3 configured review trigger rules.

@vercel

vercel Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated
grid-cards-demo Ignored Ignored Preview Oct 7, 2026 2:04am UTC
grid-flow-builder Ignored Ignored Preview Oct 7, 2026 2:04am UTC
grid-wallet-demo Ignored Ignored Preview Oct 7, 2026 2:04am UTC

Request Review

pengying commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

ExchangeRate.destinationPaymentRail and AccountDestination.paymentRail
wrapped their PaymentRail $ref in an allOf to attach a description. The
V2 normalizer turns that wrapper into an untyped field, so the Kotlin
SDK returned these as JsonValue instead of the PaymentRail enum. Put the
description next to the $ref instead, as 33 other fields already do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mintlify

mintlify Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
Grid 🟢 Ready View Preview Oct 6, 2026, 10:37 PM

@github-actions github-actions Bot added the breaking-change Introduces a breaking change to the OpenAPI spec label Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Breaking OpenAPI changes detected

oasdiff reports 20 error / 342 warning changes to openapi.yaml.
This PR will need approval from an API reviewer before merge.

Errors (20)

  • GET /agents/approvals — the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • GET /agents/me/actions — the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • GET /agents/me/actions/{actionId} — the quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST /agents/me/quotes — request property destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail was restricted to a list of enum values [request-property-became-enum].
  • POST /agents/me/quotes — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail request property type/format changed from / to string/`` [request-property-type-changed].
  • POST /agents/me/quotes — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 201 [response-property-type-changed].
  • GET /agents/me/quotes/{quoteId} — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST /agents/me/quotes/{quoteId}/execute — the quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST /agents/{agentId}/actions/{actionId}/approve — the quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST /agents/{agentId}/actions/{actionId}/reject — the quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • GET /exchange-rates — the data/items/destinationPaymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST /quotes — request property destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail was restricted to a list of enum values [request-property-became-enum].
  • POST /quotes — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail request property type/format changed from / to string/`` [request-property-type-changed].
  • POST /quotes — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST /quotes — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 202 [response-property-type-changed].
  • GET /quotes/{quoteId} — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST /quotes/{quoteId}/authorize — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST /quotes/{quoteId}/execute — the destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response's property type/format changed from / to string/`` for status 200 [response-property-type-changed].
  • POST webhook:agent-action — request property allOf[subschema #2]/data/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail was restricted to a list of enum values [request-property-became-enum].
  • POST webhook:agent-action — the allOf[subschema #2]/data/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail request property type/format changed from / to string/`` [request-property-type-changed].

Warnings (342)

Show sample
  • GET /agents/approvals — added the new ACH enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new ACH_COLOMBIA enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new ACH_SAME_DAY enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new BANK_TRANSFER enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new BRE_B enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new CIPS enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new FAST enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new FASTER_PAYMENTS enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new FEDNOW enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new INSTAPAY enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new MOBILE_MONEY enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new NEFT enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new PAYNOW enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new PESONET enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new PIX enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new RTGS enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new RTP enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new SEPA enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new SEPA_INSTANT enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new SPEI enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new SWIFT enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new UNIONPAY enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new UPI enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/approvals — added the new WIRE enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • GET /agents/me/actions — added the new ACH enum value to the data/items/quote/allOf[#/components/schemas/Quote]/destination/oneOf[subschema #1: Account]/allOf[subschema #2]/paymentRail response property for the response status 200 [response-property-enum-value-added]. Adding new enum values to response could be unexpected for clients, use x-extensible-enum instead.
  • …and 317 more warnings.

Detected by oasdiff. Full report: job summary or the oasdiff-report artifact.

pengying and others added 3 commits October 6, 2026 15:44
The transform that strips the AuthCredentialVerifyRequest $ref from each
verify variant left PasskeyCredentialVerifyRequest with no fields under
the V2 normalizer, so Kotlin SDK 1.9.0 and later can't build a passkey
verify request. Replace it with a strip of the base schemas' required
lists, the same fix as the account schemas. The create and verify unions
then dispatch on type, so SMS_OTP and EMAIL_OTP requests deserialize as
the right variant.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Restoring the quote and transaction unions left each variant inheriting
the wide sourceType or destinationType enum from its base schema, so
the Kotlin SDK required callers to set the variant's own type, for
example sourceType(ACCOUNT) on QuoteSourceOneOf.Account. Strip those
enums and their required entries from the base schemas, as for
accountType. The builders then default the type per variant, as they did
in 1.7.1.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Stop registering QuoteRequest as a quotes model. The registration made
QuoteCreateParams take a single quoteRequest(QuoteRequest) argument;
without it the builder has flat setters (source, destination,
lockedCurrencyAmount) and nested LockedCurrencySide and PurposeOfPayment
enums, as in 1.7.1.

Stop registering Refund as a sandbox card simulate model. The registration
moved IncomingTransaction.refund() and OutgoingTransaction.refund() onto
sandbox.cards.simulate.Refund; without it each transaction keeps its own
Refund type, as in 1.7.1.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch was successfully deployed

1 active (outdated) deployment
staging - mintlify — 768befb5 Deployed Oct 6, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Introduces a breaking change to the OpenAPI spec

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant