Skip to content

feat(seer): Add dual-read helpers for Seer project preferences from Sentry DB#111591

Merged
srest2021 merged 18 commits intomasterfrom
srest2021/AIML-2611
Apr 9, 2026
Merged

feat(seer): Add dual-read helpers for Seer project preferences from Sentry DB#111591
srest2021 merged 18 commits intomasterfrom
srest2021/AIML-2611

Conversation

@srest2021
Copy link
Copy Markdown
Member

@srest2021 srest2021 commented Mar 25, 2026

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, bulk), "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.

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
@linear-code
Copy link
Copy Markdown

linear-code bot commented Mar 25, 2026

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Mar 25, 2026
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
@srest2021 srest2021 changed the title feat(seer): Add dual-read paths for Seer project preferences from Sentry DB feat(seer): Add dual-read helpers for Seer project preferences from Sentry DB Mar 26, 2026
srest2021 and others added 3 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>
def _build_automation_handoff(
get_option: Callable[[str], Any],
) -> SeerAutomationHandoffConfiguration | None:
"""Build a SeerAutomationHandoffConfiguration from option values, or None if incomplete."""
Copy link
Copy Markdown
Member Author

@srest2021 srest2021 Apr 7, 2026

Choose a reason for hiding this comment

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

(Private for now since idk if we'll have any other use cases where this callable arg will be helpful or relevant)

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.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Bulk read bypasses epoch-aware default resolution
    • Updated bulk preference fallback to use lookup_well_known_key(key).get_default(project) so bulk reads resolve epoch-aware defaults exactly like project.get_option.

Create PR

Or push these changes by commenting:

@cursor push 5cf12c1e45
Preview (5cf12c1e45)
diff --git a/src/sentry/seer/autofix/utils.py b/src/sentry/seer/autofix/utils.py
--- a/src/sentry/seer/autofix/utils.py
+++ b/src/sentry/seer/autofix/utils.py
@@ -787,7 +787,7 @@
         def get_project_option(key: str) -> Any:
             value = project_options[key][project.id]
             if value is None:
-                return projectoptions.lookup_well_known_key(key).default
+                return projectoptions.lookup_well_known_key(key).get_default(project)
             return value
 
         result[project.id] = SeerProjectPreference(

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Comment thread src/sentry/seer/autofix/utils.py Outdated
@srest2021
Copy link
Copy Markdown
Member Author

@cursor push 5cf12c1

Co-authored-by: Armen Zambrano G. <armenzg@users.noreply.github.com>

Applied via @cursor push command
Copy link
Copy Markdown
Member

@JoshFerge JoshFerge left a comment

Choose a reason for hiding this comment

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

looks good!

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 2 potential issues.

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 92dd7e4. Configure here.

Comment thread src/sentry/seer/autofix/utils.py
Comment thread src/sentry/seer/autofix/utils.py
Comment thread src/sentry/seer/autofix/utils.py
Comment thread src/sentry/seer/autofix/utils.py Outdated
@srest2021 srest2021 merged commit c0e6eb8 into master Apr 9, 2026
77 checks passed
@srest2021 srest2021 deleted the srest2021/AIML-2611 branch April 9, 2026 18:49
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>
srest2021 added a commit that referenced this pull request Apr 10, 2026
…dpoints (#111594)

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](https://www.notion.so/sentry/Tech-Spec-Migrate-Seer-Settings-to-Sentry-Database-3208b10e4b5d80f58ea0d7b77a301e2a).

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.

---------

Co-authored-by: Claude Sonnet 4 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
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