From 990954435d2e67b03042ffe8f69f32ecf199b866 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 11 Nov 2026 20:03:10 +0000 Subject: [PATCH 1/5] fix(seer): Keep token-billed orgs off the legacy usage-based Seer paths Token-billed orgs were treated as legacy usage-based orgs anywhere we only checked for seat-based Seer: post-process automation, agentic triage rate limits and tuning, valid stopping points, project settings serializers, first-assignment summaries, and new-project defaults. Route them through the seat-based path instead. Refs CW-3061 Co-Authored-By: sofia --- src/sentry/receivers/seer.py | 4 +-- src/sentry/seer/agentic_triage/delivery.py | 6 ++-- src/sentry/seer/autofix/issue_summary.py | 4 +-- src/sentry/seer/autofix/utils.py | 12 ++++++- .../seer/endpoints/project_seer_settings.py | 8 ++--- src/sentry/seer/similarity/utils.py | 16 ++++----- src/sentry/tasks/post_process.py | 6 ++-- src/sentry/tasks/seer/agentic_triage/cron.py | 6 ++-- .../core/endpoints/test_team_projects.py | 6 ++-- .../seer/agentic_triage/test_delivery.py | 2 +- .../sentry/seer/autofix/test_autofix_utils.py | 34 +++++++++++++++++++ .../sentry/seer/autofix/test_issue_summary.py | 6 ++-- .../endpoints/test_project_seer_settings.py | 4 +-- .../sentry/tasks/seer/test_agentic_triage.py | 25 ++++++++++++-- tests/sentry/tasks/test_post_process.py | 18 ++++++++++ 15 files changed, 119 insertions(+), 38 deletions(-) diff --git a/src/sentry/receivers/seer.py b/src/sentry/receivers/seer.py index 1f6ed04f3da2..1ded7267f342 100644 --- a/src/sentry/receivers/seer.py +++ b/src/sentry/receivers/seer.py @@ -12,7 +12,7 @@ from sentry.models.project import Project from sentry.seer.autofix.constants import SeerAutomationSource from sentry.seer.autofix.trigger import is_issue_eligible_for_seer_automation -from sentry.seer.autofix.utils import is_seer_seat_based_tier_enabled +from sentry.seer.autofix.utils import is_seer_seat_or_token_billed from sentry.signals import integration_added, issue_assigned from sentry.tasks.seer.autofix import summarize_issue from sentry.tasks.seer.investigation import mark_investigation_telemetry_connected @@ -32,7 +32,7 @@ def dispatch_first_assignment_summary( ): return - if is_seer_seat_based_tier_enabled(project.organization): + if is_seer_seat_or_token_billed(project.organization): logger.info( "seer.issue_summary.first_assignment.skipped_seat_based_seer", extra={ diff --git a/src/sentry/seer/agentic_triage/delivery.py b/src/sentry/seer/agentic_triage/delivery.py index a1e4821bbe54..a1be7c577d14 100644 --- a/src/sentry/seer/agentic_triage/delivery.py +++ b/src/sentry/seer/agentic_triage/delivery.py @@ -27,7 +27,7 @@ AutofixStoppingPoint, bulk_read_preferences_from_sentry_db, is_seer_autotriggered_autofix_rate_limited_and_increment, - is_seer_seat_based_tier_enabled, + is_seer_seat_or_token_billed, ) from sentry.seer.models.agentic_triage import ( SeerAgenticTriageRunErrorType, @@ -313,8 +313,8 @@ def _process_verdicts( referrer = referrer_map[SeerAutomationSource.AGENTIC_TRIAGE] - # Rate limit only applies to legacy org plans - check_rate_limit = not is_seer_seat_based_tier_enabled(organization) + # Rate limit only applies to legacy usage-based orgs + check_rate_limit = not is_seer_seat_or_token_billed(organization) for group in fixable_groups: if check_rate_limit and is_seer_autotriggered_autofix_rate_limited_and_increment( diff --git a/src/sentry/seer/autofix/issue_summary.py b/src/sentry/seer/autofix/issue_summary.py index 33c2119afaf0..7ccd831f9163 100644 --- a/src/sentry/seer/autofix/issue_summary.py +++ b/src/sentry/seer/autofix/issue_summary.py @@ -41,7 +41,7 @@ AutofixStoppingPoint, is_seer_autotriggered_autofix_rate_limited, is_seer_autotriggered_autofix_rate_limited_and_increment, - is_seer_seat_based_tier_enabled, + is_seer_seat_or_token_billed, ) from sentry.seer.entrypoints.cache import SeerOperatorAutofixCache from sentry.seer.entrypoints.operator import SeerAutofixOperator @@ -481,7 +481,7 @@ def get_automation_stopping_point(group: Group) -> AutofixStoppingPoint | None: """ user_preference = group.project.get_option("sentry:seer_automated_run_stopping_point") - if is_seer_seat_based_tier_enabled(group.organization): + if is_seer_seat_or_token_billed(group.organization): fixability_score = get_and_update_group_fixability_score(group) fixability_stopping_point = _get_stopping_point_from_fixability(fixability_score) diff --git a/src/sentry/seer/autofix/utils.py b/src/sentry/seer/autofix/utils.py index b9f5f4ae9eed..3895a97897f4 100644 --- a/src/sentry/seer/autofix/utils.py +++ b/src/sentry/seer/autofix/utils.py @@ -111,7 +111,7 @@ def get_valid_automated_run_stopping_points( organization: Organization, ) -> frozenset[AutofixStoppingPoint]: """Return the set of stopping points valid for an org's billing tier.""" - if is_seer_seat_based_tier_enabled(organization): + if is_seer_seat_or_token_billed(organization): return SEAT_BASED_STOPPING_POINTS return USAGE_BASED_STOPPING_POINTS @@ -1017,6 +1017,16 @@ def is_seer_seat_based_tier_enabled(organization: Organization) -> bool: return has_seat_based_seer +def is_seer_seat_or_token_billed(organization: Organization) -> bool: + """ + Check if the organization is on seat-based or token-billed Seer, as opposed to + legacy usage-based Seer. Both use agentic triage for automation. + """ + return is_seer_seat_based_tier_enabled(organization) or features.has( + "organizations:token-billed-seer-enabled", organization + ) + + def is_seer_token_based_billing_onboarding_enabled(organization: Organization) -> bool: """Check if Seer onboarding changes for token-based billing apply to this organization.""" return features.has( diff --git a/src/sentry/seer/endpoints/project_seer_settings.py b/src/sentry/seer/endpoints/project_seer_settings.py index 754f09aafaf4..1654c0075ef6 100644 --- a/src/sentry/seer/endpoints/project_seer_settings.py +++ b/src/sentry/seer/endpoints/project_seer_settings.py @@ -42,7 +42,7 @@ get_token_based_billing_project_repos, get_token_based_billing_repo_filter, get_valid_automated_run_stopping_points, - is_seer_seat_based_tier_enabled, + is_seer_seat_or_token_billed, is_seer_token_based_billing_onboarding_enabled, update_seer_project_settings, ) @@ -410,7 +410,7 @@ def validate(self, data: dict[str, Any]) -> dict[str, Any]: class ProjectSettingsUpdateSerializer(_BaseProjectSettingsUpdateSerializer): - """Seat-based (new) Seer: restricted tuning choices, stopping point sync.""" + """Seat-based and token-billed (new) Seer: restricted tuning choices, stopping point sync.""" automation_tuning = serializers.ChoiceField( choices=[AutofixAutomationTuningSettings.OFF, AutofixAutomationTuningSettings.MEDIUM], @@ -451,7 +451,7 @@ def get(self, request: Request, project: Project) -> Response: def put(self, request: Request, project: Project) -> Response: serializer_cls = ( ProjectSettingsUpdateSerializer - if is_seer_seat_based_tier_enabled(project.organization) + if is_seer_seat_or_token_billed(project.organization) else LegacyProjectSettingsUpdateSerializer ) serializer = serializer_cls( @@ -521,7 +521,7 @@ def get(self, request: Request, organization: Organization) -> Response: def put(self, request: Request, organization: Organization) -> Response: serializer_cls = ( BulkProjectSettingsUpdateSerializer - if is_seer_seat_based_tier_enabled(organization) + if is_seer_seat_or_token_billed(organization) else LegacyBulkProjectSettingsUpdateSerializer ) serializer = serializer_cls(data=request.data, context={"organization": organization}) diff --git a/src/sentry/seer/similarity/utils.py b/src/sentry/seer/similarity/utils.py index 1fe829845ec6..30c781c5968c 100644 --- a/src/sentry/seer/similarity/utils.py +++ b/src/sentry/seer/similarity/utils.py @@ -26,7 +26,7 @@ AutomationCodingAgent, SeerProjectSettingsUpdate, get_org_default_seer_automation_handoff, - is_seer_seat_based_tier_enabled, + is_seer_seat_or_token_billed, update_seer_project_settings, ) from sentry.seer.similarity.types import GroupingVersion @@ -544,14 +544,14 @@ def set_default_project_autofix_automation_tuning( project.update_option( "sentry:autofix_automation_tuning", AutofixAutomationTuningSettings.OFF ) - elif is_seer_seat_based_tier_enabled(organization): - # Feature flag ON overrides everything except explicit "off" + elif is_seer_seat_or_token_billed(organization): + # Seat-based and token-billed orgs override everything except explicit "off" project.update_option( "sentry:autofix_automation_tuning", AutofixAutomationTuningSettings.MEDIUM, ) elif org_default: - # Feature flag OFF, use org's explicit value + # Legacy orgs use org's explicit value project.update_option("sentry:autofix_automation_tuning", org_default) @@ -559,11 +559,11 @@ def set_default_project_seer_scanner_automation( organization: Organization, project: Project ) -> None: """Called once at project creation time to set the initial seer scanner automation.""" - if is_seer_seat_based_tier_enabled(organization): - # Feature flag ON always sets scanner to True + if is_seer_seat_or_token_billed(organization): + # Seat-based and token-billed orgs always set scanner to True project.update_option("sentry:seer_scanner_automation", True) else: - # Feature flag OFF, use org's explicit value if set + # Legacy orgs use org's explicit value if set org_default = organization.get_option("sentry:default_seer_scanner_automation") if org_default is not None: project.update_option("sentry:seer_scanner_automation", org_default) @@ -573,7 +573,7 @@ def set_default_project_seer_preferences(organization: Organization, project: Pr """Called once at project creation time to set the initial automated run stopping point and automation handoff. """ - if not is_seer_seat_based_tier_enabled(organization): + if not is_seer_seat_or_token_billed(organization): return stopping_point, automation_handoff = get_org_default_seer_automation_handoff(organization) diff --git a/src/sentry/tasks/post_process.py b/src/sentry/tasks/post_process.py index c51b93d836d8..e336b0e42050 100644 --- a/src/sentry/tasks/post_process.py +++ b/src/sentry/tasks/post_process.py @@ -1643,7 +1643,7 @@ def kick_off_seer_automation(job: PostProcessJob) -> None: get_issue_summary_lock_key, ) from sentry.seer.autofix.trigger import get_default_seer_automation_skip_reason - from sentry.seer.autofix.utils import is_seer_seat_based_tier_enabled + from sentry.seer.autofix.utils import is_seer_seat_or_token_billed from sentry.tasks.seer.autofix import ( generate_issue_summary_only, generate_summary_and_run_automation, @@ -1652,7 +1652,7 @@ def kick_off_seer_automation(job: PostProcessJob) -> None: event = job["event"] group = event.group - if is_seer_seat_based_tier_enabled(group.organization): + if is_seer_seat_or_token_billed(group.organization): if group.seer_fixability_score is not None: return # Issues created in last 5 minutes only. This can be removed once this is live past 1 week. @@ -1660,7 +1660,7 @@ def kick_off_seer_automation(job: PostProcessJob) -> None: if (timezone.now() - group.first_seen).total_seconds() > 300: return # Generate issue summary and fixability score if not cached. - # Agentic Triage handles automations for seat-based orgs but still needs these. + # Agentic Triage handles automations for seat-based and token-billed orgs but still needs these. cache_key = get_issue_summary_cache_key(group.id) if cache.get(cache_key) is None: lock_key, lock_name = get_issue_summary_lock_key(group.id) diff --git a/src/sentry/tasks/seer/agentic_triage/cron.py b/src/sentry/tasks/seer/agentic_triage/cron.py index 0b67487e77ce..938c8695227c 100644 --- a/src/sentry/tasks/seer/agentic_triage/cron.py +++ b/src/sentry/tasks/seer/agentic_triage/cron.py @@ -37,7 +37,7 @@ bulk_read_preferences_from_sentry_db, is_free_cohort_org, is_seer_autotriggered_autofix_rate_limited, - is_seer_seat_based_tier_enabled, + is_seer_seat_or_token_billed, is_seer_token_based_billing_onboarding_enabled, ) from sentry.seer.models import SeerPermissionError @@ -704,7 +704,7 @@ class EligibleProject: tweaks: AgenticTriageTweaks stopping_point: AutofixStoppingPoint connected_repos: list[str] - # None for seat-based orgs, regardless of the project's own setting. + # None for seat-based and token-billed orgs, regardless of the project's own setting. automation_tuning: AutofixAutomationTuningSettings | None @@ -733,7 +733,7 @@ def _get_eligible_projects( preferences = bulk_read_preferences_from_sentry_db(organization.id, list(project_map)) - is_legacy_org = not is_seer_seat_based_tier_enabled(organization) + is_legacy_org = not is_seer_seat_or_token_billed(organization) eligible: list[EligibleProject] = [] for pid, project in project_map.items(): diff --git a/tests/sentry/core/endpoints/test_team_projects.py b/tests/sentry/core/endpoints/test_team_projects.py index 371134530053..2e3aa12a2477 100644 --- a/tests/sentry/core/endpoints/test_team_projects.py +++ b/tests/sentry/core/endpoints/test_team_projects.py @@ -645,7 +645,7 @@ def test_project_autofix_tuning_not_set_if_org_option_not_set_in_db(self) -> Non project=project, key="sentry:autofix_automation_tuning" ).exists() - @patch("sentry.seer.similarity.utils.is_seer_seat_based_tier_enabled", return_value=True) + @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed", return_value=True) def test_project_autofix_tuning_defaults_to_medium_with_seat_based_tier( self, mock_seat_based_tier: MagicMock ) -> None: @@ -665,7 +665,7 @@ def test_project_autofix_tuning_defaults_to_medium_with_seat_based_tier( ) assert autofix_tuning == "medium" - @patch("sentry.seer.similarity.utils.is_seer_seat_based_tier_enabled", return_value=True) + @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed", return_value=True) def test_project_autofix_tuning_respects_explicit_off_even_with_seat_based_tier( self, mock_seat_based_tier: MagicMock ) -> None: @@ -685,7 +685,7 @@ def test_project_autofix_tuning_respects_explicit_off_even_with_seat_based_tier( ) assert autofix_tuning == "off" - @patch("sentry.seer.similarity.utils.is_seer_seat_based_tier_enabled", return_value=True) + @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed", return_value=True) def test_project_autofix_tuning_seat_based_tier_overrides_non_off_org_default( self, mock_seat_based_tier: MagicMock ) -> None: diff --git a/tests/sentry/seer/agentic_triage/test_delivery.py b/tests/sentry/seer/agentic_triage/test_delivery.py index 4b78e696840a..ec888bc633cc 100644 --- a/tests/sentry/seer/agentic_triage/test_delivery.py +++ b/tests/sentry/seer/agentic_triage/test_delivery.py @@ -768,7 +768,7 @@ def test_seat_based_orgs_skip_the_rate_limit_check(self) -> None: return_value=True, ) as mock_rate_limited, patch( - "sentry.seer.agentic_triage.delivery.is_seer_seat_based_tier_enabled", + "sentry.seer.agentic_triage.delivery.is_seer_seat_or_token_billed", return_value=True, ), patch( diff --git a/tests/sentry/seer/autofix/test_autofix_utils.py b/tests/sentry/seer/autofix/test_autofix_utils.py index dbc69fc98654..222527bd0fe3 100644 --- a/tests/sentry/seer/autofix/test_autofix_utils.py +++ b/tests/sentry/seer/autofix/test_autofix_utils.py @@ -20,6 +20,8 @@ ) from sentry.seer.autofix.trigger import is_issue_eligible_for_seer_automation from sentry.seer.autofix.utils import ( + SEAT_BASED_STOPPING_POINTS, + USAGE_BASED_STOPPING_POINTS, AutofixState, AutofixStoppingPoint, AutomationCodingAgent, @@ -32,8 +34,10 @@ get_org_default_seer_automation_handoff, get_repo_url_path, get_token_based_billing_project_repos, + get_valid_automated_run_stopping_points, has_project_connected_repos, is_seer_seat_based_tier_enabled, + is_seer_seat_or_token_billed, read_preference_from_sentry_db, replace_all_seer_project_repos, update_seer_project_settings, @@ -273,6 +277,36 @@ def test_returns_cached_value(self) -> None: assert result is True +class TestIsSeerSeatOrTokenBilled(TestCase): + def setUp(self) -> None: + super().setUp() + self.organization = self.create_organization(name="test-org") + + def tearDown(self) -> None: + super().tearDown() + cache.delete(f"seer:seat-based-tier:{self.organization.id}") + + @with_feature("organizations:seat-based-seer-enabled") + def test_seat_based(self) -> None: + assert is_seer_seat_or_token_billed(self.organization) is True + assert get_valid_automated_run_stopping_points(self.organization) == ( + SEAT_BASED_STOPPING_POINTS + ) + + @with_feature("organizations:token-billed-seer-enabled") + def test_token_billed(self) -> None: + assert is_seer_seat_or_token_billed(self.organization) is True + assert get_valid_automated_run_stopping_points(self.organization) == ( + SEAT_BASED_STOPPING_POINTS + ) + + def test_legacy(self) -> None: + assert is_seer_seat_or_token_billed(self.organization) is False + assert get_valid_automated_run_stopping_points(self.organization) == ( + USAGE_BASED_STOPPING_POINTS + ) + + class HasProjectConnectedReposBaseTest(TestCase): """Test the has_project_connected_repos function.""" diff --git a/tests/sentry/seer/autofix/test_issue_summary.py b/tests/sentry/seer/autofix/test_issue_summary.py index 122cee1a24c1..06d2cb50b2c5 100644 --- a/tests/sentry/seer/autofix/test_issue_summary.py +++ b/tests/sentry/seer/autofix/test_issue_summary.py @@ -889,7 +889,7 @@ def test_stopping_point_mapping(self, score: float, expected: AutofixStoppingPoi @override_settings(SENTRY_SELF_HOSTED=False) -@patch("sentry.seer.autofix.issue_summary.is_seer_seat_based_tier_enabled", return_value=True) +@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed", return_value=True) class TestRunAutomationStoppingPoint(APITestCase, SnubaTestCase): def setUp(self) -> None: super().setUp() @@ -1052,7 +1052,7 @@ def test_upper_bound_combinations( @override_settings(SENTRY_SELF_HOSTED=False) -@patch("sentry.seer.autofix.issue_summary.is_seer_seat_based_tier_enabled", return_value=True) +@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed", return_value=True) class TestRunAutomationWithUpperBound(APITestCase, SnubaTestCase): def setUp(self) -> None: super().setUp() @@ -1363,7 +1363,7 @@ def test_returns_false_when_rate_limited( assert is_group_eligible_for_automation(self.group) is False -@patch("sentry.seer.autofix.issue_summary.is_seer_seat_based_tier_enabled", return_value=True) +@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed", return_value=True) @override_settings(SENTRY_SELF_HOSTED=False) class TestGetAutomationStoppingPoint(TestCase): def setUp(self) -> None: diff --git a/tests/sentry/seer/endpoints/test_project_seer_settings.py b/tests/sentry/seer/endpoints/test_project_seer_settings.py index e07e8c85944e..b5794d36d3d6 100644 --- a/tests/sentry/seer/endpoints/test_project_seer_settings.py +++ b/tests/sentry/seer/endpoints/test_project_seer_settings.py @@ -13,7 +13,7 @@ @patch( - "sentry.seer.endpoints.project_seer_settings.is_seer_seat_based_tier_enabled", + "sentry.seer.endpoints.project_seer_settings.is_seer_seat_or_token_billed", return_value=True, ) class ProjectSeerSettingsEndpointBaseTest(APITestCase): @@ -473,7 +473,7 @@ def test_get_repos_count_excludes_repos_seer_cannot_use(self) -> None: @patch( - "sentry.seer.endpoints.project_seer_settings.is_seer_seat_based_tier_enabled", + "sentry.seer.endpoints.project_seer_settings.is_seer_seat_or_token_billed", return_value=True, ) class OrganizationSeerProjectSettingsEndpointBaseTest(APITestCase): diff --git a/tests/sentry/tasks/seer/test_agentic_triage.py b/tests/sentry/tasks/seer/test_agentic_triage.py index 9b79d346e0f9..3a043d9fc47d 100644 --- a/tests/sentry/tasks/seer/test_agentic_triage.py +++ b/tests/sentry/tasks/seer/test_agentic_triage.py @@ -738,7 +738,7 @@ def test_seat_based_orgs_skip_the_rate_limit_check(self) -> None: return_value=True, ), patch( - "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_based_tier_enabled", + "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed", return_value=True, ), ): @@ -754,13 +754,32 @@ def test_seat_based_orgs_get_no_automation_tuning(self) -> None: ) with patch( - "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_based_tier_enabled", + "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed", return_value=True, ): result = _get_eligible_projects(org, "manual") assert result[0].automation_tuning is None + def test_token_billed_orgs_are_not_legacy(self) -> None: + org = self.create_organization() + project = self._make_eligible(self.create_project(organization=org)) + project.update_option( + "sentry:autofix_automation_tuning", AutofixAutomationTuningSettings.LOW + ) + + with ( + patch( + "sentry.tasks.seer.agentic_triage.cron.is_seer_autotriggered_autofix_rate_limited", + return_value=True, + ), + self.feature("organizations:token-billed-seer-enabled"), + ): + result = _get_eligible_projects(org, "manual") + + assert [ep.project for ep in result] == [project] + assert result[0].automation_tuning is None + @override_settings(SENTRY_SELF_HOSTED=False) @django_db_all @@ -1229,7 +1248,7 @@ def test_payload_omits_automation_tuning_for_seat_based_orgs(self) -> None: with ( patch( - "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_based_tier_enabled", + "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed", return_value=True, ), ): diff --git a/tests/sentry/tasks/test_post_process.py b/tests/sentry/tasks/test_post_process.py index 6c8fc0492f42..1075f0fd3727 100644 --- a/tests/sentry/tasks/test_post_process.py +++ b/tests/sentry/tasks/test_post_process.py @@ -3809,6 +3809,24 @@ def test_seat_based_org_generates_summary_for_new_issues( ) mock_generate_summary_only.assert_called_once_with(require_group(event).id) + @patch("sentry.seer.autofix.utils.is_seer_seat_based_tier_enabled", return_value=False) + @patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay") + @patch("sentry.tasks.seer.autofix.generate_issue_summary_only.delay") + @override_settings(SENTRY_SELF_HOSTED=False) + def test_token_billed_org_generates_summary_for_new_issues( + self, + mock_generate_summary_only: MagicMock, + mock_generate_summary_and_run_automation: MagicMock, + mock_seat_based_tier: MagicMock, + ) -> None: + event = self.create_event(data={"message": "testing"}, project_id=self.project.id) + with self.feature("organizations:token-billed-seer-enabled"): + self.call_post_process_group( + is_new=True, is_regression=False, is_new_group_environment=True, event=event + ) + mock_generate_summary_only.assert_called_once_with(require_group(event).id) + mock_generate_summary_and_run_automation.assert_not_called() + def setUp(self) -> None: super().setUp() From 736ae80b70b500527f13399a10c04e785674dd92 Mon Sep 17 00:00:00 2001 From: srest2021 Date: Wed, 11 Nov 2026 13:49:37 -0800 Subject: [PATCH 2/5] fix(seer): Exclude token-billed orgs from the agentic triage free cohort A free cohort org that moved to token billing kept the cohort option, so it still got free-cohort behavior: the group setup check showed a paywall on issues without a run, and automated PRs said they were generated at no cost. Refs CW-3061 --- src/sentry/seer/autofix/utils.py | 8 +++---- .../sentry/seer/autofix/test_autofix_utils.py | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/sentry/seer/autofix/utils.py b/src/sentry/seer/autofix/utils.py index 3895a97897f4..f0ca77805b36 100644 --- a/src/sentry/seer/autofix/utils.py +++ b/src/sentry/seer/autofix/utils.py @@ -1039,17 +1039,17 @@ def is_free_cohort_org(organization: Organization) -> bool: orgs that receive agentic triage and autofix without a Seer subscription. Returns True when the kill switch is NOT engaged (flag disabled = cohort - active), the org is NOT on a paid seat-based Seer plan, and the org - option is set to True. + active), the org is NOT on a paid seat-based or token-billed Seer plan, and + the org option is set to True. """ try: if features.has("organizations:agentic-triage-free-cohort-killswitch", organization): return False except Exception: return False - return not is_seer_seat_based_tier_enabled(organization) and bool( + return bool( OrganizationOption.objects.get_value(organization, "agentic-triage-free-cohort", False) - ) + ) and not is_seer_seat_or_token_billed(organization) def is_issue_category_eligible(group: Group) -> bool: diff --git a/tests/sentry/seer/autofix/test_autofix_utils.py b/tests/sentry/seer/autofix/test_autofix_utils.py index 222527bd0fe3..b7d874d9f71d 100644 --- a/tests/sentry/seer/autofix/test_autofix_utils.py +++ b/tests/sentry/seer/autofix/test_autofix_utils.py @@ -36,6 +36,7 @@ get_token_based_billing_project_repos, get_valid_automated_run_stopping_points, has_project_connected_repos, + is_free_cohort_org, is_seer_seat_based_tier_enabled, is_seer_seat_or_token_billed, read_preference_from_sentry_db, @@ -307,6 +308,28 @@ def test_legacy(self) -> None: ) +class TestIsFreeCohortOrg(TestCase): + def setUp(self) -> None: + super().setUp() + self.organization = self.create_organization(name="test-org") + self.organization.update_option("agentic-triage-free-cohort", True) + + def tearDown(self) -> None: + super().tearDown() + cache.delete(f"seer:seat-based-tier:{self.organization.id}") + + def test_legacy(self) -> None: + assert is_free_cohort_org(self.organization) is True + + @with_feature("organizations:seat-based-seer-enabled") + def test_seat_based(self) -> None: + assert is_free_cohort_org(self.organization) is False + + @with_feature("organizations:token-billed-seer-enabled") + def test_token_billed(self) -> None: + assert is_free_cohort_org(self.organization) is False + + class HasProjectConnectedReposBaseTest(TestCase): """Test the has_project_connected_repos function.""" From 43e1fb7480e76aac0ddf415e4f3b4b0c8ab1a388 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 11 Nov 2026 22:12:47 +0000 Subject: [PATCH 3/5] fix(seer): Run agentic triage for every token-billed org Post-process now skips automation for all token-billed orgs, but the agentic triage cron only counted token-billed orgs as paid when they also had the token-based billing onboarding flag. The free cohort excludes token-billed orgs, so ones without that flag got no automation at all. Treat every seat-based or token-billed org as paid in the cron. --- src/sentry/tasks/seer/agentic_triage/cron.py | 11 +++------- .../sentry/tasks/seer/test_agentic_triage.py | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/sentry/tasks/seer/agentic_triage/cron.py b/src/sentry/tasks/seer/agentic_triage/cron.py index 938c8695227c..77824daf4189 100644 --- a/src/sentry/tasks/seer/agentic_triage/cron.py +++ b/src/sentry/tasks/seer/agentic_triage/cron.py @@ -38,7 +38,6 @@ is_free_cohort_org, is_seer_autotriggered_autofix_rate_limited, is_seer_seat_or_token_billed, - is_seer_token_based_billing_onboarding_enabled, ) from sentry.seer.models import SeerPermissionError from sentry.seer.models.agentic_triage import SeerAgenticTriageRunErrorType @@ -81,11 +80,6 @@ BATCH_FEATURE_NAMES = [ "organizations:seer-night-shift", ] -PER_ORG_FEATURE_NAMES = [ - # INTERNAL handlers aren't routed through batch_has_for_organizations, - # so this gets checked per-org on the survivors of the batch loop. - "organizations:seat-based-seer-enabled", -] class SeerAgenticTriageRunOptions(TypedDict): @@ -642,8 +636,9 @@ def _get_eligible_orgs_from_batch( paid_eligible: list[Organization] = [] free_cohort_eligible: list[Organization] = [] for org in eligible: - has_seat_based_seer = all(features.has(f, org) for f in PER_ORG_FEATURE_NAMES) - if has_seat_based_seer or is_seer_token_based_billing_onboarding_enabled(org): + # Billing flags have INTERNAL handlers, which batch_has_for_organizations + # doesn't route, so they're checked per org on the batch loop's survivors. + if is_seer_seat_or_token_billed(org): paid_eligible.append(org) elif is_free_cohort_org(org): free_cohort_eligible.append(org) diff --git a/tests/sentry/tasks/seer/test_agentic_triage.py b/tests/sentry/tasks/seer/test_agentic_triage.py index 3a043d9fc47d..7036f7df054c 100644 --- a/tests/sentry/tasks/seer/test_agentic_triage.py +++ b/tests/sentry/tasks/seer/test_agentic_triage.py @@ -317,9 +317,20 @@ def test_eligible_orgs_by_billing(self) -> None: token_billed = self.create_organization() free_cohort = self.create_organization() free_cohort.update_option("agentic-triage-free-cohort", True) + token_billed_without_onboarding = self.create_organization() + token_billed_free_cohort = self.create_organization() + token_billed_free_cohort.update_option("agentic-triage-free-cohort", True) onboarding_only = self.create_organization() no_seer = self.create_organization() - all_orgs = [seat_based, token_billed, free_cohort, onboarding_only, no_seer] + all_orgs = [ + seat_based, + token_billed, + token_billed_without_onboarding, + token_billed_free_cohort, + free_cohort, + onboarding_only, + no_seer, + ] with ( self.options({"seer.night_shift.enable_for_legacy_orgs": False}), @@ -331,13 +342,19 @@ def test_eligible_orgs_by_billing(self) -> None: token_billed.slug, onboarding_only.slug, ], - "organizations:token-billed-seer-enabled": [token_billed.slug], + "organizations:token-billed-seer-enabled": [ + token_billed.slug, + token_billed_without_onboarding.slug, + token_billed_free_cohort.slug, + ], } ), ): assert _get_eligible_orgs_from_batch(all_orgs) == [ seat_based, token_billed, + token_billed_without_onboarding, + token_billed_free_cohort, free_cohort, ] From eba69bba6ba517f94e6d5cbad672ecfa295c2109 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 11 Nov 2026 22:18:00 +0000 Subject: [PATCH 4/5] Revert "fix(seer): Run agentic triage for every token-billed org" This reverts commit 43e1fb7480e76aac0ddf415e4f3b4b0c8ab1a388. Co-Authored-By: Sofia Rest --- src/sentry/tasks/seer/agentic_triage/cron.py | 11 +++++++--- .../sentry/tasks/seer/test_agentic_triage.py | 21 ++----------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/sentry/tasks/seer/agentic_triage/cron.py b/src/sentry/tasks/seer/agentic_triage/cron.py index 77824daf4189..938c8695227c 100644 --- a/src/sentry/tasks/seer/agentic_triage/cron.py +++ b/src/sentry/tasks/seer/agentic_triage/cron.py @@ -38,6 +38,7 @@ is_free_cohort_org, is_seer_autotriggered_autofix_rate_limited, is_seer_seat_or_token_billed, + is_seer_token_based_billing_onboarding_enabled, ) from sentry.seer.models import SeerPermissionError from sentry.seer.models.agentic_triage import SeerAgenticTriageRunErrorType @@ -80,6 +81,11 @@ BATCH_FEATURE_NAMES = [ "organizations:seer-night-shift", ] +PER_ORG_FEATURE_NAMES = [ + # INTERNAL handlers aren't routed through batch_has_for_organizations, + # so this gets checked per-org on the survivors of the batch loop. + "organizations:seat-based-seer-enabled", +] class SeerAgenticTriageRunOptions(TypedDict): @@ -636,9 +642,8 @@ def _get_eligible_orgs_from_batch( paid_eligible: list[Organization] = [] free_cohort_eligible: list[Organization] = [] for org in eligible: - # Billing flags have INTERNAL handlers, which batch_has_for_organizations - # doesn't route, so they're checked per org on the batch loop's survivors. - if is_seer_seat_or_token_billed(org): + has_seat_based_seer = all(features.has(f, org) for f in PER_ORG_FEATURE_NAMES) + if has_seat_based_seer or is_seer_token_based_billing_onboarding_enabled(org): paid_eligible.append(org) elif is_free_cohort_org(org): free_cohort_eligible.append(org) diff --git a/tests/sentry/tasks/seer/test_agentic_triage.py b/tests/sentry/tasks/seer/test_agentic_triage.py index 7036f7df054c..3a043d9fc47d 100644 --- a/tests/sentry/tasks/seer/test_agentic_triage.py +++ b/tests/sentry/tasks/seer/test_agentic_triage.py @@ -317,20 +317,9 @@ def test_eligible_orgs_by_billing(self) -> None: token_billed = self.create_organization() free_cohort = self.create_organization() free_cohort.update_option("agentic-triage-free-cohort", True) - token_billed_without_onboarding = self.create_organization() - token_billed_free_cohort = self.create_organization() - token_billed_free_cohort.update_option("agentic-triage-free-cohort", True) onboarding_only = self.create_organization() no_seer = self.create_organization() - all_orgs = [ - seat_based, - token_billed, - token_billed_without_onboarding, - token_billed_free_cohort, - free_cohort, - onboarding_only, - no_seer, - ] + all_orgs = [seat_based, token_billed, free_cohort, onboarding_only, no_seer] with ( self.options({"seer.night_shift.enable_for_legacy_orgs": False}), @@ -342,19 +331,13 @@ def test_eligible_orgs_by_billing(self) -> None: token_billed.slug, onboarding_only.slug, ], - "organizations:token-billed-seer-enabled": [ - token_billed.slug, - token_billed_without_onboarding.slug, - token_billed_free_cohort.slug, - ], + "organizations:token-billed-seer-enabled": [token_billed.slug], } ), ): assert _get_eligible_orgs_from_batch(all_orgs) == [ seat_based, token_billed, - token_billed_without_onboarding, - token_billed_free_cohort, free_cohort, ] From c177b1ef29fb9fa6e45bb601b0155125a4fa591f Mon Sep 17 00:00:00 2001 From: srest2021 Date: Wed, 11 Nov 2026 15:09:55 -0800 Subject: [PATCH 5/5] rename --- src/sentry/receivers/seer.py | 4 ++-- src/sentry/seer/agentic_triage/delivery.py | 4 ++-- src/sentry/seer/autofix/issue_summary.py | 4 ++-- src/sentry/seer/autofix/utils.py | 6 +++--- src/sentry/seer/endpoints/project_seer_settings.py | 6 +++--- src/sentry/seer/similarity/utils.py | 8 ++++---- src/sentry/tasks/post_process.py | 4 ++-- src/sentry/tasks/seer/agentic_triage/cron.py | 4 ++-- tests/sentry/core/endpoints/test_team_projects.py | 6 +++--- tests/sentry/seer/agentic_triage/test_delivery.py | 2 +- tests/sentry/seer/autofix/test_autofix_utils.py | 8 ++++---- tests/sentry/seer/autofix/test_issue_summary.py | 6 +++--- tests/sentry/seer/endpoints/test_project_seer_settings.py | 4 ++-- tests/sentry/tasks/seer/test_agentic_triage.py | 6 +++--- 14 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/sentry/receivers/seer.py b/src/sentry/receivers/seer.py index 1ded7267f342..0f0c7c6707c7 100644 --- a/src/sentry/receivers/seer.py +++ b/src/sentry/receivers/seer.py @@ -12,7 +12,7 @@ from sentry.models.project import Project from sentry.seer.autofix.constants import SeerAutomationSource from sentry.seer.autofix.trigger import is_issue_eligible_for_seer_automation -from sentry.seer.autofix.utils import is_seer_seat_or_token_billed +from sentry.seer.autofix.utils import is_seer_seat_or_token_billed_enabled from sentry.signals import integration_added, issue_assigned from sentry.tasks.seer.autofix import summarize_issue from sentry.tasks.seer.investigation import mark_investigation_telemetry_connected @@ -32,7 +32,7 @@ def dispatch_first_assignment_summary( ): return - if is_seer_seat_or_token_billed(project.organization): + if is_seer_seat_or_token_billed_enabled(project.organization): logger.info( "seer.issue_summary.first_assignment.skipped_seat_based_seer", extra={ diff --git a/src/sentry/seer/agentic_triage/delivery.py b/src/sentry/seer/agentic_triage/delivery.py index a1be7c577d14..83d1427d21bc 100644 --- a/src/sentry/seer/agentic_triage/delivery.py +++ b/src/sentry/seer/agentic_triage/delivery.py @@ -27,7 +27,7 @@ AutofixStoppingPoint, bulk_read_preferences_from_sentry_db, is_seer_autotriggered_autofix_rate_limited_and_increment, - is_seer_seat_or_token_billed, + is_seer_seat_or_token_billed_enabled, ) from sentry.seer.models.agentic_triage import ( SeerAgenticTriageRunErrorType, @@ -314,7 +314,7 @@ def _process_verdicts( referrer = referrer_map[SeerAutomationSource.AGENTIC_TRIAGE] # Rate limit only applies to legacy usage-based orgs - check_rate_limit = not is_seer_seat_or_token_billed(organization) + check_rate_limit = not is_seer_seat_or_token_billed_enabled(organization) for group in fixable_groups: if check_rate_limit and is_seer_autotriggered_autofix_rate_limited_and_increment( diff --git a/src/sentry/seer/autofix/issue_summary.py b/src/sentry/seer/autofix/issue_summary.py index 7ccd831f9163..b2ce1a799e38 100644 --- a/src/sentry/seer/autofix/issue_summary.py +++ b/src/sentry/seer/autofix/issue_summary.py @@ -41,7 +41,7 @@ AutofixStoppingPoint, is_seer_autotriggered_autofix_rate_limited, is_seer_autotriggered_autofix_rate_limited_and_increment, - is_seer_seat_or_token_billed, + is_seer_seat_or_token_billed_enabled, ) from sentry.seer.entrypoints.cache import SeerOperatorAutofixCache from sentry.seer.entrypoints.operator import SeerAutofixOperator @@ -481,7 +481,7 @@ def get_automation_stopping_point(group: Group) -> AutofixStoppingPoint | None: """ user_preference = group.project.get_option("sentry:seer_automated_run_stopping_point") - if is_seer_seat_or_token_billed(group.organization): + if is_seer_seat_or_token_billed_enabled(group.organization): fixability_score = get_and_update_group_fixability_score(group) fixability_stopping_point = _get_stopping_point_from_fixability(fixability_score) diff --git a/src/sentry/seer/autofix/utils.py b/src/sentry/seer/autofix/utils.py index f0ca77805b36..b3a09dc84457 100644 --- a/src/sentry/seer/autofix/utils.py +++ b/src/sentry/seer/autofix/utils.py @@ -111,7 +111,7 @@ def get_valid_automated_run_stopping_points( organization: Organization, ) -> frozenset[AutofixStoppingPoint]: """Return the set of stopping points valid for an org's billing tier.""" - if is_seer_seat_or_token_billed(organization): + if is_seer_seat_or_token_billed_enabled(organization): return SEAT_BASED_STOPPING_POINTS return USAGE_BASED_STOPPING_POINTS @@ -1017,7 +1017,7 @@ def is_seer_seat_based_tier_enabled(organization: Organization) -> bool: return has_seat_based_seer -def is_seer_seat_or_token_billed(organization: Organization) -> bool: +def is_seer_seat_or_token_billed_enabled(organization: Organization) -> bool: """ Check if the organization is on seat-based or token-billed Seer, as opposed to legacy usage-based Seer. Both use agentic triage for automation. @@ -1049,7 +1049,7 @@ def is_free_cohort_org(organization: Organization) -> bool: return False return bool( OrganizationOption.objects.get_value(organization, "agentic-triage-free-cohort", False) - ) and not is_seer_seat_or_token_billed(organization) + ) and not is_seer_seat_or_token_billed_enabled(organization) def is_issue_category_eligible(group: Group) -> bool: diff --git a/src/sentry/seer/endpoints/project_seer_settings.py b/src/sentry/seer/endpoints/project_seer_settings.py index 1654c0075ef6..c2a34811bbd1 100644 --- a/src/sentry/seer/endpoints/project_seer_settings.py +++ b/src/sentry/seer/endpoints/project_seer_settings.py @@ -42,7 +42,7 @@ get_token_based_billing_project_repos, get_token_based_billing_repo_filter, get_valid_automated_run_stopping_points, - is_seer_seat_or_token_billed, + is_seer_seat_or_token_billed_enabled, is_seer_token_based_billing_onboarding_enabled, update_seer_project_settings, ) @@ -451,7 +451,7 @@ def get(self, request: Request, project: Project) -> Response: def put(self, request: Request, project: Project) -> Response: serializer_cls = ( ProjectSettingsUpdateSerializer - if is_seer_seat_or_token_billed(project.organization) + if is_seer_seat_or_token_billed_enabled(project.organization) else LegacyProjectSettingsUpdateSerializer ) serializer = serializer_cls( @@ -521,7 +521,7 @@ def get(self, request: Request, organization: Organization) -> Response: def put(self, request: Request, organization: Organization) -> Response: serializer_cls = ( BulkProjectSettingsUpdateSerializer - if is_seer_seat_or_token_billed(organization) + if is_seer_seat_or_token_billed_enabled(organization) else LegacyBulkProjectSettingsUpdateSerializer ) serializer = serializer_cls(data=request.data, context={"organization": organization}) diff --git a/src/sentry/seer/similarity/utils.py b/src/sentry/seer/similarity/utils.py index 30c781c5968c..b1ec582ff5ab 100644 --- a/src/sentry/seer/similarity/utils.py +++ b/src/sentry/seer/similarity/utils.py @@ -26,7 +26,7 @@ AutomationCodingAgent, SeerProjectSettingsUpdate, get_org_default_seer_automation_handoff, - is_seer_seat_or_token_billed, + is_seer_seat_or_token_billed_enabled, update_seer_project_settings, ) from sentry.seer.similarity.types import GroupingVersion @@ -544,7 +544,7 @@ def set_default_project_autofix_automation_tuning( project.update_option( "sentry:autofix_automation_tuning", AutofixAutomationTuningSettings.OFF ) - elif is_seer_seat_or_token_billed(organization): + elif is_seer_seat_or_token_billed_enabled(organization): # Seat-based and token-billed orgs override everything except explicit "off" project.update_option( "sentry:autofix_automation_tuning", @@ -559,7 +559,7 @@ def set_default_project_seer_scanner_automation( organization: Organization, project: Project ) -> None: """Called once at project creation time to set the initial seer scanner automation.""" - if is_seer_seat_or_token_billed(organization): + if is_seer_seat_or_token_billed_enabled(organization): # Seat-based and token-billed orgs always set scanner to True project.update_option("sentry:seer_scanner_automation", True) else: @@ -573,7 +573,7 @@ def set_default_project_seer_preferences(organization: Organization, project: Pr """Called once at project creation time to set the initial automated run stopping point and automation handoff. """ - if not is_seer_seat_or_token_billed(organization): + if not is_seer_seat_or_token_billed_enabled(organization): return stopping_point, automation_handoff = get_org_default_seer_automation_handoff(organization) diff --git a/src/sentry/tasks/post_process.py b/src/sentry/tasks/post_process.py index e336b0e42050..a5cae01af730 100644 --- a/src/sentry/tasks/post_process.py +++ b/src/sentry/tasks/post_process.py @@ -1643,7 +1643,7 @@ def kick_off_seer_automation(job: PostProcessJob) -> None: get_issue_summary_lock_key, ) from sentry.seer.autofix.trigger import get_default_seer_automation_skip_reason - from sentry.seer.autofix.utils import is_seer_seat_or_token_billed + from sentry.seer.autofix.utils import is_seer_seat_or_token_billed_enabled from sentry.tasks.seer.autofix import ( generate_issue_summary_only, generate_summary_and_run_automation, @@ -1652,7 +1652,7 @@ def kick_off_seer_automation(job: PostProcessJob) -> None: event = job["event"] group = event.group - if is_seer_seat_or_token_billed(group.organization): + if is_seer_seat_or_token_billed_enabled(group.organization): if group.seer_fixability_score is not None: return # Issues created in last 5 minutes only. This can be removed once this is live past 1 week. diff --git a/src/sentry/tasks/seer/agentic_triage/cron.py b/src/sentry/tasks/seer/agentic_triage/cron.py index 938c8695227c..7b7adee9badb 100644 --- a/src/sentry/tasks/seer/agentic_triage/cron.py +++ b/src/sentry/tasks/seer/agentic_triage/cron.py @@ -37,7 +37,7 @@ bulk_read_preferences_from_sentry_db, is_free_cohort_org, is_seer_autotriggered_autofix_rate_limited, - is_seer_seat_or_token_billed, + is_seer_seat_or_token_billed_enabled, is_seer_token_based_billing_onboarding_enabled, ) from sentry.seer.models import SeerPermissionError @@ -733,7 +733,7 @@ def _get_eligible_projects( preferences = bulk_read_preferences_from_sentry_db(organization.id, list(project_map)) - is_legacy_org = not is_seer_seat_or_token_billed(organization) + is_legacy_org = not is_seer_seat_or_token_billed_enabled(organization) eligible: list[EligibleProject] = [] for pid, project in project_map.items(): diff --git a/tests/sentry/core/endpoints/test_team_projects.py b/tests/sentry/core/endpoints/test_team_projects.py index 2e3aa12a2477..4395b2c6e1a4 100644 --- a/tests/sentry/core/endpoints/test_team_projects.py +++ b/tests/sentry/core/endpoints/test_team_projects.py @@ -645,7 +645,7 @@ def test_project_autofix_tuning_not_set_if_org_option_not_set_in_db(self) -> Non project=project, key="sentry:autofix_automation_tuning" ).exists() - @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed", return_value=True) + @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed_enabled", return_value=True) def test_project_autofix_tuning_defaults_to_medium_with_seat_based_tier( self, mock_seat_based_tier: MagicMock ) -> None: @@ -665,7 +665,7 @@ def test_project_autofix_tuning_defaults_to_medium_with_seat_based_tier( ) assert autofix_tuning == "medium" - @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed", return_value=True) + @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed_enabled", return_value=True) def test_project_autofix_tuning_respects_explicit_off_even_with_seat_based_tier( self, mock_seat_based_tier: MagicMock ) -> None: @@ -685,7 +685,7 @@ def test_project_autofix_tuning_respects_explicit_off_even_with_seat_based_tier( ) assert autofix_tuning == "off" - @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed", return_value=True) + @patch("sentry.seer.similarity.utils.is_seer_seat_or_token_billed_enabled", return_value=True) def test_project_autofix_tuning_seat_based_tier_overrides_non_off_org_default( self, mock_seat_based_tier: MagicMock ) -> None: diff --git a/tests/sentry/seer/agentic_triage/test_delivery.py b/tests/sentry/seer/agentic_triage/test_delivery.py index ec888bc633cc..da42dad21bd7 100644 --- a/tests/sentry/seer/agentic_triage/test_delivery.py +++ b/tests/sentry/seer/agentic_triage/test_delivery.py @@ -768,7 +768,7 @@ def test_seat_based_orgs_skip_the_rate_limit_check(self) -> None: return_value=True, ) as mock_rate_limited, patch( - "sentry.seer.agentic_triage.delivery.is_seer_seat_or_token_billed", + "sentry.seer.agentic_triage.delivery.is_seer_seat_or_token_billed_enabled", return_value=True, ), patch( diff --git a/tests/sentry/seer/autofix/test_autofix_utils.py b/tests/sentry/seer/autofix/test_autofix_utils.py index b7d874d9f71d..6ba91adfc935 100644 --- a/tests/sentry/seer/autofix/test_autofix_utils.py +++ b/tests/sentry/seer/autofix/test_autofix_utils.py @@ -38,7 +38,7 @@ has_project_connected_repos, is_free_cohort_org, is_seer_seat_based_tier_enabled, - is_seer_seat_or_token_billed, + is_seer_seat_or_token_billed_enabled, read_preference_from_sentry_db, replace_all_seer_project_repos, update_seer_project_settings, @@ -289,20 +289,20 @@ def tearDown(self) -> None: @with_feature("organizations:seat-based-seer-enabled") def test_seat_based(self) -> None: - assert is_seer_seat_or_token_billed(self.organization) is True + assert is_seer_seat_or_token_billed_enabled(self.organization) is True assert get_valid_automated_run_stopping_points(self.organization) == ( SEAT_BASED_STOPPING_POINTS ) @with_feature("organizations:token-billed-seer-enabled") def test_token_billed(self) -> None: - assert is_seer_seat_or_token_billed(self.organization) is True + assert is_seer_seat_or_token_billed_enabled(self.organization) is True assert get_valid_automated_run_stopping_points(self.organization) == ( SEAT_BASED_STOPPING_POINTS ) def test_legacy(self) -> None: - assert is_seer_seat_or_token_billed(self.organization) is False + assert is_seer_seat_or_token_billed_enabled(self.organization) is False assert get_valid_automated_run_stopping_points(self.organization) == ( USAGE_BASED_STOPPING_POINTS ) diff --git a/tests/sentry/seer/autofix/test_issue_summary.py b/tests/sentry/seer/autofix/test_issue_summary.py index 06d2cb50b2c5..450856f34250 100644 --- a/tests/sentry/seer/autofix/test_issue_summary.py +++ b/tests/sentry/seer/autofix/test_issue_summary.py @@ -889,7 +889,7 @@ def test_stopping_point_mapping(self, score: float, expected: AutofixStoppingPoi @override_settings(SENTRY_SELF_HOSTED=False) -@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed", return_value=True) +@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed_enabled", return_value=True) class TestRunAutomationStoppingPoint(APITestCase, SnubaTestCase): def setUp(self) -> None: super().setUp() @@ -1052,7 +1052,7 @@ def test_upper_bound_combinations( @override_settings(SENTRY_SELF_HOSTED=False) -@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed", return_value=True) +@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed_enabled", return_value=True) class TestRunAutomationWithUpperBound(APITestCase, SnubaTestCase): def setUp(self) -> None: super().setUp() @@ -1363,7 +1363,7 @@ def test_returns_false_when_rate_limited( assert is_group_eligible_for_automation(self.group) is False -@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed", return_value=True) +@patch("sentry.seer.autofix.issue_summary.is_seer_seat_or_token_billed_enabled", return_value=True) @override_settings(SENTRY_SELF_HOSTED=False) class TestGetAutomationStoppingPoint(TestCase): def setUp(self) -> None: diff --git a/tests/sentry/seer/endpoints/test_project_seer_settings.py b/tests/sentry/seer/endpoints/test_project_seer_settings.py index b5794d36d3d6..9a4252253b3e 100644 --- a/tests/sentry/seer/endpoints/test_project_seer_settings.py +++ b/tests/sentry/seer/endpoints/test_project_seer_settings.py @@ -13,7 +13,7 @@ @patch( - "sentry.seer.endpoints.project_seer_settings.is_seer_seat_or_token_billed", + "sentry.seer.endpoints.project_seer_settings.is_seer_seat_or_token_billed_enabled", return_value=True, ) class ProjectSeerSettingsEndpointBaseTest(APITestCase): @@ -473,7 +473,7 @@ def test_get_repos_count_excludes_repos_seer_cannot_use(self) -> None: @patch( - "sentry.seer.endpoints.project_seer_settings.is_seer_seat_or_token_billed", + "sentry.seer.endpoints.project_seer_settings.is_seer_seat_or_token_billed_enabled", return_value=True, ) class OrganizationSeerProjectSettingsEndpointBaseTest(APITestCase): diff --git a/tests/sentry/tasks/seer/test_agentic_triage.py b/tests/sentry/tasks/seer/test_agentic_triage.py index 3a043d9fc47d..628f1fb7ac43 100644 --- a/tests/sentry/tasks/seer/test_agentic_triage.py +++ b/tests/sentry/tasks/seer/test_agentic_triage.py @@ -738,7 +738,7 @@ def test_seat_based_orgs_skip_the_rate_limit_check(self) -> None: return_value=True, ), patch( - "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed", + "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed_enabled", return_value=True, ), ): @@ -754,7 +754,7 @@ def test_seat_based_orgs_get_no_automation_tuning(self) -> None: ) with patch( - "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed", + "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed_enabled", return_value=True, ): result = _get_eligible_projects(org, "manual") @@ -1248,7 +1248,7 @@ def test_payload_omits_automation_tuning_for_seat_based_orgs(self) -> None: with ( patch( - "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed", + "sentry.tasks.seer.agentic_triage.cron.is_seer_seat_or_token_billed_enabled", return_value=True, ), ):