fix: depollute v02 web performance telemetry

This commit is contained in:
Codex Agent
2026-06-05 13:03:09 +08:00
parent aec6903407
commit ac2d3001fa
8 changed files with 83 additions and 5 deletions
+2 -1
View File
@@ -57,7 +57,8 @@ assertIncludes(appSource, "CommandBar", "command bar component must exist");
assertIncludes(appSource, "PerformanceView", "performance view component must exist");
assertIncludes(appSource, "SkillsView", "skills component must exist");
assertIncludes(appSource, "SettingsView", "settings component must exist");
assertIncludes(appTsx, "useWorkbenchStore(auth.authState === \"authenticated\")", "Workbench data requests must wait until auth is authenticated");
assertIncludes(appTsx, "const workspaceRoute = route === \"workspace\"", "React runtime must explicitly identify workspace route before starting workbench data flows");
assertIncludes(appTsx, "useWorkbenchStore(auth.authState === \"authenticated\" && workspaceRoute)", "Workbench data requests must wait until auth is authenticated and workspace route is active");
assertIncludes(apiClientSource, "adapter: (): Promise<ApiResult<LiveProbePayload>> => fetchJson(\"/v1/rpc/system.health\", { method: \"POST\"", "adapter health must use POST /v1/rpc/system.health");
assertIncludes(appSource, "selectVisibleDevicePodId", "Device Pod detail probes must be selected from the authenticated visible list");
assertIncludes(appSource, "api.devicePods()", "Device Pod list must be fetched before detail probes");
+2 -2
View File
@@ -45,8 +45,9 @@ const routeLabels: Record<RouteId, string> = {
export function App(): ReactElement {
const auth = useAuth();
const store = useWorkbenchStore(auth.authState === "authenticated");
const [route, setRoute] = useState<RouteId>(() => routeFromLocation());
const workspaceRoute = route === "workspace";
const store = useWorkbenchStore(auth.authState === "authenticated" && workspaceRoute);
const [pickedDraft, setPickedDraft] = useState<string | null>(null);
const [leftCollapsed, setLeftCollapsed] = useState(false);
const [rightCollapsed, setRightCollapsed] = useState(false);
@@ -113,7 +114,6 @@ export function App(): ReactElement {
}, [route]);
const modelLabel = useMemo(() => store.state.providerProfile === "codex-api" ? "Codex API" : store.state.providerProfile === "minimax-m3" ? "MiniMax-M3" : "DeepSeek", [store.state.providerProfile]);
const workspaceRoute = route === "workspace";
const sessionSidebarVisible = workspaceRoute && !leftCollapsed;
const devicePodSidebarVisible = workspaceRoute && !rightCollapsed;
@@ -1,7 +1,7 @@
import assert from "node:assert/strict";
import { test } from "bun:test";
import { __resetWebPerformanceTelemetryForTest, webPerformanceRouteTemplate, webPerformanceStatusClass } from "./rum";
import { __resetWebPerformanceTelemetryForTest, recordApiTiming, webPerformanceRouteTemplate, webPerformanceStatusClass } from "./rum";
test("web performance route templates remove high-cardinality identifiers", () => {
assert.equal(webPerformanceRouteTemplate("/v1/agent/chat/result/trc_abc123456789"), "/v1/agent/chat/result/:id");
@@ -17,3 +17,36 @@ test("web performance status class keeps API labels low-cardinality", () => {
assert.equal(webPerformanceStatusClass("bad"), "network");
__resetWebPerformanceTelemetryForTest();
});
test("web performance telemetry does not record observability page API calls", () => {
const previousWindow = globalThis.window;
const previousDocument = globalThis.document;
const previousPerformance = globalThis.performance;
const previousFetch = globalThis.fetch;
const events = [];
globalThis.window = {
location: { hash: "#/performance", origin: "http://web.test" },
setTimeout: (handler: TimerHandler) => {
if (typeof handler === "function") handler();
return 0;
},
clearTimeout: () => undefined
} as unknown as Window & typeof globalThis;
globalThis.document = {} as Document;
globalThis.performance = {} as Performance;
globalThis.fetch = (async (_path: string, init?: RequestInit) => {
events.push(JSON.parse(String(init?.body ?? "{}")));
return new Response("{}", { status: 202, headers: { "content-type": "application/json" } });
}) as typeof fetch;
try {
recordApiTiming({ path: "/v1/live-builds", durationMs: 1200, status: 200, outcome: "ok" });
recordApiTiming({ path: "/v1/web-performance/summary", durationMs: 600, status: 200, outcome: "ok" });
assert.equal(events.length, 0);
} finally {
globalThis.window = previousWindow;
globalThis.document = previousDocument;
globalThis.performance = previousPerformance;
globalThis.fetch = previousFetch;
__resetWebPerformanceTelemetryForTest();
}
});
@@ -77,7 +77,7 @@ export function installWebPerformanceTelemetry(): void {
export function recordApiTiming(input: ApiTimingInput): void {
if (!browserRuntimeAvailable()) return;
const route = webPerformanceRouteTemplate(input.path);
if (route === TELEMETRY_ENDPOINT) return;
if (isObservabilityRoute(route) || currentPageRoute() === "/performance") return;
const durationMs = finitePositive(input.durationMs, 0);
if (durationMs <= 0) return;
enqueueWebPerformanceEvent({
@@ -205,6 +205,7 @@ function enqueueDuration(kind: WebPerformanceKind, metric: string, route: string
function enqueueWebPerformanceEvent(event: WebPerformanceEvent): void {
if (!browserRuntimeAvailable()) return;
if (isObservabilityPageRoute(currentPageRoute())) return;
queue.push(event);
if (queue.length > MAX_QUEUE_EVENTS) queue = queue.slice(queue.length - MAX_QUEUE_EVENTS);
scheduleFlush();
@@ -222,6 +223,10 @@ function flushWebPerformanceQueue(useBeacon: boolean): void {
flushTimer = null;
}
if (queue.length === 0) return;
if (isObservabilityPageRoute(currentPageRoute())) {
queue = [];
return;
}
const events = queue.splice(0, MAX_BATCH_EVENTS);
const payload = JSON.stringify({
schemaVersion: PAYLOAD_SCHEMA_VERSION,
@@ -248,6 +253,14 @@ function currentPageRoute(): string {
return webPerformanceRouteTemplate(hash || "/workspace");
}
function isObservabilityRoute(route: string): boolean {
return route === TELEMETRY_ENDPOINT || route.startsWith(`${TELEMETRY_ENDPOINT}/`);
}
function isObservabilityPageRoute(route: string): boolean {
return route === "/performance";
}
function supportsEntryType(type: string): boolean {
const supported = PerformanceObserver.supportedEntryTypes;
return Array.isArray(supported) && supported.includes(type);