From be6bacf74bf45721ae38b1be2e530f925e38bbe3 Mon Sep 17 00:00:00 2001 From: Miguel Angel Simon Sierra Date: Fri, 6 Nov 2026 03:05:30 -0500 Subject: [PATCH 1/2] feat(cli): check_report names the finding that stopped the check --- packages/cli/src/commands/check.test.ts | 4 +++ packages/cli/src/telemetry/events.test.ts | 40 +++++++++++++++++++++++ packages/cli/src/telemetry/events.ts | 3 ++ packages/cli/src/utils/checkPipeline.ts | 8 +++++ 4 files changed, 55 insertions(+) diff --git a/packages/cli/src/commands/check.test.ts b/packages/cli/src/commands/check.test.ts index ddc146961a4..5b43dfd7176 100644 --- a/packages/cli/src/commands/check.test.ts +++ b/packages/cli/src/commands/check.test.ts @@ -2562,6 +2562,9 @@ describe("check pipeline", () => { expect(reached?.message).toMatch( /^Check reached its --budget of 70 s while running the overlap pass .*Seconds per phase: /, ); + expect(trackCheckReport).toHaveBeenCalledWith( + expect.objectContaining({ ok: false, stopCode: "check_budget_reached" }), + ); expect(report.ok).toBe(false); expect(report.layout.findings.some((finding) => finding.code === "container_overflow")).toBe( true, @@ -3626,6 +3629,7 @@ describe("check report telemetry", () => { }), ); expect(report.contrast.errorCount).toBe(1); + expect(trackCheckReport.mock.calls[0]?.[0].stopCode).toBeUndefined(); }); it("reports zero browser samples and timings after a lint short circuit", async () => { diff --git a/packages/cli/src/telemetry/events.test.ts b/packages/cli/src/telemetry/events.test.ts index 9c8146465a9..461e2e7c217 100644 --- a/packages/cli/src/telemetry/events.test.ts +++ b/packages/cli/src/telemetry/events.test.ts @@ -167,6 +167,46 @@ describe("trackCheckReport", () => { }); }); + it("names the finding that stopped the check, and omits it when none did", () => { + const props = { + contrastGate: false, + motionGate: false, + captionZoneGate: false, + frameCheckGate: false, + snapshotsGate: false, + lintErrors: 0, + lintWarnings: 0, + runtimeErrors: 1, + runtimeWarnings: 0, + layoutErrors: 0, + layoutWarnings: 0, + motionErrors: 0, + motionWarnings: 0, + contrastErrors: 0, + contrastWarnings: 0, + craftWarnings: 0, + launchSettleMs: 0, + renderReadyTimedOut: false, + seekLoopMs: 0, + contrastMs: 0, + gridMs: 0, + craftMs: 0, + overlapMs: 0, + durationSec: 0, + gridPoints: 0, + contrastPoints: 0, + ok: false, + exitCode: 1, + }; + trackCheckReport({ ...props, stopCode: "media_copies_unfinished" }); + expect(trackEvent).toHaveBeenLastCalledWith( + "check_report", + expect.objectContaining({ stop_code: "media_copies_unfinished" }), + ); + trackCheckReport(props); + expect(trackEvent.mock.calls.at(-1)?.[1]).not.toHaveProperty("stop_code"); + }); + it("omits run_id when no run ID is provided", () => { trackCheckReport({ contrastGate: false, diff --git a/packages/cli/src/telemetry/events.ts b/packages/cli/src/telemetry/events.ts index 301a23c6b24..44a6de3a890 100644 --- a/packages/cli/src/telemetry/events.ts +++ b/packages/cli/src/telemetry/events.ts @@ -1193,6 +1193,8 @@ export function trackCheckReport(props: { contrastPoints: number; ok: boolean; exitCode: number; + /** The code of the finding that stopped the check, one of a closed set (checkPipeline's STOP_CODES). */ + stopCode?: string; runId?: string; }): void { trackEvent("check_report", { @@ -1224,6 +1226,7 @@ export function trackCheckReport(props: { contrast_points: props.contrastPoints, ok: props.ok, exit_code: props.exitCode, + ...(props.stopCode && { stop_code: props.stopCode }), ...runIdField(props.runId), }); } diff --git a/packages/cli/src/utils/checkPipeline.ts b/packages/cli/src/utils/checkPipeline.ts index 97817aacc27..9057991a7ec 100644 --- a/packages/cli/src/utils/checkPipeline.ts +++ b/packages/cli/src/utils/checkPipeline.ts @@ -2130,6 +2130,7 @@ function buildReport( contrastPoints: browser.contrastChecked, ok: report.ok, exitCode: checkExitCode(report), + stopCode: browser.runtimeFindings.find((finding) => STOP_CODES.has(finding.code))?.code, runId: getRunId(), }); return filterComponentReport(report, options); @@ -2264,6 +2265,13 @@ function emptyBrowserResult(): CheckBrowserResult { }; } +/** The codes `runtimeFailure` gives a finding that ends the browser check early; `check_report` sends the first one. */ +const STOP_CODES: ReadonlySet = new Set([ + "media_copies_unfinished", + "check_budget_reached", + "check_runtime_failure", +]); + function runtimeFailure(error: unknown, code?: string): CheckFinding { return findingAtRoot( code ?? From 021c04e7566790946c556f35fb9873f7a42857e8 Mon Sep 17 00:00:00 2001 From: Miguel Angel Simon Sierra Date: Fri, 6 Nov 2026 03:35:49 -0500 Subject: [PATCH 2/2] fix(cli): type stop_code as a closed union instead of a comment --- packages/cli/src/telemetry/events.ts | 4 ++-- packages/cli/src/utils/checkPipeline.ts | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/telemetry/events.ts b/packages/cli/src/telemetry/events.ts index 44a6de3a890..9ad85e492b0 100644 --- a/packages/cli/src/telemetry/events.ts +++ b/packages/cli/src/telemetry/events.ts @@ -1,4 +1,5 @@ import type { BrowserInstallFacts } from "../browser/installFacts.js"; +import type { StopCode } from "../utils/checkPipeline.js"; import type { OutputResolutionIssueKind } from "@hyperframes/core"; import { redactTelemetryString } from "@hyperframes/core/telemetry-redaction"; import type { SubTimelineWaitOutcome } from "@hyperframes/engine"; @@ -1193,8 +1194,7 @@ export function trackCheckReport(props: { contrastPoints: number; ok: boolean; exitCode: number; - /** The code of the finding that stopped the check, one of a closed set (checkPipeline's STOP_CODES). */ - stopCode?: string; + stopCode?: StopCode; runId?: string; }): void { trackEvent("check_report", { diff --git a/packages/cli/src/utils/checkPipeline.ts b/packages/cli/src/utils/checkPipeline.ts index 9057991a7ec..fc4044b48be 100644 --- a/packages/cli/src/utils/checkPipeline.ts +++ b/packages/cli/src/utils/checkPipeline.ts @@ -2130,7 +2130,9 @@ function buildReport( contrastPoints: browser.contrastChecked, ok: report.ok, exitCode: checkExitCode(report), - stopCode: browser.runtimeFindings.find((finding) => STOP_CODES.has(finding.code))?.code, + stopCode: browser.runtimeFindings + .map((finding) => finding.code) + .find((code): code is StopCode => (STOP_CODES as readonly string[]).includes(code)), runId: getRunId(), }); return filterComponentReport(report, options); @@ -2265,12 +2267,12 @@ function emptyBrowserResult(): CheckBrowserResult { }; } -/** The codes `runtimeFailure` gives a finding that ends the browser check early; `check_report` sends the first one. */ -const STOP_CODES: ReadonlySet = new Set([ +export const STOP_CODES = [ "media_copies_unfinished", "check_budget_reached", "check_runtime_failure", -]); +] as const; +export type StopCode = (typeof STOP_CODES)[number]; function runtimeFailure(error: unknown, code?: string): CheckFinding { return findingAtRoot(