diff --git a/static/app/views/settings/seer/overview/utils/seerStoppingPoint.ts b/static/app/views/settings/seer/overview/utils/seerStoppingPoint.ts index 091186a21fd845..bbffba4dabcaa2 100644 --- a/static/app/views/settings/seer/overview/utils/seerStoppingPoint.ts +++ b/static/app/views/settings/seer/overview/utils/seerStoppingPoint.ts @@ -134,6 +134,14 @@ export function getProjectStoppingPointMutationOptions({ project.slug ); + function resolveStoppingPointValue(stoppingPoint: SelectValue) { + return stoppingPoint === 'root_cause' + ? ('root_cause' as const) + : organization.autoOpenPrs + ? ('open_pr' as const) + : ('code_changes' as const); + } + return mutationOptions({ mutationFn: async ({stoppingPoint}: {stoppingPoint: SelectValue}) => { const tuning = stoppingPoint === 'off' ? ('off' as const) : ('medium' as const); @@ -144,39 +152,59 @@ export function getProjectStoppingPointMutationOptions({ data: {autofixAutomationTuning: tuning}, }); - let preferencePromise: Promise = - Promise.resolve(undefined); - if (stoppingPoint !== 'off') { - const repositories = preference?.repositories ?? []; - const automationHandoff = preference?.automation_handoff; - - const stoppingPointValue = - stoppingPoint === 'root_cause' - ? ('root_cause' as const) - : organization.autoOpenPrs - ? ('open_pr' as const) - : ('code_changes' as const); - - const preferencePayload = { - repositories, - automated_run_stopping_point: stoppingPointValue, - automation_handoff: automationHandoff - ? { - ...automationHandoff, - auto_create_pr: stoppingPointValue === 'open_pr', - } - : automationHandoff, - }; - - preferencePromise = fetchMutation({ - method: 'POST', - url: `/projects/${organization.slug}/${project.slug}/seer/preferences/`, - data: preferencePayload as unknown as Record, - }); + if (stoppingPoint === 'off') { + // When the stopping pointis set to 'off' we can leave the stoppingPoint + // value as-is because the tuning will take precedence and stop execution + // before we look at the handoff value. + // If we wanted to update the handoff what would we set it to? We can't + // clear it beacuse that would/could change the preferred agent value. + // Therefore we'll skip any update. + return await Promise.all([projectPromise, Promise.resolve(undefined)]); } + const stoppingPointValue = resolveStoppingPointValue(stoppingPoint); + const repositories = preference?.repositories ?? []; + const automationHandoff = preference?.automation_handoff; + + const preferencePayload = { + repositories, + automated_run_stopping_point: stoppingPointValue, + automation_handoff: automationHandoff, + }; + + const preferencePromise = fetchMutation({ + method: 'POST', + url: `/projects/${organization.slug}/${project.slug}/seer/preferences/`, + data: preferencePayload as unknown as Record, + }); return await Promise.all([projectPromise, preferencePromise]); }, + onMutate: ({stoppingPoint}: {stoppingPoint: SelectValue}) => { + const previousProject = ProjectsStore.getById(project.id); + const previousPreference = getApiQueryData( + queryClient, + seerPrefsQueryKey + ); + + const tuning = stoppingPoint === 'off' ? ('off' as const) : ('medium' as const); + ProjectsStore.onUpdateSuccess({...project, autofixAutomationTuning: tuning}); + + if (stoppingPoint !== 'off' && previousPreference?.preference) { + // If stopping point is 'off' then we're not updating the handoff value. + // Instead we'll just leave it (and the preferred agent value) as-is. + // The tuning value will take precedence and stop execution before we + // look at the handoff value. + setApiQueryData(queryClient, seerPrefsQueryKey, { + ...previousPreference, + preference: { + ...previousPreference.preference, + automated_run_stopping_point: resolveStoppingPointValue(stoppingPoint), + }, + }); + } + + return {previousProject, previousPreference}; + }, onSuccess: ([updatedProject, preferencePayload]) => { ProjectsStore.onUpdateSuccess(updatedProject); @@ -197,6 +225,24 @@ export function getProjectStoppingPointMutationOptions({ } } }, + onError: (_error: unknown, _variables: unknown, context: unknown) => { + const ctx = context as + | { + previousPreference: SeerPreferencesResponse | undefined; + previousProject: Project | undefined; + } + | undefined; + if (ctx?.previousProject) { + ProjectsStore.onUpdateSuccess(ctx.previousProject); + } + if (ctx?.previousPreference) { + setApiQueryData( + queryClient, + seerPrefsQueryKey, + ctx.previousPreference + ); + } + }, onSettled: () => { queryClient.invalidateQueries({queryKey: seerPrefsQueryKey}); diff --git a/static/gsApp/views/seerAutomation/components/projectDetails/autofixAgent.tsx b/static/gsApp/views/seerAutomation/components/projectDetails/autofixAgent.tsx index 958b32849a1050..8efef224645598 100644 --- a/static/gsApp/views/seerAutomation/components/projectDetails/autofixAgent.tsx +++ b/static/gsApp/views/seerAutomation/components/projectDetails/autofixAgent.tsx @@ -179,7 +179,8 @@ function StoppingPointField({ stoppingPointMutationOpts.onSuccess?.(data, variables, onMutateResult, context); addSuccessMessage(t('Stopping point updated')); }, - onError: () => { + onError: (error, variables, onMutateResult, context) => { + stoppingPointMutationOpts.onError?.(error, variables, onMutateResult, context); addErrorMessage(t('Failed to update stopping point')); }, });