diff --git a/frontend/src/renderer/components/NotificationCenter.tsx b/frontend/src/renderer/components/NotificationCenter.tsx index 02da22b80f..90a0a444d0 100644 --- a/frontend/src/renderer/components/NotificationCenter.tsx +++ b/frontend/src/renderer/components/NotificationCenter.tsx @@ -69,20 +69,24 @@ export function NotificationCenter({ style }: NotificationCenterProps) { const markOneRead = async (id: string) => { setActionError(null); - void captureRendererEvent("ao.renderer.notification_marked_read", { scope: "single" }); + void captureRendererEvent("ao.renderer.notification_mark_read_requested", { scope: "single" }); try { await markRead.mutateAsync(id); + void captureRendererEvent("ao.renderer.notification_mark_read_succeeded", { scope: "single" }); } catch (error) { + void captureRendererEvent("ao.renderer.notification_mark_read_failed", { scope: "single" }); setActionError(error instanceof Error ? error.message : "Could not mark notification read"); } }; const markAll = async () => { setActionError(null); - void captureRendererEvent("ao.renderer.notification_marked_read", { scope: "all" }); + void captureRendererEvent("ao.renderer.notification_mark_read_requested", { scope: "all" }); try { await markAllRead.mutateAsync(); + void captureRendererEvent("ao.renderer.notification_mark_read_succeeded", { scope: "all" }); } catch (error) { + void captureRendererEvent("ao.renderer.notification_mark_read_failed", { scope: "all" }); setActionError(error instanceof Error ? error.message : "Could not mark notifications read"); } }; diff --git a/frontend/src/renderer/lib/daemon-telemetry.test.ts b/frontend/src/renderer/lib/daemon-telemetry.test.ts index 718f120cd1..808e0692f4 100644 --- a/frontend/src/renderer/lib/daemon-telemetry.test.ts +++ b/frontend/src/renderer/lib/daemon-telemetry.test.ts @@ -75,6 +75,14 @@ describe("daemon failure telemetry", () => { expect(captureMock).not.toHaveBeenCalled(); }); + it("does not count ready-state daemon warnings as failures", () => { + stop = startDaemonFailureTelemetry(); + + pushStatus({ state: "ready", code: "port_unconfirmed" }); + + expect(captureMock).not.toHaveBeenCalled(); + }); + it("dedupes identical consecutive failures and resets on recovery", () => { stop = startDaemonFailureTelemetry(); diff --git a/frontend/src/renderer/lib/daemon-telemetry.ts b/frontend/src/renderer/lib/daemon-telemetry.ts index a029872273..b5b2674e75 100644 --- a/frontend/src/renderer/lib/daemon-telemetry.ts +++ b/frontend/src/renderer/lib/daemon-telemetry.ts @@ -10,6 +10,7 @@ let started = false; let lastFailureKey: string | null = null; function failureKey(status: DaemonStatus): string | null { + if (status.state === "ready") return null; if (!status.code) return null; return [status.state, status.code, status.exitCode ?? "", status.signal ?? ""].join("|"); } diff --git a/frontend/src/renderer/lib/telemetry.test.ts b/frontend/src/renderer/lib/telemetry.test.ts index b14cb1cf52..24ff389d84 100644 --- a/frontend/src/renderer/lib/telemetry.test.ts +++ b/frontend/src/renderer/lib/telemetry.test.ts @@ -165,12 +165,18 @@ describe("telemetry sanitizers", () => { target: "pr", }); expect(await sanitizeRendererProperties("ao.renderer.notification_opened", { target: "http://x" })).toEqual({}); - expect(await sanitizeRendererProperties("ao.renderer.notification_marked_read", { scope: "all" })).toEqual({ + expect(await sanitizeRendererProperties("ao.renderer.notification_mark_read_requested", { scope: "all" })).toEqual({ scope: "all", }); - expect(await sanitizeRendererProperties("ao.renderer.notification_marked_read", { scope: "everything" })).toEqual( - {}, - ); + expect(await sanitizeRendererProperties("ao.renderer.notification_mark_read_succeeded", { scope: "all" })).toEqual({ + scope: "all", + }); + expect(await sanitizeRendererProperties("ao.renderer.notification_mark_read_failed", { scope: "all" })).toEqual({ + scope: "all", + }); + expect( + await sanitizeRendererProperties("ao.renderer.notification_mark_read_requested", { scope: "everything" }), + ).toEqual({}); }); it("whitelists coarse daemon failure fields and drops messages", async () => { diff --git a/frontend/src/renderer/lib/telemetry.ts b/frontend/src/renderer/lib/telemetry.ts index e71f0bdbb7..535d804d20 100644 --- a/frontend/src/renderer/lib/telemetry.ts +++ b/frontend/src/renderer/lib/telemetry.ts @@ -187,7 +187,9 @@ export async function sanitizeRendererProperties( case "ao.renderer.notification_opened": if (properties?.target === "pr" || properties?.target === "session") safe.target = properties.target; break; - case "ao.renderer.notification_marked_read": + case "ao.renderer.notification_mark_read_requested": + case "ao.renderer.notification_mark_read_succeeded": + case "ao.renderer.notification_mark_read_failed": if (properties?.scope === "single" || properties?.scope === "all") safe.scope = properties.scope; break; case "ao.renderer.daemon_failure":