You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(azure): web path silently coerces partial-upfront to all-upfront (bill schedule mismatch) #1503
crossProviderPaymentAlias (internal/config/validation.go:151-153) coerces an Azure partial-upfront payment option to "upfront"before validation, with the comment "coerce to the all-upfront tier so the rec survives validation."
On the web/API purchase path this means:
POST purchase {provider: azure, service: compute, payment: "partial-upfront"}
-> normalized to "upfront" (internal/api/validation.go:568)
-> passes the {upfront, monthly} whitelist
-> rec.Payment = "upfront"
-> buildReservationBody maps to billingPlan=Upfront
-> purchased ALL-UPFRONT silently, on a schedule the caller did not request
Why it matters now
PR #1495 wired Azure billingPlan from the payment option. Before that, Azure always billed upfront regardless, so the coercion was invisible. Now the coerced value drives a live billing schedule, so the silent coercion produces a real cash-flow outcome the caller did not choose. It also contradicts the contract the MCP tool and docs advertise ("partial-upfront is a hard error rather than silently purchased under all-upfront or no-upfront").
This is the exact class the project's feedback_no_silent_fallbacks rule targets: a money-affecting field silently rewritten instead of rejected.
Mitigating factors (why should-fix, not blocker)
The normal web UI only offers converter-generated recommendations (providers/azure/internal/recommendations/converter.go:391,397 emit only upfront/monthly), so a typical user cannot trigger this; it requires a hand-crafted partial-upfront payload.
Azure charges the same total for upfront vs monthly, so this is a billing-schedule/cash-flow mismatch, not an overcharge.
Surface inconsistency: the CLI path now fails loud on Azure partial-upfront (errors in buildReservationBody), while the web path coerces. The two surfaces should behave the same.
Fix
Reject Azure partial-upfront at the web/API validation boundary (fail loud, consistent with the CLI and MCP surfaces) instead of aliasing it to upfront. Azure supports only Upfront and Monthly; partial-upfront has no Azure equivalent and must not be silently mapped.
Found by
Adversarial money-path review during PR #1495 (Azure billing-plan wiring). Pre-existing (the alias predates #1495); #1495 is what makes the coercion reach a live billingPlan.
Activity
fix(azure): coerce web partial-upfront to monthly, not upfront
fix(azure): coerce web partial-upfront to monthly, not upfront
feat(api): surface payment-option coercions in the purchase response
fix(frontend): coerce azure partial-upfront to monthly, not upfront
docs(config): correct azure payment-coercion rationale
fix(frontend): surface azure payment coercion to the user (#1503)
fix(api): disclose payment coercion only when the schedule changes
fix(azure): coerce web partial-upfront to monthly, not upfront
feat(api): surface payment-option coercions in the purchase response
fix(frontend): coerce azure partial-upfront to monthly, not upfront
docs(config): correct azure payment-coercion rationale
fix(frontend): surface azure payment coercion to the user (#1503)
fix(api): disclose payment coercion only when the schedule changes