Summary
The frontend still groups bulk purchases without account identity, while the backend now requires every plan-less execution to contain one cloud account. Mixed-account selections can therefore produce a purchase request that the backend rejects with HTTP 400.
Current behavior and evidence
Statically confirmed against main eac9a62a88d49cbb30abf1dc3943037dd1d58b0b while reviewing LeanerCloud/cloud-commitments-cli#2071:
frontend/src/recommendations.ts:4028-4035 groups selected rows by provider, service, term and payment, but not cloud_account_id.
frontend/src/app.ts submits each resulting modal bucket as one executePurchase request.
internal/api/handler_purchases.go:2334 calls purchase.SingleCloudAccountIDFromRecs and returns HTTP 400 on ambiguity.
internal/purchase/execution.go:893-920 rejects both multiple account IDs and a mix of attributed and unattributed selected rows.
The API rejection is intentional safety behavior added in LeanerCloud/cloud-commitments-cli#2072 for LeanerCloud/cloud-commitments-cli#1902. Do not relax it. This is a frontend contract mismatch, not evidence that a real purchase was made incorrectly. No live cloud purchase or browser reproduction was performed for this finding.
Steps to reproduce
- Load two purchasable recommendations from different cloud accounts with the same provider, service, term and payment.
- Select both and open the bulk purchase modal.
- In an isolated fixture-backed verification environment, intercept the resulting submission and inspect its recommendations: both accounts share one POST.
- Exercise the real backend validation with that body: it returns HTTP 400 requiring one purchase per account.
Expected behavior
Each submitted execution contains one account. The modal clearly represents the resulting per-account submissions, totals and approval outcome. Unattributed recommendations must not be silently assigned to another account.
Proposed fix
Include account identity in frontend grouping, or split submissions per account with matching modal representation. Cover both the ordinary bulk modal and fan-out path in frontend/src/recommendations.ts and frontend/src/app.ts. Preserve the backend guard. Add regression coverage using real frontend grouping and the backend request contract, including mixed attributed/unattributed rows.
Severity and priority
High severity, P1: a supported multi-account bulk workflow is blocked after the safety fix. Selecting one account at a time is a workaround. Scope is limited to multi-account users; estimated effort S; urgency this sprint.
References
Summary
The frontend still groups bulk purchases without account identity, while the backend now requires every plan-less execution to contain one cloud account. Mixed-account selections can therefore produce a purchase request that the backend rejects with HTTP 400.
Current behavior and evidence
Statically confirmed against main
eac9a62a88d49cbb30abf1dc3943037dd1d58b0bwhile reviewing LeanerCloud/cloud-commitments-cli#2071:frontend/src/recommendations.ts:4028-4035groups selected rows by provider, service, term and payment, but notcloud_account_id.frontend/src/app.tssubmits each resulting modal bucket as oneexecutePurchaserequest.internal/api/handler_purchases.go:2334callspurchase.SingleCloudAccountIDFromRecsand returns HTTP 400 on ambiguity.internal/purchase/execution.go:893-920rejects both multiple account IDs and a mix of attributed and unattributed selected rows.The API rejection is intentional safety behavior added in LeanerCloud/cloud-commitments-cli#2072 for LeanerCloud/cloud-commitments-cli#1902. Do not relax it. This is a frontend contract mismatch, not evidence that a real purchase was made incorrectly. No live cloud purchase or browser reproduction was performed for this finding.
Steps to reproduce
Expected behavior
Each submitted execution contains one account. The modal clearly represents the resulting per-account submissions, totals and approval outcome. Unattributed recommendations must not be silently assigned to another account.
Proposed fix
Include account identity in frontend grouping, or split submissions per account with matching modal representation. Cover both the ordinary bulk modal and fan-out path in
frontend/src/recommendations.tsandfrontend/src/app.ts. Preserve the backend guard. Add regression coverage using real frontend grouping and the backend request contract, including mixed attributed/unattributed rows.Severity and priority
High severity, P1: a supported multi-account bulk workflow is blocked after the safety fix. Selecting one account at a time is a workaround. Scope is limited to multi-account users; estimated effort S; urgency this sprint.
References