Skip to content

feat(seer): Add trigger_explorer method to SeerOperator#109675

Merged
alexsohn1126 merged 237 commits intomasterfrom
alexsohn/iswf-2022-add-trigger-explorer-method-to-seeroperator
Mar 30, 2026
Merged

feat(seer): Add trigger_explorer method to SeerOperator#109675
alexsohn1126 merged 237 commits intomasterfrom
alexsohn/iswf-2022-add-trigger-explorer-method-to-seeroperator

Conversation

@alexsohn1126
Copy link
Copy Markdown
Member

@alexsohn1126 alexsohn1126 commented Mar 2, 2026

Summary

  • Add SeerExplorerOperator class with trigger_explorer() method that starts or continues Explorer runs via SeerExplorerClient, checking for existing runs by category (e.g. Slack thread) and routing to continue_run() or start_run() accordingly
  • Handle errors through entrypoint callbacks (on_trigger_explorer_error) and cache payload for the ExplorerOnCompletionHook via SeerOperatorExplorerCache
  • Add Explorer-specific interaction types (OPERATOR_TRIGGER_EXPLORER, ENTRYPOINT_ON_TRIGGER_EXPLORER, ENTRYPOINT_CREATE_EXPLORER_CACHE_PAYLOAD, ENTRYPOINT_ON_EXPLORER_UPDATE) to SeerOperatorInteractionType for metrics instrumentation
  • Widen SeerExplorerClient and collect_user_org_context to accept RpcUser in addition to User/AnonymousUser, enabling use from cross-silo contexts like Slack
  • Harden SeerOperatorCompletionHook.execute(): fetch-run-status errors now log instead of aborting, add org-id mismatch guard on cache payloads, and wrap on_explorer_update calls with per-entrypoint lifecycle metrics

Refs ISWF-2022

leeandher and others added 11 commits February 6, 2026 12:22
Add organizations:seer-slack-explorer flag to gate the new Slack
@mention functionality that allows users to interact with Seer Explorer
by mentioning the Sentry bot.

Refs ISWF-2016
Co-Authored-By: Claude <noreply@anthropic.com>
Add three new methods to SlackIntegration for Seer Explorer @mentions:
- get_thread_history(): Fetch thread replies via conversations.replies
- add_reaction(): Add emoji reaction (idempotent)
- remove_reaction(): Remove emoji reaction (idempotent)

Methods check installation scopes and fail silently if scope is missing,
logging a warning. Extended scopes (reactions:write, channels:history,
groups:history, app_mentions:read) are gated by slack.extended-scopes-enabled
option, which controls whether new installations request these scopes.

Refs ISWF-2017
Co-Authored-By: Claude <noreply@anthropic.com>
Previously the integration metadata always stored `identity_oauth_scopes`
regardless of what scopes Slack actually granted. Now we parse the
`scope` field from the OAuth response to store the actual granted scopes,
enabling proper scope checking for installations that have been upgraded
to extended scopes.

Also adds scopes to debug metadata keys for troubleshooting.

Co-Authored-By: Claude <noreply@anthropic.com>
… scopes

When the OAuth response doesn't include granted scopes, fall back to
identity_oauth_scopes (old permissions) instead of _get_oauth_scopes()
which includes extended permissions.
Add a second generic (ExplorerCachePayloadT) to SeerEntrypoint and
SeerOperator, and define Explorer lifecycle methods on the protocol:
on_trigger_explorer_error, on_trigger_explorer_success,
create_explorer_cache_payload, and on_explorer_update.

ISWF-2020
…ocol

SlackEntrypoint and MockEntrypoint now implement the new Explorer methods
with no-op stubs. Also fixes MockEntrypoint to use 2 type args.

ISWF-2020
…erer

Add Explorer completion notification support to the notification platform:
- SeerExplorerResponse dataclass and template for Explorer completion data
- SEER_EXPLORER_RESPONSE NotificationSource
- _render_explorer_response() method in SeerSlackRenderer

ISWF-2025
Ref: ISWF-2018

Add routing and handler for Slack `app_mention` events to enable Seer
Explorer via @mentions. The handler resolves the organization, checks
the `seer-slack-explorer` feature flag, and dispatches a
`process_explorer_mention` task with the event context.
Use a more generic name since this task will handle all Slack mentions,
not just Explorer-specific ones.

Refs ISWF-2018
Co-Authored-By: Claude <noreply@anthropic.com>
@linear
Copy link
Copy Markdown

linear bot commented Mar 2, 2026

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Mar 2, 2026
@alexsohn1126 alexsohn1126 changed the base branch from leanderrodrigues/iswf-2017-add-slack-api-methods-for-thread-history-and-reactions to alexsohn/iswf-2021-add-explorer-support-to-slackentrypoint March 2, 2026 18:43
@alexsohn1126 alexsohn1126 force-pushed the alexsohn/iswf-2021-add-explorer-support-to-slackentrypoint branch from 4df6820 to c34a704 Compare March 2, 2026 19:53
@alexsohn1126 alexsohn1126 force-pushed the alexsohn/iswf-2022-add-trigger-explorer-method-to-seeroperator branch from 54bbfc6 to 881c751 Compare March 2, 2026 19:53
@alexsohn1126 alexsohn1126 force-pushed the alexsohn/iswf-2022-add-trigger-explorer-method-to-seeroperator branch from 881c751 to c96afad Compare March 3, 2026 18:02
@alexsohn1126 alexsohn1126 force-pushed the alexsohn/iswf-2022-add-trigger-explorer-method-to-seeroperator branch from c96afad to 740d420 Compare March 3, 2026 21:29
Comment thread src/sentry/seer/entrypoints/operator.py Outdated
@alexsohn1126 alexsohn1126 marked this pull request as ready for review March 23, 2026 18:31
@alexsohn1126 alexsohn1126 requested a review from a team as a code owner March 23, 2026 18:31
Comment thread src/sentry/seer/entrypoints/operator.py
Comment thread src/sentry/seer/entrypoints/operator.py
The get_runs call in trigger_explorer defaulted to only_current_user=True,
which meant a second user interacting with an Explorer session in a shared
context (e.g. Slack thread) would not find the existing run and would
incorrectly create a new one. Pass only_current_user=False so the lookup
finds any run matching the category regardless of who started it.

Co-Authored-By: Claude Opus 4.6 <noreply@example.com>
Comment thread src/sentry/seer/entrypoints/operator.py
Comment on lines +462 to +468
with SeerOperatorEventLifecycleMetric(
interaction_type=SeerOperatorInteractionType.ENTRYPOINT_ON_TRIGGER_EXPLORER_ERROR,
entrypoint_key=self.entrypoint.key,
).capture():
self.entrypoint.on_trigger_explorer_error(error=str(e))
lifecycle.record_failure(failure_reason=e)
return None
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh interesting. What is this entrypoint call doing on failure? I wonder if it needs to be contained to a task if we expect to propagate this to users reliably.

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.

The entrypoint will send an error message to the thread, with a generic error message.

Maybe we should make this into a task, but at the same time, spinning up a task will require a lot more overhead, so I don't know if it's worth it 🤔

Comment thread src/sentry/seer/entrypoints/operator.py Outdated
run_id=run_id,
)
if cache_payload.get("organization_id") != organization.id:
lifecycle.add_extra("org_mismatch", str(entrypoint_key))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's a chance this lifecycle will never log this case, so it may be worth explicitly logging, and possibly creating a Sentry issue for this case. This seems serious if we somehow mix up organization IDs at this phase of a run.

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.

Makes sense. If we find a cache with the same run_id, it better have have the same org_id, otherwise something has indeed gone wrong 😓

Added:

if cache_payload.get("organization_id") != organization.id:
lifecycle.record_failure(failure_reason="org_mismatch")
return

Copy link
Copy Markdown
Member

@aliu39 aliu39 left a comment

Choose a reason for hiding this comment

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

explorer client changes and callsites lgtm!

organization=organization,
user=user,
category_key=category_key,
category_value=category_value,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
category_value=category_value,
category_value=category_value,
is_interactive=True,

You can add this flag as a reminder to the agent there's a user to chat with/get feedback from

Nit can also explicitly set enable_coding=False (already the default) as it's considered experimental

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.

Thank you! I've changed to include both of your suggestions:

client = SeerExplorerClient(
organization=organization,
user=user,
category_key=category_key,
category_value=category_value,
on_completion_hook=SeerOperatorCompletionHook,
is_interactive=True,
enable_coding=False,
)

Comment thread src/sentry/seer/entrypoints/operator.py
Comment thread src/sentry/seer/entrypoints/operator.py
The return is intentional: run_id is globally unique in Seer, so only one entrypoint will ever have a cache entry per run. An org mismatch is anomalous and should abort the entire method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment on lines +542 to +552
with SeerOperatorEventLifecycleMetric(
interaction_type=SeerOperatorInteractionType.ENTRYPOINT_CREATE_EXPLORER_CACHE_PAYLOAD,
entrypoint_key=self.entrypoint.key,
).capture():
cache_payload = self.entrypoint.create_explorer_cache_payload()

SeerOperatorExplorerCache.set(
entrypoint_key=str(self.entrypoint.key),
run_id=run_id,
cache_payload=cache_payload,
)
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: In trigger_explorer, an unhandled exception after the success notification can cause a silent failure, preventing the user from receiving explorer results.
Severity: HIGH

Suggested Fix

Wrap the calls to create_explorer_cache_payload() and SeerOperatorExplorerCache.set() within a try...except block. This will handle potential exceptions after the success notification has been sent, preventing the process from silently failing and ensuring state consistency. This approach aligns with the exception handling already present earlier in the method.

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/entrypoints/operator.py#L542-L552

Potential issue: In the `trigger_explorer` method, the success callback
`on_trigger_explorer_success()` is invoked before the explorer payload is cached. If a
subsequent operation, such as `create_explorer_cache_payload()` or
`SeerOperatorExplorerCache.set()`, fails and raises an exception (e.g., due to a Redis
error), the exception is not handled. This causes the method to fail after the user has
already been notified of success. As a result, the completion hook will later find no
cache entry and will silently fail to deliver the explorer results to the user, creating
a state inconsistency and a broken user experience.

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.

Comment thread src/sentry/seer/entrypoints/operator.py
@alexsohn1126 alexsohn1126 merged commit dcce8d5 into master Mar 30, 2026
67 checks passed
@alexsohn1126 alexsohn1126 deleted the alexsohn/iswf-2022-add-trigger-explorer-method-to-seeroperator branch March 30, 2026 15:43
@github-actions github-actions bot locked and limited conversation to collaborators Apr 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants