Skip to content

Commit c87396b

Browse files
jrangelramosclaude
andcommitted
fix: make approveStage idempotent to prevent duplicate Analysis stage
When an ApprovalPolicy auto-approves the Analysis stage, calling approveStage('Analysis') again would fail with a duplicate error. Move the guard into the hook itself so all callers are protected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bdf72d8 commit c87396b

2 files changed

Lines changed: 12 additions & 3 deletions

File tree

src/components/update-plan/DecisionActions.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ const DecisionActions: React.FC<DecisionActionsProps> = ({ proposal, clusterVers
4444
[approvals, proposal.metadata?.name, proposal.metadata?.namespace],
4545
);
4646

47-
const { approveStage, denyStage, error, clearError, inProgress } =
48-
useApprovalActions(approval);
47+
const { approveStage, denyStage, error, clearError, inProgress } = useApprovalActions(approval);
4948

5049
const [confirmingApprove, setConfirmingApprove] = React.useState(false);
5150
const [confirmingDeny, setConfirmingDeny] = React.useState(false);
@@ -109,7 +108,15 @@ const DecisionActions: React.FC<DecisionActionsProps> = ({ proposal, clusterVers
109108
setConfirmingApprove(false);
110109
}, CONFIRM_TIMEOUT_MS);
111110
}
112-
}, [confirmingApprove, approveStage, proposal, clusterVersion, history, t]);
111+
}, [
112+
confirmingApprove,
113+
approveStage,
114+
approval?.spec?.stages,
115+
proposal,
116+
clusterVersion,
117+
history,
118+
t,
119+
]);
113120

114121
const handleDenyClick = React.useCallback(async () => {
115122
if (confirmingDeny) {

src/hooks/useApprovalActions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export const useApprovalActions = (approval?: LightspeedProposalApproval) => {
2323
setError(null);
2424
try {
2525
const existingStages = currentApproval.spec?.stages ?? [];
26+
if (existingStages.some((s) => s.type === stageType)) return true; // already approved
27+
2628
const stageEntry: Record<string, unknown> = {
2729
type: stageType,
2830
decision: 'Approved',

0 commit comments

Comments
 (0)