Repository navigation
feat(azure): wire payment_option to reservation billingPlanType (Azure RIs always bill upfront) #1502
Description
Activity
- addedtriagedItem has been triagedItem has been triagedpriority/p3Polish / idea / may never shipPolish / idea / may never shipseverity/lowMinor harmMinor harmurgency/eventuallyNo deadlineNo deadlineimpact/fewLimited audienceLimited audienceeffort/mDaysDaystype/featNew capabilityNew capability
on Jul 23, 2026 Scope widened: the same billingPlan omission affects 6 sibling Azure services
The compute wiring is landing in PR #1495 (new helper
reservations.BillingPlanForPaymentOption+billingPlanset in compute'sbuildReservationBody). An adversarial review confirmed the other six Azure reservation-based services build their own request bodies and never setbillingPlan, so ano-upfront/monthlyrecommendation for any of them is still silently billed at Azure's Upfront default:providers/azure/services/databaseproviders/azure/services/cacheproviders/azure/services/cosmosdbproviders/azure/services/searchproviders/azure/services/managedredisproviders/azure/services/synapse
Remaining work for this issue
Wire each of the six clients' reservation-body builders through the shared
reservations.BillingPlanForPaymentOptionhelper (same pattern PR #1495 applied to compute), with a per-service test asserting the marshaled body carriesbillingPlan: Monthlyfor no-upfront andUpfrontfor all-upfront, and fails loud on empty/partial-upfront. Compute is done in #1495; this issue now tracks the remaining six.- addedseverity/mediumModerate harmModerate harmand removedseverity/lowMinor harmMinor harm
on Jul 23, 2026 I can take the six remaining Azure service clients as an assignment-first fixed scope for 120 USDC, delivered within 48 hours after escrow funding.
Proposed acceptance checklist:
- thread each service's requested payment option through the existing shared
reservations.BillingPlanForPaymentOptionhelper fordatabase,cache,cosmosdb,search,managedredis, andsynapse; - serialize the SDK's typed billing-plan enum into each reservation purchase body —
Upfrontforall-upfront,Monthlyforno-upfront; - preserve fail-loud behavior for empty/unsupported/
partial-upfrontvalues rather than allowing Azure's implicit Upfront default; - add a table-driven or per-service body-marshalling test that proves both supported values and the rejection path for every client;
- run the affected provider tests plus
go test ./..., and provide exact output; - no Azure credentials, real reservations, or production purchases required.
Before implementation, please confirm:
- assignment to
bughunter-dotcom; - whether the branch should target current
mainor be based on open PR feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure, GCP #1495 where the shared helper and compute reference implementation currently live; - that the remaining scope is exactly the six services listed in your follow-up;
- a funded 120 USDC milestone and payout network.
Escrow option (LaborX, Base USDC): https://laborx.com/gigs/i-will-debug-and-fix-your-api-ci-or-qa-automation-with-tests-109222
Relevant deterministic test-delivery samples: https://github.com/bughunter-dotcom/resilient-http-client-sample and https://github.com/bughunter-dotcom/playwright-ci-sample
If direct fixed-USDC contracting is not available, no problem — I will not submit a speculative implementation.
- thread each service's requested payment option through the existing shared
- added 4 commits that reference this issue
on Jul 25, 2026 9 remaining items
- added 10 commits that reference this issue
on Jul 27, 2026 Additional evidence from the 2026-07-28 full-repo review (reviewed commit
be11bdcb5, origin/main). Confirmed still live onorigin/main; filing here rather than as a duplicate issue.The gap is wider than "Azure RIs always bill upfront": the system actively offers and prices a billing schedule that the purchase path cannot request.
Where (all seven purchase bodies, none of which sets
billingPlan):providers/azure/services/cache/client.go:305-321providers/azure/services/compute/client.go:422-438providers/azure/services/cosmosdb/client.go:306-322providers/azure/services/database/client.go:336-352providers/azure/services/managedredis/client.go:273-281providers/azure/services/search/client.go:256-272providers/azure/services/synapse/client.go:279-287
What:
rec.PaymentOptionis only consumed to build thedisplayNamestring.billingPlan(armreservations.ReservationBillingPlanMonthly/...Upfront) is never emitted in any of the seven.The aggravating detail:
ExpandPaymentVariants(providers/azure/internal/recommendations/converter.go:358-403) actively fans every recommendation into an "upfront" and a "monthly" variant, andGetOfferingDetails(e.g.cache/client.go) renders a monthly cashflow preview for the monthly variant that the purchase path cannot honor.Failure scenario: a user picks the "monthly" variant of a 3-year VM reservation quoted at roughly $X/month. Azure defaults the omitted
billingPlantoUpfrontand charges the full 3-year price on day one. The approved cashflow and the actual charge differ by the entire upfront amount.Fix direction (unchanged from this issue): map
rec.PaymentOptionto thearmreservations.ReservationBillingPlanenum constant, send it in both the calculatePrice and purchase bodies, and fail closed on an unrecognised value.Given that the monthly variant is surfaced and priced to the user, the severity and priority on this issue (currently
severity/medium,priority/p3) may be worth revisiting.- added a commit that references this issue
on Jul 28, 2026 Verified resolved at 3c0f8ac: rec.PaymentOption is mapped through reservations.BillingPlanForPaymentOption to the typed armreservations.ReservationBillingPlan enum and emitted as properties.billingPlan in the reservation body, with empty and partial-upfront values failing loud instead of silently defaulting to Upfront. Evidence: providers/azure/services/compute/client.go:499-505 and :436 (#1495, commit 887d51f). Residual axes checked: all seven sibling services (compute, cache, cosmosdb, database, managedredis, search, synapse) set billingPlan and each carries a billingPlan regression test; the same body is sent to both calculatePrice and purchase so the quoted and charged schedules agree; the MCP tool's blanket non-all-upfront rejection was lifted and only partial-upfront is now refused, which Azure genuinely cannot express; the RI-exchange path hardcodes Upfront but exposes no caller-supplied payment option, so nothing is silently overridden there. Closing as completed; reopen if the behaviour recurs.
- added a commit that references this issue
on Sep 27, 2026
Gap
Azure Reserved Instance purchases always bill upfront regardless of the requested
payment_option. The purchase body builder (providers/azure/services/compute/client.go,buildReservationBody) is called withopts.Sourceandopts.IdempotencyTokenbut never receivesopts.PaymentOption, so Azure's reservation API sends nobillingPlanTypeand defaults to upfront.Impact
no-upfrontorpartial-upfrontfor an Azure RI would silently get an upfront commitment (different cash-flow schedule than requested).all-upfrontAzure RI real purchases rather than silently mis-billing. So there is no active money-safety hole; this issue is to actually support the other billing plans.Fix
opts.PaymentOptionintobuildReservationBodyand map it to Azure'sbillingPlanType(UpfrontvsMonthly) on the reservation purchase request. Azure reservations supportUpfrontandMonthlybilling plans; mapall-upfront->Upfront,no-upfront/partial-upfront-> the closest supported plan (Monthly), or reject combinations Azure genuinely cannot express (fail loud, no silent default).all-upfrontinmcp/tools/azure_compute_ri.goand let the value flow through.Scope note
This is a provider-layer change that also benefits the CLI and web (
internal/api) purchase paths, not just MCP. Verify against Azure's reservation billing-plan API surface before wiring; do not assume the mapping.Related