Skip to content

feat(seer): Read Seer project preferences from Sentry DB in Sentry endpoints#111594

Merged
srest2021 merged 35 commits intomasterfrom
srest2021/AIML-2611-endpoints
Apr 10, 2026
Merged

feat(seer): Read Seer project preferences from Sentry DB in Sentry endpoints#111594
srest2021 merged 35 commits intomasterfrom
srest2021/AIML-2611-endpoints

Conversation

@srest2021
Copy link
Copy Markdown
Member

@srest2021 srest2021 commented Mar 26, 2026

Fixes AIML-2611

Depends on #111591

Behind organizations:seer-project-settings-read-from-sentry, read Seer project preferences directly from ProjectOption + SeerProjectRepository instead of proxying to the Seer API. Part of Phase 3 of the Seer project preferences migration.

Updated call sites:

  • autofix_agent.py — trigger_coding_agent_handoff
  • coding_agent.py — _launch_agents_for_repos
  • issue_summary.py — get_automation_stopping_point
  • on_completion_hook.py — _get_handoff_config_if_applicable, _clear_handoff_preference
  • utils.py — has_project_connected_repos
  • project_seer_preferences.py — GET
  • organization_autofix_automation_settings.py — _serialize_projects_with_settings (GET), POST
  • tasks/seer/autofix.py — configure_seer_for_existing_org
  • tasks/seer/context_engine_index.py — index_repos
  • seer_rpc.py — trigger_coding_agent_launch
  • autofix.py — _resolve_project_preference

Note: Generally I chose not to catch DB errors. We shouldn't be getting any. We'll roll out gradually and if something happens we should get a Sentry exception so I can fix it.

srest2021 and others added 2 commits March 25, 2026 16:36
Add read_preference_from_sentry_db() and bulk_read_preferences_from_sentry_db()
to read Seer project preferences directly from Sentry's database instead of
proxying to the Seer API. This is the foundation for cutting over reads behind
the organizations:seer-project-settings-read-from-sentry feature flag.

- Register organizations:seer-project-settings-read-from-sentry feature flag
- Add SEER_PREFERENCE_OPTION_KEYS constant in projectoptions/defaults.py
- Add build_repo_definition_from_project_repo() and build_automation_handoff()
  shared helpers using a Callable interface for option getters
- Single read uses project.get_option() with caching
- Bulk read uses ProjectOption.objects.get_value_bulk_id() with fallback to
  registered defaults via projectoptions.lookup_well_known_key()

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
Made-with: Cursor
Cover read_preference_from_sentry_db and
bulk_read_preferences_from_sentry_db with tests for unconfigured
projects, repos with branch overrides, options-only projects, full
and partial handoff configs, org scoping, and configured-vs-absent
filtering.

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
Made-with: Cursor
@linear-code
Copy link
Copy Markdown

linear-code bot commented Mar 26, 2026

@github-actions
Copy link
Copy Markdown
Contributor

Backend Test Failures

Failures on 4c0c5ff in this run:

tests/sentry/seer/endpoints/test_project_seer_preferences.py::ProjectSeerPreferencesEndpointTest::test_api_invalid_response_datalog
tests/sentry/seer/endpoints/test_project_seer_preferences.py:236: in test_api_invalid_response_data
    assert response.status_code == 200
E   assert 500 == 200
E    +  where 500 = <Response status_code=500, "application/json">.status_code
tests/sentry/seer/endpoints/test_project_seer_preferences.py::ProjectSeerPreferencesEndpointTest::test_no_preferences_foundlog
tests/sentry/seer/endpoints/test_project_seer_preferences.py:219: in test_no_preferences_found
    assert response.status_code == 200
E   assert 500 == 200
E    +  where 500 = <Response status_code=500, "application/json">.status_code

srest2021 and others added 9 commits April 3, 2026 16:48
read_preference_from_sentry_db now returns None for unconfigured
projects (no explicit option keys set and no repos), matching the
bulk function's behavior. bulk_read_preferences_from_sentry_db is
simplified to delegate to the single-project reader.

Uses ProjectOption.objects.isset() and project.get_option() which
both go through get_all_values(), a cached lookup, instead of the
previous get_value_bulk_id() approach that issued a separate DB
query per option key bypassing the cache. This is efficient for
the small-project-count callers (settings endpoints, org setup).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous commit simplified bulk_read to delegate to the
single-project reader, but that trades O(1) batched queries for
O(N) per-project queries (one repo query + one cache/DB hit per
project). The bulk function is called from configure_seer_for_existing_org
which operates on all projects in an org, so N can be large.

Keep the batched approach (1 repo query + 5 option queries total)
for the bulk path, while the single-project reader uses the cached
isset()/get_option() path which is efficient for its callers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Backend Test Failures

Failures on b30a4bc in this run:

tests/sentry/seer/autofix/test_autofix_on_completion_hook.py::TestAutofixOnCompletionHookHandoff::test_get_handoff_config_reads_from_sentry_dblog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/autofix/test_autofix_on_completion_hook.py:602: in test_get_handoff_config_reads_from_sentry_db
    result = AutofixOnCompletionHook._get_handoff_config_if_applicable(
E   TypeError: AutofixOnCompletionHook._get_handoff_config_if_applicable() got an unexpected keyword argument 'group_id'
tests/sentry/seer/endpoints/test_organization_autofix_automation_settings.py::OrganizationAutofixAutomationSettingsSentryDbEndpointTest::test_post_append_resolves_repo_id_for_existing_reposlog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/endpoints/test_organization_autofix_automation_settings.py:1114: in test_post_append_resolves_repo_id_for_existing_repos
    assert response.status_code == 204
E   assert 500 == 204
E    +  where 500 = <Response status_code=500, "application/json">.status_code
tests/sentry/seer/autofix/test_autofix_agent.py::TestTriggerCodingAgentHandoff::test_trigger_coding_agent_handoff_reads_from_sentry_dblog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/autofix/test_autofix_agent.py:914: in test_trigger_coding_agent_handoff_reads_from_sentry_db
    result = trigger_coding_agent_handoff(
E   TypeError: trigger_coding_agent_handoff() missing 1 required positional argument: 'referrer'
tests/sentry/seer/endpoints/test_organization_autofix_automation_settings.py::OrganizationAutofixAutomationSettingsSentryDbEndpointTest::test_get_reads_project_preferenceslog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/endpoints/test_organization_autofix_automation_settings.py:947: in test_get_reads_project_preferences
    assert response.status_code == 200
E   assert 500 == 200
E    +  where 500 = <Response status_code=500, "application/json">.status_code
tests/sentry/seer/endpoints/test_organization_autofix_automation_settings.py::OrganizationAutofixAutomationSettingsSentryDbEndpointTest::test_post_creates_seer_project_repositorylog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/endpoints/test_organization_autofix_automation_settings.py:1037: in test_post_creates_seer_project_repository
    assert response.status_code == 204
E   assert 500 == 204
E    +  where 500 = <Response status_code=500, "application/json">.status_code

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Backend Test Failures

Failures on 0f6670f in this run:

tests/sentry/seer/autofix/test_autofix_utils.py::TestHasProjectConnectedRepos::test_falls_back_to_code_mappings_on_api_errorlog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/seer/autofix/test_autofix_utils.py:607: in test_falls_back_to_code_mappings_on_api_error
    result = has_project_connected_repos(self.organization, self.project)
src/sentry/seer/autofix/utils.py:843: in has_project_connected_repos
    if not has_repos:
E   UnboundLocalError: cannot access local variable 'has_repos' where it is not associated with a value

srest2021 added a commit that referenced this pull request Apr 9, 2026
…entry DB (#111591)

relates to AIML-2611, AIML-2610

Add `read_preference_from_sentry_db()` and
`bulk_read_preferences_from_sentry_db()` helpers to read Seer project
preferences directly from Sentry DB instead of Seer API. This is the
foundation for cutting over reads behind the
`organizations:seer-project-settings-read-from-sentry` feature flag.

We don't actually use the helpers in this PR. WIP followup PR:
#111594

### Extra

We return None if a project is unconfigured. Matching Seer's behavior
([single](https://github.com/getsentry/seer/blob/3cec03cac3abd9f1f26ac42296d6c870669c66f0/src/seer/automation/preferences.py#L54),
[bulk](https://github.com/getsentry/seer/blob/3cec03cac3abd9f1f26ac42296d6c870669c66f0/src/seer/automation/preferences.py#L98)),
"unconfigured" means we don't have any SeerProjectRepository rows _and_
none of the project options are set.

We use `project.get_option` and `ProjectOptions.objects.isset` for
single reads, and `ProjectOption.objects.get_value_bulk_id` for bulk
read. Why:
1. `project.get_option` and `ProjectOptions.objects.isset` use cache.
For a single project we want to check the cache first.
2. `ProjectOption.objects.get_value_bulk_id` is one DB query. For our
bulk use cases (`OrganizationAutofixAutomationSettingsEndpoint`
get/post, `configure_seer_for_existing_org`) where we may be querying
many projects for a single org, we do max 5 option queries, one for each
project option. It also returns None for missing options so we can
easily tell which projects have configured options and which don't.

---------

Co-authored-by: Claude Sonnet 4 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Base automatically changed from srest2021/AIML-2611 to master April 9, 2026 18:49
@srest2021 srest2021 marked this pull request as ready for review April 9, 2026 19:51
@srest2021 srest2021 requested a review from a team as a code owner April 9, 2026 19:51
Comment thread src/sentry/seer/endpoints/seer_rpc.py
Comment thread src/sentry/seer/autofix/on_completion_hook.py
Comment thread src/sentry/seer/autofix/on_completion_hook.py
Comment thread src/sentry/seer/autofix/autofix_agent.py
george-sentry pushed a commit that referenced this pull request Apr 9, 2026
…entry DB (#111591)

relates to AIML-2611, AIML-2610

Add `read_preference_from_sentry_db()` and
`bulk_read_preferences_from_sentry_db()` helpers to read Seer project
preferences directly from Sentry DB instead of Seer API. This is the
foundation for cutting over reads behind the
`organizations:seer-project-settings-read-from-sentry` feature flag.

We don't actually use the helpers in this PR. WIP followup PR:
#111594

### Extra

We return None if a project is unconfigured. Matching Seer's behavior
([single](https://github.com/getsentry/seer/blob/3cec03cac3abd9f1f26ac42296d6c870669c66f0/src/seer/automation/preferences.py#L54),
[bulk](https://github.com/getsentry/seer/blob/3cec03cac3abd9f1f26ac42296d6c870669c66f0/src/seer/automation/preferences.py#L98)),
"unconfigured" means we don't have any SeerProjectRepository rows _and_
none of the project options are set.

We use `project.get_option` and `ProjectOptions.objects.isset` for
single reads, and `ProjectOption.objects.get_value_bulk_id` for bulk
read. Why:
1. `project.get_option` and `ProjectOptions.objects.isset` use cache.
For a single project we want to check the cache first.
2. `ProjectOption.objects.get_value_bulk_id` is one DB query. For our
bulk use cases (`OrganizationAutofixAutomationSettingsEndpoint`
get/post, `configure_seer_for_existing_org`) where we may be querying
many projects for a single org, we do max 5 option queries, one for each
project option. It also returns None for missing options so we can
easily tell which projects have configured options and which don't.

---------

Co-authored-by: Claude Sonnet 4 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Comment thread src/sentry/seer/autofix/on_completion_hook.py
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e2d9ecb. Configure here.

@srest2021 srest2021 merged commit 2464008 into master Apr 10, 2026
61 checks passed
@srest2021 srest2021 deleted the srest2021/AIML-2611-endpoints branch April 10, 2026 18:16
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.

3 participants