--- title: CPM Web Extension — карта кода по worktree и строкам date: '2026-10-09' tags: - work - cpm - webkit --- > **App integration update — 16 октября 2026, 01:48 Бишкек.** В `cpm-e2e-init` добавлены 20 методов настоящего app-hosted CPM suite: финальные **20/20 PASS, 0 failures/skips/expected failures**, группы 18+1+1 на одной signed build. Схема `CPM Integration Tests`; файлы/guards/запуск/ограничения — §5 ниже, полный реестр и история — [[cpm-status-2026-10-15#Финальный app-hosted итог — 16 октября 2026]]. Без commits. Карта остальных worktree/PR ниже сохраняет свою датированную сверку 15 октября. # CPM Web Extension — карта кода по worktree и строкам > **Актуальная карта — 15 октября 2026.** Текущий статус PR/CI, данные и все 14 новых P5/P6 результатов: [[cpm-status-2026-10-15]] (§2, §5, §10–12). Датированные исходные разделы 1–4 ниже сохранены; их «ничего не закоммичено», номера строк и общий worktree описывают 9 октября, а не текущее состояние. ## 0. Текущая карта worktree и запусков Корень: `/Users/admin/DuckDuckGo/apple-browsers.git/`. Проверены `git worktree list`, HEAD и наличие тестовых файлов. **Единого worktree со всеми suites нет.** Общая база в истории не включает local dirty/untracked файлы; особенно это относится к remaining-failures и scratch init-progress E2E. | Worktree | Ветка / текущий HEAD | Назначение и границы | |---|---|---| | `alex/cpm-web-extension-remaining-failures` | та же; `a0717cf098cd` | Основное расследование без A/B watchdog: P1 (3 wake), P2 (9 load failure), P3 (2 cache/cleanup), новые P5/P6 (12 lifecycle + 2 flag). Все 28 repro opt-in; `CPMRepro/` untracked. Production dirty telemetry — прежняя копия, не переносить вместе с тестами | | `cpm-repro-harness` | `alex/cpm-repro-harness`; `dfb96279196d` | PR #7378: базовый harness, быстрые CI-контроли, медленные сценарии и raw WebKit characterization; без новых P5/P6 | | `cpm-fix-init-loss` | `alex/cpm-fix-init-loss`; `7ad771e227bb` | PR #7379: A, первые init при install/upgrade mid-session; красное/зелёное доказательство отдельно от remaining-failures | | `cpm-fix-network-crash` | `alex/cpm-fix-network-crash`; local `f1085846e7ff` | B поверх A; 5 network/termination сценариев в FullFailure + 11 watchdog unit tests. Remote PR #7381 остаётся на `39f94e12eaa4`, красный Non-Sandbox; локальный fix не запушен | | `cpm-fix-pixels` | `alex/cpm-fix-pixels`; `d6d800846d1f` | A/B + PR0, измерение `first_load_wait`/`background_restart`; отдельный PR не зарегистрирован в текущем статусе | | `alex/cpm-messaging-readiness-telemetry` | та же; `619869ee84c6` | PR0 #7376: readiness/failure/recovery параметры, CI без failure/pending, review required | | `cpm-readiness-baseline` | `alex/cpm-readiness-baseline`; `99ecf8b12787` | #7384 поверх PR0: baseline readiness; Danger=FAILURE | | `cpm-init-progress-native` | `alex/cpm-init-progress-native`; `46f25175c7c4` | #7385 поверх baseline: `init_stage`, 11 CPMInitProgress unit tests; Danger=FAILURE. Нужен JS privacy-extension#3727 | | `cpm-e2e-init` | `scratch/cpm-e2e-init`; `46f25175c7c4` (HEAD сохранён, без новых commits) | Прежний scratch native init-progress/SPM harness плюс новые **20 app-hosted XCTest methods** в `macOS/IntegrationTests/CPM/` и схема `CPM Integration Tests`; результаты и ограничения — §13 status. Наличие HEAD, совпадающего с #7385, не означает наличие untracked suites в PR | | `alex/cpm-web-extension-ready-handshake` | та же; `a0717cf098cd` | Старый объединённый локальный A/B harness и slow-wake-with-fixes; архив сравнения. Текущие PR A/B нарезаны в worktree выше | | `cpm-stable-base-url` | `alex/cpm-stable-base-url`; `570c2f8ef479` | Старый M2 experiment: меняет baseURL всем extensions, не доведён и не прогнан через нужный integration harness. Прежнее «production-ready» в старых планах не является актуальным итогом | CI статусы — read-only snapshot GitHub 15 октября, 17:26 UTC. Подробные head/base/checks и все оговорки — основной статус; локальные воспроизведения не дают зелёного CI незапушенному коду. ### 0.1. Файлы remaining-failures и полный охват Путь тестов — `SharedPackages/WebExtensions/Tests/WebExtensionsTests/CPMRepro/`. | Файл | Что искать | |---|---| | `CPMReproLifecycleHypothesisTests.swift:30` | 12 методов: nil/previous controller, startup healthy control, source-linked shared-window close + unrelated-window control, forgotten live tab, listener metadata, long documents/dashboard loss, host main-actor stall | | `CPMReproProductionFlagTests.swift:30` | 2 метода: главный flag true→false→true даёт P6; true→true не меняет manager; embedded-only cycle сохраняет controller и работает после explicit ready wait | | `CPMReproBackgroundLoadFailureTests.swift:31` | 9 методов P2: ≥30-секундный медленный запуск, 10-секундный контроль, file/script/bindings ошибки и четыре отрицательных process/load проверки | | `CPMReproExtensionLoadCleanupTests.swift:36` | 2 метода P3: relaunch и reload при большом Network cache и смене baseURL | | `CPMReproWakeExperimentTests.swift:25` | 3 метода P1: 5/12-секундный wake и сравнение readiness signals | | `CPMReproTestCase.swift:37` | Базовый opt-in `CPM_REPRO=1`, Options, cleanup. `existingTabs` (68) предоставляет сохранённые view до load нового context | | `CPMReproHarness.swift:258,269,296` | open/close additional window через production eventsListener; adopt existing view. `:175` watcher без strong reference через await; `:365` content marks | | `CPMReproTabs.swift:29,148,165` | Реальные WKWebView, navigation gate/monitor callbacks, window/tab delegates и multiple-window provider | | `CPMReproDelayedResource.swift:26` | Реальный 35-секундный image subresource; URL уникален на каждую навигацию | | `CPMReproPrelude.swift` | DOM init/initResp marks, trace всех autoconsent payloads, startup/listener test seams | | `CPMReproSupport.swift:43,92,175,213` | Pixel recorder; optional native-method suppression; temporary instrumented extension copy; WebKit process SPI | **Результаты:** все 14 новых методов P5/P6 имеют pass по нескольким запускам; последние обычные tests — 478 XCTest / 28 skipped / 0 failures (10.469 с), strict SwiftLint восьми изменённых файлов — 0. P1/P2/P3 в этом продолжении заново не запускались. Таблица всех 14, начальные красные результаты, log paths и SHA-256 — основной статус §12. ### 0.2. Browser → WebKit paths **P5:** `MainWindowController.windowWillClose` (611–640) фильтрует сохраняемые tabs только для явных `didCloseTab`, затем unregister и `didCloseWindow`. `MainWindowController+WKWebExtensionWindow.tabs(for:)` (31) всё ещё возвращает loaded pinned + regular tabs. `WindowControllersManager.unregister` (178) не очищает closing-window collections. WebKit внутри `WebExtensionContext::didCloseWindow` сам вызывает `didCloseTab` для каждого delegate tab; тот попадает в `forgetTab`. `runtimeSendMessage` возвращает tab-not-found **до** wake. Проверены текущий browser worktree и macOS tag `1.210.0+macos`; shared-window effect исполнен на системном WebKit. **P6:** `AppDelegate.setupWebExtensions/initializeWebExtensions` (2153–2166, 2209–2221) и shared `FeatureFlag/WebExtensionFeatureFlagHandler.swift`: main disable → uninstall + clear references; enable при manager=nil → новый manager/controller без пересоздания старых WKWebView. `WebExtensionTab::webView` отвергает неправильный controller. Только test-controlled main-flag cycle доказан; исторические данные не подтверждают такой production transition. Default main flag enabled; повторный true и embedded-only toggle не заменяют controller. WebKit source checkout: `/Users/admin/Downloads/WebKit_sparse`, HEAD `99051d5d08cd9f19ec76ce599f7539d070b1ae09`: - `Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionContextCocoa.mm:1351,1419`; - `Source/WebKit/UIProcess/Extensions/Cocoa/API/WebExtensionContextAPIRuntimeCocoa.mm:129`; - `Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionTabCocoa.mm:339`. Это карта source contract и system-WebKit проверки, не утверждение о полном совпадении checkout с shipped binary. В SPM исполняются production shared classes и WebKit, а app-side host callbacks/delegates реализованы harness. ### 0.3. Команды и предусловия Из `alex/cpm-web-extension-remaining-failures/SharedPackages/WebExtensions`: ```sh CPM_REPRO=1 CPM_REPRO_P5_P6=1 swift test --disable-automatic-resolution --filter 'CPMReproLifecycleHypothesisTests|CPMReproProductionFlagTests' CPM_REPRO=1 CPM_REPRO_P5_P6=1 swift test --disable-automatic-resolution --filter 'CPMReproProductionFlagTests|CPMReproLifecycleHypothesisTests/testClosing' CPM_REPRO=1 swift test --disable-automatic-resolution --filter CPMReproWakeExperimentTests CPM_REPRO=1 swift test --disable-automatic-resolution --filter CPMReproBackgroundLoadFailureTests swift test --disable-automatic-resolution --skip-build ``` Первая команда всех 14 дана для повторения: в текущей сессии 14 методов проверялись раздельными группами. `--skip-build` применим после актуальной сборки. Для P3 нужны `CPM_REPRO_CACHE_URL` (server `/fill.html?n=N`) и `CPM_REPRO_CACHE_ENTRIES` (default 20000; заполнение порядка 10 минут), затем filter `CPMReproExtensionLoadCleanupTests`. Выделенный PR-harness в `cpm-repro-harness` имеет быстрые tests без общего opt-in и `requireLocalRun` для тяжёлых; raw WebKit characterization требует также `CPM_REPRO_WEBKIT=1`. Новые P5/P6 живут только в remaining-failures и в PR-harness ещё не переносились. Environment P5/P6: macOS 26.7.1 (25G241), Xcode 27.1 (27A9275), embedded bundle 2026.9.29. M4 в этих 14 tests выключен, gate включён, Network PID прогрет до context-load; production bundle zip не правился, temporary copy содержит trace/DOM instrumentation. Нативные flags в production не переключались. --- **Архив карты на 9 октября.** Все следующие разделы сохранены для старых source line references и истории экспериментов; текущие точки входа и статус — выше. Контекст: [[cpm-web-extension-breakage-session-summary-2026-10-07]] (раздел 10), [[cpm-web-extension-repro-and-fix-2026-10-08]]. Номера строк — на 9 октября. **Ничего не закоммичено.** Корень всех worktree — `/Users/admin/DuckDuckGo/apple-browsers.git/`; пути ниже даны от корня worktree. `SP` = `SharedPackages/WebExtensions/Sources/WebExtensions`, `TP` = `SharedPackages/WebExtensions/Tests/WebExtensionsTests`. | Worktree | Ветка, база | Что внутри | |---|---|---| | `alex/cpm-web-extension-ready-handshake` | та же, `a0717cf098` | Фиксы + основной harness | | `alex/cpm-web-extension-remaining-failures` | та же, `a0717cf098` | Прод-код без фиксов, error 6 телеметрия (старая копия), репро error 6, очистки, медленного пробуждения | | `alex/cpm-messaging-readiness-telemetry` | та же, `main` `92e09cdbbc` | Телеметрия фазы 1 (включает error 6) — кандидат на первый PR | | `cpm-stable-base-url` | `alex/cpm-stable-base-url`, старая база `570c2f8ef4` | Эксперимент со стабильным `baseURL` | ## 1. Фиксы — `alex/cpm-web-extension-ready-handshake` Цепочка: навигация → coordinator спрашивает менеджер → менеджер проверяет watchdog и незавершённый первый старт background → при сбое монитор зовёт перезапуск. 1. `SP/Lifecycle/WebExtensionLifecycleCoordinator.swift:79-93` — `initialLoadWaiter`, его вызывают обе платформы на каждой main-frame навигации. Было: после открытия стартового gate → `nil`. Стало: `:89` зовёт `manager.prepareEmbeddedBackgroundForNavigation()` и ждёт его не дольше `initialLoadTimeout` (5 с, `:91-92`). 2. `SP/Manager/WebExtensionManager+EmbeddedBackground.swift` (новый): - `:35` `prepareEmbeddedBackgroundForNavigation()` — сначала `watchdog.restartBackgroundIfStale()` (`:36`), потом возвращает waiter, если идёт первый старт background только что установленного extension. - `:45` `trackEmbeddedBackgroundStartup(identifier:)` — запускает `context.loadBackgroundContent()` (`:49`) как `Task` и держит его в `embeddedBackgroundStartup`, пока не завершится. Зачем: WebKit при первой загрузке нового идентификатора грузит background сразу и теряет `init`, пришедший до выполнения скрипта (317981). 3. `SP/Manager/WebExtensionManager+EmbeddedExtensions.swift:178-180` — после `installationStore.add` для `.embedded` вызывается `trackEmbeddedBackgroundStartup`. Покрывает установку и обновление посреди сессии (на запуске это уже делал стартовый gate). 4. `SP/Manager/WebExtensionManager.swift`: - `:134-139` — свойства `embeddedBackgroundWatchdog` и `embeddedBackgroundStartup`. - `:167` — параметр init `isEmbeddedBackgroundRecoveryEnabled` (по умолчанию `{ true }`, к удалённому флагу не подключён). - `:186` — создание watchdog. - `:217-221` — `healthMonitor.onEpisodeStarted` → `restartBackgroundIfResponsive(reason:)`: перезапуск по первому сбою. - `:757` — в делегате `didCreateBackgroundWebView` watchdog получает новую background view. 5. `SP/Lifecycle/EmbeddedBackgroundLivenessWatchdog.swift` (новый): - `:63` init — все зависимости подменяемые (PID, перезапуск, проба отзывчивости). - `:76` `backgroundWebViewDidChange` — запоминает view и PID Network-процесса, запускает периодическую проверку. - `:85` `restartBackgroundIfStale` — PID Network-процесса сменился (или исчез) → перезапуск. Это и есть детектор «Network-процесс упал, worker мёртв». - `:107` `restartBackgroundIfResponsive` — перезапуск по сбою, только если процесс ответил на `return 1` за 1 с (`:166-170`). Замороженный не трогаем: убийство остановленного процесса давало глухой новый worker. - `:137` периодическая проверка раз в 1 с; `:156` `_networkProcessIdentifier` (SPI через KVC); `:173-180` перезапуск через `_killWebContentProcessAndResetState` — WebKit поднимает новый worker, и чинятся даже уже открытые документы. 6. `SP/Autoconsent/CPMMessagingHealthMonitor.swift:96-100` — колбэк `onEpisodeStarted`; `:656-658` — вызывается на первом сбое нового эпизода, только для `failureReason == .other` (не session restoration). 7. `SP/Manager/WebExtensionManaging.swift:164-168` — требование протокола; `:176-178` — реализация по умолчанию `{ nil }`, чтобы моки и платформы не ломались. **Тесты:** - `TP/EmbeddedBackgroundLivenessWatchdogTests.swift` — 11 unit-тестов (`:48`–`:150`): смена PID, исчезновение Network-процесса, отзывчивый и неотзывчивый процесс, выключатель, `stop()`, периодическая проверка. - `TP/WebExtensionLifecycleCoordinatorTests.swift:193, :217, :227, :243` — навигация ждёт первый старт; не ждёт, если готов; ожидание ограничено 5 с; при выключенном gate ничего не готовится. Мок — `:667`. - `TP/CPMMessagingHealthMonitorTests.swift:50, :61, :71` — `onEpisodeStarted` на первом сбое; не на session restoration; снова для нового эпизода. - **Harness** `TP/CPMRepro/` (запуск: `CPM_REPRO=1 swift test --filter CPMRepro` из `SharedPackages/WebExtensions`): - `CPMReproSingleTabTests.swift` — 10 тестов одной вкладки: холодный старт `:32`, обновление на запуске `:46`, **установка/обновление посреди сессии `:64`, `:87`** (до фикса терялись), перезапуск `:111`, `:128`, после reload `:146`, `:165`, пробуждение `:187`, падение background `:206`. - `CPMReproFullFailureTests.swift` — краш Network-процесса `:33`, `:38`, `:55` (без M4); смерть worker'а через SPI `:76` и удаление регистраций `:85` (стоит одну навигацию); не-триггеры `:124`, `:129`, `:136`. - `CPMReproWebKitTests.swift` — 7 тестов, нужен `CPM_REPRO_WEBKIT=1`: защиты выключены, утверждают, что баги WebKit ещё есть (`:45` потеря `init` при первой загрузке, `:80`/`:87`/`:151` мёртвый worker, `:97` только перезапуск background чинит открытые документы, `:133` сообщение из страницы background не доходит до worker'а). - `CPMReproWakeExperimentTests.swift:27-28` — **эксперимент, не для коммита**: медленное пробуждение 5 и 12 с с фиксами. Для него в `CPMReproHarness.swift` свойство `storage` сделано не `private`. ## 2. Расследование без фиксов — `alex/cpm-web-extension-remaining-failures` Прод-код как в релизе; harness перенесён без watchdog, с production-recorder'ом, поэтому сбои дают те же параметры пикселей, что в проде. - `TP/CPMRepro/CPMReproHarness.swift`: `:51` recorder диагностики; `:191` `contextErrorCodes`; `:196` папка установленного extension; `:203` `prependToBackgroundScript` — дописывает код в начало background-скрипта на диске (WebKit читает его заново на каждом пробуждении); `:212` PID Network-процесса; `:262` `loadRealPage` (для наполнения кэша). - `TP/CPMRepro/CPMReproBackgroundLoadFailureTests.swift` — **error 6**, 9 тестов, все с проверками: - `:59` новый процесс background заморожен на 45 с при пробуждении → только `:6`, `bg_load_error_after=25s_to_35s`. Это таймер выгрузки WebKit. - `:70` 10 с → error 6 нет. - `:79`, `:89`, `:96` — другие пути к error 6 (нет файлов, скрипт бросает, нет `browser`) — быстрые, `under_5s`; в проде их сигнатур нет. - `:104`, `:111`, `:118`, `:125` — условия, которые error 6 не дают. - `TP/CPMRepro/CPMReproExtensionLoadCleanupTests.swift:49, :69` — задержка очистки при смене `baseURL` на перезапуске и reload. Нужен локальный сервер: `CPM_REPRO_CACHE_URL`, `CPM_REPRO_CACHE_ENTRIES` (наполнение ~10 мин). - `TP/CPMRepro/CPMReproWakeExperimentTests.swift` — **эксперимент, не для коммита**: - `:27` старт worker'а 5 с на пробуждении → `initialization_failed` с сигнатурой группы 1, затем поздний ответ. - `:28` 12 с + навигация → `stuck` → M4. - `:31` сигналы готовности: `isLoading=false` через 0,1 с (бесполезен), completion `loadBackgroundContent` и первое нативное сообщение — ровно на старте worker'а. - Продовые изменения здесь (`CPMMessagingDiagnostics*`, JSON5, `WebExtensionManager+iOS.swift`, `AppDelegate.swift`) — **старая копия телеметрии error 6**, она целиком перенесена в worktree телеметрии. Отсюда не коммитить. ## 3. Телеметрия фазы 1 — `alex/cpm-messaging-readiness-telemetry` Только параметры пикселей, поведение детектора не меняется. 1. `SP/Autoconsent/CPMMessagingDiagnostics.swift`: - `:29` `ApplicationState`, `:38` провайдер состояния приложения в `CPMAppSessionDiagnostics`. - `:50` `CPMBackgroundReadiness`: `noBackground` / `pending` / `loaded(seconds)` / `failed`. - `:109-123` новые поля снимка (error 6, `applicationState`, возраст view, готовность, вкладки). - `:182-188` имена параметров (`bg_load_error_*`, `app_state`, `bg_view_age`, `bg_ready_after`, `open_tabs`, `loading_tabs`). - `:217-231` сериализация: error 6 — `none`, если ошибки не было, и опускается, если неизвестно; остальные опускаются при `nil`. - Бакеты: `:248` error 6 (вокруг таймера 30 с), `:258` возраст view, `:269` готовность (вокруг 4 с монитора и 30 с WebKit), `:285` число вкладок. 2. `SP/Autoconsent/CPMBackgroundReadinessObserver.swift` (новый) — откуда берётся `bg_ready_after`: - `:52` `observe` на создании view: ставит `pending` и на **следующем шаге run loop** (`:56`) зовёт `loadBackgroundContent`. View уже есть, поэтому вызов лишь присоединяется к идущей загрузке и ничего не будит. - completion → `loaded(секунды от создания view)` или `failed`; ответ для заменённой view игнорируется. - `:34` загрузчик подменяется в тестах; `:72` `readiness(of:)` — только для текущей view. 3. `SP/Autoconsent/CPMMessagingDiagnosticsRecorder.swift`: - `:125`, `:141` наблюдатель; `:223` сброс при новом контексте. - `:311-316` наблюдение включается тем же флагом, что и delegate proxy (`cpmBackgroundDelegateProxy`). - `:413-415` поля error 6; `:426` состояние приложения; `:430-431` возраст view и готовность; `:435-438` `open_tabs` / `loading_tabs` из `context.openTabs` и `webView(for:).isLoading`. - `:503-517` `recordContextErrors` — при error 6 фиксирует состояние приложения и секунды от создания view. 4. `SP/Pixels/WebExtensionPixelFiring.swift`: - `:71`, `:73` — у recovery-событий появился `details` (по умолчанию `nil`, старые вызовы не ломаются). - `:85` `CPMMessagingRecoveryDetails`: `failed_navigation` (опоздавший ответ) / `later_navigation` / `unattributed` + `:108` бакеты `recovery_after`. - `:181-188` параметры пикселя. 5. `SP/Autoconsent/CPMMessagingHealthMonitor.swift`: - `:451-452` неатрибутированный ответ закрывает stuck → `unattributed`. - `:712-716` обычное восстановление: тот же упавший замер → `failed_navigation`, иначе `later_navigation`. - `:722` `recoveryDetails` — секунды от первого сбоя эпизода. 6. Приложения: `iOS/DuckDuckGo/WebExtensions/WebExtensionManager+iOS.swift:107-114` (`UIApplication.applicationState`), `macOS/DuckDuckGo/Application/AppDelegate.swift:122-123` (`NSApp.isActive`). 7. JSON5: - словари `iOS/PixelDefinitions/pixels/params_dictionary.json5` (`:602` recovery, `:619` error 6, `:643` готовность и вкладки) и `macOS/.../params_dictionary.json5` (`:677`, `:694`, `:718`); - определения — iOS `debug_pixels.json5`, macOS `web_extensions.json5`: по 8 пикселям сбоя и 2 пикселям восстановления. **Тесты:** - `TP/CPMMessagingDiagnosticsTests.swift`: - бакеты `:135`, `:144`, `:149`; полный набор параметров `:154`; - error 6 `:279`, `:417`; - готовность: загрузилась `:304`, упала `:327`, заменённая view `:343`, наблюдение выключено `:360`; - состояние приложения `:377`; вкладки на реальном контроллере с незавершающейся загрузкой `:384`. - `TP/CPMMessagingHealthMonitorTests.swift`: опоздавший ответ `:1061`, следующая навигация `:1081`, `unattributed` `:1095`, бакеты `:1112`, обновлённый тест параметров `:1119`. - Проверка на реальном WebKit (harness временно копировался): медленное пробуждение 5 с → `bg_ready_after=pending`, `bg_view_age=4s_to_8s`, затем `recovery_document=failed_navigation`, `recovery_after=under_4s`. ## 4. Стабильный `baseURL` — `cpm-stable-base-url` - `SP/Loading/WebExtensionLoader.swift:56`, `:93`, `:134`, `:154`, `:158-165` — в `makeContext` выставляется `context.baseURL = webkit-extension:///`; если из идентификатора не собрать URL — ошибка `invalidIdentifier`. - `TP/WebExtensionLoaderDelegateTests.swift:61-94` — 2 теста: URL выставлен до загрузки; неверный идентификатор → загрузка не начинается. - Открыто: сейчас меняет URL **всем** extension (нужно — только встроенным без `web_accessible_resources`); не прогнан через harness (M4, краш Network-процесса, обновление); старая база. ## 5. App-hosted CPM integration tests — 16 октября 2026 Worktree `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-e2e-init`, branch `scratch/cpm-e2e-init`, HEAD `46f25175c7c42766cab23ea39f447f075c7bdd24`, **без commits**. Новая схема `macOS/DuckDuckGo-macOS.xcodeproj/xcshareddata/xcschemes/CPM Integration Tests.xcscheme` выбирает только две CPM classes, отключает parallel testing и включает `CPM_APP_INTEGRATION=1`. В ней нет Review-profile wipe/pre-action или `killall tests-server`. | Файл в `macOS/IntegrationTests/CPM/` | Роль | |---|---| | `CPMAppIntegrationTests.swift` | 10 deterministic baseline/diagnostics/scope/recovery tests: slow HTTP/native, одна навигация, одна таба, несколько табов одного окна, все окна, M4, dashboard transport, proxy flag | | `CPMAppKnownFailureIntegrationTests.swift` | 10 characterization methods: actual AppDelegate flags, shared-pin window closure, Network/background crashes, first install, slow idle wake, frozen WebKit load timeout, 20k persistent-cache reload | | `CPMAppIntegrationHarness.swift` | Реальные AppDelegate/WindowsManager/MainWindowController/Tab, production navigation/monitor/recorder, JSON attachments, owned windows/data stores, bounded cleanup | | `CPMAppIntegrationSupport.swift` | Реальный macOS native handler/provider/delegate с fault boundary; recorder transport before daily dedup; extraction через production storage; in-memory installation store; test-only PID/background SPI | | `CPMAppIntegrationBridge.swift` | Instrumentation только распакованной временной extension copy: page initResp mark, реальные runtime messages и scoped drop/release controls | | `CPMAppFixtureServer.swift` | Owned HTTP loopback, eight-second resource delay, cacheable 2KiB resources with unique URLs | | `README.md` | Повторение, signing, flags, все сценарии, ограничения причинной интерпретации | Все 6 Swift files включены в **Integration Tests и Integration Tests App Store**. Минимальные production seams: optional manager `controllerConfiguration` с прежним default; DEBUG AppDelegate factory с двумя guards; отдельный ConfigurationStore directory для CPM test host; разрешён только loopback HTTP в opted-in integration Tab; macOS diagnostics flag adapter доступен через `@testable` без изменения поведения. SecureVault/AppMain bypass отсутствует. Существующие dirty ZIP/SPM files сохранены. Запускать с **project signing defaults**, как в Xcode; unsigned override оказался ошибкой bootstrap и удалён. CLI run recipe находится в README. Для реального 30s WebKit timeout нужен test-host env `CPM_APP_INTEGRATION_EXPENSIVE=1`; для persistent cache — `CPM_APP_INTEGRATION_CACHE=1`, при необходимости `CPM_APP_INTEGRATION_CACHE_ENTRIES`, execution allowance 2400 s. Cache case лучше выбирать отдельно; этот opt-in включает fresh persistent UUID configuration в harness. Обычные запуски используют nonpersistent stores. Артефакты `/private/tmp/cpm-app-integration-20261015/`: signed `.xcresult`, logs, exported JSON, `app-test-results.json` (история результатов и hashes исходников). **Финальные результаты сверять с §13 [[cpm-status-2026-10-15]], не считать каждый characterization test доказательством воспроизведения причины.** P3 может пройти с отрицательным outcome; background crash может самоизлечиться. App P5 ограничен одной потерянной навигацией и контролями: более длинная попытка прерывалась DEBUG guard закрытого окна в XCTest. Persistent P5/stuck доказан отдельно SPM, что не превращает app attempt в green. iOS/device-only причины и естественная потеря listener metadata этим suite не доказаны. App harness isolation follow-up: extension resources now use an owned UUID directory under **unswizzled `NSTemporaryDirectory()`**, outside `com.duckduckgo.macos.tests`. The latter is a shared FileManager swizzle target that TestRunHelper removes on host initialization/bundle completion. A preceding joint run passed 16/18 but lost its Network-test extension resource before reload (`extensionNotFound`), then the next wake test hit INT. The deleter was not identified; latest status must include that red run and the resource-isolation recheck, rather than treating prior isolated passes as a green combined suite. `CPMAppExtensionStorage.resolveInstalledExtension` still delegates to production and adds only missing-folder/root logging. Финальная сверка §5: `signed-isolated-regression` **18/18**, `signed-isolated-pinned` **1/1 (72.644 s)**, `signed-isolated-cache` **1/1 (43.053 s)**. Все summaries: 0 failures, 0 skipped, 0 expected failures. В последнем cache run 20k ресурсов за 39.113 s, post-reload probe 0.900 s, P3 не воспроизведён, UUID store удалён. Все 20 JSON exported в одноимённые `*-evidence/` directories; `/private/tmp/cpm-app-integration-20261015/app-test-results.json` содержит текущие hashes исходников и всю per-method историю. HEAD `46f25175c7c42766cab23ea39f447f075c7bdd24` сохранён, исходный dirty ZIP не менялся. Финальный strict lint: 11 Swift files, 0 violations; PBX и scheme XML валидны. Предыдущие red attempts сохранены, не превращены в pass; единый invocation всех 20 не выполнялся. 16 Oct clarification/rerun: all 20 current app methods passed again in 18+1+1 groups, same signed build and source hashes; `/private/tmp/cpm-app-integration-20261015/rerun-failed-20261016-042858/rerun-summary.json`. This is an instrumented app-test environment, not an unchanged shipping binary. P1/P2 delay/SIGSTOP and message/native/dashboard loss are imposed faults; P5/P6/Network use real lifecycle paths but their production occurrence/causal attribution is a separate claim. Old long P5 is still outside the verified app scope; P3 remains unreproduced. Cache teardown has a recorded nonfatal SQLite warning. Submodules are not required by these CPM cases: privacy-reference-tests resources map to Unit Tests targets, SnapshotReferences to snapshot testing support, not XCUI accessibility snapshots. Neither submodule was initialized or modified by this rerun. ## Локальный кандидат P1/P2 — 16 октября 2026 (без commits) Worktree `cpm-e2e-init`, HEAD всё ещё `46f25175c7c42766cab23ea39f447f075c7bdd24`. Эта секция дополняет прежнюю карту: #7379 не изменялся, новая работа пока только локальная. - `WebExtensionLifecycleCoordinator.initialLoadWaiter`: после launch gate проверяет подготовку background на каждом HTTP(S) main-frame navigation. Await ограничен прежними 5 s и отменой navigation; AsyncStream позволяет освободить navigation даже при callback WebKit, игнорирующем cancellation. Жизненный цикл load/sync этим не блокируется. - `WebExtensionManaging.prepareEmbeddedBackgroundForNavigation`: новый protocol hook с no-op default для лёгких conformers. Реализация — `WebExtensionManager.swift`, без нового project file. - `WebExtensionManager.loadEmbeddedExtensionBackgroundContent`: публичный callback API + одна shared startup task на текущий context. Error notification завершает/очищает старую readiness task, чтобы новая попытка не приклеилась к завершённому старому startup. - `WebExtensionManager.recoverEmbeddedBackgroundLoadFailure`: собственный текущий embedded context, настоящий domain/code 6, существующий recovery kill switch, known app_state != background, отсутствие M4 in-progress и cooldown 60 s. **Повторяется только background load; context/Tab/controller не заменяются.** Полный M4 reload остаётся при confirmed stuck. Старый full-context P2 prototype отброшен: он терял page initResp в existing tab при повторе. - `CPMAppKnownFailureIntegrationTests.testSlowBackgroundWakeKeepsFirstNavigationHealthy`: actual idle + 8 s worker delay, первый документ получает native+page без detector failure. - `...testFrozenBackgroundRecoversAutomaticallyForNextNavigation`: actual one-tab idle wake, owned SIGSTOP 45 s, настоящий error 6. Требует native+page на **том же failed document до новой navigation**, тот же объект context и recovery_document=failed_navigation; затем healthy existing/new-window/new-tab. - Raw P1 и P3 characterization отключают navigation gate; P5 остаётся с gate включённым и ожидает bucket bg_view_creations=3_to_5: подготовка P1 создаёт дополнительный background перед rejected unknown-tab message. P5 tab-registration loss и healthy controls по-прежнему проверяются. Артефакты и snapshots: `/private/tmp/cpm-p1-p2-fixes-20261016/`. `before-changes.tar.gz` сохраняет прежние dirty changes; `point-fixes.diff` предназначен для delta этой работы. Начальный background-only result — 2/2 PASS, пакет 806/14 skipped/0 failures. Общий run/negative controls/P5/cache ещё выполняются; окончательный исход — в [[cpm-status-2026-10-15]]. Зелёный случай относится к macOS с imposed timing/process faults; natural iOS cause по-прежнему не доказан. **Финальная сверка P1/P2, 16 октября 12:30 UTC:** работа завершена без commits. `validation-summary.json`: **22/22 уникальных app methods PASS**, positive groups 2+19+1+1 (P2 повторён), 0 failures/skips/expected failures; оба disabled-fix controls functional FAIL. `unit-package-background-retry.log`: 806 / 14 skipped / 0 failures; strict lint 6 файлов — 0 violations. Source hashes после сборки не менялись, прежние dirty files/ZIP сохранены. Окончательный P2 — background-only retry и same-document native+page recovery, **не context replacement**. Предыдущая full-reset реализация отвергнута после повторной потери page initResp. Current patch — `/private/tmp/cpm-p1-p2-fixes-20261016/point-fixes.diff`; P5 ожидает дополнительный view от P1, P3 остаётся отрицательным на 20,000 resources. Cache assertions прошли, но teardown снова имеет известный SQLite vnode warning для owned UUID. Подробная проверенная матрица/ограничения и весь red-history — [[cpm-status-2026-10-15]]. ## Отдельный worktree P1/P2 — 16 октября 2026 `/Users/admin/DuckDuckGo/apple-browsers.git/alex/cpm-fix-p1-p2`, branch `alex/cpm-fix-p1-p2`, от `alex/cpm-messaging-readiness-telemetry` (`01d4a0ec22`), без коммитов. Отличается от полного scratch research-набора: - `WebExtensionLifecycleCoordinator.initialLoadWaiter`: после launch открывает ограниченное 5 s ожидание background wake; cancellation-safe AsyncStream, существующий gate flag. - `WebExtensionManager.prepareEmbeddedBackgroundForNavigation` / `loadEmbeddedExtensionBackgroundContent`: один startup job на конкретный context object, public WK callback, release pending callers при error/reload. - Observer `WKWebExtensionContext.errorsDidUpdateNotification` / `recoverEmbeddedBackgroundLoadFailure`: error 6 только своего embedded context, существующий recovery flag, current background guard, throttle 60 s; повтор background load без context replacement. M4 reload остаётся fallback. - Recorder `isApplicationInBackground`: читает уже существующий appSession lifecycle provider, не добавляет telemetry dimension. - `macOS/IntegrationTests/CPM/`: пять Swift-файлов, ровно два app-теста `CPMBackgroundRecoveryIntegrationTests`, сокращённая инфраструктура; схема `CPM Integration Tests`. Реальные app windows/tabs/native handlers/WebKit, неизменный bundled ZIP, extracted startup-delay и page initResp observation, SIGSTOP только своего background PID. Scoped drop/cache/Network/P3/P5 сценарии остались в research worktree. - Четыре новых метода `WebExtensionLifecycleCoordinatorTests`: wake после launch, bounded non-cancellable callback, cancellation, disabled flag. Всего 6 новых test methods; 22 — весь прежний app-набор. Текущая база уже перешла на episode contract. Test-only diagnostics proxy фиксирует реальный first-failure вызов, не синтезируя monitor events. P2 требует terminal `failed_measurement_response` / `reload_state=none` плюс page initResp исходного документа до другой навигации и ту же context identity. Signed app 2/2 PASS; unit 31+134=165 PASS; strict lint 14 files / 0 violations. Оба disabled-flag контроля на той же сборке функционально FAIL. Test autorelease pools исправили deallocation guard без отключения проверок. Source hashes и исходный scratch неизменны; schemas/ZIP/dependencies в diff не менялись. Артефакты и полная история: `/private/tmp/cpm-p1-p2-clean-20261016/validation-summary.json`, [[cpm-status-2026-10-15]]. Границы P1 cap / macOS injection / неустановленного natural iOS trigger сохраняются. ## Объединение #7379 с P1/P2 и независимый lifecycle guard — 16 октября 2026 Текущий `alex/cpm-fix-p1-p2` семантически заменяет #7379 в пределах прежнего navigation timeout 5 s: - `WebExtensionManager+EmbeddedExtensions.installEmbeddedExtension` вызывает `trackEmbeddedBackgroundStartup(identifier:)` именно для вновь установленного embedded context. - Manager сохраняет preferred context, а `prepareEmbeddedBackgroundForNavigation` захватывает конкретный context object. Fallback учитывает append-порядок installation store и не выбирает произвольный context при неизвестном порядке нескольких версий. `loadEmbeddedExtensionBackgroundContent(in:)` coalesces job на тот же object. - `recoverEmbeddedBackgroundLoadFailure` игнорирует superseded context. Состояние background приходит через самостоятельный `isApplicationInBackground` provider; iOS factory читает `UIApplication.shared.applicationState` независимо от diagnostics flag. Старый diagnostics getter удалён. Перед queued retry повторно проверяются flag/background. - Два install/upgrade-теста #7379 адаптированы в `CPMBackgroundRecoveryIntegrationTests` рядом с P1/P2. Реальный upgrade даёт context sets `[0,1,2,1]`; assertions требуют native dashboard + page initResp первого документа, а не просто загрузку context. Default delay 4500 ms. - Пять новых manager unit tests проверяют coexistence/newest selection, fallback после удаления replacement, diagnostics=nil + background→foreground, runtime kill switch и отказ retry старого context. Вместе с четырьмя lifecycle tests в ветке 9 новых unit methods. Финальная валидация: общий signed app **4/4 PASS**; install/upgrade **10/10 + 10/10 PASS** на той же сборке; targeted unit **170 PASS**, strict lint 16 files / 0 violations; WebExtensions generic iOS Simulator module **BUILD SUCCEEDED**. Все 4 disabled-flag controls функционально FAIL без teardown errors. Коммитов и изменений scratch/ZIP/schema/dependencies нет. Локальные `[CPM Background]` logs показывают медленный WK callback (>=1 s) и release gate по deadline. Они подтвердили предел: fixture 5000 ms даёт фактическую готовность после 5 s cap и может потерять первый upgrade document (6/10 ранее). Этот over-budget режим не исправлен и сохраняется через `CPM_APP_INTEGRATION_INSTALL_DELAY_MS=5000`; здоровые contract regression cases проверяют 4500 ms. Это не доказательство всех natural production causes и не реальный iOS suspension/resume run. Артефакты и история: `/private/tmp/cpm-p1-p2-replacement-20261016/validation-summary.json`, [[cpm-status-2026-10-15]]. ## Дополнение: late background recovery — 16 октября 2026, работа продолжается По запросу сначала сохранён прежний проверенный стейт: commit `dff29dfb1e` (`Fix CPM background wake and installation readiness`) в `alex/cpm-fix-p1-p2`. Предыдущие утверждения «коммитов нет» относятся к проверке до этого checkpoint. Новые изменения пока не закоммичены. Добавлен кандидат восстановления потерянного первого документа при startup за пределами 5 s: отдельный remote flag `webExtensions.cpmLateBackgroundDocumentRecovery`, default disabled. Navigation cap остаётся 5 s. Кандидат привязывает документ к конкретному unfinished startup, требует настоящий missing-dashboard timeout и успешный public WK background callback; после этого допускает одну GET-перезагрузку в пределах 30 s от старта. Смена/закрытие/ошибка документа, context replacement, background app state, выключение flag, изменённые form fields/contenteditable/loading document отменяют reload. Логика независима от diagnostics flag. Пять новых dailyAndCount pixels различают install readiness, upgrade readiness, idle background wake, error-6 background retry и late document reload. `background_ready` — факт успешного WK callback после использованного navigation wait (для error-6 retry — любого успешного retry), не доказательство initResp; `reload_started` — принятый WK reload; `response_received` — однозначно атрибутированный dashboard response после reload, не ACK content script. App tests отдельно требуют настоящий page initResp. macOS names заканчиваются `_macos`, iOS — стандартные platform/form-factor suffixes. Только outcome, appVersion и стандартные platform parameters, без URL/tab ID/form values/raw error. На данном этапе: targeted unit suite **178 PASS**, strict lint **17 Swift / 0 violations**, pixel definitions + wide-event consistency/immutability validators обеих платформ PASS. Подписанная app-сборка и реальные install/upgrade 8000 ms regression runs ещё не завершены; over-budget failure пока НЕ объявлен исправленным. Новые артефакты: `/private/tmp/cpm-late-recovery-20261016/`. ## Итог late background recovery и fix instrumentation — 16 октября 2026 Актуальный стейт: checkpoint **`dff29dfb1e`** сохранён до новых изменений; новый код/tests/pixels в `alex/cpm-fix-p1-p2` остаются **незакоммиченными**. Новый flag **`webExtensions.cpmLateBackgroundDocumentRecovery` default disabled**, динамически проверяется до попытки и после async safety check; remote config не менялась. Navigation cap по-прежнему **5 s**. Новый `CPMBackgroundReadinessRecovery.swift` в shared Autoconsent получает startup UUID manager, commit/failure/response/navigation events monitor и зарегистрированный GET WKWebView от platform tabs. Документ должен commit во время известного unfinished startup; только genuine missing-response timeout вместе с successful public background callback допускают одну GET-перезагрузку в пределах 30 s startup. Изменённый/loading/closed/failed document, main-document form edits/contenteditable, background app state, flag OFF и context replacement запрещают reload. Идентичность проверяется после async JS. Результат — replacement document на том же URL, сохранённый extension context; P2 background-only recovery исходного документа остаётся отдельным механизмом. Safety check использует настоящий async WK API label `contentWorld:`; ранние красные app runs поймали ошибочный callback overload с Void, и вся история сохранена. Пять отдельных dailyAndCount pixels с prefix `web_extension_cpm_`: `install_readiness`, `upgrade_readiness`, `background_wake`, `background_load_retry`, `late_document_reload`. macOS suffix `_macos`, iOS standard suffixes. Первые четыре: `background_ready` после использованного slow navigation wait (для P2 любого successful retry), без page-ACK claim; последний: `reload_started` и однозначный dashboard `response_received` после replacement navigation. Ответ исходного документа во время safety check не получает credit. Только outcome и стандартные параметры, без URL/tab IDs/field values/raw errors. Injection + PixelKitMock adapter test, definitions/validators обеих платформ PASS. Existing episode contract сохранён; document reload даёт `failed_tab_response`, extension `reload_state=none`. **Проверено на одной финальной подписанной сборке:** общий набор **8/8 PASS** (7 app/WebKit scenarios + adapter test); новые 8-second install/upgrade **2/2 PASS**, затем **10/10 repetitions PASS**; новый flag OFF **2/2 функционально FAIL**, 0 skips/expected failures/teardown errors. Во всех 14 positive over-budget executions первая загрузка **5.085–5.328 s**, затем ровно один reload, настоящий page initResp + native dashboard, URL/context сохранены. Edit-veto app test PASS; прежние P1/P2/install/upgrade PASS. Targeted units **179 PASS** (9 новых recovery; всего 18 новых unit methods в ветке), lint **17 files / 0 violations**, новый recovery без compiler warnings. Shared iOS Simulator и FeatureFlags-iOS builds PASS; полный iOS app/runtime не запускался. Code entry points: manager background startup callback/typed fix marker; monitor `backgroundReadinessRecovery` и `registerReadinessRecoveryDocument`; macOS PrivacyDashboardTabExtension weak view + real initiating request method; iOS TabViewController main-frame GET tracking; platform factories dedicated provider; PixelFiring adapters + platform JSON5 definitions. Все 8 app methods остаются в существующем CPM file/scheme; SharedPackages files подхватывает SwiftPM без pbxproj изменений. Все facts/history/guards/pixel semantics: [[cpm-status-2026-10-15]], `/private/tmp/cpm-late-recovery-20261016/validation-summary.json`. Source/app/test binary hashes стабильны; bundled ZIP равен checkpoint; package pin side effect убран; scratch/#7379 не менялись. Это opt-in bounded GET recovery, не универсальное исправление permanent background failures/unsafe documents. Прежние P3/P5 и natural-production/iOS suspension ограничения не сняты; 22 historical research methods отдельно.