Skip to content

feat(seer): migrate autofix tool to explorer-mode endpoint#961

Closed
JoshFerge wants to merge 2 commits into
mainfrom
feat/autofix-explorer-mode
Closed

feat(seer): migrate autofix tool to explorer-mode endpoint#961
JoshFerge wants to merge 2 commits into
mainfrom
feat/autofix-explorer-mode

Conversation

@JoshFerge
Copy link
Copy Markdown
Member

@JoshFerge JoshFerge commented May 13, 2026

Summary

  • Migrates analyze_issue_with_seer and the issue-details Seer summary off the legacy steps[] autofix payload and onto the explorer-mode schema (blocks keyed by message.metadata.step, typed artifacts, merged_file_patches, repo_pr_states).
  • Both autofix endpoints now hit ?mode=explorer. The POST takes {step, run_id?, user_context?, insert_index?}; the tool drives the run sequentially: root_causesolution. A run_id is reused from the GET if one already exists, mirroring the upstream useExplorerAutofix flow.
  • Rewrites the seer formatting helpers around explorer sections: a new getOrderedAutofixSections mirrors getOrderedAutofixSections in static/app/components/events/autofix/useExplorerAutofix.tsx and surfaces the typed root_cause / solution artifacts plus a synthesized pull_request section from repo_pr_states.
  • Status enum collapsed to the explorer values (processing / completed / error / awaiting_user_input).
  • MSW + undici mocks and fixtures updated to the explorer shape. Schema, formatting, and tool tests rewritten against the new payload.
  • Drive-by: ToolPredictionScorer was broken in two ways unrelated to autofix (--all-scopes flag no longer exists; z.record(z.any()) is rejected by OpenAI strict response_format). Both fixed in a follow-up commit so the eval can actually run.

Test plan

  • pnpm run tsc
  • pnpm run lint
  • pnpm run test — 307 tests pass across all packages
  • pnpm run --filter @sentry/mcp-core generate-definitions (regenerated tool description)
  • pnpm vitest run autofix.eval.ts — both cases score 1.00 with the new tool flow

Notes for reviewers

  • Schemas in api-client/schema.ts are passthrough where the upstream endpoint is marked experimental, so additive fields shouldn't break parsing.
  • The tool now polls for each section's completion individually (waitForSection) rather than a single global terminal-state poll. The 5-minute timeout still applies and renders a partial-output summary on timeout.
  • awaiting_user_input is surfaced via getHumanInterventionGuidance and ends the run gracefully (same retry hint as before).
  • formatSeerSummary falls back to the most substantive assistant message in a section when the structured artifact is missing, preserving the prior "give the reader something" behavior.

🤖 Generated with Claude Code

Switch `analyze_issue_with_seer` and the issue-details Seer summary off the
legacy `steps[]` payload and onto the explorer schema (`blocks` keyed by
`message.metadata.step`, typed `artifacts`, `merged_file_patches`,
`repo_pr_states`). Both autofix endpoints now hit `?mode=explorer`, with
POSTs driving the run one step at a time (`root_cause`, then `solution`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Agent transcript: https://claudescope.sentry.dev/share/eARu0DHoypI1IqAI03asQcPX__pjX3J8ESCzdONusbs
- Drop the stale `--all-scopes` flag the bin no longer accepts; the
  scorer-side stdio spawn was printing usage and exiting, surfacing as
  `MCPClientError: Connection closed` across every eval.
- Switch the `predictedTools[*].arguments` field to a JSON-encoded string;
  `z.record(z.any())` emits `additionalProperties` with no `type`, which
  OpenAI's strict response_format rejects.

With both fixes, `autofix.eval.ts` scores 1.00 / 1.00 against the new
explorer-mode tool flow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment on lines +279 to +286
if (!hasSection(sections, step) && runId !== undefined) {
try {
await apiService.startAutofix({
organizationSlug: orgSlug,
issueId,
step,
runId,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Existing terminal/errored run still triggers solution step start

When an existing autofix run is already in a terminal state (e.g., error) or awaiting_user_input and only has a completed root_cause section, the new loop will still call startAutofix({step: "solution", runId}) on that dead run because it only short-circuits per-section via findCompletedSection, not on the run's overall status. The previous implementation returned terminal results immediately without issuing further POSTs.

Verification

Read lines 238-300 of the new handler. For an existing run with status === "error" and only a completed root_cause section: the loop iteration for root_cause hits findCompletedSection and continues. The iteration for solution sees !hasSection(sections, "solution") (true) and runId !== undefined (true, since runId = state.autofix?.run_id). It therefore POSTs startAutofix with step: "solution" against a run that is already terminal. Old code at the deleted lines 154-176 short-circuited via isTerminalStatus(existingStatus) and returned without further POSTs. No guard in the new flow inspects state.autofix.status before issuing the start, so this regression is reachable from a normal saved-state path.

Identified by Warden code-review · YBH-RMV

@JoshFerge
Copy link
Copy Markdown
Member Author

Superseded by a 3-PR stack for easier review:

Closing here so review happens on the stack.

@JoshFerge JoshFerge closed this May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant