Skip to content

Commit 10d26f3

Browse files
roagakddubey
andauthored
chore(seer): Settings clarity improvements (#95335)
- updates copy - lets any project member update automation settings - update layout to be less overwhelming - other minor tweaks <img width="1422" height="881" alt="Screenshot 2025-07-11 at 10 16 03 AM" src="https://github.com/user-attachments/assets/9a5f6775-2b50-4b6e-a8bc-fac57ac73a6f" /> <img width="1423" height="879" alt="Screenshot 2025-07-11 at 10 16 31 AM" src="https://github.com/user-attachments/assets/576b80a5-e3b7-44a2-b601-9289b67323bf" /> <img width="1425" height="871" alt="Screenshot 2025-07-11 at 10 32 40 AM" src="https://github.com/user-attachments/assets/df0b3817-df91-4007-9acb-d860277ad438" /> --------- Co-authored-by: kddubey <kushdubey63@gmail.com>
1 parent 9164908 commit 10d26f3

File tree

8 files changed

+228
-181
lines changed

8 files changed

+228
-181
lines changed

static/app/views/settings/projectSeer/autofixRepositories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ export function AutofixRepositories({project}: ProjectSeerProps) {
279279
</LoadingContainer>
280280
) : filteredSelectedRepositories.length === 0 ? (
281281
<EmptyMessage>
282-
{t('No repositories selected. Click "Add Repos" to get started.')}
282+
{t("Seer can't see your code. Click 'Add Repos' to give Seer access.")}
283283
</EmptyMessage>
284284
) : (
285285
<ReposContainer>
@@ -313,7 +313,7 @@ const ReposContainer = styled('div')`
313313

314314
const EmptyMessage = styled('div')`
315315
padding: ${space(2)};
316-
color: ${p => p.theme.subText};
316+
color: ${p => p.theme.errorText};
317317
text-align: center;
318318
font-size: ${p => p.theme.fontSize.md};
319319
`;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,44 @@
1+
import {t} from 'sentry/locale';
2+
13
export const MAX_REPOS_LIMIT = 8;
4+
5+
export const SEER_THRESHOLD_OPTIONS = [
6+
{
7+
value: 'off',
8+
label: t('Off'),
9+
details: t('Seer only runs when you click Start.'),
10+
},
11+
{
12+
value: 'super_low',
13+
label: t('Only the Most Actionable Issues'),
14+
details: t(
15+
'Seer will automatically run on issues that it thinks have an actionability of "super high." This targets around 2% of issues, but may vary by project.'
16+
),
17+
},
18+
{
19+
value: 'low',
20+
label: t('Highly Actionable and Above'),
21+
details: t(
22+
'Seer will automatically run on issues that it thinks have an actionability of "high" or above. This targets around 10% of issues, but may vary by project.'
23+
),
24+
},
25+
{
26+
value: 'medium',
27+
label: t('Moderately Actionable and Above'),
28+
details: t(
29+
'Seer will automatically run on issues that it thinks have an actionability of "medium" or above. This targets around 30% of issues, but may vary by project.'
30+
),
31+
},
32+
{
33+
value: 'high',
34+
label: t('Minimally Actionable and Above'),
35+
details: t(
36+
'Seer will automatically run on issues that it thinks have an actionability of "low" or above. This targets around 70% of issues, but may vary by project.'
37+
),
38+
},
39+
{
40+
value: 'always',
41+
label: t('All Issues'),
42+
details: t('Seer will automatically run on all new issues.'),
43+
},
44+
] as const;

static/app/views/settings/projectSeer/index.spec.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ describe('ProjectSeer', function () {
271271
it('can update the autofix autorun threshold setting', async function () {
272272
const initialProject: Project = {
273273
...project,
274-
autofixAutomationTuning: 'medium', // Start from medium
274+
autofixAutomationTuning: 'high', // Start from high
275275
seerScannerAutomation: true,
276276
};
277277

@@ -284,33 +284,39 @@ describe('ProjectSeer', function () {
284284
const projectPutRequest = MockApiClient.addMockResponse({
285285
url: `/projects/${organization.slug}/${project.slug}/`,
286286
method: 'PUT',
287-
body: {},
287+
body: {
288+
autofixAutomationTuning: 'high',
289+
},
288290
});
289291

290292
render(<ProjectSeer project={initialProject} />, {organization});
291293

292294
// Find the select menu
293295
const select = await screen.findByRole('textbox', {
294-
name: /Automate Issue Fixes/i,
296+
name: /Auto-Triggered Fixes/i,
295297
});
296298

297299
act(() => {
298300
select.focus();
299301
});
300302

301-
// Open the menu and select a new value (e.g., 'Minimally Actionable and Above')
303+
// Open the menu and select a new value
302304
await userEvent.click(select);
305+
303306
const option = await screen.findByText('Minimally Actionable and Above');
304307
await userEvent.click(option);
305308

309+
const option2 = await screen.findByText('Highly Actionable and Above');
310+
await userEvent.click(option2);
311+
306312
// Form has saveOnBlur=true, so wait for the PUT request
307313
await waitFor(() => {
308314
expect(projectPutRequest).toHaveBeenCalledTimes(1);
309315
});
310316
await waitFor(() => {
311317
expect(projectPutRequest).toHaveBeenCalledWith(
312318
expect.any(String),
313-
expect.objectContaining({data: {autofixAutomationTuning: 'high'}})
319+
expect.objectContaining({data: {autofixAutomationTuning: 'low'}})
314320
);
315321
});
316322
});
@@ -337,7 +343,7 @@ describe('ProjectSeer', function () {
337343

338344
// Find the toggle for Automate Issue Scans
339345
const toggle = await screen.findByRole('checkbox', {
340-
name: /Automate Issue Scans/i,
346+
name: /Scan Issues/i,
341347
});
342348
expect(toggle).toBeInTheDocument();
343349
expect(toggle).not.toBeChecked();
@@ -383,9 +389,9 @@ describe('ProjectSeer', function () {
383389

384390
render(<ProjectSeer project={initialProject} />, {organization});
385391

386-
// Find the select menu for Stopping Point for Automatic Fixes
392+
// Find the select menu for Stopping Point for Auto-Triggered Fixes
387393
const select = await screen.findByRole('textbox', {
388-
name: /Stopping Point for Automatic Fixes/i,
394+
name: /Stopping Point for Auto-Triggered Fixes/i,
389395
});
390396

391397
act(() => {

static/app/views/settings/projectSeer/index.tsx

Lines changed: 42 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {useQueryClient} from '@tanstack/react-query';
44

55
import {hasEveryAccess} from 'sentry/components/acl/access';
66
import FeatureDisabled from 'sentry/components/acl/featureDisabled';
7-
import {Alert} from 'sentry/components/core/alert';
87
import {Link} from 'sentry/components/core/link';
98
import {useProjectSeerPreferences} from 'sentry/components/events/autofix/preferences/hooks/useProjectSeerPreferences';
109
import {useUpdateProjectSeerPreferences} from 'sentry/components/events/autofix/preferences/hooks/useUpdateProjectSeerPreferences';
@@ -30,6 +29,7 @@ import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHea
3029
import {ProjectPermissionAlert} from 'sentry/views/settings/project/projectPermissionAlert';
3130

3231
import {AutofixRepositories} from './autofixRepositories';
32+
import {SEER_THRESHOLD_OPTIONS} from './constants';
3333

3434
const AiSetupDataConsent = HookOrDefault({
3535
hookName: 'component:ai-setup-data-consent',
@@ -55,63 +55,28 @@ const SeerSelectLabel = styled('div')`
5555

5656
export const seerScannerAutomationField = {
5757
name: 'seerScannerAutomation',
58-
label: t('Automate Issue Scans'),
58+
label: t('Scan Issues'),
5959
help: () =>
6060
t(
61-
'Seer will scan all new issues in your project, helping you focus on the most actionable and quick-to-fix ones, giving more context in Slack alerts, and enabling automatic Issue Fixes.'
61+
'Seer will scan all new and ongoing issues in your project, flagging the most actionable issues, giving more context in Slack alerts, and enabling Issue Fixes to be triggered automatically.'
6262
),
6363
type: 'boolean',
6464
saveOnBlur: true,
6565
} satisfies FieldObject;
6666

6767
export const autofixAutomatingTuningField = {
6868
name: 'autofixAutomationTuning',
69-
label: t('Automate Issue Fixes'),
69+
label: t('Auto-Trigger Fixes'),
7070
help: () =>
7171
t(
72-
"Seer will automatically find a root cause and solution for incoming issues if it thinks the issue is actionable enough. By default, it won't open PRs without your approval."
72+
'If Seer detects that an issue is actionable enough, it will automatically analyze it in the background. By the time you see it, the root cause and solution will already be there for you.'
7373
),
7474
type: 'choice',
75-
options: [
76-
{
77-
value: 'off',
78-
label: <SeerSelectLabel>{t('Off')}</SeerSelectLabel>,
79-
details: t('Seer will never analyze any issues without manually clicking Start.'),
80-
},
81-
{
82-
value: 'super_low',
83-
label: <SeerSelectLabel>{t('Only the Most Actionable Issues')}</SeerSelectLabel>,
84-
details: t(
85-
'Seer will automatically run on issues that it thinks have an actionability of "super high." This targets around 2% of issues, but may vary by project.'
86-
),
87-
},
88-
{
89-
value: 'low',
90-
label: <SeerSelectLabel>{t('Highly Actionable and Above')}</SeerSelectLabel>,
91-
details: t(
92-
'Seer will automatically run on issues that it thinks have an actionability of "high" or above. This targets around 10% of issues, but may vary by project.'
93-
),
94-
},
95-
{
96-
value: 'medium',
97-
label: <SeerSelectLabel>{t('Moderately Actionable and Above')}</SeerSelectLabel>,
98-
details: t(
99-
'Seer will automatically run on issues that it thinks have an actionability of "medium" or above. This targets around 30% of issues, but may vary by project.'
100-
),
101-
},
102-
{
103-
value: 'high',
104-
label: <SeerSelectLabel>{t('Minimally Actionable and Above')}</SeerSelectLabel>,
105-
details: t(
106-
'Seer will automatically run on issues that it thinks have an actionability of "low" or above. This targets around 70% of issues, but may vary by project.'
107-
),
108-
},
109-
{
110-
value: 'always',
111-
label: <SeerSelectLabel>{t('All Issues')}</SeerSelectLabel>,
112-
details: t('Seer will automatically run on all new issues.'),
113-
},
114-
],
75+
options: SEER_THRESHOLD_OPTIONS.map(option => ({
76+
value: option.value,
77+
label: <SeerSelectLabel>{option.label}</SeerSelectLabel>,
78+
details: option.details,
79+
})),
11580
saveOnBlur: true,
11681
saveMessage: t('Automatic Seer settings updated'),
11782
visible: ({model}) => model?.getValue('seerScannerAutomation') === true,
@@ -123,7 +88,7 @@ function ProjectSeerGeneralForm({project}: ProjectSeerProps) {
12388
const {preference} = useProjectSeerPreferences(project);
12489
const {mutate: updateProjectSeerPreferences} = useUpdateProjectSeerPreferences(project);
12590

126-
const canWriteProject = hasEveryAccess(['project:write'], {organization, project});
91+
const canWriteProject = hasEveryAccess(['project:read'], {organization, project});
12792

12893
const handleSubmitSuccess = useCallback(
12994
(resp: Project) => {
@@ -150,10 +115,10 @@ function ProjectSeerGeneralForm({project}: ProjectSeerProps) {
150115

151116
const automatedRunStoppingPointField = {
152117
name: 'automated_run_stopping_point',
153-
label: t('Stopping Point for Automatic Fixes'),
118+
label: t('Stopping Point for Auto-Triggered Fixes'),
154119
help: () =>
155120
t(
156-
'Choose how far Seer should go without your approval when running automatically. This does not affect Issue Fixes that you manually start.'
121+
'Choose how far Seer should go before stopping for your approval. This does not affect Issue Fixes that you manually start.'
157122
),
158123
type: 'choice',
159124
options: [
@@ -183,7 +148,25 @@ function ProjectSeerGeneralForm({project}: ProjectSeerProps) {
183148

184149
const seerFormGroups: JsonFormObject[] = [
185150
{
186-
title: t('Automation'),
151+
title: (
152+
<div>
153+
{t('Automation')}
154+
<Subheading>
155+
{tct(
156+
"Choose how Seer automatically triages and root-causes incoming issues, before you even notice them. This analysis is billed at the [link:standard rates] for Seer's Issue Scan and Issue Fix. See [spendlink:docs] on how to manage your Seer spend. You can also [bulklink:configure automation for other projects].",
157+
{
158+
link: <Link to={'https://docs.sentry.io/pricing/#seer-pricing'} />,
159+
spendlink: (
160+
<Link
161+
to={getPricingDocsLinkForEventType(DataCategoryExact.SEER_AUTOFIX)}
162+
/>
163+
),
164+
bulklink: <Link to={`/settings/${organization.slug}/seer/`} />,
165+
}
166+
)}
167+
</Subheading>
168+
</div>
169+
),
187170
fields: [
188171
seerScannerAutomationField,
189172
autofixAutomatingTuningField,
@@ -214,24 +197,7 @@ function ProjectSeerGeneralForm({project}: ProjectSeerProps) {
214197
disabled={!canWriteProject}
215198
renderHeader={() => (
216199
<Fragment>
217-
<Alert type="info" system>
218-
{tct(
219-
"Choose how Seer automates analysis of incoming issues. Automated scans and fixes are charged at the [link:standard billing rates] for Seer's Issue Scan and Issue Fix. See [spendlink:docs] on how to manage your Seer spend.[break][break]You can also [bulklink:configure automation for other projects].",
220-
{
221-
link: <Link to={'https://docs.sentry.io/pricing/#seer-pricing'} />,
222-
spendlink: (
223-
<Link
224-
to={getPricingDocsLinkForEventType(
225-
DataCategoryExact.SEER_AUTOFIX
226-
)}
227-
/>
228-
),
229-
break: <br />,
230-
bulklink: <Link to={`/settings/${organization.slug}/seer/`} />,
231-
}
232-
)}
233-
</Alert>
234-
<ProjectPermissionAlert project={project} system />
200+
{!canWriteProject && <ProjectPermissionAlert project={project} system />}
235201
</Fragment>
236202
)}
237203
/>
@@ -321,3 +287,12 @@ export default function ProjectSeerContainer({project}: ProjectSeerProps) {
321287

322288
return <ProjectSeer project={project} />;
323289
}
290+
291+
const Subheading = styled('div')`
292+
font-size: ${p => p.theme.fontSize.sm};
293+
color: ${p => p.theme.subText};
294+
font-weight: ${p => p.theme.fontWeight.normal};
295+
text-transform: none;
296+
margin-top: ${space(1)};
297+
line-height: 1.4;
298+
`;

static/gsApp/views/seerAutomation/index.spec.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,17 @@ describe('SeerAutomation', function () {
6565
render(<SeerAutomationRoot />, {organization});
6666

6767
// Project details populate the project list
68-
const projectItem = await screen.findByRole('link', {name: project.slug});
68+
const projectItem = await screen.findByText(project.slug);
6969
expect(projectItem).toBeInTheDocument();
70-
expect(projectItem.parentElement!.parentElement).toHaveTextContent('Off');
70+
71+
// Find the panel item containing the project
72+
const panelItem = projectItem.closest('[class*="PanelItem"]');
73+
expect(panelItem).toBeInTheDocument();
74+
expect(panelItem).toHaveTextContent('Off');
7175

7276
// Find the select menu
7377
const select = await screen.findByRole('textbox', {
74-
name: /Default for Automatic Issue Fixes/i,
78+
name: /Default for Auto-Triggered Fixes/i,
7579
});
7680

7781
act(() => {
@@ -124,9 +128,9 @@ describe('SeerAutomation', function () {
124128

125129
render(<SeerAutomationRoot />, {organization});
126130

127-
// Find the toggle for Default for Automatic Issue Scans
131+
// Find the toggle for Default for Issue Scans
128132
const toggle = await screen.findByRole('checkbox', {
129-
name: /Default for Automatic Issue Scans/i,
133+
name: /Default for Issue Scans/i,
130134
});
131135
expect(toggle).toBeInTheDocument();
132136
expect(toggle).not.toBeChecked();

0 commit comments

Comments
 (0)