Skip to content

Add telemetry sampling for non-Pro users#2642

Merged
wwwillchen merged 1 commit into
dyad-sh:mainfrom
wwwillchen-bot:agent--1770850805023-1770851031
Feb 12, 2026
Merged

Add telemetry sampling for non-Pro users#2642
wwwillchen merged 1 commit into
dyad-sh:mainfrom
wwwillchen-bot:agent--1770850805023-1770851031

Conversation

@wwwillchen-bot

@wwwillchen-bot wwwillchen-bot commented Feb 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Store Dyad Pro status in localStorage for telemetry access
  • Sample 10% of events for non-Pro users (error events always sent)
  • Pro users continue to send all events

Test plan

  • Verify telemetry events are sampled correctly for non-Pro users
  • Confirm error events are always sent regardless of Pro status
  • Test Pro users still send all events

🤖 Generated with Claude Code


Open with Devin

Summary by cubic

Adds telemetry sampling for non‑Pro users to reduce event volume while keeping error visibility. Stores Dyad Pro status in localStorage; Pro users keep sending all events.

  • New Features
    • Save Dyad Pro status in localStorage (dyadProStatus) and expose isDyadProUser().
    • Sample PostHog events for non‑Pro: send 10% of non‑error events; always send errors.
    • No change for Pro users; all events continue to be sent.

Written for commit 685922c. Summary will update on new commits.

- Store Dyad Pro status in localStorage for telemetry access
- Sample 10% of events for non-Pro users (always send errors)
- Pro users continue to send all events

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@wwwillchen

Copy link
Copy Markdown
Collaborator

@BugBot run

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @wwwillchen-bot, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new telemetry sampling strategy to optimize data collection. It differentiates between Pro and non-Pro users, significantly reducing the volume of telemetry data sent by non-Pro users while ensuring that all critical error events are still captured. This change aims to manage data ingestion more efficiently without compromising the ability to identify and address issues.

Highlights

  • Telemetry Sampling for Non-Pro Users: Implemented a mechanism to sample telemetry events for users who do not have Dyad Pro status, sending only 10% of their events.
  • Error Event Exemption: Ensured that error events are always sent, regardless of the user's Pro status or the sampling rate, to maintain critical debugging information.
  • Dyad Pro Status Storage: Added functionality to store the user's Dyad Pro status in local storage, making it accessible for telemetry decisions.
  • Pro User Telemetry: Confirmed that Dyad Pro users continue to send all telemetry events without any sampling.
Changelog
  • src/hooks/useSettings.ts
    • Added a new constant DYAD_PRO_STATUS_KEY for local storage.
    • Introduced isDyadProUser() function to check Pro status from local storage.
    • Modified processSettingsForTelemetry() to store the user's Dyad Pro status in local storage.
  • src/renderer.tsx
    • Imported the new isDyadProUser function.
    • Implemented a capture_event_callback in PostHog initialization to apply telemetry sampling.
    • Added logic to sample 90% of non-error events for non-Pro users.
    • Ensured error events are explicitly excluded from sampling.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@devin-ai-integration devin-ai-integration Bot left a comment

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.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces telemetry sampling for non-Pro users, sending only 10% of events while ensuring all error events are still reported. The implementation correctly uses localStorage to make the user's Pro status available to the telemetry module for sampling decisions.

My review found one significant issue with the sampling logic. It would incorrectly sample critical PostHog events like $identify and $pageview, which would corrupt analytics data for non-Pro users. I've provided a suggestion to ensure these events are always sent, similar to how error events are handled, to maintain data integrity.

Comment thread src/renderer.tsx
Comment on lines +96 to +105
const isErrorEvent =
event?.event === "$exception" ||
event?.event?.toLowerCase().includes("error") ||
event?.properties?.$exception_type ||
event?.properties?.error;

if (!isErrorEvent && Math.random() > 0.1) {
console.debug("Non-Pro user: sampling out event", event?.event);
return null;
}

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.

high

The current sampling logic for non-Pro users will also sample crucial PostHog events like $identify and $pageview, which can lead to data integrity issues.

  • $identify events: Sampling these will cause user identification to fail for 90% of non-Pro user sessions. This makes it impossible to reliably associate other telemetry events with a specific user, skewing all user-centric analytics.
  • $pageview events: Sampling these will make user flow analysis (like funnels and paths) unreliable for this user segment, as most navigation actions won't be recorded.

These special events should always be sent, just like error events, to ensure the collected data is accurate and useful. The suggested change updates the logic to prevent these events from being sampled.

        const shouldAlwaysSend =
          event?.event === "$exception" ||
          event?.event === "$identify" ||
          event?.event === "$pageview" ||
          event?.event?.toLowerCase().includes("error") ||
          event?.properties?.$exception_type ||
          event?.properties?.error;

        if (!shouldAlwaysSend && Math.random() > 0.1) {
          console.debug("Non-Pro user: sampling out event", event?.event);
          return null;
        }

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

1 issue found across 2 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/renderer.tsx">

<violation number="1" location="src/renderer.tsx:98">
P2: The optional chaining stops before `.includes`, so `event?.event?.toLowerCase()` can return `undefined` and then `.includes()` throws. Guard the string check (or optional-chain the result) before calling `.includes()` to avoid runtime errors in telemetry sampling.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/renderer.tsx
if (!isDyadProUser()) {
const isErrorEvent =
event?.event === "$exception" ||
event?.event?.toLowerCase().includes("error") ||

@cubic-dev-ai cubic-dev-ai Bot Feb 11, 2026

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: The optional chaining stops before .includes, so event?.event?.toLowerCase() can return undefined and then .includes() throws. Guard the string check (or optional-chain the result) before calling .includes() to avoid runtime errors in telemetry sampling.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/renderer.tsx, line 98:

<comment>The optional chaining stops before `.includes`, so `event?.event?.toLowerCase()` can return `undefined` and then `.includes()` throws. Guard the string check (or optional-chain the result) before calling `.includes()` to avoid runtime errors in telemetry sampling.</comment>

<file context>
@@ -87,6 +91,20 @@ const posthogClient = posthog.init(
+      if (!isDyadProUser()) {
+        const isErrorEvent =
+          event?.event === "$exception" ||
+          event?.event?.toLowerCase().includes("error") ||
+          event?.properties?.$exception_type ||
+          event?.properties?.error;
</file context>
Suggested change
event?.event?.toLowerCase().includes("error") ||
(typeof event?.event === "string" &&
event.event.toLowerCase().includes("error")) ||
Fix with Cubic

@github-actions github-actions Bot left a comment

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.

Dyadbot swarm review: 2 medium issue(s) found

Comment thread src/renderer.tsx

if (!isErrorEvent && Math.random() > 0.1) {
console.debug("Non-Pro user: sampling out event", event?.event);
return null;

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.

🟡 MEDIUM | race-condition | Found by: Correctness, Endorsed by: Code Health, UX

Pro status in localStorage may be stale at PostHog init time

isDyadProUser() reads from localStorage, but processSettingsForTelemetry() (which writes the Pro status) is called from a React hook. PostHog is initialized at module level before React renders. On first app launch (or after clearing storage), the Pro status won't be in localStorage yet, so all users will be treated as non-Pro until settings are fetched and processed. This means some early startup events could be incorrectly sampled for Pro users.

💡 Suggestion: Add a comment documenting this known timing gap, e.g. // Note: on first launch, Pro status may not be in localStorage yet. This is acceptable since it only affects telemetry sampling during the brief startup window.

Comment thread src/renderer.tsx
event?.event?.toLowerCase().includes("error") ||
event?.properties?.$exception_type ||
event?.properties?.error;

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.

🟡 MEDIUM | complexity | Found by: Code Health, Endorsed by: Correctness, UX

Magic number for sampling rate

The sampling rate 0.1 (10%) is a magic number embedded in the callback. If this rate needs tuning, someone has to hunt through PostHog init config to find it.

💡 Suggestion: Extract to a named constant, e.g. const NON_PRO_TELEMETRY_SAMPLE_RATE = 0.1; defined near the top of the file.

@github-actions

github-actions Bot commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

🔍 Dyadbot Code Review Summary

Verdict: ✅ YES - Ready to merge

Reviewed by 3 specialized agents: Correctness Expert, Code Health Expert, UX Wizard.

Issues Summary

# Severity File Issue Found By Endorsed By
1 🟡 MEDIUM src/renderer.tsx:94-106 Pro status in localStorage defaults to non-Pro before settings load — early events for Pro users may be incorrectly sampled Correctness, Code Health, UX All 3 (consensus)
2 🟡 MEDIUM src/renderer.tsx:101 Magic number 0.1 for sampling rate should be a named constant Code Health Correctness, UX
🟢 Low Priority Notes (3 items)
  • Error event detection heuristic may be too broad - src/renderer.tsx:96-100 - .includes('error') could match non-error events like "error_modal_dismissed"; also could miss errors without "error" in the name. Benign since it only over-sends, not under-sends. (All 3 reviewers)
  • Truthy check on error property could match unexpected values - src/renderer.tsx:99 - Pragmatic and consistent with typical JS patterns. (Correctness, endorsed by Code Health)
  • Comment explains WHAT not WHY - src/renderer.tsx:93 - Consider adding the business reason (e.g., cost reduction) alongside the existing summary. (Code Health; UX challenged as nice-to-have)
🚫 Dropped Issues (0 items)

No issues were dropped during discussion.


Generated by Dyadbot swarm code review

@greptile-apps

greptile-apps Bot commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Overview

Greptile Summary

This PR implements telemetry sampling for non-Pro users to reduce PostHog event volume. Pro status is now persisted to localStorage via processSettingsForTelemetry() and checked during PostHog's before_send callback to apply a 10% sampling rate for non-Pro users, while always allowing error events through.

Key changes:

  • Added isDyadProUser() helper and DYAD_PRO_STATUS_KEY localStorage constant in useSettings.ts
  • Pro status is automatically synced to localStorage whenever settings load or update
  • Non-Pro users have 90% of non-error events filtered out via random sampling
  • Error events (detected by $exception event type, "error" in event name, or exception properties) are always sent regardless of Pro status

Notes:

  • The random sampling approach means the same event may be sent sometimes and not others for a given user, which could make analytics less predictable
  • The implementation correctly handles the async nature of settings loading by reading from localStorage which persists across sessions

Confidence Score: 4/5

  • This PR is safe to merge with low risk
  • The implementation is straightforward and uses existing patterns. Minor concerns include non-deterministic sampling and potential edge cases in error detection logic, but these don't affect core functionality
  • No files require special attention, though testing the sampling behavior with both Pro and non-Pro users would be valuable

Important Files Changed

Filename Overview
src/hooks/useSettings.ts Added isDyadProUser() helper and localStorage persistence for Pro status via processSettingsForTelemetry()
src/renderer.tsx Implemented 10% sampling for non-Pro users in PostHog before_send, with error events always sent

Sequence Diagram

sequenceDiagram
    participant App as App Startup
    participant Settings as useSettings Hook
    participant LocalStorage as localStorage
    participant PostHog as PostHog before_send

    App->>Settings: Load user settings
    Settings->>Settings: Call processSettingsForTelemetry()
    Settings->>LocalStorage: Store Pro status (hasDyadProKey)
    
    Note over PostHog: Event triggered
    PostHog->>LocalStorage: isDyadProUser() reads Pro status
    alt Non-Pro User
        PostHog->>PostHog: Check if error event
        alt Error Event
            PostHog->>PostHog: Always send
        else Non-Error Event
            PostHog->>PostHog: Sample 10% (Math.random)
            alt Sampled Out
                PostHog->>PostHog: Return null (drop event)
            else Sampled In
                PostHog->>PostHog: Send event
            end
        end
    else Pro User
        PostHog->>PostHog: Send all events
    end
Loading

@github-actions github-actions Bot added the needs-human:review-issue ai agent flagged an issue that requires human review label Feb 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🎭 Playwright Test Results

⚠️ WARNING: Missing Test Shards!

Some test shards did not report results. This may indicate CI failures or timeouts.

  • 🍎 macOS: found 2/4 shards (2 missing)
  • 🪟 Windows: found 0/4 shards (4 missing)

❌ Some tests failed

OS Passed Failed Flaky Skipped
🍎 macOS 222 4 12 6

Summary: 222 passed, 4 failed, 12 flaky, 6 skipped

Failed Tests

🍎 macOS

  • capacitor.spec.ts > capacitor upgrade and sync works
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • security_review.spec.ts > security review - edit and use knowledge
    • Error: expect(string).toMatchSnapshot(expected) failed
  • select_component.spec.ts > select component next.js
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • template-create-nextjs.spec.ts > create next.js app
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed

📋 Re-run Failing Tests (macOS)

Copy and paste to re-run all failing spec files locally:

npm run e2e \
  e2e-tests/capacitor.spec.ts \
  e2e-tests/security_review.spec.ts \
  e2e-tests/select_component.spec.ts \
  e2e-tests/template-create-nextjs.spec.ts

⚠️ Flaky Tests

🍎 macOS

  • annotator.spec.ts > annotator - capture and submit screenshot (passed after 1 retry)
  • approve.spec.ts > write to index, approve, check preview (passed after 1 retry)
  • context_manage.spec.ts > manage context - exclude paths with smart context (passed after 1 retry)
  • debugging_logs.spec.ts > console logs should appear in the console (passed after 1 retry)
  • import.spec.ts > import app (passed after 1 retry)
  • local_agent_advanced.spec.ts > local-agent - security review fix (passed after 1 retry)
  • local_agent_ask.spec.ts > local-agent ask mode (passed after 1 retry)
  • refresh.spec.ts > refresh preserves current route (passed after 1 retry)
  • setup_flow.spec.ts > Setup Flow > setup banner shows correct state when node.js is installed (passed after 1 retry)
  • setup.spec.ts > setup ai provider (passed after 1 retry)
  • ... and 2 more

📊 View full report

@wwwillchen wwwillchen merged commit aceda41 into dyad-sh:main Feb 12, 2026
8 of 10 checks passed
azizmejri1 pushed a commit to azizmejri1/dyad that referenced this pull request Feb 12, 2026
## Summary
- Store Dyad Pro status in localStorage for telemetry access
- Sample 10% of events for non-Pro users (error events always sent)
- Pro users continue to send all events

## Test plan
- Verify telemetry events are sampled correctly for non-Pro users
- Confirm error events are always sent regardless of Pro status
- Test Pro users still send all events

🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2642"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds telemetry sampling for non‑Pro users to reduce event volume while
keeping error visibility. Stores Dyad Pro status in localStorage; Pro
users keep sending all events.

- **New Features**
- Save Dyad Pro status in localStorage (dyadProStatus) and expose
isDyadProUser().
- Sample PostHog events for non‑Pro: send 10% of non‑error events;
always send errors.
  - No change for Pro users; all events continue to be sent.

<sup>Written for commit 685922c.
Summary will update on new commits.</sup>

<!-- End of auto-generated description by cubic. -->

Co-authored-by: Will Chen <willchen90@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
azizmejri1 pushed a commit to azizmejri1/dyad that referenced this pull request Feb 12, 2026
## Summary
- Store Dyad Pro status in localStorage for telemetry access
- Sample 10% of events for non-Pro users (error events always sent)
- Pro users continue to send all events

## Test plan
- Verify telemetry events are sampled correctly for non-Pro users
- Confirm error events are always sent regardless of Pro status
- Test Pro users still send all events

🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2642"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds telemetry sampling for non‑Pro users to reduce event volume while
keeping error visibility. Stores Dyad Pro status in localStorage; Pro
users keep sending all events.

- **New Features**
- Save Dyad Pro status in localStorage (dyadProStatus) and expose
isDyadProUser().
- Sample PostHog events for non‑Pro: send 10% of non‑error events;
always send errors.
  - No change for Pro users; all events continue to be sent.

<sup>Written for commit 685922c.
Summary will update on new commits.</sup>

<!-- End of auto-generated description by cubic. -->

Co-authored-by: Will Chen <willchen90@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human:review-issue ai agent flagged an issue that requires human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants