docs(spec): railSelectionMode is an optional enum, not a nullable one - #899
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
✱ Stainless preview builds for gridThis PR will update the cli go kotlin openapi php python ruby typescript ✅ grid-ruby studio · code
|
|
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>
81b7142 to
7a1a631
Compare
Greptile SummaryThis PR changes
Confidence Score: 4/5The 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 Files Needing Attention: openapi/components/schemas/transactions/OutgoingTransaction.yaml, openapi.yaml, mintlify/openapi.yaml
|
| 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 |
There was a problem hiding this 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)
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.There was a problem hiding this comment.
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.
Reason
Two problems with
OutgoingTransaction.railSelectionMode— one in the prose, one in the schema.The description was wrong. It said:
That is not how the value is chosen.
MANUALmeans the caller specified apaymentRailon the destination andAUTOmeans Lightspark selects one, and that is settled when the payment is quoted — before any rail has been resolved. So an auto-routed payment reportsAUTOwhilepaymentRailis 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 fromrequired, 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:
nullThe field stays optional. Only the nullability goes, and the stale sentence goes with it:
A bare
$refalongside adescriptionis valid in OpenAPI 3.1, andreconciliationInstructionsin 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
nullfor 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 norailSelectionModekey 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
$refand an optional nullable$refboth produce an optional enum — verified by comparing againstreconciliationInstructions, which is already a bare$ref.Test Plan
openapi/components/schemas/transactions/OutgoingTransaction.yamlis the authored source;openapi.yamlandmintlify/openapi.yamlare 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=error— 0 errors (179 warnings, 722 infos, all pre-existing across the spec). Filtering the output forrailSelectionModereturns nothing, so this introduces no new finding on the field.🤖 Generated with Claude Code