diff --git a/static/app/views/settings/projectSeer/index.spec.tsx b/static/app/views/settings/projectSeer/index.spec.tsx
index 4578e19a11597a..d0a8159a57fabb 100644
--- a/static/app/views/settings/projectSeer/index.spec.tsx
+++ b/static/app/views/settings/projectSeer/index.spec.tsx
@@ -583,4 +583,118 @@ describe('ProjectSeer', () => {
});
expect(toggle).toBeInTheDocument();
});
+
+ describe('Auto-Trigger Fixes with triage-signals-v0', () => {
+ it('shows as toggle when flag enabled, dropdown when disabled', async () => {
+ const projectWithFlag = ProjectFixture({
+ features: ['triage-signals-v0'],
+ seerScannerAutomation: true,
+ autofixAutomationTuning: 'off',
+ });
+
+ const {unmount} = render(, {
+ organization,
+ outletContext: {project: projectWithFlag},
+ });
+
+ await screen.findByText(/Automation/i);
+ expect(
+ screen.getByRole('checkbox', {name: /Auto-Trigger Fixes/i})
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByRole('textbox', {name: /Auto-Trigger Fixes/i})
+ ).not.toBeInTheDocument();
+
+ unmount();
+
+ render(, {
+ organization,
+ outletContext: {
+ project: ProjectFixture({
+ seerScannerAutomation: true,
+ autofixAutomationTuning: 'high',
+ }),
+ },
+ });
+
+ await screen.findByText(/Automation/i);
+ expect(
+ screen.getByRole('textbox', {name: /Auto-Trigger Fixes/i})
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByRole('checkbox', {name: /Auto-Trigger Fixes/i})
+ ).not.toBeInTheDocument();
+ });
+
+ it('maps values correctly: off=unchecked, others=checked', async () => {
+ const {unmount} = render(, {
+ organization,
+ outletContext: {
+ project: ProjectFixture({
+ features: ['triage-signals-v0'],
+ seerScannerAutomation: true,
+ autofixAutomationTuning: 'off',
+ }),
+ },
+ });
+
+ expect(
+ await screen.findByRole('checkbox', {name: /Auto-Trigger Fixes/i})
+ ).not.toBeChecked();
+ unmount();
+
+ render(, {
+ organization,
+ outletContext: {
+ project: ProjectFixture({
+ features: ['triage-signals-v0'],
+ seerScannerAutomation: true,
+ autofixAutomationTuning: 'high',
+ }),
+ },
+ });
+
+ expect(
+ await screen.findByRole('checkbox', {name: /Auto-Trigger Fixes/i})
+ ).toBeChecked();
+ });
+
+ it('saves "always" when toggled ON, "off" when toggled OFF', async () => {
+ const projectPutRequest = MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${project.slug}/`,
+ method: 'PUT',
+ body: {},
+ });
+
+ render(, {
+ organization,
+ outletContext: {
+ project: ProjectFixture({
+ features: ['triage-signals-v0'],
+ seerScannerAutomation: true,
+ autofixAutomationTuning: 'off',
+ }),
+ },
+ });
+
+ const toggle = await screen.findByRole('checkbox', {name: /Auto-Trigger Fixes/i});
+ await userEvent.click(toggle);
+
+ await waitFor(() => {
+ expect(projectPutRequest).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.objectContaining({data: {autofixAutomationTuning: 'always'}})
+ );
+ });
+
+ await userEvent.click(toggle);
+
+ await waitFor(() => {
+ expect(projectPutRequest).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.objectContaining({data: {autofixAutomationTuning: 'off'}})
+ );
+ });
+ });
+ });
});
diff --git a/static/app/views/settings/projectSeer/index.tsx b/static/app/views/settings/projectSeer/index.tsx
index 7a482282c13f3e..e440068764c0c2 100644
--- a/static/app/views/settings/projectSeer/index.tsx
+++ b/static/app/views/settings/projectSeer/index.tsx
@@ -84,6 +84,21 @@ export const autofixAutomatingTuningField = {
visible: ({model}) => model?.getValue('seerScannerAutomation') === true,
} satisfies FieldObject;
+const autofixAutomationToggleField = {
+ name: 'autofixAutomationTuning',
+ label: t('Auto-Trigger Fixes'),
+ help: () =>
+ t(
+ 'When enabled, Seer will automatically analyze actionable issues in the background.'
+ ),
+ type: 'boolean',
+ saveOnBlur: true,
+ saveMessage: t('Automatic Seer settings updated'),
+ getData: (data: Record) => ({
+ autofixAutomationTuning: data.autofixAutomationTuning ? 'always' : 'off',
+ }),
+} satisfies FieldObject;
+
function ProjectSeerGeneralForm({project}: {project: Project}) {
const organization = useOrganization();
const queryClient = useQueryClient();
@@ -91,6 +106,7 @@ function ProjectSeerGeneralForm({project}: {project: Project}) {
const {mutate: updateProjectSeerPreferences} = useUpdateProjectSeerPreferences(project);
const {data: codingAgentIntegrations} = useCodingAgentIntegrations();
+ const isTriageSignalsFeatureOn = project.features.includes('triage-signals-v0');
const canWriteProject = hasEveryAccess(['project:read'], {organization, project});
const cursorIntegration = codingAgentIntegrations?.integrations.find(
@@ -190,12 +206,22 @@ function ProjectSeerGeneralForm({project}: {project: Project}) {
saveOnBlur: true,
saveMessage: t('Stopping point updated'),
onChange: handleStoppingPointChange,
- visible: ({model}) =>
- model?.getValue('seerScannerAutomation') === true &&
- model?.getValue('autofixAutomationTuning') !== 'off',
- } satisfies FieldObject;
+ visible: ({model}) => {
+ const tuningValue = model?.getValue('autofixAutomationTuning');
+ // Handle both boolean (toggle) and string (dropdown) values
+ const automationEnabled =
+ typeof tuningValue === 'boolean' ? tuningValue : tuningValue !== 'off';
- const isTriageSignalsFeatureOn = project.features.includes('triage-signals-v0');
+ // When feature flag is ON (toggle mode): only check automation
+ // When feature flag is OFF (dropdown mode): check both scanner and automation
+ if (isTriageSignalsFeatureOn) {
+ return automationEnabled;
+ }
+
+ const scannerEnabled = model?.getValue('seerScannerAutomation') === true;
+ return scannerEnabled && automationEnabled;
+ },
+ } satisfies FieldObject;
const seerFormGroups: JsonFormObject[] = [
{
@@ -222,7 +248,9 @@ function ProjectSeerGeneralForm({project}: {project: Project}) {
),
fields: [
...(isTriageSignalsFeatureOn ? [] : [seerScannerAutomationField]),
- autofixAutomatingTuningField,
+ isTriageSignalsFeatureOn
+ ? autofixAutomationToggleField
+ : autofixAutomatingTuningField,
automatedRunStoppingPointField,
],
},
@@ -235,14 +263,17 @@ function ProjectSeerGeneralForm({project}: {project: Project}) {
preference?.automation_handoff
? 'cursor_handoff'
: (preference?.automated_run_stopping_point ?? 'root_cause')
- }`}
+ }-${isTriageSignalsFeatureOn}`}
saveOnBlur
apiMethod="PUT"
apiEndpoint={`/projects/${organization.slug}/${project.slug}/`}
allowUndo
initialData={{
seerScannerAutomation: project.seerScannerAutomation ?? false,
- autofixAutomationTuning: project.autofixAutomationTuning ?? 'off',
+ // Same DB field, different UI: toggle (boolean) vs dropdown (string)
+ autofixAutomationTuning: isTriageSignalsFeatureOn
+ ? (project.autofixAutomationTuning ?? 'off') !== 'off'
+ : (project.autofixAutomationTuning ?? 'off'),
automated_run_stopping_point: preference?.automation_handoff
? 'cursor_handoff'
: (preference?.automated_run_stopping_point ?? 'root_cause'),