Skip to content

Add debug reports with context to feedback flow#1798

Open
richiemcilroy wants to merge 6 commits into
mainfrom
debug-report
Open

Add debug reports with context to feedback flow#1798
richiemcilroy wants to merge 6 commits into
mainfrom
debug-report

Conversation

@richiemcilroy
Copy link
Copy Markdown
Member

@richiemcilroy richiemcilroy commented May 11, 2026

adds an optional debug report path alongside normal feedback: the desktop app can generate a stable report ID, collect structured client context (runtime, route, stores, diagnostics, sanitized settings), upload logs via an authenticated multipart request when submitting a report, and notify the team through the existing Discord webhook and feedback email pipeline.

Greptile Summary

This PR adds an optional debug report path to the existing feedback flow: the desktop app collects structured client context (runtime, stores, diagnostics, sanitized settings), uploads logs via an authenticated multipart request, and notifies the team via Discord and email.

  • Rust layer (lib.rs, logging.rs): a new upload_debug_report Tauri command refactors the existing log-upload path into upload_log_file_with_context, switching to authed_api_request and appending report fields only when a debug report is present — logic is correct and clean.
  • Server layer (root.ts): the /logs endpoint now accepts and uploads the context and diagnostics as separate Discord file attachments, with individual section truncation and a final 1900-char cap; the expanded Zod schemas use .passthrough() to stay forward-compatible.
  • Client layer (feedback.tsx) and email template: the two-step submission flow creates a duplicate-Discord-notification risk on retry, and the full JSON context blob is passed to the email component without a size limit, which can silently fail email delivery for large payloads.

Confidence Score: 3/5

Two issues in the submission flow need attention before merging: oversized email payloads can silently fail, and retries generate duplicate Discord alerts.

The Rust and server changes are solid, but the client-side action sends the full tab-indented JSON context (potentially 50–100 KB) directly into the email template with no truncation, which can cause silent delivery failures at the email provider. Separately, the two-step submit design means a transient failure on step 2 leaves the Discord side-effect already fired — retries produce duplicate webhook notifications for the same issue.

apps/desktop/src/routes/(window-chrome)/settings/feedback.tsx (two-step retry logic) and packages/database/emails/feedback.tsx (unbounded debugReport field).

Important Files Changed

Filename Overview
apps/desktop/src/routes/(window-chrome)/settings/feedback.tsx Adds debug report collection and a two-step submission flow (native log upload + web feedback API); the two-step design creates a duplicate-notification risk on retry, and sends the full JSON context to the email pipeline without truncation.
packages/database/emails/feedback.tsx Extends the Feedback email component to render the full debugReport JSON blob inline; no size limit means oversized payloads could silently fail email delivery.
apps/desktop/src-tauri/src/logging.rs Refactors upload_log_file into upload_log_file_with_context, adding optional debug report fields and switching to authed_api_request when a report is present; logic is correct and clean.
apps/desktop/src-tauri/src/lib.rs Adds upload_debug_report Tauri command and registers it; straightforward delegation to logging.rs.
apps/web/app/api/desktop/[...route]/root.ts Extends the /logs endpoint to accept and upload report context/diagnostics as Discord file attachments; expands the diagnostics schema with .passthrough() and adds Discord message formatting helpers with appropriate truncation.
packages/web-api-contract/src/desktop.ts Adds optional reportId, kind, and debugReport fields to the submitFeedback contract; non-breaking change.
packages/web-api-contract-effect/src/index.ts Mirrors the contract change for the Effect-based API client; clean and consistent.

Comments Outside Diff (1)

  1. apps/desktop/src/routes/(window-chrome)/settings/feedback.tsx, line 382-420 (link)

    P1 Retry causes duplicate Discord notifications

    sendDebugReportAction calls invoke("upload_debug_report") first (which triggers the Discord webhook via /api/desktop/logs) and then calls apiClient.desktop.submitFeedback. If the second call fails (network hiccup, 500, etc.), the user sees the error toast and may click "Send Debug Report" again. Each retry re-fires the Discord webhook with a freshly-generated reportId and uploads a new log file to storage, so the support team receives duplicate Discord alerts that look like separate reports for the same issue.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/routes/(window-chrome)/settings/feedback.tsx
    Line: 382-420
    
    Comment:
    **Retry causes duplicate Discord notifications**
    
    `sendDebugReportAction` calls `invoke("upload_debug_report")` first (which triggers the Discord webhook via `/api/desktop/logs`) and then calls `apiClient.desktop.submitFeedback`. If the second call fails (network hiccup, 500, etc.), the user sees the error toast and may click "Send Debug Report" again. Each retry re-fires the Discord webhook with a freshly-generated `reportId` and uploads a new log file to storage, so the support team receives duplicate Discord alerts that look like separate reports for the same issue.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/database/emails/feedback.tsx:74-80
**Unbounded JSON blob in email body**

`debugReport` is the full tab-indented `clientContext` JSON, which includes up to 20 recordings, 20 screenshots, all device formats, all stores, and diagnostics. This can easily reach 50–100 KB. Many transactional email providers (e.g. Resend) impose payload size limits, and silently hitting that ceiling would cause the `sendEmail` call to throw, returning a 500 to the client with no useful error message. At minimum the field should be truncated before passing to this component.

### Issue 2 of 3
apps/desktop/src/routes/(window-chrome)/settings/feedback.tsx:382-420
**Retry causes duplicate Discord notifications**

`sendDebugReportAction` calls `invoke("upload_debug_report")` first (which triggers the Discord webhook via `/api/desktop/logs`) and then calls `apiClient.desktop.submitFeedback`. If the second call fails (network hiccup, 500, etc.), the user sees the error toast and may click "Send Debug Report" again. Each retry re-fires the Discord webhook with a freshly-generated `reportId` and uploads a new log file to storage, so the support team receives duplicate Discord alerts that look like separate reports for the same issue.

### Issue 3 of 3
apps/desktop/src/routes/(window-chrome)/settings/feedback.tsx:90-91
`navigator.platform` is deprecated in modern browsers and may return an empty string in some Chromium-based webviews. Consider using `navigator.userAgentData?.platform` as the preferred source with `navigator.platform` as a fallback.

```suggestion
		platform: (navigator as unknown as { userAgentData?: { platform?: string } }).userAgentData?.platform ?? navigator.platform,
		online: navigator.onLine,
```

Reviews (1): Last reviewed commit: "feat(desktop): submit debug reports from..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

@brin-security-scanner brin-security-scanner Bot added contributor:verified Contributor passed trust analysis. pr:verified PR passed security analysis. labels May 11, 2026
@paragon-review
Copy link
Copy Markdown

Paragon Review Skipped

Hi @richiemcilroy! Your Polarity credit balance is insufficient to complete this review.

Please visit https://app.paragon.run to finish your review.

Comment on lines +74 to +80
{debugReport && (
<Section className="my-4 p-4 bg-gray-50 rounded-lg">
<Text className="text-xs leading-5 text-gray-700 whitespace-pre-wrap">
{debugReport}
</Text>
</Section>
)}
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.

P1 Unbounded JSON blob in email body

debugReport is the full tab-indented clientContext JSON, which includes up to 20 recordings, 20 screenshots, all device formats, all stores, and diagnostics. This can easily reach 50–100 KB. Many transactional email providers (e.g. Resend) impose payload size limits, and silently hitting that ceiling would cause the sendEmail call to throw, returning a 500 to the client with no useful error message. At minimum the field should be truncated before passing to this component.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/database/emails/feedback.tsx
Line: 74-80

Comment:
**Unbounded JSON blob in email body**

`debugReport` is the full tab-indented `clientContext` JSON, which includes up to 20 recordings, 20 screenshots, all device formats, all stores, and diagnostics. This can easily reach 50–100 KB. Many transactional email providers (e.g. Resend) impose payload size limits, and silently hitting that ceiling would cause the `sendEmail` call to throw, returning a 500 to the client with no useful error message. At minimum the field should be truncated before passing to this component.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +90 to +91
platform: navigator.platform,
online: navigator.onLine,
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.

P2 navigator.platform is deprecated in modern browsers and may return an empty string in some Chromium-based webviews. Consider using navigator.userAgentData?.platform as the preferred source with navigator.platform as a fallback.

Suggested change
platform: navigator.platform,
online: navigator.onLine,
platform: (navigator as unknown as { userAgentData?: { platform?: string } }).userAgentData?.platform ?? navigator.platform,
online: navigator.onLine,
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/routes/(window-chrome)/settings/feedback.tsx
Line: 90-91

Comment:
`navigator.platform` is deprecated in modern browsers and may return an empty string in some Chromium-based webviews. Consider using `navigator.userAgentData?.platform` as the preferred source with `navigator.platform` as a fallback.

```suggestion
		platform: (navigator as unknown as { userAgentData?: { platform?: string } }).userAgentData?.platform ?? navigator.platform,
		online: navigator.onLine,
```

How can I resolve this? If you propose a fix, please make it concise.

feedback,
clientContext,
} = c.req.valid("form");
const user = c.get("user");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If reportId (or clientContext) is present, it probably means “debug report” and should require auth. Otherwise this endpoint can be hit anonymously to post “debug report” messages + attachments to the webhook.

Suggested change
const user = c.get("user");
if (reportId && !user) {
return c.json(
{ error: "Authentication required for debug reports" },
{ status: 401 },
);
}

Comment on lines +496 to +497
const fileBase = `cap-desktop-${os || "unknown"}-${version || "unknown"}-${reportId || Date.now()}`;
const fileName = `${fileBase}.log`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

os / version / reportId are ultimately client-provided, so it’s worth sanitizing before using them in filenames (avoid weird characters or accidental header-ish values).

Suggested change
const fileBase = `cap-desktop-${os || "unknown"}-${version || "unknown"}-${reportId || Date.now()}`;
const fileName = `${fileBase}.log`;
const safeFilePart = (value: string) =>
value.replaceAll(/[^a-zA-Z0-9_.-]/g, "_");
const fileBase = `cap-desktop-${safeFilePart(os || "unknown")}-${safeFilePart(version || "unknown")}-${safeFilePart(reportId || String(Date.now()))}`;
const fileName = `${fileBase}.log`;

os,
version,
});
const clientContext = JSON.stringify(context, null, "\t");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

clientContext gets sent to the API twice (Tauri upload_debug_report + /feedback). Pretty-printing with tabs can significantly inflate the payload; I’d keep it compact for transport (and if you want readability, derive a separate pretty string for display/email with a size cap).

Suggested change
const clientContext = JSON.stringify(context, null, "\t");
const clientContext = JSON.stringify(context);

{debugReport && (
<Section className="my-4 p-4 bg-gray-50 rounded-lg">
<Text className="text-xs leading-5 text-gray-700 whitespace-pre-wrap">
{debugReport}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Potential footgun: debugReport can be very large (it’s a full JSON dump right now). Capping what gets embedded in the email helps avoid oversized emails / deliverability issues.

Suggested change
{debugReport}
{debugReport.length > 20000
? `${debugReport.slice(0, 20000)}…`
: debugReport}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor:verified Contributor passed trust analysis. pr:verified PR passed security analysis.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant