Repository navigation
fix(frontend/history): Retry button still offered on Azure savings-plans rows the backend now refuses #161
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/sHoursHourstype/bugDefectDefectpriority/p2Backlog-worthyBacklog-worthyseverity/mediumModerate harmModerate harmurgency/this-sprintWithin the current sprintWithin the current sprintand removedpriority/p3Polish / idea / may never shipPolish / idea / may never shipseverity/lowMinor harmMinor harmurgency/eventuallyNo deadlineNo deadline
on Aug 3, 2026 Raised p3 -> p2, severity/low -> medium, urgency/eventually -> this-sprint.
A control that is always offered and always fails is worse than "eventually". Once LeanerCloud/cloud-commitments-cli#1713 lands, every Azure savings-plans row in History keeps a Retry button that returns a permanent 409, and the user has no way to tell that from a transient failure they should retry later. The likely outcomes are repeated clicking and a support contact asking to have it unblocked, which is the behaviour the p0 exists to discourage even though the money risk is now closed server-side.
CodeRabbit independently flagged the same thing on PR LeanerCloud/cloud-commitments-cli#1713 (
internal/api/handler_purchases.go:1751): the frontend ignores theredrive_unsafedetail and renders the permanent refusal as an operator-fixableops_hint. So the response already carries the signal needed to fix this; the frontend just does not read it.Correcting my own reasoning on the record: I initially pushed for this to be fixed inside LeanerCloud/cloud-commitments-cli#1713 and told the implementer it outranked the other finding. That was wrong. I had assumed a user could still reach a double-purchase through the UI, and the backend gate makes that impossible, so the remaining defect is presentational. The implementer's judgement to split it out was better than my instruction, and holding a p0 for it would have been the wrong trade. p2 with the signal already present in the API response is the right shape.
Scope when someone picks this up:
handler_history.goderivesops_hintfrom the failure-reason map, and the frontend hides the Retry button only on that. It needs to also treatredrive_unsafeas terminal. Keep it to that.
Follow-up to LeanerCloud/cloud-commitments-cli#1668 / LeanerCloud/cloud-commitments-cli#1713, which closed the money hole: the retry endpoint now returns a 409 with
ops_hint+redrive_unsafe: truewhen an execution's recommendations are not safe to re-drive (Azure savings-plans, or an unrecognized provider).The History UI still renders the Retry button on those rows. It hides the button only when the row carries a server-computed
ops_hint(frontend/src/history.ts:603), andhandler_history.gocomputes that field from the failure-reason map alone, which knows nothing about provider re-drive safety. So the operator clicks Retry, the backend refuses, and they get an error toast instead of never being offered the action.Purely cosmetic now that the backend fails closed, but the button promises something that can never succeed.
Suggested shape: have the History row projection surface the same
purchase.RedriveRefusalReasonresult (asops_hintplus aredrive_unsafeflag so the badge can word itself as permanent rather than operator-fixable), and let the existingif (p.ops_hint) return falseincanRetrydo the rest. Touchesinternal/api/handler_history.go,internal/config/types.go(theOpsHintprojection struct),frontend/src/types.tsandfrontend/src/history.ts.