Skip to content

docs(spec): railSelectionMode is an optional enum, not a nullable one - #899

Merged
shreyav merged 1 commit into
mainfrom
claude/rail-selection-mode-description
Sep 8, 2026
Merged

docs(spec): railSelectionMode is an optional enum, not a nullable one#899
shreyav merged 1 commit into
mainfrom
claude/rail-selection-mode-description

Conversation

@shreyav

@shreyav shreyav commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Reason

Two problems with OutgoingTransaction.railSelectionMode — one in the prose, one in the schema.

The description was wrong. It said:

…AUTO when Lightspark selects it. Null when no rail is resolved.

That is not how the value is chosen. MANUAL means the caller specified a paymentRail on the destination and AUTO means Lightspark selects one, and that is settled when the payment is quoted — before any rail has been resolved. So an auto-routed payment reports AUTO while paymentRail is still null, which is exactly the combination the sentence said would not occur.

The field was nullable for no reason. It was anyOf: [RailSelectionMode, 'null'] while also absent from required, making it both optional and nullable. Nothing needs the null: absent and null carry the same meaning for this field.

Overview

Optional and nullable are independent, which is the whole of the change:

key absent key present as null
optional, not nullable
optional and nullable

The field stays optional. Only the nullability goes, and the stale sentence goes with it:

railSelectionMode:
  $ref: ../common/RailSelectionMode.yaml
  description: >-
    How the rail was chosen — MANUAL when the platform specified a
    paymentRail on the destination, AUTO when Lightspark selects it.

A bare $ref alongside a description is valid in OpenAPI 3.1, and reconciliationInstructions in this same schema already uses that form — this follows an existing pattern rather than introducing one.

What changes for consumers

The key is omitted rather than sent as null.

Generators that emit an explicit null for a field that is both nullable and set will drop it from that list once the field is no longer nullable, so an unresolved rail produces no railSelectionMode key at all. Anything reading the field by direct index rather than a safe accessor should move to the latter.

The generated type does not change. An optional $ref and an optional nullable $ref both produce an optional enum — verified by comparing against reconciliationInstructions, which is already a bare $ref.

Test Plan

openapi/components/schemas/transactions/OutgoingTransaction.yaml is the authored source; openapi.yaml and mintlify/openapi.yaml are build outputs, regenerated rather than hand-edited.

  • npm run build:openapi — the rebuild touches only this property in each bundle. No incidental churn, which also confirms the checked-in bundles were in sync with their sources beforehand.
  • npx @redocly/cli lint openapi.yaml"Your API description is valid." 55 warnings, all pre-existing.
  • npx @stoplight/spectral-cli lint openapi.yaml --fail-severity=error0 errors (179 warnings, 722 infos, all pre-existing across the spec). Filtering the output for railSelectionMode returns nothing, so this introduces no new finding on the field.
  • The serialization behaviour described above was measured against a generated client rather than assumed — both construction paths (field passed as null, field never passed) and the resulting payloads.

🤖 Generated with Claude Code

@mintlify

mintlify Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

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

Project Status Preview Updated
Grid 🟢 Ready View Preview Sep 4, 2026, 10:24 PM

@vercel

vercel Bot commented Sep 4, 2026

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Actions Updated
grid-flow-builder Ignored Ignored Preview Sep 4, 2026 10:56pm UTC
grid-wallet-demo Ignored Ignored Preview Sep 4, 2026 10:56pm UTC

Request Review

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds for grid

This PR will update the grid SDKs with the following commit messages.

cli

chore(internal): regenerate SDK with no functional changes

go

docs(api): clarify RailSelectionMode behavior in OutgoingTransaction

kotlin

docs(api): clarify railSelectionMode behavior in OutgoingTransaction

openapi

docs(types): clarify railSelectionMode field description

php

docs(api): clarify railSelectionMode timing in OutgoingTransaction

python

docs(types): clarify rail_selection_mode description in OutgoingTransaction

ruby

docs(api): clarify rail_selection_mode behavior in OutgoingTransaction

typescript

docs(types): clarify railSelectionMode documentation in OutgoingTransaction
grid-typescript studio · conflict

Your SDK build had at least one note diagnostic.

⚠️ grid-openapi studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️

grid-ruby studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ✅lint ✅test ✅

⚠️ grid-kotlin studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ⚠️build ✅lint ✅test ❗

⚠️ grid-go studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

go get github.com/stainless-sdks/grid-go@e07b1ef68aee2daa7d5ef8e7fba4e11c725f0558
⚠️ grid-python studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

pip install https://pkg.stainless.com/s/grid-python/44a096c8f9f0600e5de948f5ce1f1db403a53752/grid-0.0.1-py3-none-any.whl
grid-php studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅lint ✅test ✅

⚠️ grid-cli studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ⚠️build ⏭️lint ⏭️test ❗


This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-09-08 16:10:22 UTC

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

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

⚠️ Breaking OpenAPI changes detected

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

Errors (4)

  • POST webhook:agent-action — request property allOf[subschema #2]/data/transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode was restricted to a list of enum values [request-property-became-enum].
  • POST webhook:agent-action — request property allOf[subschema #2]/data/transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode list-of-types was narrowed by removing types null from media type application/json [request-property-list-of-types-narrowed].
  • POST webhook:outgoing-payment — request property allOf[subschema #2]/data/allOf[subschema #2]/railSelectionMode was restricted to a list of enum values [request-property-became-enum].
  • POST webhook:outgoing-payment — request property allOf[subschema #2]/data/allOf[subschema #2]/railSelectionMode list-of-types was narrowed by removing types null from media type application/json [request-property-list-of-types-narrowed].

Warnings (30)

Show sample
  • GET /agents/approvals — added the new AUTO enum value to the data/items/transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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 MANUAL enum value to the data/items/transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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 AUTO enum value to the data/items/transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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 MANUAL enum value to the data/items/transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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/{actionId} — added the new AUTO enum value to the transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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/{actionId} — added the new MANUAL enum value to the transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /agents/me/quotes/{quoteId}/execute — added the new AUTO enum value to the transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /agents/me/quotes/{quoteId}/execute — added the new MANUAL enum value to the transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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/transactions — added the new AUTO enum value to the data/items/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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/transactions — added the new MANUAL enum value to the data/items/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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/transactions/{transactionId} — added the new AUTO enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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/transactions/{transactionId} — added the new MANUAL enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /agents/{agentId}/actions/{actionId}/approve — added the new AUTO enum value to the transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /agents/{agentId}/actions/{actionId}/approve — added the new MANUAL enum value to the transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /agents/{agentId}/actions/{actionId}/reject — added the new AUTO enum value to the transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /agents/{agentId}/actions/{actionId}/reject — added the new MANUAL enum value to the transaction/allOf[#/components/schemas/TransactionOneOf]/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /sandbox/send — added the new AUTO enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /sandbox/send — added the new MANUAL enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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 /transactions — added the new AUTO enum value to the data/items/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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 /transactions — added the new MANUAL enum value to the data/items/oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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 /transactions/{transactionId} — added the new AUTO enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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 /transactions/{transactionId} — added the new MANUAL enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /transactions/{transactionId}/cancel — added the new AUTO enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /transactions/{transactionId}/cancel — added the new MANUAL enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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.
  • POST /transactions/{transactionId}/confirm — added the new AUTO enum value to the oneOf[subschema #2: Outgoing Transaction]/allOf[subschema #2]/railSelectionMode 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 5 more warnings.

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

@shreyav shreyav changed the title docs(spec): railSelectionMode is decided at quote time, not at resolution docs(spec): railSelectionMode is decided at quote time, and is a plain optional enum Sep 4, 2026
Two fixes to `OutgoingTransaction.railSelectionMode`.

The description claimed "Null when no rail is resolved." That is not how the
value is chosen: MANUAL means the caller specified a paymentRail on the
destination and AUTO means Lightspark selects one, and that is settled when
the payment is quoted, before any rail has been resolved. An auto-routed
payment therefore reports AUTO while paymentRail is still null — the
combination the sentence said would not occur.

The field was also `anyOf: [RailSelectionMode, 'null']` while absent from
`required`, making it both optional and nullable. Optional and nullable are
independent: optional allows the key to be absent, nullable allows it to be
present as `null`. Nothing needs the null here — absent and null carry the
same meaning — so only the nullability is dropped and the field stays
optional.

A bare `$ref` alongside a `description` is valid in OpenAPI 3.1, and
`reconciliationInstructions` in this schema already uses that form.

Generated clients that emit an explicit null for nullable-and-set fields will
now omit the key instead. The generated type is unchanged: an optional `$ref`
and an optional nullable `$ref` both produce an optional enum.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@shreyav
shreyav force-pushed the claude/rail-selection-mode-description branch from 81b7142 to 7a1a631 Compare September 4, 2026 22:55
@shreyav shreyav changed the title docs(spec): railSelectionMode is decided at quote time, and is a plain optional enum docs(spec): railSelectionMode is an optional enum, not a nullable one Sep 4, 2026
@shreyav
shreyav marked this pull request as ready for review September 4, 2026 22:56
@greptile-apps

greptile-apps Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes OutgoingTransaction.railSelectionMode from an optional nullable enum to an optional non-nullable enum and removes the stale statement tying nullability to rail resolution.

  • Updates the modular transaction schema.
  • Regenerates both published OpenAPI bundles consistently.
  • Narrows the accepted response shape without introducing the repository-required API version boundary.

Confidence Score: 4/5

The PR is not safe to merge as written because it narrows a published response contract without the required API version and server-path change.

Existing consumers of the 2025-10-13 contract may validly handle or validate an explicit null, but the revised schema rejects that value while continuing to identify itself as the same API version.

Files Needing Attention: openapi/components/schemas/transactions/OutgoingTransaction.yaml, openapi.yaml, mintlify/openapi.yaml

Important Files Changed

Filename Overview
openapi/components/schemas/transactions/OutgoingTransaction.yaml Narrows railSelectionMode to an optional non-null enum, creating an unversioned response-contract break.
openapi.yaml Correctly reflects the source change in the assembled contract, but retains the existing API version.
mintlify/openapi.yaml Keeps the documentation bundle synchronized with the narrowed source contract.
Prompt To Fix All With AI
### Issue 1
openapi/components/schemas/transactions/OutgoingTransaction.yaml:85
**Unversioned response contract break**

For the existing API version, `railSelectionMode: null` was a valid response value. Removing the `null` branch makes those payloads fail schema validation or deserialization, while the contract still uses version and server path `2025-10-13`. The repository requires a new `info.version` and matching server URL when a breaking change ships. Preserve nullability for this version or publish the narrowed contract under a new version.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "docs(spec): railSelectionMode is an opti..." | Re-trigger Greptile

anyOf:
- $ref: ../common/RailSelectionMode.yaml
- type: 'null'
$ref: ../common/RailSelectionMode.yaml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unversioned response contract break

For the existing API version, railSelectionMode: null was a valid response value. Removing the null branch makes those payloads fail schema validation or deserialization, while the contract still uses version and server path 2025-10-13. The repository requires a new info.version and matching server URL when a breaking change ships. Preserve nullability for this version or publish the narrowed contract under a new version.

Context Used: CLAUDE.md (source)

Knowledge Base Used: Grid API contract

Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/transactions/OutgoingTransaction.yaml
Line: 85

Comment:
**Unversioned response contract break**

For the existing API version, `railSelectionMode: null` was a valid response value. Removing the `null` branch makes those payloads fail schema validation or deserialization, while the contract still uses version and server path `2025-10-13`. The repository requires a new `info.version` and matching server URL when a breaking change ships. Preserve nullability for this version or publish the narrowed contract under a new version.

**Context Used:** CLAUDE.md ([source](https://github.com/lightsparkdev/grid-api/blob/main/CLAUDE.md))

**Knowledge Base Used:** [Grid API contract](https://app.greptile.com/lightspark/-/custom-context/knowledge-base/lightsparkdev/grid-api/-/docs/api-contract.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not being used

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood — if the server never emits null, there may be no runtime impact for current responses. My concern is that the published 2025-10-13 schema still advertises null as valid, so clients or validators may rely on that contract even if it is not currently observed. Narrowing it is therefore still a breaking schema change; either retain nullability for that version or publish the narrowed contract under a new API version/server path.

@shreyav
shreyav merged commit cf254dc into main Sep 8, 2026
10 checks passed
@shreyav
shreyav deleted the claude/rail-selection-mode-description branch September 8, 2026 16:04
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.

3 participants