fix: surface OTel and sentinel visibility gaps

This commit is contained in:
Codex
2026-06-30 06:44:10 +00:00
parent 5e2276a305
commit 9e3747d9ed
6 changed files with 116 additions and 7 deletions
@@ -2743,7 +2743,7 @@ function buildFindings(samples, control, network, errors, sampleMetrics, promptN
const effectiveApiDomLag = apiDomLag || buildApiDomLagReport(samples, network);
if (commandFailures.length > 0) findings.push({ id: "observer-command-failed", severity: "red", summary: "observer control commands failed; analyze must surface command failure instead of hiding it in command artifacts", count: commandFailures.length, commands: commandFailures.slice(0, 20) });
findings.push(...buildFrontendFreezeFindings(errors, control));
findings.push(...buildBrowserProcessFindings(browserProcess));
findings.push(...buildBrowserProcessFindings(browserProcess, runtimeAlerts));
findings.push(...buildControlledNavigationRootCauseFindings(control, manifest));
findings.push(...buildSessionInvariantFindings(control, manifest));
const commandTimes = control
@@ -3220,10 +3220,11 @@ function buildBrowserProcessReport(rows) {
};
}
function buildBrowserProcessFindings(report) {
function buildBrowserProcessFindings(report, runtimeAlerts = null) {
const summary = report?.summary || {};
if (!summary || Number(summary.sampleCount ?? 0) <= 0) return [];
const findings = [];
const rootCauseSignals = browserRootCauseSignals(report, runtimeAlerts);
const maxTotalRssMb = Number(summary.maxTotalRssMb ?? 0);
const maxProcessRssMb = Number(summary.maxProcessRssMb ?? 0);
if (maxTotalRssMb >= alertThresholds.browserTotalRssRedMb || maxProcessRssMb >= alertThresholds.browserProcessRssRedMb) {
@@ -3240,6 +3241,7 @@ function buildBrowserProcessFindings(report) {
rootCause: "frontend_browser_process_memory_pressure",
rootCauseStatus: "confirmed-from-runner-process-rss",
rootCauseConfidence: "high",
rootCauseSignals,
fallbackAllowed: false,
valuesRedacted: true,
});
@@ -3260,6 +3262,7 @@ function buildBrowserProcessFindings(report) {
rootCause: "frontend_browser_process_memory_leak_or_unbounded_render_growth",
rootCauseStatus: "confirmed-from-runner-process-rss-growth",
rootCauseConfidence: "high",
rootCauseSignals,
fallbackAllowed: false,
valuesRedacted: true,
});
@@ -3285,6 +3288,7 @@ function buildBrowserProcessFindings(report) {
rootCause: "frontend_browser_page_unresponsive_to_playwright",
rootCauseStatus: "confirmed-from-cdp-runtime-evaluate",
rootCauseConfidence: "high",
rootCauseSignals,
fallbackAllowed: false,
valuesRedacted: true,
});
@@ -3303,6 +3307,7 @@ function buildBrowserProcessFindings(report) {
rootCause: "frontend_browser_cdp_metrics_unresponsive",
rootCauseStatus: "confirmed-from-cdp-metrics-timeouts",
rootCauseConfidence: "high",
rootCauseSignals,
fallbackAllowed: false,
valuesRedacted: true,
});
@@ -3310,6 +3315,32 @@ function buildBrowserProcessFindings(report) {
return findings;
}
function browserRootCauseSignals(report, runtimeAlerts) {
const browserSummary = report?.summary || {};
const runtimeSummary = runtimeAlerts?.summary || {};
const sampleCount = Number(browserSummary.sampleCount ?? 0);
const sessionListReadCount = Number(runtimeSummary.workbenchSessionListReadCount ?? 0);
const traceEventsReadCount = Number(runtimeSummary.workbenchTraceEventsReadCount ?? 0);
const webPerformanceBeaconFailureCount = Number(runtimeSummary.webPerformanceBeaconFailureCount ?? 0);
const eventSourceFailureCount = Number(runtimeSummary.workbenchEventSourceFailureCount ?? 0);
const suspectedFrontendRefreshStorm = sessionListReadCount >= Math.max(20, sampleCount * 2);
return {
suspectedFrontendRefreshStorm,
sessionListReadCount,
traceEventsReadCount,
webPerformanceBeaconFailureCount,
eventSourceFailureCount,
requestFailedCount: runtimeSummary.significantRequestFailedCount ?? runtimeSummary.requestFailedCount ?? 0,
httpErrorCount: runtimeSummary.httpErrorCount ?? 0,
topRequestFailedPaths: (runtimeAlerts?.networkSignificantRequestFailedByPath ?? runtimeAlerts?.networkRequestFailedByPath ?? []).slice(0, 5),
topHttpErrorPaths: (runtimeAlerts?.networkHttpErrorsByPath ?? []).slice(0, 5),
note: suspectedFrontendRefreshStorm
? "suspected frontend refresh storm: session list reads exceed the sample-derived budget during a browser red finding"
: "root-cause signals are included so memory/responsiveness/CDP red findings can be correlated without manual grep",
valuesRedacted: true,
};
}
function computeBrowserProcessGrowth(samples, windowMs) {
const budgetMs = Math.max(1000, Number(windowMs || 0));
const sorted = (samples || []).filter((item) => Number.isFinite(item.tsMs)).sort((a, b) => a.tsMs - b.tsMs);
@@ -4154,6 +4185,18 @@ function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) {
const requestFailed = naturalNetwork
.filter((item) => item?.type === "requestfailed")
.map((item) => networkAlertEvent(item, promptTimes));
const workbenchSessionListReadCount = naturalNetwork
.filter((item) => urlPath(item?.url) === "/v1/workbench/sessions")
.length;
const workbenchTraceEventsReadCount = naturalNetwork
.filter((item) => /^\/v1\/workbench\/traces\/[^/]+\/events$/u.test(urlPath(item?.url)))
.length;
const webPerformanceBeaconFailureCount = naturalNetwork
.filter((item) => urlPath(item?.url) === "/v1/web-performance" && (item?.type === "requestfailed" || Number(item?.status) >= 400))
.length;
const workbenchEventSourceFailureCount = naturalNetwork
.filter((item) => urlPath(item?.url) === "/v1/workbench/events" && (item?.type === "requestfailed" || Number(item?.status) >= 400))
.length;
const significantRequestFailed = requestFailed.filter(
(item) => !isBenignLongLivedStreamClosureAlert(item) && !isObserverRefreshClosureAlert(item, observerRefreshTimes),
);
@@ -4284,6 +4327,10 @@ function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) {
httpErrorCount: httpErrors.length,
requestFailedCount: requestFailed.length,
significantRequestFailedCount: significantRequestFailed.length,
workbenchSessionListReadCount,
workbenchTraceEventsReadCount,
webPerformanceBeaconFailureCount,
workbenchEventSourceFailureCount,
benignLongLivedStreamClosureCount: requestFailed.length - significantRequestFailed.length,
domDiagnosticSampleCount: domDiagnostics.length,
domDiagnosticGroupCount: groupDomDiagnostics(domDiagnostics).length,