From f24cc8a0bb5c66aa33ddce9ea155942787e3bef7 Mon Sep 17 00:00:00 2001 From: Shayna Chambless Date: Tue, 14 Jul 2026 11:05:43 -0700 Subject: [PATCH 1/7] get response from endpoint --- static/app/types/event.tsx | 3 ++- static/app/views/issueDetails/utils.tsx | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/static/app/types/event.tsx b/static/app/types/event.tsx index a50826dc0f10..eea749744fe6 100644 --- a/static/app/types/event.tsx +++ b/static/app/types/event.tsx @@ -781,6 +781,7 @@ interface EventBase { dateCreated?: string; device?: Record; endTimestamp?: number; + formatted?: {content: string; format: string}; groupID?: string; groupingConfig?: { enhancements: string; @@ -802,7 +803,7 @@ interface EventBase { version: string | null; } | null; sdkUpdates?: SDKUpdatesSuggestion[]; - userReport?: UserReport | null; + userReport?: UserReport | null; // add to eventBase so it knows it can receive formatted } interface TraceEventContexts extends EventContexts { diff --git a/static/app/views/issueDetails/utils.tsx b/static/app/views/issueDetails/utils.tsx index 6241a5b867b4..0a1fb5472cd5 100644 --- a/static/app/views/issueDetails/utils.tsx +++ b/static/app/views/issueDetails/utils.tsx @@ -198,6 +198,7 @@ export function useEnvironmentsFromUrl(): string[] { } function getGroupEventDetailsQueryData({ + // this is where the frontend gets its data to display environments, query, start, @@ -212,6 +213,7 @@ function getGroupEventDetailsQueryData({ }): Record { const params: Record = { collapse: ['fullRelease'], + llmFormat: 'markdown', // adding llmformat here so it gets the markdown }; if (query) { From 17fc369d3047f3d854939af20e8b9e117f5d859a Mon Sep 17 00:00:00 2001 From: Shayna Chambless Date: Tue, 14 Jul 2026 11:19:34 -0700 Subject: [PATCH 2/7] use formattedllmtext in copy --- .../hooks/useCopyIssueDetails.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx index 908d559aa7a3..acff304014a2 100644 --- a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx +++ b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx @@ -266,6 +266,25 @@ export const issueAndEventToMarkdown = ({ activeThreadId, organization, }: IssueAndEventToMarkdownOptions): string => { + // copy to markdown specific things + let llmFormattedMarkdownText = ''; + llmFormattedMarkdownText += `**Issue ID:** ${group.id}\n`; // add issue id + if (group.project?.slug) { + llmFormattedMarkdownText += `**Project:** ${group.project?.slug}\n`; // project + } + if (event && typeof event.dateCreated === 'string') { + llmFormattedMarkdownText += `**Date:** ${new Date(event.dateCreated).toLocaleString()}\n`; // date + } + + const formatted = event?.formatted?.content; + if (formatted) { + llmFormattedMarkdownText += `\n${formatted}`; + return llmFormattedMarkdownText; + } + + // TODO: delete the rest of this when it is working + // also TODO: use a feature flag probably + // Format the basic issue information let markdownText = `# ${group.title}\n\n`; markdownText += `**Issue ID:** ${group.id}\n`; From 5dafe74f79835572b01ee76e19fd187153ed9cbd Mon Sep 17 00:00:00 2001 From: Shayna Chambless Date: Tue, 14 Jul 2026 14:02:44 -0700 Subject: [PATCH 3/7] autofix --- .../hooks/useCopyIssueDetails.tsx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx index acff304014a2..99e64509c709 100644 --- a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx +++ b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx @@ -279,6 +279,32 @@ export const issueAndEventToMarkdown = ({ const formatted = event?.formatted?.content; if (formatted) { llmFormattedMarkdownText += `\n${formatted}`; + if (autofixData) { + const sections = getOrderedAutofixSections(autofixData); + const rootCauseSection = sections.find(isRootCauseSection); + const solutionSection = sections.find(isSolutionSection); + + const rootCauseArtifact = rootCauseSection + ? getAutofixArtifactFromSection(rootCauseSection) + : null; + const solutionArtifact = solutionSection + ? getAutofixArtifactFromSection(solutionSection) + : null; + + const rootCauseCopyText = rootCauseArtifact + ? artifactToMarkdown(rootCauseArtifact, 2) + : null; + const solutionCopyText = solutionArtifact + ? artifactToMarkdown(solutionArtifact, 2) + : null; + + if (rootCauseCopyText) { + llmFormattedMarkdownText += `\n${rootCauseCopyText}\n`; + } + if (solutionCopyText) { + llmFormattedMarkdownText += `\n${solutionCopyText}\n`; + } + } return llmFormattedMarkdownText; } From b84d31789f75444e8b72f93a17b2d44822ca0a0e Mon Sep 17 00:00:00 2001 From: Shayna Chambless Date: Thu, 16 Jul 2026 11:28:15 -0700 Subject: [PATCH 4/7] autofix --- .../events/autofix/useExplorerAutofix.tsx | 7 +++- .../hooks/useCopyIssueDetails.tsx | 38 +++++-------------- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/static/app/components/events/autofix/useExplorerAutofix.tsx b/static/app/components/events/autofix/useExplorerAutofix.tsx index 6b5322cf060e..53b333e95694 100644 --- a/static/app/components/events/autofix/useExplorerAutofix.tsx +++ b/static/app/components/events/autofix/useExplorerAutofix.tsx @@ -161,6 +161,7 @@ export interface ExplorerAutofixState { */ interface ExplorerAutofixResponse { autofix: ExplorerAutofixState | null; + formatted?: {content: string; format: string}; // add formatted to response } const POLL_INTERVAL = 1000; @@ -170,7 +171,7 @@ function explorerAutofixApiOptions(orgSlug: string, groupId: string) { '/organizations/$organizationIdOrSlug/issues/$issueId/autofix/', { path: {organizationIdOrSlug: orgSlug, issueId: groupId}, - query: {mode: 'explorer'}, + query: {mode: 'explorer', llmFormat: 'markdown'}, // add markdown as an option staleTime: 0, } ); @@ -810,6 +811,10 @@ export function useExplorerAutofix( * Current autofix run state, or null if no run exists. */ runState, + /** + * Formatted markdown for LLM prompts + */ + autofixFormatted: apiData?.formatted?.content ?? null, /** * Whether the initial data fetch is pending. */ diff --git a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx index 99e64509c709..083daca382d3 100644 --- a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx +++ b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx @@ -256,7 +256,8 @@ interface IssueAndEventToMarkdownOptions { organization: Organization; activeThreadId?: number; autofixData?: ExplorerAutofixState | null; - event?: Event | null; + autofixFormatted?: string | null; + event?: Event | null; // add it to interface } export const issueAndEventToMarkdown = ({ @@ -265,6 +266,7 @@ export const issueAndEventToMarkdown = ({ autofixData, activeThreadId, organization, + autofixFormatted, }: IssueAndEventToMarkdownOptions): string => { // copy to markdown specific things let llmFormattedMarkdownText = ''; @@ -279,31 +281,8 @@ export const issueAndEventToMarkdown = ({ const formatted = event?.formatted?.content; if (formatted) { llmFormattedMarkdownText += `\n${formatted}`; - if (autofixData) { - const sections = getOrderedAutofixSections(autofixData); - const rootCauseSection = sections.find(isRootCauseSection); - const solutionSection = sections.find(isSolutionSection); - - const rootCauseArtifact = rootCauseSection - ? getAutofixArtifactFromSection(rootCauseSection) - : null; - const solutionArtifact = solutionSection - ? getAutofixArtifactFromSection(solutionSection) - : null; - - const rootCauseCopyText = rootCauseArtifact - ? artifactToMarkdown(rootCauseArtifact, 2) - : null; - const solutionCopyText = solutionArtifact - ? artifactToMarkdown(solutionArtifact, 2) - : null; - - if (rootCauseCopyText) { - llmFormattedMarkdownText += `\n${rootCauseCopyText}\n`; - } - if (solutionCopyText) { - llmFormattedMarkdownText += `\n${solutionCopyText}\n`; - } + if (autofixFormatted) { + llmFormattedMarkdownText += `\n${autofixFormatted}`; } return llmFormattedMarkdownText; } @@ -368,7 +347,9 @@ export const issueAndEventToMarkdown = ({ export const useCopyIssueDetails = (group: Group, event?: Event) => { const organization = useOrganization(); - const {runState: autofixData} = useExplorerAutofix(group.id, {enabled: false}); + const {runState: autofixData, autofixFormatted} = useExplorerAutofix(group.id, { + enabled: false, + }); const activeThreadId = useActiveThreadId(); const text = useMemo(() => { @@ -378,8 +359,9 @@ export const useCopyIssueDetails = (group: Group, event?: Event) => { autofixData, activeThreadId, organization, + autofixFormatted, }); - }, [group, event, autofixData, activeThreadId, organization]); + }, [group, event, autofixData, activeThreadId, organization, autofixFormatted]); const {copy} = useCopyToClipboard(); From cdd85048ab895d46b1bf090fb124ce35487fc8a2 Mon Sep 17 00:00:00 2001 From: Shayna Chambless Date: Thu, 16 Jul 2026 12:48:26 -0700 Subject: [PATCH 5/7] autofixMarkdown --- .../app/components/events/autofix/v3/autofixCards.spec.tsx | 1 + static/app/components/events/autofix/v3/nextStep.spec.tsx | 1 + .../events/autofix/v3/prIterationFeedbackForm.spec.tsx | 1 + .../events/autofix/v3/useAutoTriggerAutofix.spec.tsx | 1 + .../events/autofix/v3/useResetAutofixStep.spec.tsx | 1 + static/app/views/issueDetails/eventNavigation/index.tsx | 7 +++++-- .../app/views/issueDetails/hooks/useCopyIssueDetails.tsx | 2 +- 7 files changed, 11 insertions(+), 3 deletions(-) diff --git a/static/app/components/events/autofix/v3/autofixCards.spec.tsx b/static/app/components/events/autofix/v3/autofixCards.spec.tsx index d4472579b7c3..93512a243d23 100644 --- a/static/app/components/events/autofix/v3/autofixCards.spec.tsx +++ b/static/app/components/events/autofix/v3/autofixCards.spec.tsx @@ -102,6 +102,7 @@ function makePR(overrides: Partial = {}): RepoPRState { const mockAutofix: ReturnType = { runState: null, + autofixFormatted: null, isLoading: false, isPolling: false, startStep: jest.fn(), diff --git a/static/app/components/events/autofix/v3/nextStep.spec.tsx b/static/app/components/events/autofix/v3/nextStep.spec.tsx index 6b653d1f4360..79dd7eb6a6b9 100644 --- a/static/app/components/events/autofix/v3/nextStep.spec.tsx +++ b/static/app/components/events/autofix/v3/nextStep.spec.tsx @@ -20,6 +20,7 @@ function makeAutofix( ): ReturnType { const base: ReturnType = { runState: {run_id: 1} as any, + autofixFormatted: null, startStep: jest.fn(), createPR: jest.fn(), reset: jest.fn(), diff --git a/static/app/components/events/autofix/v3/prIterationFeedbackForm.spec.tsx b/static/app/components/events/autofix/v3/prIterationFeedbackForm.spec.tsx index 17f5812e6147..fa0dfdc8dc75 100644 --- a/static/app/components/events/autofix/v3/prIterationFeedbackForm.spec.tsx +++ b/static/app/components/events/autofix/v3/prIterationFeedbackForm.spec.tsx @@ -13,6 +13,7 @@ function makeAutofix( ): ReturnType { return { runState: {run_id: 1, blocks: []} as any, + autofixFormatted: null, startStep: jest.fn().mockResolvedValue(undefined), createPR: jest.fn(), reset: jest.fn(), diff --git a/static/app/components/events/autofix/v3/useAutoTriggerAutofix.spec.tsx b/static/app/components/events/autofix/v3/useAutoTriggerAutofix.spec.tsx index e5d28f2c4643..c79a33c0ed2a 100644 --- a/static/app/components/events/autofix/v3/useAutoTriggerAutofix.spec.tsx +++ b/static/app/components/events/autofix/v3/useAutoTriggerAutofix.spec.tsx @@ -10,6 +10,7 @@ function makeAutofix( ): ReturnType { const base: ReturnType = { runState: null, + autofixFormatted: null, startStep: jest.fn(), createPR: jest.fn(), reset: jest.fn(), diff --git a/static/app/components/events/autofix/v3/useResetAutofixStep.spec.tsx b/static/app/components/events/autofix/v3/useResetAutofixStep.spec.tsx index ed587513f702..341b0ad23034 100644 --- a/static/app/components/events/autofix/v3/useResetAutofixStep.spec.tsx +++ b/static/app/components/events/autofix/v3/useResetAutofixStep.spec.tsx @@ -11,6 +11,7 @@ function makeAutofix( ): ReturnType { const base: ReturnType = { runState: null, + autofixFormatted: null, startStep: jest.fn(), createPR: jest.fn(), reset: jest.fn(), diff --git a/static/app/views/issueDetails/eventNavigation/index.tsx b/static/app/views/issueDetails/eventNavigation/index.tsx index 92fcf914f7db..601c7be92b09 100644 --- a/static/app/views/issueDetails/eventNavigation/index.tsx +++ b/static/app/views/issueDetails/eventNavigation/index.tsx @@ -115,7 +115,9 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps) const activeThreadId = useActiveThreadId(); // Get data for markdown copy functionality - const {runState: autofixData} = useExplorerAutofix(group.id, {enabled: false}); + const {runState: autofixData, autofixFormatted} = useExplorerAutofix(group.id, { + enabled: false, + }); const handleCopyMarkdown = useCallback(() => { const markdownText = issueAndEventToMarkdown({ @@ -124,6 +126,7 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps) autofixData, activeThreadId, organization, + autofixFormatted, }); trackAnalytics('issue_details.copy_issue_details_as_markdown', { @@ -134,7 +137,7 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps) }); return markdownText; - }, [activeThreadId, event, group, autofixData, organization]); + }, [activeThreadId, event, group, autofixData, organization, autofixFormatted]); return ( diff --git a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx index 083daca382d3..fae1fac9128d 100644 --- a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx +++ b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx @@ -282,7 +282,7 @@ export const issueAndEventToMarkdown = ({ if (formatted) { llmFormattedMarkdownText += `\n${formatted}`; if (autofixFormatted) { - llmFormattedMarkdownText += `\n${autofixFormatted}`; + llmFormattedMarkdownText += `\n\n${autofixFormatted}`; } return llmFormattedMarkdownText; } From 0e30e1961b58003d59c3879a675845aa2e3d80f3 Mon Sep 17 00:00:00 2001 From: Shayna Chambless Date: Mon, 20 Jul 2026 11:24:22 -0700 Subject: [PATCH 6/7] clean up --- .../events/autofix/useExplorerAutofix.tsx | 4 +-- static/app/types/event.tsx | 2 +- .../hooks/useCopyIssueDetails.tsx | 25 ++++++++----------- static/app/views/issueDetails/utils.tsx | 3 +-- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/static/app/components/events/autofix/useExplorerAutofix.tsx b/static/app/components/events/autofix/useExplorerAutofix.tsx index 53b333e95694..112513824acd 100644 --- a/static/app/components/events/autofix/useExplorerAutofix.tsx +++ b/static/app/components/events/autofix/useExplorerAutofix.tsx @@ -161,7 +161,7 @@ export interface ExplorerAutofixState { */ interface ExplorerAutofixResponse { autofix: ExplorerAutofixState | null; - formatted?: {content: string; format: string}; // add formatted to response + formatted?: {content: string; format: string}; } const POLL_INTERVAL = 1000; @@ -171,7 +171,7 @@ function explorerAutofixApiOptions(orgSlug: string, groupId: string) { '/organizations/$organizationIdOrSlug/issues/$issueId/autofix/', { path: {organizationIdOrSlug: orgSlug, issueId: groupId}, - query: {mode: 'explorer', llmFormat: 'markdown'}, // add markdown as an option + query: {mode: 'explorer', llmFormat: 'markdown'}, staleTime: 0, } ); diff --git a/static/app/types/event.tsx b/static/app/types/event.tsx index eea749744fe6..2c5c3572a4f1 100644 --- a/static/app/types/event.tsx +++ b/static/app/types/event.tsx @@ -803,7 +803,7 @@ interface EventBase { version: string | null; } | null; sdkUpdates?: SDKUpdatesSuggestion[]; - userReport?: UserReport | null; // add to eventBase so it knows it can receive formatted + userReport?: UserReport | null; } interface TraceEventContexts extends EventContexts { diff --git a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx index fae1fac9128d..8f39aba28d21 100644 --- a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx +++ b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx @@ -257,7 +257,7 @@ interface IssueAndEventToMarkdownOptions { activeThreadId?: number; autofixData?: ExplorerAutofixState | null; autofixFormatted?: string | null; - event?: Event | null; // add it to interface + event?: Event | null; } export const issueAndEventToMarkdown = ({ @@ -268,23 +268,20 @@ export const issueAndEventToMarkdown = ({ organization, autofixFormatted, }: IssueAndEventToMarkdownOptions): string => { - // copy to markdown specific things - let llmFormattedMarkdownText = ''; - llmFormattedMarkdownText += `**Issue ID:** ${group.id}\n`; // add issue id - if (group.project?.slug) { - llmFormattedMarkdownText += `**Project:** ${group.project?.slug}\n`; // project - } - if (event && typeof event.dateCreated === 'string') { - llmFormattedMarkdownText += `**Date:** ${new Date(event.dateCreated).toLocaleString()}\n`; // date - } - const formatted = event?.formatted?.content; if (formatted) { - llmFormattedMarkdownText += `\n${formatted}`; + let llmMarkdown = `**Issue ID:** ${group.id}\n`; + if (group.project?.slug) { + llmMarkdown += `**Project:** ${group.project.slug}\n`; + } + if (typeof event?.dateCreated === 'string') { + llmMarkdown += `**Date:** ${new Date(event.dateCreated).toLocaleString()}\n`; + } + llmMarkdown += `\n${formatted}`; if (autofixFormatted) { - llmFormattedMarkdownText += `\n\n${autofixFormatted}`; + llmMarkdown += `\n\n${autofixFormatted}`; } - return llmFormattedMarkdownText; + return llmMarkdown; } // TODO: delete the rest of this when it is working diff --git a/static/app/views/issueDetails/utils.tsx b/static/app/views/issueDetails/utils.tsx index 0a1fb5472cd5..d19fa4637c34 100644 --- a/static/app/views/issueDetails/utils.tsx +++ b/static/app/views/issueDetails/utils.tsx @@ -198,7 +198,6 @@ export function useEnvironmentsFromUrl(): string[] { } function getGroupEventDetailsQueryData({ - // this is where the frontend gets its data to display environments, query, start, @@ -213,7 +212,7 @@ function getGroupEventDetailsQueryData({ }): Record { const params: Record = { collapse: ['fullRelease'], - llmFormat: 'markdown', // adding llmformat here so it gets the markdown + llmFormat: 'markdown', }; if (query) { From 7a5ecc69a58e6d4c18c2e280d1291e2d50281cf0 Mon Sep 17 00:00:00 2001 From: Shayna Chambless Date: Mon, 20 Jul 2026 12:43:01 -0700 Subject: [PATCH 7/7] comments --- static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx index 8f39aba28d21..f8ca3c13fef7 100644 --- a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx +++ b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx @@ -284,9 +284,6 @@ export const issueAndEventToMarkdown = ({ return llmMarkdown; } - // TODO: delete the rest of this when it is working - // also TODO: use a feature flag probably - // Format the basic issue information let markdownText = `# ${group.title}\n\n`; markdownText += `**Issue ID:** ${group.id}\n`;