From 3e9a6f281ebc74f1a4e5e872d8edec225a4d56c7 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Fri, 25 Sep 2026 19:13:22 +0600 Subject: [PATCH] [2026-09-25] eagle: work/projects/cpm-web-extension-breakage-validation.md --- work/projects/cpm-web-extension-breakage-validation.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/work/projects/cpm-web-extension-breakage-validation.md b/work/projects/cpm-web-extension-breakage-validation.md index 0e2d54eb..d45e9ade 100644 --- a/work/projects/cpm-web-extension-breakage-validation.md +++ b/work/projects/cpm-web-extension-breakage-validation.md @@ -37,7 +37,7 @@ M4 перезагружает Web Extensions при первом сигнале | M3 attribution telemetry | `alex/cpm-failure-telemetry` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-failure-telemetry` | **Merged 2026-09-23:** [PR #6844](https://github.com/duckduckgo/apple-browsers/pull/6844), CI green. Bucketed diagnostics, app launch/version attribution, `extension_updated`, `base_url_changed`, `recovery_from`, reload-error pixel; recorder и proxy имеют отдельные flags. [[cpm-m3-telemetry-privacy-triage]]. | | M4 hung recovery | `codex/cpm-hung-recovery` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-hung-recovery` | **In progress:** первый `messaging init failure` открывает diagnostic episode; первый последующий `hung` перезагружает Web Extensions. | | M5+M6 graveyard mitigation + A/B | `alex/cpm-graveyard-mitigation` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-graveyard-mitigation` | **Production-ready, default off; one PR over merged M3:** one retained view maximum, 3-second bounded hold, weak scheduled release, runtime cleanup, lazy stable cohort enrollment, control/treatment isolation, bounded pending outcomes, operational and experiment pixels. 431/431 WebExtensions tests; iOS/macOS builds and pixel validators pass; earlier real forced-kill iOS E2E recovered with release at ≈3.15 s. | -| M7 state-restoration readiness gate | `alex/cpm-state-restoration-race` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-state-restoration-race` | **Production-ready locally from `origin/main` (`a18fca47c3`):** restored main-frame HTTP(S) navigation now waits for initial Web Extension `loadAndSync`; Swift package and full `macOS Browser` builds pass. Targeted tests pass: 19/19 coordinator suite (including 3 new M7 cases) and 3/3 navigation-gate tests. | +| M7 state-restoration readiness gate | `alex/cpm-state-restoration-race` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-state-restoration-race` | **Production-ready locally from current `origin/main` (`b93091e769`):** restored main-frame HTTP(S) navigation waits for initial Web Extension `loadAndSync`, bounded by `withTimeout(3)` in `decidePolicy`. Timeout cancels only the cancellation-aware waiter; the underlying load continues and its retained task is cleared on completion/cancellation. Swift package and `macOS Browser` test builds pass. Targeted tests: 20/20 coordinator and 4/4 navigation-gate, including a non-cooperative load hang, waiter timeout, and late load completion. | All milestone diffs pass `git diff --check`; changed Swift lines are within 150 columns. @@ -45,11 +45,11 @@ All milestone diffs pass `git diff --check`; changed Swift lines are within 150 **Confirmed race:** `setupWebExtensions()` creates the manager/controller before state restoration, but starts `WebExtensionLifecycleCoordinator.loadAndSync()` without awaiting it. `stateRestorationManager.applicationDidFinishLaunching()` runs immediately afterward. A restored tab can therefore begin its main-frame navigation while the CPM `WKWebExtensionContext` is not loaded, so WebKit cannot inject the CPM content script at document start. The existing Content Blocking navigation responder already waits for internal user scripts and rule lists; M7 adds the equivalent readiness boundary for Web Extensions. -1. Add an awaitable initial-load readiness API to `WebExtensionLifecycleCoordinator`; it must reuse the existing coalesced `loadAndSync` task and complete on success, empty/disabled embedded set, or cancellation. -2. Inject a narrow readiness waiter into tab extensions and await it from the navigation policy path before main-frame HTTP(S) navigation proceeds. Do not poll `isAutoconsentExtensionLoaded`, because a disabled/failed CPM extension must not deadlock navigation. +1. Retain only the first coalesced `loadAndSync` task in `WebExtensionLifecycleCoordinator`, clear it on completion/cancellation, and expose a cancellation-aware waiter that never cancels the underlying load. +2. Inject that waiter into tab extensions. In the `decidePolicy` path, wrap it in the project `withTimeout(3)` helper before allowing main-frame HTTP(S) navigation to proceed. Timeout is fail-open and cancels only the waiter. 3. Keep the wait startup-only: once initial `loadAndSync` finishes, later navigations pass immediately. Non-web and subframe navigations do not wait. -4. Cover coordinator blocking/release/cancellation and navigation-gate scope with unit tests; run targeted WebExtensions/macOS tests after explicit approval. -5. Acceptance: a restored page cannot start before the initial extension load/sync settles; disabled Web Extensions and cancelled startup work never hold a tab; normal post-launch navigation has no added asynchronous work. +4. Cover blocking/release/cancellation, a non-cooperative underlying load hang, late load completion, the three-second default, and navigation scope with unit tests. +5. Acceptance: restored page loading normally waits for extension context readiness; a hung load cannot hold `decidePolicy` beyond three seconds; disabled/cancelled startup work and normal post-launch navigation do not add a persistent wait. ## Stack and Git state