Skip to content

Commit c868880

Browse files
committed
remove code mappings fallback in trigger autofix
1 parent 3192866 commit c868880

File tree

2 files changed

+40
-80
lines changed

2 files changed

+40
-80
lines changed

src/sentry/seer/autofix/autofix.py

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
)
3636
from sentry.seer.autofix.utils import (
3737
AutofixStoppingPoint,
38-
get_autofix_repos_from_project_code_mappings,
3938
get_org_default_seer_automation_handoff,
4039
get_project_seer_preferences,
4140
make_autofix_start_request,
@@ -638,46 +637,36 @@ def get_all_tags_overview(
638637

639638

640639
def _resolve_project_preference(
641-
organization: Organization, project: Project, fallback_repos: list[dict]
640+
organization: Organization, project: Project
642641
) -> SeerProjectPreference | None:
643642
"""
644643
Resolve the Seer project preference for a project before triggering autofix.
645644
646-
If an existing preference with repositories is found, returns it.
647-
Otherwise, creates one using fallback repos and preserves any existing
648-
stopping point / handoff settings.
645+
Returns the existing preference if one exists. If not, creates a new one
646+
with empty repos and org default settings.
649647
"""
650-
preference: SeerProjectPreference | None = None
651648
if features.has("organizations:seer-project-settings-read-from-sentry", organization):
652-
preference = read_preference_from_sentry_db(project)
653-
else:
654-
try:
655-
preference = get_project_seer_preferences(project.id).preference
656-
except (SeerApiError, SeerApiResponseValidationError):
657-
logger.exception(
658-
"seer.resolve_project_preference.get_failed",
659-
extra={"project_id": project.id, "organization_id": organization.id},
660-
)
661-
return None
649+
return read_preference_from_sentry_db(project)
662650

663-
if preference and preference.repositories:
664-
return preference
665-
666-
if preference:
667-
# Preference exists but has no repos —
668-
# keep the user's existing stopping point and handoff settings.
669-
stopping_point = preference.automated_run_stopping_point
670-
handoff = preference.automation_handoff
671-
else:
672-
# No preference at all — use org defaults.
673-
stopping_point, handoff = get_org_default_seer_automation_handoff(organization)
651+
try:
652+
preference = get_project_seer_preferences(project.id).preference
653+
if preference:
654+
return preference
655+
except (SeerApiError, SeerApiResponseValidationError):
656+
logger.exception(
657+
"seer.resolve_project_preference.get_failed",
658+
extra={"project_id": project.id, "organization_id": organization.id},
659+
)
660+
return None
674661

662+
# No preference exists — create one with org defaults.
663+
default_stopping_point, default_handoff = get_org_default_seer_automation_handoff(organization)
675664
preference = SeerProjectPreference(
676665
organization_id=organization.id,
677666
project_id=project.id,
678-
repositories=fallback_repos,
679-
automated_run_stopping_point=stopping_point,
680-
automation_handoff=handoff,
667+
repositories=[],
668+
automated_run_stopping_point=default_stopping_point,
669+
automation_handoff=default_handoff,
681670
)
682671

683672
try:
@@ -748,19 +737,15 @@ def trigger_autofix(
748737
return _respond_with_error("Cannot fix issues without an event.", 400)
749738

750739
code_mappings = get_sorted_code_mapping_configs(group.project)
751-
code_mappings_repos = get_autofix_repos_from_project_code_mappings(
752-
group.project, code_mappings=code_mappings
753-
)
754740

755-
# Resolve the project preference from Seer, or bootstrap one from code mapping repos.
756-
# On success, preference.repositories becomes the source of truth for repos
757-
# (even if empty — matching Seer's behavior of unconditionally using preference repos).
758-
# On failure, we fall back to the original code mapping repos above.
759-
preference = _resolve_project_preference(group.organization, group.project, code_mappings_repos)
741+
# Resolve the project preference, or create a new one with org defaults.
742+
# Preference repos are the source of truth (even if empty).
743+
# On failure, fall back to code mapping repos.
744+
preference = _resolve_project_preference(group.organization, group.project)
760745
if preference:
761746
repos = [repo.dict() for repo in preference.repositories]
762747
else:
763-
repos = code_mappings_repos
748+
repos = []
764749

765750
# Pre-resolve stacktrace frame paths using code mappings so Seer can skip
766751
# expensive git tree fetches for large repos.

tests/sentry/seer/autofix/test_autofix.py

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,19 +1118,16 @@ def _mock_repo(self, name: str = "sentry", external_id: str = "123") -> dict:
11181118
@patch("sentry.seer.autofix.autofix.set_project_seer_preference")
11191119
@patch("sentry.seer.autofix.autofix.get_project_seer_preferences")
11201120
def test_returns_existing_preference(self, mock_get_prefs, mock_set_pref, mock_write_sentry):
1121-
existing_repos = [self._mock_repo("seer", "999")]
11221121
mock_get_prefs.return_value = SeerRawPreferenceResponse(
11231122
preference=SeerProjectPreference(
11241123
organization_id=self.organization.id,
11251124
project_id=self.project.id,
1126-
repositories=existing_repos,
1125+
repositories=[self._mock_repo("seer", "999")],
11271126
automated_run_stopping_point="root_cause",
11281127
)
11291128
)
11301129

1131-
result = _resolve_project_preference(
1132-
self.organization, self.project, [self._mock_repo("sentry", "123")]
1133-
)
1130+
result = _resolve_project_preference(self.organization, self.project)
11341131

11351132
assert result is not None
11361133
assert len(result.repositories) == 1
@@ -1143,7 +1140,7 @@ def test_returns_existing_preference(self, mock_get_prefs, mock_set_pref, mock_w
11431140
@patch("sentry.seer.autofix.autofix.write_preference_to_sentry_db")
11441141
@patch("sentry.seer.autofix.autofix.set_project_seer_preference")
11451142
@patch("sentry.seer.autofix.autofix.get_project_seer_preferences")
1146-
def test_empty_repos_falls_back_to_code_mappings_and_preserves_settings(
1143+
def test_returns_existing_preference_with_empty_repos(
11471144
self, mock_get_prefs, mock_set_pref, mock_write_sentry
11481145
):
11491146
mock_get_prefs.return_value = SeerRawPreferenceResponse(
@@ -1161,56 +1158,36 @@ def test_empty_repos_falls_back_to_code_mappings_and_preserves_settings(
11611158
)
11621159
)
11631160

1164-
fallback_repos = [self._mock_repo("sentry", "123")]
1165-
result = _resolve_project_preference(self.organization, self.project, fallback_repos)
1161+
result = _resolve_project_preference(self.organization, self.project)
11661162

11671163
assert result is not None
1168-
assert len(result.repositories) == 1
1169-
assert result.repositories[0].name == "sentry"
1164+
assert result.repositories == []
11701165
assert result.automated_run_stopping_point == "root_cause"
11711166
assert result.automation_handoff is not None
11721167
assert result.automation_handoff.handoff_point == "root_cause"
11731168
assert result.automation_handoff.target == "cursor_background_agent"
11741169
assert result.automation_handoff.integration_id == 42
11751170
assert result.automation_handoff.auto_create_pr is True
1176-
mock_set_pref.assert_called_once()
1177-
mock_write_sentry.assert_called_once()
1171+
mock_set_pref.assert_not_called()
1172+
mock_write_sentry.assert_not_called()
11781173

11791174
@patch("sentry.seer.autofix.autofix.write_preference_to_sentry_db")
11801175
@patch("sentry.seer.autofix.autofix.set_project_seer_preference")
11811176
@patch("sentry.seer.autofix.autofix.get_project_seer_preferences")
1182-
def test_no_preference_falls_back_to_code_mappings_and_org_defaults(
1177+
def test_no_preference_creates_one_with_org_defaults(
11831178
self, mock_get_prefs, mock_set_pref, mock_write_sentry
11841179
):
11851180
mock_get_prefs.return_value = SeerRawPreferenceResponse(preference=None)
11861181
self.organization.update_option("sentry:default_automated_run_stopping_point", "open_pr")
11871182
self.organization.update_option("sentry:auto_open_prs", True)
11881183

1189-
code_mapping_repos = [self._mock_repo("sentry", "123")]
1190-
result = _resolve_project_preference(self.organization, self.project, code_mapping_repos)
1184+
result = _resolve_project_preference(self.organization, self.project)
11911185

11921186
assert result is not None
11931187
assert result.project_id == self.project.id
11941188
assert result.organization_id == self.organization.id
1195-
assert len(result.repositories) == 1
1196-
assert result.repositories[0].name == "sentry"
1197-
assert result.repositories[0].external_id == "123"
1198-
assert result.automated_run_stopping_point == "open_pr"
1199-
mock_set_pref.assert_called_once()
1200-
mock_write_sentry.assert_called_once()
1201-
1202-
@patch("sentry.seer.autofix.autofix.write_preference_to_sentry_db")
1203-
@patch("sentry.seer.autofix.autofix.set_project_seer_preference")
1204-
@patch("sentry.seer.autofix.autofix.get_project_seer_preferences")
1205-
def test_creates_preference_with_empty_repos_when_no_fallback(
1206-
self, mock_get_prefs, mock_set_pref, mock_write_sentry
1207-
):
1208-
mock_get_prefs.return_value = SeerRawPreferenceResponse(preference=None)
1209-
1210-
result = _resolve_project_preference(self.organization, self.project, [])
1211-
1212-
assert result is not None
12131189
assert result.repositories == []
1190+
assert result.automated_run_stopping_point == "open_pr"
12141191
mock_set_pref.assert_called_once()
12151192
mock_write_sentry.assert_called_once()
12161193

@@ -1220,7 +1197,7 @@ def test_creates_preference_with_empty_repos_when_no_fallback(
12201197
def test_returns_none_on_get_api_error(self, mock_get_prefs, mock_set_pref, mock_write_sentry):
12211198
mock_get_prefs.side_effect = SeerApiError("test error", 500)
12221199

1223-
result = _resolve_project_preference(self.organization, self.project, [self._mock_repo()])
1200+
result = _resolve_project_preference(self.organization, self.project)
12241201

12251202
assert result is None
12261203
mock_set_pref.assert_not_called()
@@ -1233,7 +1210,7 @@ def test_returns_none_on_set_api_error(self, mock_get_prefs, mock_set_pref, mock
12331210
mock_get_prefs.return_value = SeerRawPreferenceResponse(preference=None)
12341211
mock_set_pref.side_effect = SeerApiError("test error", 500)
12351212

1236-
result = _resolve_project_preference(self.organization, self.project, [self._mock_repo()])
1213+
result = _resolve_project_preference(self.organization, self.project)
12371214

12381215
assert result is None
12391216
mock_write_sentry.assert_not_called()
@@ -1247,10 +1224,11 @@ def test_returns_preference_on_sentry_db_write_error(
12471224
mock_get_prefs.return_value = SeerRawPreferenceResponse(preference=None)
12481225
mock_write_sentry.side_effect = Exception()
12491226

1250-
result = _resolve_project_preference(self.organization, self.project, [self._mock_repo()])
1227+
result = _resolve_project_preference(self.organization, self.project)
12511228

12521229
assert result is not None
12531230
assert result.project_id == self.project.id
1231+
assert result.repositories == []
12541232
mock_set_pref.assert_called_once()
12551233
mock_write_sentry.assert_called_once()
12561234

@@ -1263,17 +1241,14 @@ def test_reads_from_sentry_db(
12631241
self, mock_get_prefs, mock_read_db, mock_set_pref, mock_write_sentry
12641242
):
12651243
"""When feature flag enabled, reads preferences from Sentry DB instead of Seer API."""
1266-
existing_repos = [self._mock_repo("seer", "999")]
12671244
mock_read_db.return_value = SeerProjectPreference(
12681245
organization_id=self.organization.id,
12691246
project_id=self.project.id,
1270-
repositories=existing_repos,
1247+
repositories=[self._mock_repo("seer", "999")],
12711248
automated_run_stopping_point="root_cause",
12721249
)
12731250

1274-
result = _resolve_project_preference(
1275-
self.organization, self.project, [self._mock_repo("sentry", "123")]
1276-
)
1251+
result = _resolve_project_preference(self.organization, self.project)
12771252

12781253
assert result is not None
12791254
assert result.repositories[0].name == "seer"

0 commit comments

Comments
 (0)