Skip to content

fix(autofix): Fall back to code mappings when preference has empty repos#113077

Draft
srest2021 wants to merge 5 commits intomasterfrom
srest2021/fix-empty-repos-fallback
Draft

fix(autofix): Fall back to code mappings when preference has empty repos#113077
srest2021 wants to merge 5 commits intomasterfrom
srest2021/fix-empty-repos-fallback

Conversation

@srest2021
Copy link
Copy Markdown
Member

@srest2021 srest2021 commented Apr 15, 2026

The Sentry DB project preference read helpers had a has_configured_options gate that returned None for projects with no repos and no non-default options. This was problematic because the gate could treat a pref with no repos and explicitly set default options as unconfigured and return None, and _resolve_project_preference would then create a new pref using code mapping repos.

In this PR we:

  • Remove the has_configured_options gate entirely. read_preference_from_sentry_db and bulk_read_preferences_from_sentry_db now always return a SeerProjectPreference (never None).
  • Check if preference.repositories in _resolve_project_preference instead of if preference:. When repos are empty, it falls through to the code mapping fallback while preserving the user's existing stopping point and handoff settings (instead of overwriting them with org defaults).
  • Clean up callsites that had redundant if preference: guards on the Sentry DB read path, since it can no longer return None.

The night shift cron job is unaffected — it already filters on pref.repositories and pref.autofix_automation_tuning != OFF, so receiving a default preference instead of None doesn't change which projects are selected.

read_preference_from_sentry_db now always returns a SeerProjectPreference
instead of None, removing the has_configured_options gate that could
incorrectly treat mechanically-written default options as "configured".

_resolve_project_preference now checks preference.repositories instead
of just truthiness — when repos are empty it falls through to the code
mapping fallback while preserving the user's existing stopping point
and handoff settings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 15, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 15, 2026

Backend Test Failures

Failures on a29e676 in this run:

tests/sentry/seer/autofix/test_autofix_utils.py::TestBulkReadPreferencesFromSentryDb::test_autofix_automation_tuning_populatedlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/autofix/test_autofix_utils.py:1529: in test_autofix_automation_tuning_populated
    assert result[self.project2.id] is None
E   AssertionError: assert SeerProjectPreference(organization_id=4557973359689744, project_id=4557973359689745, repositories=[], automated_run_st..._point='code_changes', automation_handoff=None, autofix_automation_tuning=<AutofixAutomationTuningSettings.OFF: 'off'>) is None
tests/sentry/seer/autofix/test_autofix_utils.py::TestBulkReadPreferencesFromSentryDb::test_unconfigured_project_returns_nonelog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/autofix/test_autofix_utils.py:1475: in test_unconfigured_project_returns_none
    assert result == {self.project1.id: None}
E   AssertionError: assert {455797335995....OFF: 'off'>)} == {4557973359951888: None}
E     
E     Differing items:
E     {4557973359951888: SeerProjectPreference(organization_id=4557973359951888, project_id=4557973359951888, repositories=[...point='code_changes', automation_handoff=None, autofix_automation_tuning=<AutofixAutomationTuningSettings.OFF: 'off'>)} != {4557973359951888: None}
E     
E     Full diff:
E       {
E     -     4557973359951888: None,
E     +     4557973359951888: SeerProjectPreference(organization_id=4557973359951888, project_id=4557973359951888, repositories=[], automated_run_stopping_point='code_changes', automation_handoff=None, autofix_automation_tuning=<AutofixAutomationTuningSettings.OFF: 'off'>),
E       }
tests/sentry/seer/autofix/test_autofix.py::TestResolveProjectPreference::test_returns_preference_with_empty_reposlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/autofix/test_autofix.py:1235: in test_returns_preference_with_empty_repos
    assert len(result.repositories) == 0
E   AssertionError: assert 1 == 0
E    +  where 1 = len([SeerRepoDefinition(repository_id=None, organization_id=None, integration_id=None, provider='integrations:github', own...', is_private=None, branch_name=None, branch_overrides=[], instructions=None, base_commit_sha=None, provider_raw=None)])
E    +    where [SeerRepoDefinition(repository_id=None, organization_id=None, integration_id=None, provider='integrations:github', own...', is_private=None, branch_name=None, branch_overrides=[], instructions=None, base_commit_sha=None, provider_raw=None)] = SeerProjectPreference(organization_id=4557973359820816, project_id=4557973359820816, repositories=[SeerRepoDefinition(...ng_point='root_cause', automation_handoff=None, autofix_automation_tuning=<AutofixAutomationTuningSettings.OFF: 'off'>).repositories
tests/sentry/seer/autofix/test_autofix_utils.py::TestReadPreferenceFromSentryDb::test_unconfigured_project_returns_nonelog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/autofix/test_autofix_utils.py:1273: in test_unconfigured_project_returns_none
    assert result is None
E   AssertionError: assert SeerProjectPreference(organization_id=4557973359886368, project_id=4557973359886368, repositories=[], automated_run_st..._point='code_changes', automation_handoff=None, autofix_automation_tuning=<AutofixAutomationTuningSettings.OFF: 'off'>) is None

if pref:
repo_definitions = pref.repositories
if pref.automation_handoff:
auto_create_pr = pref.automation_handoff.auto_create_pr
Copy link
Copy Markdown
Member Author

@srest2021 srest2021 Apr 15, 2026

Choose a reason for hiding this comment

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

This renaming stuff is due to mypy. read_preference_from_sentry_db guarantees SeerProjectPreference but get_project_seer_preferences can return None. I'll just rename the Seer API call result since it'll get removed soon.

underlying read path (Sentry DB or Seer API)."""
if features.has("organizations:seer-project-settings-read-from-sentry", organization):
return bulk_read_preferences_from_sentry_db(organization.id, project_ids)
return bulk_read_preferences_from_sentry_db(organization.id, project_ids) # type: ignore[return-value]
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Again mypy. Return type here is dict[int, SeerProjectPreference] so I think it's ok to override mypy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@srest2021 srest2021 marked this pull request as ready for review April 15, 2026 19:45
@srest2021 srest2021 requested a review from a team as a code owner April 15, 2026 19:45
Comment on lines 787 to 794
for key in SEER_PROJECT_PREFERENCE_OPTION_KEYS
}

result: dict[int, SeerProjectPreference | None] = {}
result: dict[int, SeerProjectPreference] = {}
for project in projects:
has_configured_options = any(
project_options[key][project.id] is not None
for key in SEER_PROJECT_PREFERENCE_OPTION_KEYS
)
if project.id not in repo_definitions_by_project and not has_configured_options:
result[project.id] = None
continue

def _get_project_option(key: str) -> Any:
value = project_options[key][project.id]
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.

Bug: When the feature flag is on, the fallback to populate repositories from code mappings never runs because the if not existing_pref: check is always false.
Severity: MEDIUM

Suggested Fix

The logic should be updated to check if the repositories key within the existing_pref dictionary is empty, rather than checking the existence of existing_pref itself. This will correctly trigger the fallback to get_autofix_repos_from_project_code_mappings when a project's preferences do not specify any repositories.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/sentry/seer/autofix/utils.py#L787-L794

Potential issue: When the `organizations:seer-project-settings-read-from-sentry` feature
flag is enabled, the `bulk_read_preferences_from_sentry_db` function returns a
`SeerProjectPreference` object for every project, which is then converted to a
dictionary. As a result, the `existing_pref` variable in
`configure_seer_for_existing_org` is always a non-empty dictionary. This causes the `if
not existing_pref:` check to always evaluate to false, preventing the intended fallback
logic that populates repositories from code mappings when a project has no repositories
configured in its preferences. Consequently, projects without configured repositories
will incorrectly end up with an empty repository list instead of using the code mapping
data.

Did we get this right? 👍 / 👎 to inform future reviews.

@srest2021 srest2021 marked this pull request as draft April 15, 2026 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant