Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {GroupFixture} from 'sentry-fixture/group';

import {renderHook} from 'sentry-test/reactTestingLibrary';

import type {useExplorerAutofix} from 'sentry/components/events/autofix/useExplorerAutofix';
import {useAutoTriggerAutofix} from 'sentry/components/events/autofix/v3/useAutoTriggerAutofix';

function makeAutofix(
overrides: Partial<ReturnType<typeof useExplorerAutofix>> = {}
): ReturnType<typeof useExplorerAutofix> {
return {
runState: null,
startStep: jest.fn(),
createPR: jest.fn(),
reset: jest.fn(),
triggerCodingAgentHandoff: jest.fn(),
isLoading: false,
isPolling: false,
...overrides,
} as ReturnType<typeof useExplorerAutofix>;
}

describe('useAutoTriggerAutofix', () => {
it('starts root_cause when seerAutofixLastTriggered is set but seerExplorerAutofixLastTriggered is not', () => {
const autofix = makeAutofix();
const group = GroupFixture({
seerAutofixLastTriggered: '2024-01-01T00:00:00Z',
seerExplorerAutofixLastTriggered: null,
});

renderHook(() => useAutoTriggerAutofix({autofix, group}));

expect(autofix.startStep).toHaveBeenCalledWith('root_cause');
expect(autofix.startStep).toHaveBeenCalledTimes(1);
});

it('does not start root_cause when seerExplorerAutofixLastTriggered is set', () => {
const autofix = makeAutofix();
const group = GroupFixture({
seerAutofixLastTriggered: '2024-01-01T00:00:00Z',
seerExplorerAutofixLastTriggered: '2024-01-02T00:00:00Z',
});

renderHook(() => useAutoTriggerAutofix({autofix, group}));

expect(autofix.startStep).not.toHaveBeenCalled();
});

it('does not trigger root_cause more than once on re-render', () => {
const autofix = makeAutofix();
const group = GroupFixture({
seerAutofixLastTriggered: '2024-01-01T00:00:00Z',
seerExplorerAutofixLastTriggered: null,
});

const {rerender} = renderHook(() => useAutoTriggerAutofix({autofix, group}));

rerender();
rerender();

expect(autofix.startStep).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {useEffect, useRef} from 'react';

import {useExplorerAutofix} from 'sentry/components/events/autofix/useExplorerAutofix';
import type {Group} from 'sentry/types/group';

interface UseAutoTriggerAutofixOptions {
autofix: ReturnType<typeof useExplorerAutofix>;
group: Group;
}

export function useAutoTriggerAutofix({autofix, group}: UseAutoTriggerAutofixOptions) {
const alreadyTriggered = useRef(false);

// extract startStep first here so we can depend on it directly as `autofix` itself is unstable.
const startStep = autofix.startStep;

useEffect(() => {
if (alreadyTriggered.current) {
return;
}

// In order to have a smooth transition from legacy to explorer autofix, we want to automatically
// trigger autofix when users view an issue that had legacy but not explorer autofix.
const shouldAutotriggerAutofix =
!!group.seerAutofixLastTriggered && !group.seerExplorerAutofixLastTriggered;

if (!shouldAutotriggerAutofix) {
return;
}

alreadyTriggered.current = true;
startStep('root_cause');
}, [group, startStep]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ref not reset when group changes prevents re-triggering

Medium Severity

The alreadyTriggered ref never resets when group.id changes. When a user navigates between issues without the component unmounting (e.g., prev/next navigation), the ref stays true from the first issue, preventing auto-trigger for any subsequent qualifying issue. The ref needs to be tied to the group identity so it resets when viewing a different issue.

Fix in Cursor Fix in Web

}
1 change: 1 addition & 0 deletions static/app/types/group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,7 @@ export interface BaseGroup {
latestEventHasAttachments?: boolean;
owners?: SuggestedOwner[] | null;
seerAutofixLastTriggered?: string | null;
seerExplorerAutofixLastTriggered?: string | null;
seerFixabilityScore?: number | null;
sentryAppIssues?: PlatformExternalIssue[];
substatus?: GroupSubstatus | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
RootCausePreview,
SolutionPreview,
} from 'sentry/components/events/autofix/v3/autofixPreviews';
import {useAutoTriggerAutofix} from 'sentry/components/events/autofix/v3/useAutoTriggerAutofix';
import {useGroupSummaryData} from 'sentry/components/group/groupSummary';
import {HookOrDefault} from 'sentry/components/hookOrDefault';
import {Placeholder} from 'sentry/components/placeholder';
Expand Down Expand Up @@ -125,6 +126,8 @@ export function AutofixContent({aiConfig, group, project, event}: AutofixContent
const autofix = useExplorerAutofix(group.id);
const {data: setupCheck, isPending} = useSeerOnboardingCheck();

useAutoTriggerAutofix({autofix, group});

if (
// waiting on the onboarding checks to load
isPending ||
Expand Down
Loading