diff --git a/static/app/components/events/autofix/claudeCodeIntegrationCta.spec.tsx b/static/app/components/events/autofix/claudeCodeIntegrationCta.spec.tsx
index 77d6ab8c1e39d2..73c6e0f7e8bbe5 100644
--- a/static/app/components/events/autofix/claudeCodeIntegrationCta.spec.tsx
+++ b/static/app/components/events/autofix/claudeCodeIntegrationCta.spec.tsx
@@ -9,14 +9,28 @@ import {ProjectsStore} from 'sentry/stores/projectsStore';
describe('ClaudeCodeIntegrationCta', () => {
const project = ProjectFixture();
+ const enabledProject = ProjectFixture({
+ ...project,
+ seerScannerAutomation: true,
+ autofixAutomationTuning: 'medium',
+ });
const organization = OrganizationFixture({
features: ['integrations-claude-code'],
});
+ function mockDetailedProject(projectBody = enabledProject) {
+ return MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${projectBody.slug}/`,
+ body: projectBody,
+ });
+ }
+
beforeEach(() => {
MockApiClient.clearMockResponses();
localStorage.clear();
+ mockDetailedProject();
+
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/seer/preferences/`,
body: {
@@ -209,6 +223,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
+ mockDetailedProject(projectWithAutomation);
const projectUpdateMock = MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${projectWithAutomation.slug}/`,
@@ -256,6 +271,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: false,
autofixAutomationTuning: 'off',
});
+ mockDetailedProject(projectWithoutAutomation);
const updatedProject = {
...projectWithoutAutomation,
@@ -370,6 +386,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: false,
autofixAutomationTuning: 'off',
});
+ mockDetailedProject(projectWithoutAutomation);
render(, {
organization,
@@ -423,6 +440,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
+ mockDetailedProject(projectWithAutomation);
render(, {
organization,
@@ -440,6 +458,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
+ mockDetailedProject(projectWithAutomation);
render(, {
organization,
diff --git a/static/app/components/events/autofix/codingAgentIntegrationCta.tsx b/static/app/components/events/autofix/codingAgentIntegrationCta.tsx
index 17db18b18b0ef7..85ec4b9d785a02 100644
--- a/static/app/components/events/autofix/codingAgentIntegrationCta.tsx
+++ b/static/app/components/events/autofix/codingAgentIntegrationCta.tsx
@@ -14,6 +14,7 @@ import {t, tct} from 'sentry/locale';
import {PluginIcon} from 'sentry/plugins/components/pluginIcon';
import type {Project} from 'sentry/types/project';
import {trackAnalytics} from 'sentry/utils/analytics';
+import {useDetailedProject} from 'sentry/utils/project/useDetailedProject';
import {useUpdateProject} from 'sentry/utils/project/useUpdateProject';
import {useOrganization} from 'sentry/utils/useOrganization';
import {useUser} from 'sentry/utils/useUser';
@@ -40,6 +41,16 @@ export function makeCodingAgentIntegrationCta(config: AgentConfig) {
const organization = useOrganization();
const user = useUser();
+ const hasFeatureFlag =
+ !config.featureFlag || organization.features.includes(config.featureFlag);
+ const {data: projectDetails = project, isPending: isLoadingProject} =
+ useDetailedProject(
+ {
+ orgSlug: organization.slug,
+ projectSlug: project.slug,
+ },
+ {enabled: hasFeatureFlag}
+ );
const {data, isFetching: isLoadingPreferences} = useProjectSeerPreferences(project);
const preference = data?.preference;
const {mutate: updateProjectSeerPreferences, isPending: isUpdatingPreferences} =
@@ -53,12 +64,10 @@ export function makeCodingAgentIntegrationCta(config: AgentConfig) {
i => i.provider === config.provider
);
- const hasFeatureFlag =
- !config.featureFlag || organization.features.includes(config.featureFlag);
const hasIntegration = Boolean(integration);
const isAutomationEnabled =
- project.seerScannerAutomation !== false &&
- project.autofixAutomationTuning !== 'off';
+ projectDetails.seerScannerAutomation === true &&
+ projectDetails.autofixAutomationTuning !== 'off';
const isConfigured =
preference?.automation_handoff?.target === config.target && isAutomationEnabled;
@@ -86,8 +95,8 @@ export function makeCodingAgentIntegrationCta(config: AgentConfig) {
});
const isAutomationDisabled =
- project.seerScannerAutomation === false ||
- project.autofixAutomationTuning === 'off';
+ projectDetails.seerScannerAutomation !== true ||
+ projectDetails.autofixAutomationTuning === 'off';
if (isAutomationDisabled) {
await updateProjectAutomation({
@@ -111,7 +120,12 @@ export function makeCodingAgentIntegrationCta(config: AgentConfig) {
return null;
}
- if (isLoadingPreferences || isLoadingIntegrations || isUpdatingPreferences) {
+ if (
+ isLoadingProject ||
+ isLoadingPreferences ||
+ isLoadingIntegrations ||
+ isUpdatingPreferences
+ ) {
return (
{
const project = ProjectFixture();
+ const enabledProject = ProjectFixture({
+ ...project,
+ seerScannerAutomation: true,
+ autofixAutomationTuning: 'medium',
+ });
const organization = OrganizationFixture();
beforeEach(() => {
MockApiClient.clearMockResponses();
localStorage.clear();
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${enabledProject.slug}/`,
+ body: enabledProject,
+ });
+
// Default mock for seer preferences
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/seer/preferences/`,
@@ -187,6 +197,10 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: false,
autofixAutomationTuning: 'off',
});
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${projectWithoutAutomation.slug}/`,
+ body: projectWithoutAutomation,
+ });
const updatedProject = {
...projectWithoutAutomation,
@@ -271,6 +285,10 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${projectWithAutomation.slug}/`,
+ body: projectWithAutomation,
+ });
const projectUpdateMock = MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${projectWithAutomation.slug}/`,
@@ -353,6 +371,10 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: false,
autofixAutomationTuning: 'off',
});
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${projectWithoutAutomation.slug}/`,
+ body: projectWithoutAutomation,
+ });
render(, {
organization,
@@ -409,6 +431,10 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${projectWithAutomation.slug}/`,
+ body: projectWithAutomation,
+ });
render(, {
organization,
@@ -426,6 +452,10 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${projectWithAutomation.slug}/`,
+ body: projectWithAutomation,
+ });
render(, {
organization,
diff --git a/static/app/views/issueDetails/streamline/sidebar/seerNotices.tsx b/static/app/views/issueDetails/streamline/sidebar/seerNotices.tsx
index 1f6ca51359b939..20289dd5e49455 100644
--- a/static/app/views/issueDetails/streamline/sidebar/seerNotices.tsx
+++ b/static/app/views/issueDetails/streamline/sidebar/seerNotices.tsx
@@ -101,7 +101,7 @@ export function SeerNotices({groupId, hasGithubIntegration, project}: SeerNotice
);
const {starredViews: views} = useStarredIssueViews();
- const detailedProject = useDetailedProject({
+ const {data: projectDetails} = useDetailedProject({
orgSlug: organization.slug,
projectSlug: project.slug,
});
@@ -125,11 +125,11 @@ export function SeerNotices({groupId, hasGithubIntegration, project}: SeerNotice
const needsRepoSelection =
repos.length === 0 && !preference?.repositories?.length && !codeMappingRepos?.length;
const needsAutomation =
- detailedProject?.data &&
- (detailedProject?.data?.autofixAutomationTuning === 'off' ||
- detailedProject?.data?.autofixAutomationTuning === undefined ||
- detailedProject?.data?.seerScannerAutomation === false ||
- detailedProject?.data?.seerScannerAutomation === undefined);
+ projectDetails !== undefined &&
+ (projectDetails.autofixAutomationTuning === 'off' ||
+ projectDetails.autofixAutomationTuning === undefined ||
+ projectDetails.seerScannerAutomation === false ||
+ projectDetails.seerScannerAutomation === undefined);
const needsFixabilityView =
!views.some(view => view.query.includes(FieldKey.ISSUE_SEER_ACTIONABILITY)) &&
isStarredViewAllowed;
@@ -166,8 +166,8 @@ export function SeerNotices({groupId, hasGithubIntegration, project}: SeerNotice
}
const isAutomationDisabled =
- project.seerScannerAutomation === false ||
- project.autofixAutomationTuning === 'off';
+ projectDetails?.seerScannerAutomation !== true ||
+ projectDetails.autofixAutomationTuning === 'off';
if (isAutomationDisabled) {
await updateProjectAutomation({
diff --git a/static/app/views/settings/projectSeer/index.spec.tsx b/static/app/views/settings/projectSeer/index.spec.tsx
index 0ec61e7ae03e2c..ba34262fdbd3a9 100644
--- a/static/app/views/settings/projectSeer/index.spec.tsx
+++ b/static/app/views/settings/projectSeer/index.spec.tsx
@@ -38,6 +38,10 @@ describe('ProjectSeer', () => {
beforeEach(() => {
project = ProjectFixture();
organization = OrganizationFixture();
+ MockApiClient.addMockResponse({
+ url: `/projects/org-slug/${project.slug}/`,
+ body: project,
+ });
// Mock the seer setup check endpoint
MockApiClient.addMockResponse({
@@ -716,6 +720,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${project.slug}/`,
+ body: initialProject,
+ });
MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
@@ -795,6 +803,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${project.slug}/`,
+ body: initialProject,
+ });
MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
@@ -903,6 +915,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${project.slug}/`,
+ body: initialProject,
+ });
MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
@@ -993,6 +1009,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${project.slug}/`,
+ body: initialProject,
+ });
MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
@@ -1116,6 +1136,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${project.slug}/`,
+ body: initialProject,
+ });
MockApiClient.addMockResponse({
url: `/organizations/${orgWithBothFeatures.slug}/seer/setup-check/`,
@@ -1198,6 +1222,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
+ MockApiClient.addMockResponse({
+ url: `/projects/${organization.slug}/${project.slug}/`,
+ body: initialProject,
+ });
MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,