From d8a006fdb39768084b569aac1cfc4a779b8f5059 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Wed, 28 Oct 2026 21:10:00 +0600 Subject: [PATCH] [2026-10-28] eagle: work/projects/cpm-post-release-analysis-2026-10-27.md --- .../cpm-post-release-analysis-2026-10-27.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/work/projects/cpm-post-release-analysis-2026-10-27.md b/work/projects/cpm-post-release-analysis-2026-10-27.md index a4ba3c3b..6ce514b6 100644 --- a/work/projects/cpm-post-release-analysis-2026-10-27.md +++ b/work/projects/cpm-post-release-analysis-2026-10-27.md @@ -327,3 +327,38 @@ The background page loads with `navigator.serviceWorker.register(...)`; its reje - What makes a start hang longer than 30 s (candidates from earlier notes: origin-change data migration, host main thread stall, process throttling), and why that survives M4. - How often the failed view is retained. - Pixel for the restart (none exists yet). + +## Idle wake investigation — 28 October 2026 + +Re-cut of the post-release data (macOS 1.213.x, 26–27 Oct, matured since the sections above, so shares differ a little; per-episode `web.extension.cpm.episode.count`) and of the old contract (1.211.x / 1.210, 18–20 Oct, `debug.web.extension.cpm.messaging.stuck..count`, one report per tab, confirmed by a second timeout). Not like-for-like: compare shapes, not percentages. OS comes from the `os_version` column of `metrics.pixels`, not a pixel parameter. Wake = the token rule `view@30s` plus an earlier `dealloc` (excluding no-view, no-scripts and error 6). + +### Was it there before Network recovery and P1? Yes +- 1.211.1 shipped neither fix and already had wake at ≈33% of stuck; 1.213 is the same. +- `died_client` in `bg_events` means the app killed the background process. The only shipped code path that does it to a background view is `CPMNetworkProcessRecovery` (`_killWebContentProcessAndResetState`). It is **0 of 65,374** reports in 1.211 and 3.6% of 1.213 episodes (5.1% of wake ones). It is *more* common among wake episodes that got an answer (9.2%) than among those that did not (2.3%). So recovery is a small new source of delays, not the cause. The "16%" in the priorities section did not reproduce with this cut (probably another denominator). +- 1.211 `network_restarted` is depleted in wake (12% vs 23% outside wake; 1.210: 14% vs 21%), without any recovery shipped. + +### What the wake group looks like +- Everything the pixel can see is healthy (7,787 episodes): `context_loaded`, `bg_view_alive`, `bg_process_responsive`, `handler_registered` true in 100%; `bg_process_alive` 99.95%; `bg_ready_after=under_1s` 98.8%; scripts present and controller matching 93% (3.8% P6, 2.9% unknown); error 6 0.3%; interrupted 0.03%. Outside wake the same fields do find things (no view 5.8%, no scripts 7.9%). +- `bg_view_age` 4s–8s in 66% (view created at the navigation), `tabs_affected=1` in 78%, no response 57.6%, `reload_state` none 70% / loaded 19% / response_timed_out 11%, `reason=other` 90.5%. +- The timeline tail of permanent cross-tab wake (1,134) is the same as for answered ones (`dealloc>view>proxy_on` twice, 71% vs 67%). It does not separate them. +- **No OS signal.** Wake share 43–57% on every macOS 15.x / 26.x / 27.x; 27.2 is inside the range (wake 47.7%, no response 42.7%, unmarked 53.7%). Episodes per loaded install-day ≈0.7–0.9, 27.2 at 1.07. +- **Unmarked core.** Wake no-response episodes with no `base_url_changed`, `extension_updated`, `load`, `died_*`, `error_*`, `interruption` or `unresponsive` in the 12-event window: **54% in 1.213 (2,319 of 4,279)**, **52% in 1.211** (15,089 of 28,849; also no network restart, app update or memory pressure). It is uniform by hour, OS, dmg/App Store and tab count. In 1.211 it is 94% `app_launch_age` >15 min and 86% `bg_view_creations` >5: long sessions with many 30 s cycles, not startup. Nothing in the available parameters separates it. +- `extension_updated` is depleted in wake in both versions (2.8% vs 9.4%, 15.1% vs 30.7%); recent updates are not a trigger. `base_url_changed` was enriched in 1.213 permanent cross-tab wake (25% vs 13% of answered; tail 14.8% vs 5.8%) but is depleted in 1.211 wake (13% vs 28%): the lead does not replicate. Both timeline tokens fall out of the 255-character window after a few idle cycles, so older updates are invisible. +- **Context error 7 is not a lead.** The old `context_errors` showed `WKWebExtensionContextErrorDomain:7` (= `ScriptExecutionError`, no underlying pair; WebKit also raises it for `console.error`) in 27% of 1.211 wake reports vs 17% outside, and 22% vs 16% on 1.210. The rate rises with context age (0 / 6 / 20 / 22 / 26% for `bg_view_creations` 0 / 1 / 2 / 3–5 / >5) and wake differs by ≤5 points inside a bucket. The shipped contract keeps other context errors locally and sends only error 6. +- **Extension load-error pixels are negligible:** `load.error`, `embedded.not.loaded`, `reload.failed` are ≈0.1% of loaded installs or less in both versions (1.213: 9 / 8 / 5 events on 18,299 install-days; 1.211 one day: 17 / 101 / 72 on 102,348). +- **Session restoration** is 4.5% of macOS episodes (16% no response) and 21% of iOS episodes (9.6% no response); only 0.6% of the wake group. It mostly recovers; `reason` labels only a first failing navigation that is itself restored. + +### History of reproductions vs this signature +- 8 Oct: a 5 s busy-wait in the worker at wake reproduced the *coarse* wake signature of that time (`responsive=true`, no errors, `dealloc/view/proxy_on@30s`) but recovered about 1 s later; 12 s plus a next navigation gave `stuck` and M4. Production `bg_ready_after` is under 1 s, so a slow worker start does not explain the permanent group. +- Reproduced but different: `kill -9` of the background (all tabs, self-heals ~1 min), Network crash (fixed by recovery), stale failed view H3 (iOS pattern B), origin-changing M4 blackout (seconds to ~32 s), P6 (single tab). +- Not reproduced: plain idle wake (0 lost of 1,500 and 1,333 wake navigations, with ad-blocking and real pages). Nobody has reproduced the permanent, cross-tab, healthy-looking, ready <1 s case that survives M4. + +### What the monitor measures: http(s) only, but error pages slip through +- `isEligibleForCPMMessagingHealthMeasurement` accepts only `http`/`https` (`CPMMessagingHealthMonitor.swift:1094`). `about:blank`, `duck://settings`, `duck://bookmarks` and `duck://player` are not measured (harness test `testNonHTTPPagesAreNotMeasured`: no episodes). Events come only from tabs (`PrivacyDashboardTabExtension`, `TabViewController`); PIR web views were not checked. +- The embedded extension manifest (`duckduckgo-privacy-extension/browsers/embedded/manifest.json`, 2026.10.14) matches `*://*/*` in all frames with no `exclude_matches`; the app grants every requested pattern (`WebExtensionLoader.swift:271`). There are no per-site exclusions. The content script is plain autoconsent; a main-frame `init` always ends in a dashboard refresh. +- **Error pages are measured as CPM documents and reported as failures (reproduced in the harness).** macOS shows a failed load with `loadAlternateHTML(html, baseURL: duck://error, forUnreachableURL: failedURL)` (`Tab.swift:1620`). Then `webView.url` is the failed http(s) URL, so the filter passes; the navigation action URL for alternate loads is also `webView.url` (`NavigationAction.swift:183`); `PrivacyDashboardTabExtension` does not skip `.alternateHtmlLoad` (`PageSignalsTabExtension` does). In `CPMReproErrorPageTests.testErrorPageIsNotReportedAsCPMFailure` (worktree `cpm-remaining-failures-repro`, uncommitted; `CPM_REPRO=1 swift test --filter CPMReproErrorPageTests`): navigation failed, then the alternate load finished with the failed http URL; **no `init` reached the background and no content-script mark appeared** (the script does not run on the error document); 4 s later the monitor counted a failure, and the next healthy navigation closed an `other / failed_tab_response / tabs=1` episode. The harness feeds the monitor `webView.url` as iOS does; the macOS `Navigation.url` path was read in code, not run in the app. iOS error pages were not tested. +- **Hypothesis, not verified:** pages that fail to load are shown on every tab after wake from sleep or a network drop. Each would count as a CPM failure right after an idle unload (fresh `dealloc>view>proxy_on`, background ready in under 1 s, healthy diagnostics), in several tabs (cross-tab) and with no answer until a normal navigation (permanent-looking, and M4 would not help). A same-tab second timeout also triggers M4 on a healthy extension (see `was_stuck` note above), which then adds the origin-change blackout. The pixel has no URL, so data cannot separate this; the check has to be an app run (debug build, Wi-Fi off, several tabs, wake) with the monitor log, or filtering `.alternateHtmlLoad` / `duck://error` and comparing wake rates. + +### Notes +- The "P3" lines above disagree: priorities say the baseURL fix is released; "What shipped" says P3 (#7493) is on main only and not in 1.213. Re-check before relying on either. +- Scratch SQL runner and queries are in the session scratchpad; the harness test is not committed or pushed.