fix(telemetry): send feedback as plain events, not PostHog surveys

CLI and Studio feedback were emitted as `survey sent` with `$survey_*`
properties, so every rating was ingested as a PostHog survey response even
though no survey definition, targeting, or popover backs them.

Emit `cli_render_feedback` and `studio_feedback` with plain `rating` /
`comment` properties instead. Same fields, same call sites, same opt-out.
This commit is contained in:
Miguel Angel Simon Sierra
2026-07-27 16:46:51 +02:00
parent 3a0590925c
commit 597c14a887
6 changed files with 25 additions and 21 deletions
+8 -6
View File
@@ -127,12 +127,13 @@ Only the existence (or in some cases the value) of these variables is checked
### CLI feedback
Event: `cli_render_feedback`
| Field | Value |
|-------|-------|
| `$survey_id` | `render_satisfaction` |
| `$survey_response` | Raw rating (010) |
| `rating` | Raw rating (010) |
| `rating_scale` | `10` for the current recommendation scale |
| `$survey_response_2` | Free-text comment (only when provided) |
| `comment` | Free-text comment (only when provided) |
| `render_duration_ms` | Time the render took in milliseconds |
| `doctor_summary` | System context (see below) |
@@ -146,12 +147,13 @@ It may also include `wsl` or sandbox runtime flags when those environments are d
### Studio feedback
Event: `studio_feedback`
| Field | Value |
|-------|-------|
| `$survey_id` | `studio_experience` |
| `$survey_response` | Raw rating (010) |
| `rating` | Raw rating (010) |
| `rating_scale` | `10` for the current recommendation scale |
| `$survey_response_2` | Free-text comment (only when provided) |
| `comment` | Free-text comment (only when provided) |
| `source` | `studio` |
| `doctor_summary` | Browser context (platform, screen, CPU cores, device memory, network type) |
+1 -1
View File
@@ -33,7 +33,7 @@ function normalizeComment(raw?: string): string | undefined {
/**
* Compact PostHog join keys appended to the environment string that rides
* along with the forwarded report (and therefore lands verbatim in the wild
* feedback channel): `fid` = this submission's PostHog `survey sent`
* feedback channel): `fid` = this submission's PostHog `cli_render_feedback`
* `feedback_id`; `tid` = the install's telemetry distinct_id; `renders` =
* recent `render_job_id`s (newest last, `!` suffix = the render failed).
* Together they turn a wild report into an exact telemetry lookup instead of
+3 -3
View File
@@ -231,7 +231,7 @@ describe("render telemetry events", () => {
});
expect(trackEvent).toHaveBeenCalledWith(
"survey sent",
"cli_render_feedback",
expect.objectContaining({
feedback_id: "feedback-uuid",
recent_render_ids: "render-a,render-b",
@@ -504,7 +504,7 @@ describe("trackRenderFeedback", () => {
const [, props] = trackEvent.mock.calls[0] as [string, Record<string, unknown>];
expect(props).not.toHaveProperty("render_duration_ms");
expect(props.$survey_response).toBe(4);
expect(props.rating).toBe(4);
expect(props.rating_scale).toBe(10);
});
@@ -512,7 +512,7 @@ describe("trackRenderFeedback", () => {
trackRenderFeedback({ rating: 5, renderDurationMs: 6000 });
expect(trackEvent).toHaveBeenCalledWith(
"survey sent",
"cli_render_feedback",
expect.objectContaining({ render_duration_ms: 6000 }),
);
});
+6 -5
View File
@@ -630,17 +630,18 @@ export function trackRenderFeedback(props: {
/**
* Join key shared with the forwarded feedback report (Slack/backend): the
* same uuid rides in the report's env string as `fid=…`, so a wild report
* resolves to exactly one PostHog "survey sent" event and vice versa.
* resolves to exactly one PostHog `cli_render_feedback` event and vice versa.
*/
feedbackId?: string;
/** render_job_id values of this install's recent renders (newest last). */
recentRenderIds?: string[];
}): void {
trackEvent("survey sent", {
$survey_id: "render_satisfaction",
$survey_response: props.rating,
// Plain product event, not a PostHog survey response: nothing here is served
// by the surveys product (no survey definition, no targeting, no popover).
trackEvent("cli_render_feedback", {
rating: props.rating,
rating_scale: FEEDBACK_RATING_SCALE,
...(props.comment ? { $survey_response_2: props.comment } : {}),
...(props.comment ? { comment: props.comment } : {}),
...(props.renderDurationMs !== undefined ? { render_duration_ms: props.renderDurationMs } : {}),
...(props.doctorSummary ? { doctor_summary: props.doctorSummary } : {}),
...(props.feedbackId ? { feedback_id: props.feedbackId } : {}),
+2 -2
View File
@@ -84,8 +84,8 @@ describe("studio telemetry events", () => {
trackStudioFeedback({ rating });
expect(trackEvent).toHaveBeenCalledWith(
"survey sent",
expect.objectContaining({ $survey_response: rating, rating_scale: 10 }),
"studio_feedback",
expect.objectContaining({ rating, rating_scale: 10 }),
);
});
});
+5 -4
View File
@@ -69,11 +69,12 @@ export function trackStudioSegmentEaseEdit(props: { ease: string }): void {
}
export function trackStudioFeedback(props: { rating: number; comment?: string }): void {
trackEvent("survey sent", {
$survey_id: "studio_experience",
$survey_response: props.rating,
// Plain product event, not a PostHog survey response: nothing here is served
// by the surveys product (no survey definition, no targeting, no popover).
trackEvent("studio_feedback", {
rating: props.rating,
rating_scale: 10,
...(props.comment ? { $survey_response_2: props.comment } : {}),
...(props.comment ? { comment: props.comment } : {}),
doctor_summary: getBrowserDoctorSummary(),
source: "studio",
});