521 lines
104 KiB
Markdown
Executable File
521 lines
104 KiB
Markdown
Executable File
---
|
||
title: CPM Web Extension — карта кода по worktree и строкам
|
||
date: '2026-10-09'
|
||
tags:
|
||
- work
|
||
- cpm
|
||
- webkit
|
||
---
|
||
> **Актуально — 19 октября 2026, uninstall fix опубликован.** [#7379](https://github.com/duckduckgo/apple-browsers/pull/7379): production **`e970e8c90e`**, 6 commits / 22 paths; отдельный review-only harness **`e380cf98f7`**, 12 paths вне main. Удаление активного CPM context отменяет pending late-document recovery; удаление старой upgrade-версии и ошибка unload сохраняют recovery. **190 unit PASS**, red-before-fix 2 PASS / 1 FAIL, signed macOS и iOS Simulator builds PASS, strict lint 11 / 0. На новом app head проверено на одних подписанных бинарниках: **ON 8 PASS → OFF 6 ожидаемых functional FAIL → ON 8 PASS**, без skips/expected failures; все app/XCTest SHA256 совпали. CI `e970e8c90e`: **48 SUCCESS / 21 SKIPPED**, pending/failures нет. PR description и reviewer flag steps актуализированы с сохранением ручных правок. Формы оставлены в прежнем виде; late reload flag default disabled. Предыдущие heads/results ниже — история.
|
||
|
||
> **Актуально — 19 октября 2026, полный повторный review #7379.** Проверен локальный `5708197362` против #7376: 22 changed paths, **187 существующих тестов PASS + 1 новый probe FAIL**. Новый P2: uninstall активного CPM context не отменяет pending late-document recovery. Общий launch→navigation deadline и привязка recovery pixel к точной WKNavigation исправлены локально и прошли повторные регрессии; усиление защиты форм явно отложено пользователем, исходная проверка сохранена за default-disabled флагом. В GitHub пока `08866c7eda` (CI: 48 SUCCESS / 21 SKIPPED), новые production commits не опубликованы. Локальный review-only harness `4308751d59` синхронизирован; его новый ON/OFF/ON ещё не запускался. Подробности — в конце; прежние heads/results ниже относятся к истории.
|
||
|
||
> **Актуально — 16 октября 2026, PR review fixes.** В [#7379](https://github.com/duckduckgo/apple-browsers/pull/7379) проверены оба inline замечания: cancellation уже покрыта, повторные 5 секунд на каждую navigation исправлены общим deadline на startup. Production **`08866c7eda`**, 3 commits / 21 paths; review-only harness **`3f4d755511`**, 12 test-only paths, вне main. Проверки: red-before-fix **2 FAIL**, после исправления **182 unit PASS**, signed macOS build и iOS shared-module build PASS, strict lint **8 / 0**; на одних app/XCTest binaries **ON 8 PASS → OFF 6 ожидаемых functional FAIL → ON 8 PASS**, без skips. Оба review threads resolved; Danger 0 failures, только PR-size advisory; flag tracking links добавлены, defaults не менялись. Пользовательские правки и reviewer steps сохранены. Подробности этого review — в конце. Более ранние heads/results ниже сохранены как история.
|
||
|
||
> **Актуально — 16 октября 2026, reviewer ON → OFF → ON.** В [#7379](https://github.com/duckduckgo/apple-browsers/pull/7379) восстановлена проверка выключенных production flags. Review-only branch **`codex/cpm-fix-init-loss-integration-tests` / `543e37164b`** содержит два schemes: **CPM Integration Tests** → 8 PASS; **CPM Integration Tests - Fixes Disabled** → ровно 6 functional FAIL; затем normal scheme → снова 8 PASS. Проверено на одних подписанных app/XCTest binaries, без пересборки и изменения Swift code. Production PR **`da126120a9`**, 2 commits / 21 files, не менялся; harness остаётся вне main (2 test-only commits / 12 paths). Пользовательские правки PR сохранены. Подробные результаты и artifacts — в конце; прежние односхемные steps и harness head `6e4e39e67c` относятся к истории.
|
||
|
||
> **Актуально — 16 октября 2026, harness вынесен из PR.** [#7379](https://github.com/duckduckgo/apple-browsers/pull/7379): **`alex/cpm-fix-init-loss` / `da126120a9`**, база #7376, **2 коммита / 21 файл**, production fixes + 18 новых shared unit regressions. Все local-only app tests, Xcode entries и DEBUG app hooks — только в **[`codex/cpm-fix-init-loss-integration-tests`](https://github.com/duckduckgo/apple-browsers/tree/codex/cpm-fix-init-loss-integration-tests) / `6e4e39e67c`**, один дочерний commit от PR head; **эта ветка не идёт в main**. Проверено: production app build PASS, unit **179 PASS**, harness app **8/8 PASS**, strict lint **19+8 / 0**. Reviewer steps в PR обновлены и пользовательские правки сохранены. Ниже сохранена датированная история; прежние 3 commits / 32 files и app harness в PR больше не актуальны.
|
||
|
||
> **Актуальный итог — 16 октября 2026, после force push.** [PR #7379](https://github.com/duckduckgo/apple-browsers/pull/7379) теперь поверх **#7376** (`alex/cpm-messaging-readiness-telemetry`), head **`aec5be9a83`**, ровно **3 коммита / 32 файла**. Старый worktree `cpm-fix-init-loss` удалён; текущая директория `alex/cpm-fix-p1-p2` теперь на ветке `alex/cpm-fix-init-loss`. Checkpoint **`367759cbc1`** и backup старой истории сохранены. Проверка через production feature flags **без создания/переключения коммитов**: signed app **8/8 PASS ON**, **6/6 функциональных FAIL OFF**, unit **179 PASS**, strict lint **27 / 0**, pixel validators обеих платформ PASS. Новый late-reload flag **default disabled**. Описание PR/шаги и local-only `to-be-disabled before merge` annotations обновлены; GitHub CI ещё не объявляется green. Подробная финальная сверка — в конце документа. **Ниже сохранена датированная история: прежние «не закоммичено», старые head/base и over-budget limitation не являются текущим статусом.**
|
||
|
||
> **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` — историческая строка; worktree удалён 16 Oct | `alex/cpm-fix-init-loss`; `7ad771e227bb` | PR #7379: A, первые init при install/upgrade mid-session; красное/зелёное доказательство отдельно от remaining-failures |
|
||
| **`alex/cpm-fix-p1-p2` — актуально 19 Oct** | **`alex/cpm-fix-init-loss`; local/remote `e970e8c90e`** | PR #7379 / база #7376: 6 commits / 22 paths, 29 новых shared tests, 190 PASS. Launch deadline, WKNavigation attribution и uninstall/preflight race исправлены и опубликованы; формы отложены, late reload flag default disabled; builds/lint PASS |
|
||
| — (отдельная ветка, без своего worktree) | **`codex/cpm-fix-init-loss-integration-tests`; local/remote `e380cf98f7`** | Синхронизирована с production `e970e8c90e`, README: 29 новых shared tests; 8 app methods, ON/OFF schemes, PBX + 3 DEBUG app hooks; 12 paths вне main. На одних signed binaries ON 8 PASS → OFF 6 functional FAIL → ON 8 PASS; SHA256 совпали |
|
||
| `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://<uniqueIdentifier>/`; если из идентификатора не собрать 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 отдельно.
|
||
|
||
|
||
## Перенос в PR #7379 поверх #7376 — 16 октября 2026
|
||
|
||
### Git и состав PR
|
||
|
||
По запросу до переноса сохранён checkpoint `367759cbc17d717b05e53ab30792c077ff103a8b` в `alex/cpm-fix-p1-p2` (предыдущий checkpoint `dff29dfb1e` также сохранён). Старый чистый worktree `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-fix-init-loss` удалён штатно; локальные ignored settings сохранены в `/private/tmp/cpm-pr7379-port-20261016/retired-worktree-local-settings.tar.gz`. Research worktree `cpm-e2e-init` и его незакоммиченные данные не менялись.
|
||
|
||
Рабочая директория остаётся `/Users/admin/DuckDuckGo/apple-browsers.git/alex/cpm-fix-p1-p2`, но её текущая ветка теперь **`alex/cpm-fix-init-loss`**. Финальный HEAD **`aec5be9a834e97984d4cbbee01a207548222427c`**, ровно **3 коммита / 32 изменённых файла** поверх **#7376**, base branch `alex/cpm-messaging-readiness-telemetry`, base SHA `713d67419a776b235b66210b1c594ce39da6fe03`:
|
||
|
||
1. `05b499e355` — P1/P2, ожидание нового install/upgrade context и необходимые app/unit tests.
|
||
2. `c661f805b1` — late GET document recovery, независимый flag, пять fix pixels, регрессии.
|
||
3. `aec5be9a83` — маркировка local-only fault-injection suite/scheme/README как `to-be-disabled before merge`.
|
||
|
||
[PR #7379](https://github.com/duckduckgo/apple-browsers/pull/7379) обновлён разрешённым `force-with-lease` с точным ожидаемым старым head `7ad771e227bbd68348ed7b719684783575dd7c9f`; новый head/base проверены через GitHub. Старый head и промежуточная история сохранены в backup branch `alex/cpm-fix-init-loss-before-force-20261016` (`70869f5c69`). Зависимость от #7378/research SPM harness убрана из diff; episode contract #7376 сохранён. Промежуточный перенос на прежнюю базу с 167 unit tests — историческая попытка, окончательный результат относится к новой базе и 179 tests.
|
||
|
||
PR title/description/Testing Steps переписаны под окончательный код. **Коммитить или переключать test-only/fix commits для проверки не требуется.** Удалены старые инструкции и placeholder `GREEN_RUN_URL`; используется одна подписанная сборка с переключением production flags в test fixture.
|
||
|
||
### Feature flags
|
||
|
||
| Механизм | Remote flag | Default |
|
||
|---|---|---|
|
||
| Install/upgrade readiness + P1 navigation wake wait | `webExtensions.stateRestorationGate` | Enabled, существующий |
|
||
| P2 background-only error-6 retry | `webExtensions.cpmMessagingHangRecovery` | Enabled, существующий |
|
||
| Late first-document GET reload | `webExtensions.cpmLateBackgroundDocumentRecovery` | **Disabled**, новый независимый |
|
||
|
||
Флаги явно задаются fixture; ENV `CPM_APP_INTEGRATION_DISABLE_P1_P2_FIXES=1` и `CPM_APP_INTEGRATION_DISABLE_LATE_RECOVERY=1` выбирают соответствующие negative controls без изменения исходников или binary. В двух over-budget controls отключается late recovery при сохранённом 5 s navigation gate, чтобы изолировать новый механизм. Remote configuration не изменялась.
|
||
|
||
|
||
### Финальная проверка запушенного head `aec5be9a83`
|
||
|
||
Новый прогон на базе #7376, macOS **26.7.1**, Xcode **27.1**, нормальная подпись **Apple Development: Alexey Martemyanov (9SAT8X9S84)** / team `HKE973VLUW`. Проверены identity подписи app и strict signatures app debug dylib / XCTest bundle; это не заявление о distribution signing/notarization или полной deep-проверке вложенных продуктов.
|
||
|
||
| Проверка | Результат |
|
||
|---|---|
|
||
| Все восемь методов dedicated app suite, соответствующие flags ON | **8/8 PASS**, 0 failures/skips/expected failures |
|
||
| Шесть recovery controls, соответствующие flags OFF, те же бинарники | **0/6 PASS, 6 функциональных FAIL**, 0 skips/expected failures, без teardown/deallocation errors |
|
||
| Targeted shared suites | **179 PASS**, 0 failures/skips (13 diagnostics + 9 recovery + 78 monitor + 31 lifecycle + 48 manager) |
|
||
| Strict lint относительно новой PR base | **27 Swift files**, 0 violations |
|
||
| Pixel schemas/formatting + wide-event consistency/immutability | **macOS и iOS PASS** |
|
||
| Source/binary integrity | Все **32 source hashes и 2 app/test binary hashes** неизменны; git status чистый |
|
||
|
||
Положительный набор: P1 slow idle wake, P2 реальная WK error 6 с background-only retry и ответом исходному документу без навигации, install, upgrade при coexistence contexts, late install, late upgrade, edited-document veto и PixelKitMock adapter. При 8000 ms startup в новых late install/upgrade runs первый документ завершился за **5.159 / 5.093 s**, затем ровно один automatic reload (итого два finish), настоящий page `initResp` и native dashboard, URL/context сохранены. Navigation cap остаётся **5 s**. Отдельные десять stress repetitions и более ранние iOS module/flags builds относятся к предыдущему checkpoint/проверке, не объявляются новым stress/iOS runtime run на этом head.
|
||
|
||
Красные controls на той же сборке: install/upgrade теряют первый native/page ответ; P1 получает response timeout; P2 достигает реальной error 6, но без flag не получает same-document recovery; оба over-budget случая не получают automatic document recovery. Это намеренные функциональные FAIL, не инфраструктурные crashes и не `XCTExpectFailure`. У controls и positive runs отдельные `.xcresult` / summaries / JSON attachments.
|
||
|
||
Local app suite, setup и dedicated scheme/README помечены **`to-be-disabled before merge`**. Они уже требуют `CPM_APP_INTEGRATION=1` и пропускаются обычным CI без opt-in. Сценарии используют GUI-host и 45 s SIGSTOP только собственного background PID; deterministic unit regressions оставлены включёнными. General macOS UI Tests scheme локально не запускалась из-за её profile-clearing preaction.
|
||
|
||
Артефакты нового прогона: **`/private/tmp/cpm-pr7379-final-20261016/validation-summary.json`**, рядом `enabled.xcresult`, `disabled.xcresult`, JSON evidence, logs, signature/hash snapshots, `pr-after.json`, финальный PR body и CI snapshots. Bundled extension ZIP неизменён относительно base/checkpoint: SHA256 **`ff98a2b0c07819b2ee5ee6de67d6dd8d28696363c08c9fbc2c32255e9d9efa94`**; package pin side effect убран. Прежние red runs и расследовательские данные сохранены.
|
||
|
||
Новый remote CI после push запущен; локальный PASS не выдаётся за завершённый зелёный GitHub CI. Контракт fix pixels остаётся прежним: `background_ready` — callback readiness, `response_received` — dashboard correlation после reload; page ACK проверяет app assertion отдельно. Default-disabled late flag не включался в remote config. Natural production attribution, unreproduced P3, прежние P5 app ограничения и реальный iOS suspension/resume этой работой не закрыты.
|
||
|
||
### GitHub checks после push
|
||
|
||
Финальная сверка CI для `aec5be9a83`: основные jobs ещё IN_PROGRESS/QUEUED; **Danger — FAILURE**, точная причина — новые pixels требуют label **`privacy review required`**, который создаёт privacy-review task в Asana. Label в этой работе не менялся. Дополнительно Danger предупреждает об отсутствующем Asana Feature Flags project task link для `cpmLateBackgroundDocumentRecovery` в macOS/iOS enum comments и размере PR; это warnings, не причина функциональных app failures. Значения flags и локальные ON/OFF результаты выше подтверждены независимо. Evidence: `/private/tmp/cpm-pr7379-final-20261016/danger-comment.md`, `ci-final.json`.
|
||
|
||
|
||
## Вынос app harness из PR #7379 — 16 октября 2026
|
||
|
||
По запросу весь local-only app harness вынесен в отдельную review-only ветку **`codex/cpm-fix-init-loss-integration-tests`**, основанную непосредственно на очищенном **`alex/cpm-fix-init-loss`**. Тестовая ветка не предназначена для merge в main, отдельный PR на неё не создавался.
|
||
|
||
- Production PR #7379: head **`da126120a94b625cf1d9705302c29099b4dbc62d`**, база #7376 / `713d67419a`, **2 коммита, 21 файл**. `7dbe2a25bb` — P1/P2/install/upgrade; `da126120a9` — late recovery + flags/pixels. Все 21 production/unit/schema файла побайтово совпадают с прежним `aec5be9a83`; поведение и defaults не менялись.
|
||
- Harness branch: head **`6e4e39e67c64907127bd17197a932b0117a4cb09`**, parent ровно production head `da126120a9`, **один дополнительный commit, 11 test-only paths**. Это пять Swift app-test files + README, CPM scheme и PBX registrations, DEBUG manager factory/config storage/loopback navigation hooks в AppDelegate, ConfigurationStore, Tab.
|
||
- Все app hooks и Xcode project changes исключены не только из финального diff PR, но и из его двух новых коммитов. 18 новых deterministic shared unit methods остаются в production PR; восемь app methods — только в harness branch.
|
||
- В harness комментариях прежнее `to-be-disabled before merge` заменено на ясное review-only назначение: эта ветка не идёт в main. Explicit scheme opt-in сохранён. Старые checkpoints и исходная история сохранены; дополнительный backup `codex/cpm-fix-init-loss-before-harness-split-20261016` -> `aec5be9a83`.
|
||
- Reviewer instructions в PR: unit suites на PR branch; `git fetch origin`, `git switch --detach origin/codex/cpm-fix-init-loss-integration-tests`; открыть workspace, выбрать **CPM Integration Tests / My Mac**, **⌘U**, ожидать 8 PASS / 0 skips; затем вернуться `git switch --detach origin/alex/cpm-fix-init-loss`. Production description и пользовательские правки сохранены. Исторические intentional-red controls не возвращены в reviewer steps.
|
||
|
||
Артефакты разделения: `/private/tmp/cpm-harness-split-20261016/`: manifest с production/harness paths и исходными SHA256, раздельные patches, build/unit/lint/signing logs и snapshots PR description. Точные окончательные результаты новой проверки и remote SHA — ниже.
|
||
|
||
|
||
### Проверка после разделения
|
||
|
||
- Production `da126120a9`: обычная **macOS Browser Debug app build PASS** без harness; нормальная Apple Development подпись, team `HKE973VLUW`; strict app dylib verification PASS.
|
||
- Production shared regressions: **179/179 PASS**, 0 failures/skips; 18 новых unit methods сохранены в PR.
|
||
- Review-only harness `6e4e39e67c`: полный **`xcodebuild test`** (эквивалент Product → Test), **8/8 PASS**, 0 failures/skips/expected failures, отдельная подписанная app/XCTest сборка. Все 32 source hashes неизменны во время run; strict dylib/XCTest signature checks PASS.
|
||
- Strict lint: **19 production Swift files / 0 violations**, **8 harness Swift files / 0 violations**. PBX syntax и scheme XML валидны; каждый из пяти harness Swift files зарегистрирован ровно по одному разу в обоих native Integration Tests targets. Выполнен normal signed target, не отдельный App Store runtime run.
|
||
- Подтверждены remote SHA обоих branches и GitHub PR diff: production `da126120a94b625cf1d9705302c29099b4dbc62d`, harness `6e4e39e67c64907127bd17197a932b0117a4cb09`; parent harness совпадает с production head. Production PR остаётся на базе #7376. В рабочей директории снова `alex/cpm-fix-init-loss`, git status чистый.
|
||
- `validation-summary.json`, `pr-after.json`, `remote-heads.txt`, `harness-enabled.xcresult` и JSON evidence находятся в `/private/tmp/cpm-harness-split-20261016/`. Generated Package.resolved side effect сохранён и убран из рабочей копии; production dependencies/ZIP не менялись.
|
||
- Новые шесть disabled controls после branch-only split не повторялись: предыдущие **6/6 functional FAIL** на эквивалентном production/harness code остаются историческим evidence в `/private/tmp/cpm-pr7379-final-20261016/`. Нового stress/iOS runtime run не заявляется. P3, прежние P5 app и natural-production/iOS ограничения сохраняются. Remote CI нового head — отдельная незавершённая проверка, snapshot `ci-after-split.json`.
|
||
|
||
Ранние формулировки о local-only suite внутри production PR и необходимости disable перед merge описывают прежнюю структуру. В актуальном PR этих файлов и hooks нет; review-only branch целиком остаётся вне main.
|
||
|
||
|
||
## Возврат проверки production feature flags OFF → failures — 16 октября 2026
|
||
|
||
При упрощении reviewer instructions ошибочно удалён необходимый negative-control шаг. В PR #7379 восстановлен полный **ON → OFF → ON** workflow с конкретными Xcode schemes; остальные пользовательские правки сохранены.
|
||
|
||
В review-only branch `codex/cpm-fix-init-loss-integration-tests` добавлен commit **`543e37164b369c8da8b6734e500af4a203944943`**, только два файла: новый shared scheme **CPM Integration Tests - Fixes Disabled** и README с точными cases/flags/ожидаемыми failures. Эта ветка теперь содержит **2 test-only commits / 12 paths** сверх production `da126120a9`; в main она не идёт. Production PR остаётся **2 commits / 21 files**, его head/code/unit regressions не менялись.
|
||
|
||
- **CPM Integration Tests** → 8 tests должны PASS.
|
||
- **CPM Integration Tests - Fixes Disabled** → автоматически выбирает ровно 6 recovery methods и задаёт обе fixture ENV variables `CPM_APP_INTEGRATION_DISABLE_P1_P2_FIXES=1` / `CPM_APP_INTEGRATION_DISABLE_LATE_RECOVERY=1`. Ожидаются **6 functional FAIL / 0 PASS / 0 skips**.
|
||
- Снова **CPM Integration Tests** → все 8 должны PASS.
|
||
|
||
Ручное редактирование ENV или выбор отдельных tests reviewer больше не нужны. OFF scheme через fixture отключает реальные production flags: `webExtensions.stateRestorationGate` для install/upgrade/slow wake, `webExtensions.cpmMessagingHangRecovery` для error-6 retry, `webExtensions.cpmLateBackgroundDocumentRecovery` для late GET recovery. Late recovery выключен и в ранних controls, чтобы не маскировать их; в двух over-budget controls navigation gate сохраняется ON.
|
||
|
||
Ожидаемые причины FAIL: потерянный native/page first response после install/upgrade; CPM timeout после slow wake; timeout same-document recovery после реальной WK error 6; timeout automatic first-document recovery в late install/upgrade. Setup failures, host crashes или skips не подтверждают работу флага. Полные test names и mapping находятся в README тестовой ветки. В PR сохранены checkout/test/return commands и добавлен возврат к ON scheme.
|
||
|
||
Артефакты этой проверки: `/private/tmp/cpm-flag-controls-20261016/`; compiler/test Swift sources и production ref не менялись. Предыдущие 179 unit PASS / production build PASS остаются предыдущей проверкой, не объявляются новым unit/build run.
|
||
|
||
|
||
### Финальная проверка ON → OFF → ON
|
||
|
||
Baseline normal scheme на `6e4e39e67c` ранее дал **8/8 PASS**. После добавления только OFF scheme/README на `543e37164b` выполнены оба shared schemes через `xcodebuild test-without-building -workspace DuckDuckGo.xcworkspace -scheme …` на тех же подписанных app/XCTest binaries:
|
||
|
||
| Run | Результат |
|
||
|---|---|
|
||
| CPM Integration Tests - Fixes Disabled | **6 failed / 0 passed / 0 skipped / 0 expected failures**, exit 65 |
|
||
| CPM Integration Tests после OFF | **8 passed / 0 failed / 0 skipped / 0 expected failures**, exit 0 |
|
||
|
||
У OFF все шесть failure records сопоставлены точным test names и ожидаемым функциональным причинам; никаких deallocation/setup failures или подмены через `XCTExpectFailure`. Возврат ON восстанавливает PASS для прежних P1/P2/install/upgrade, обоих late cases, edit veto и pixel adapter. SHA256 app debug dylib и XCTest binary неизменны до/после обоих runs, Swift sources не менялись относительно baseline harness. Remote config и production defaults не редактировались.
|
||
|
||
Artifacts: **`/private/tmp/cpm-flag-controls-20261016/validation-summary.json`**, `off.xcresult`, `on.xcresult`, summaries, exported JSON evidence, `binary-hashes-before.json`, `pr-after.json`, source/branch verification. Это новая runtime-проверка переключения flags, без нового production build/unit run. Reviewer теперь выбирает два готовых schemes: не редактирует ENV вручную, не выбирает шесть methods и не переключает commits. Production PR и прежние P3/P5/natural-production/iOS ограничения сохраняются.
|
||
|
||
|
||
## 16 октября 2026 — исправления замечаний review #7379
|
||
|
||
Production head: **`08866c7eda9b1395f7c0baf9887c827bd3f59ec5`**, branch **`alex/cpm-fix-init-loss`**, PR [#7379](https://github.com/duckduckgo/apple-browsers/pull/7379), база **#7376 / `alex/cpm-messaging-readiness-telemetry` / `713d67419a`**. Три commits / 21 changed paths. Новый commit **Share CPM startup deadline across navigations** изменяет 8 существующих Swift files; app fixture/hooks в production diff не возвращены. Production и review-only branch обновлены обычным push без переписывания истории.
|
||
|
||
### Что проверено и исправлено
|
||
|
||
- [Замечание о неотменяемом `Task.value`](https://github.com/duckduckgo/apple-browsers/pull/7379#discussion_r4296231857): исходное замечание уже было закрыто реализацией cancellable AsyncStream bridge и regression `testBackgroundWakeWaitIsBoundedWhenWebKitIgnoresCancellation`. Проверено повторно на текущем коде: timeout освобождает navigation, даже когда реальный startup task не реагирует на cancellation; физический WebKit startup не отменяется.
|
||
- [Повторные пять секунд на каждом navigation](https://github.com/duckduckgo/apple-browsers/pull/7379#discussion_r4296231864): замечание оказалось актуальным по поведению, несмотря на устаревшее имя файла в тексте. Раньше каждый waiter имел отдельный deadline. Теперь manager возвращает стабильный `Task` текущего startup, а coordinator хранит один bounded wait для его identity. Оба уже подготовленных waiter и последующие navigation после timeout используют завершившийся wait и не платят ещё по 5 секунд.
|
||
- Новый startup/replacement получает собственный deadline. `cancelAll()` отменяет только navigation wait и очищает его cache. Startup completion очищается при matching uninstall/unload/reload/error-6 retry или замене context; поздний callback старого task не может очистить текущий startup. Timeout сам по себе не отменяет background: поздний успешный callback по-прежнему может запустить отдельный late-document recovery.
|
||
- В Mac `PrivacyDashboardTabExtension` и iOS `TabViewController` вызов provider ограничен main-frame HTTP(S), поскольку получение task теперь сразу запускает подготовку background. Iframes и другие URL schemes не начинают этот startup через navigation gate.
|
||
- Добавлены три CI unit regressions: `testTimedOutBackgroundStartupDoesNotDelayLaterNavigations` (включая waiter, подготовленный до истечения deadline), `testNewBackgroundStartupCanWaitAfterEarlierStartupTimesOut`, `testCancelAllReleasesPendingBackgroundNavigation`. Test double сохраняет identity pending startup; зависший callback моделируется неотменяемым task, затем continuation явно освобождается при cleanup.
|
||
|
||
### Feature flag tracking и review status
|
||
|
||
Default/config flags не менялись: `stateRestorationGate` и `cpmMessagingHangRecovery` сохраняют прежние default; отдельный `cpmLateBackgroundDocumentRecovery` остаётся **false**. Remote configuration не редактировалась. Для нового flag добавлены реальные per-platform задачи в **Apple Feature Flags Registry** и ссылки непосредственно над enum case:
|
||
- [macOS tracking task](https://app.asana.com/1/137249556945/project/1211834678943996/task/1219588497257482).
|
||
- [iOS tracking task](https://app.asana.com/1/137249556945/project/1211834678943996/task/1219588320073672).
|
||
|
||
Оба review threads проверены и resolved; открытых inline threads после readback — 0. Danger на `08866c7eda`: **0 failures / 1 warning**, только advisory о размере PR (1090 added lines); предупреждение об отсутствующих flag tracking links исчезло. Отдельная Privacy Review task уже существовала, дубликат не создавался.
|
||
|
||
### Проверки и отдельная test branch
|
||
|
||
- До исправления две новые проверки дали **2/2 functional FAIL**, после успешной компиляции: повторный navigation wait и освобождение через `cancelAll`.
|
||
- После исправления пять выбранных shared suites: **182 PASS / 0 failures** (13 diagnostics + 9 readiness recovery + 78 monitor + 34 lifecycle + 48 manager). Относительно базы PR теперь 21 новый shared unit method.
|
||
- Strict SwiftLint: **8 changed Swift files / 0 violations**.
|
||
- Production **macOS Browser Debug**, обычная **Apple Development signing**, build PASS. Подпись app debug dylib проверена. **Shared WebExtensions module для iOS Simulator build PASS**; это не full iOS app build и не runtime validation iOS suspension/resume.
|
||
- Review-only **`codex/cpm-fix-init-loss-integration-tests` / `3f4d7555113d547797b45ff6b2c7b883efedd520`** синхронизирована merge нового production head; отдельный doc-only commit актуализировал README (7 новых coordinator regressions, 21 shared regression суммарно). Над production — два прежних harness commits + sync merge + README correction; diff всё ещё ровно **12 test-only paths**, 8 app methods и два shared schemes. Эта ветка не идёт в main.
|
||
- Description #7379 изменена только в двух местах для shared deadline и риска зависшего callback. Пользовательские правки и понятные Xcode steps **ON → OFF → ON**, названия schemes и ожидаемые 8 PASS / 6 FAIL / 8 PASS сохранены. Коммитить или редактировать Swift для reviewer run не нужно.
|
||
|
||
|
||
Финальный runtime cycle на одном наборе подписанных app/XCTest binaries:
|
||
| Scheme / запуск | Passed | Failed | Skipped | Результат |
|
||
|---|---:|---:|---:|---|
|
||
| CPM Integration Tests — ON | 8 | 0 | 0 | PASS |
|
||
| CPM Integration Tests - Fixes Disabled — OFF | 0 | 6 | 0 | Все шесть functional failures совпали с ожидаемыми симптомами |
|
||
| CPM Integration Tests — ON повторно, без сборки | 8 | 0 | 0 | PASS |
|
||
|
||
OFF действительно отключает production flag для соответствующего сценария: install/upgrade/slow-wake потеряли первый ответ, P2 не дождался same-document recovery после фактического error 6, late install/upgrade не дождались automatic first-document recovery. `expectedFailures` в XCTest — 0: OFF остаётся явным красным контрольным запуском, а не ignored CI failure. Fixture setup/host crash/skips не засчитаны как воспроизведение. SHA256 app dylib и XCTest executable совпали до OFF, после OFF и после повторного ON. Бинарники собраны на sync head `d0922bb277`; финальный test-branch commit `3f4d755511` меняет только README.
|
||
|
||
CI snapshot `08866c7eda` на 16:19 UTC: 31 SUCCESS, 12 SKIPPED, 3 QUEUED, 5 IN_PROGRESS; failures отсутствуют. SwiftLint, macOS/iOS pixel validation и Danger — SUCCESS. Полный CI ещё не объявлен зелёным: оставшиеся unit/UI/release jobs были pending/running.
|
||
|
||
Артефакты этого прогона: **`/private/tmp/cpm-pr7379-review-fixes-20261016/`**. `red-tests.log` — red-before-fix; `unit-final.log`, `lint-final.log`, `production-build-final.log`, `ios-module.log` — final validation. Отдельные `app-on*`, `app-off*`, `app-on-restored*` содержат xcresult summaries и exported evidence. SHA256 app/XCTest сохранены до OFF и после каждого последующего run; review/PR/Danger/CI snapshots и `validation-summary.json` находятся там же.
|
||
|
||
Это не новая декларация воспроизведения всех production причин. Ограничения прежнего исследования P3/P5 и iOS runtime сохраняются. Точные два новых navigation/cancellation failures сначала воспроизведены deterministic shared tests; восемь app methods проверяют реальные WebKit/app пути существующих install/upgrade, P1/P2 и late recovery сценариев. В ON evidence P2 снова записан фактический **`WKWebExtensionContextErrorDomain:6`**; late install/upgrade завершили первый navigation примерно за **5.18 / 5.10 секунды**, затем получили отдельный recovery reload, без увеличения navigation timeout.
|
||
|
||
## 19 октября 2026 — полный повторный review после точечных правок
|
||
|
||
**Объём.** Повторно прочитан весь production diff: локальный `alex/cpm-fix-init-loss / 57081973629e1ad43b9312fee479d27e34232abf` против `alex/cpm-messaging-readiness-telemetry / 713d67419a` (#7376), 5 commits / 22 changed paths; manager/lifecycle, native callers обеих платформ, recovery/health monitor, flags, pixel adapters/definitions и shared tests. Опубликованный #7379 пока остаётся на `08866c7eda`: 3 commits / 21 files; его CI snapshot — 48 SUCCESS, 21 SKIPPED, без failures. Это не CI непушенных исправлений.
|
||
|
||
**Сохранённые исправления предыдущего review.** Launch gate переносит уже израсходованный deadline на тот же pending startup; следующий navigation не платит ещё пять секунд. Safety preflight отделён от синхронного reload; attribution вооружается после проверок и сопоставляется с конкретной WKNavigation из принятого automatic reload, включая синхронный didStart. После await повторно проверяются flag, app state, document identity и 30-секундный срок. Пять новых regression methods увеличили выбранные suites со 182 до 187 PASS (13 diagnostics + 13 readiness recovery + 78 monitor + 35 lifecycle + 48 manager). Оба production builds macOS/iOS Simulator и strict lint десяти изменённых Swift files прошли на этом коде; logs сохранены в `/private/tmp/cpm-pr7379-review-selected-fixes-20261016/`.
|
||
|
||
**Формы — явно отложено пользователем.** После сообщения «пока не парься на счёт форм, оставить как есть оно за фича флагом» document-start tracker, новые conservative veto и четыре form-safety tests убраны отдельным коммитом `5708197362`. Исходный JavaScript predicate побайтно совпадает с `08866c7eda`; новая instrumentation не установлена ни на macOS, ни на iOS. Известное ограничение для controlled inputs с изменяемым defaultValue и Shadow DOM остаётся нерешённым; результаты предыдущего реального WKWebView воспроизведения не отменены. Независимый `webExtensions.cpmLateBackgroundDocumentRecovery` по-прежнему default disabled на обеих платформах. Форма не заявляется исправленной.
|
||
|
||
**Новый P2 — удаление extension не инвалидирует поздний reload.** `WebExtensionManager.uninstallExtension` после реальной выгрузки вызывает `clearEmbeddedBackgroundStartup(identifier:)`, но не инвалидирует document recovery. Если успешный startup уже запустил async safety preflight и в это время отключить/удалить активный embedded context, late-recovery flag может остаться включённым. Document identity сохранена; после завершения preflight вызывается reload closure для уже отключённого CPM. Изолированный `testFreshReviewUninstallInvalidatesPendingLateRecovery` использовал production manager и default loader: context действительно исчез из controller, installation record удалён, затем reload callback вызван **1 раз вместо 0**. Следует инвалидировать recovery удаляемого активного context; удаление старой версии при upgrade не должно отменять recovery её замены. Production-код в ходе повторного review не исправлялся.
|
||
|
||
**Проверка finding.** Production sources скопированы побайтно (96 файлов) в изолированный package; добавлен только временный probe. Финальный запуск: **188 методов выполнены, 187 PASS / 1 assertion FAIL**, без падения host. Первый запуск временного probe упал в WebKit dealloc искусственно созданной WKNavigation (CFRetain NULL); этот тестовый setup исправлен, и затем получен именно функциональный failure callback count. Артефакты: `/private/tmp/cpm-pr7379-full-review-20261019/selected-tests-final.log`, `review-results.json`, `source-copy.json`. В production tests этот probe не добавлен.
|
||
|
||
**Harness и публикация.** Локальный `codex/cpm-fix-init-loss-integration-tests / 4308751d593bcc63abfb4baaced83e35bd19255f` обычным merge синхронизирован с `5708197362`; README исправлен на 26 новых shared regressions и прежнюю защиту форм. Над production по-прежнему ровно 12 harness-only paths, 8 app methods и два ON/OFF schemes; они не идут в main. Удалён только собственный generated Package.resolved diff от предыдущего test run. Рабочая production branch чистая. Production и harness commits пока не push; PR body не перезаписан. Старый ON 8 PASS → OFF 6 functional FAIL → ON 8 PASS относится к опубликованному `08866c7eda` / test branch `3f4d755511`; на новом локальном head этот app cycle ещё не повторён.
|
||
|
||
**Замечание о стиле URL check.** Для HTTP(S) подходит `URL.NavigationalScheme.hypertextSchemes` из Common; initializer сохраняет регистр, поэтому lowercased необходимо сохранить. Это ответ на вопрос, source replacement не выполнялся.
|
||
|
||
## 19 октября 2026 — исправление uninstall/preflight race и push
|
||
|
||
Production **`e970e8c90eca76e55217f75c13122735a72664c5`**, branch `alex/cpm-fix-init-loss`, [#7379](https://github.com/duckduckgo/apple-browsers/pull/7379), база #7376 / `713d67419a`. Опубликованы все три локальных commits после `08866c7eda`; итоговый diff — 6 commits / 22 paths. Push обычный fast-forward, без переписывания истории. Review-only **`codex/cpm-fix-init-loss-integration-tests / e380cf98f7e9709085ce76289211f3e1e43c8395`** синхронизирована обычным merge, над production по-прежнему ровно 12 harness-only paths; они не идут в main.
|
||
|
||
**Исправление.** До unload запоминается, удаляется ли текущий выбранный embedded context. После успешного unload его background document recovery инвалидируется, включая уже ожидающую async safety preflight. Неудачный unload выходит до invalidation; удаление старого context при upgrade не отменяет recovery replacement. Добавлены постоянные shared regressions `testUninstallingActiveEmbeddedContextCancelsPendingDocumentRecovery`, `testUninstallingOldEmbeddedContextPreservesReplacementDocumentRecovery` и `testFailedUninstallPreservesActiveDocumentRecovery`. Первые два используют production loader с настоящими WKWebExtensionContext; третий моделирует ошибку unload и проверяет сохранение live context. HTTP(S)-checks в native navigation callers macOS/iOS переведены на `URL.NavigationalScheme.hypertextSchemes` с сохранением lowercased.
|
||
|
||
**Проверки.** До fix новые три tests: **2 PASS / 1 functional FAIL** — активное удаление оставляло выполняющийся reload callback. После fix пять выбранных shared suites: **190 PASS / 0 failures** (13 background diagnostics + 13 readiness recovery + 78 monitor + 35 lifecycle + 51 manager), всего 29 новых shared regression methods относительно базы PR. Unit run выполнен в изолированной копии 96 побайтно совпадающих production source files. Полные macOS и iOS Simulator builds — PASS; macOS app подписан Apple Development, team HKE973VLUW, strict verification всего app bundle PASS. Strict lint всех 11 непушенных Swift files — 0 violations. Схемы/имена/параметры/frequency pixels не менялись.
|
||
|
||
**Формы и flags.** По явному указанию пользователя новая защита форм не возвращена. Original JavaScript predicate побайтно совпадает с `08866c7eda`; known limitation controlled/defaultValue и Shadow DOM остаётся. Независимый `webExtensions.cpmLateBackgroundDocumentRecovery` всё ещё default disabled на обеих платформах. Deadline launch→navigation и attribution по конкретной WKNavigation сохранены из предыдущих локальных исправлений и теперь опубликованы.
|
||
|
||
**Документация и app validation.** PR body точечно уточняет общий launch/navigation deadline, invalidation при удалении активного context и точную WKNavigation attribution. Пользовательские правки, testing branch link и ON 8 PASS → OFF 6 functional FAIL → ON 8 PASS instructions сохранены. README harness обновлён: 8 новых manager regressions / 29 новых shared methods. На новом head первый ON: 8 PASS; OFF: ровно 6 functional FAIL, 0 PASS/skips/expected failures. Проверены реальные причины отказа: install/upgrade/slow-wake first response, P2 same-document recovery timeout и late install/upgrade automatic-reload timeout. App/XCTest SHA256 до и после OFF совпали. Повторный ON завершился 8 PASS; SHA256 до OFF, после OFF и после повторного ON совпали. CI опубликованного e970e8c90e: 48 SUCCESS / 21 SKIPPED, failures/pending нет. Старые app результаты не перенесены на новый head.
|
||
|
||
Артефакты: `/private/tmp/cpm-pr7379-uninstall-fix-20261019/` (`unit-green.log`, `unit-evidence.json`, `lint.log`, `mac-build.log`, `ios-build.log`, signature logs, `heads.json`, PR snapshots); red control — `/private/tmp/cpm-pr7379-full-review-20261019/uninstall-red.log`.
|
||
|
||
### Итог app validation и CI на опубликованном head
|
||
|
||
Production `e970e8c90e`, review-only harness `e380cf98f7`. Все три запуска выполнены на одних Apple Development-signed app/XCTest binaries; OFF и повторный ON — `test-without-building`.
|
||
|
||
| Запуск | Passed | Failed | Skipped | Expected failures |
|
||
|---|---:|---:|---:|---:|
|
||
| CPM Integration Tests — первый ON | 8 | 0 | 0 | 0 |
|
||
| CPM Integration Tests - Fixes Disabled | 0 | 6 | 0 | 0 |
|
||
| CPM Integration Tests — повторный ON | 8 | 0 | 0 | 0 |
|
||
|
||
OFF failure reasons проверены по xcresult: install/upgrade/slow-wake потеряли первый ответ; P2 дождался реального WebKit error 6, но не получил same-document recovery; late install/upgrade не дождались automatic first-document reload. Это шесть намеренно красных функциональных контролей, без host crashes/setup failures/skips. Все 8 ON assertions восстановились после включения flags. SHA256 app debug dylib и XCTest executable совпали до OFF, после OFF и после повторного ON.
|
||
|
||
Из exported ON evidence: P2 записал **actualBackgroundLoadError=[6]** и затем прошёл проверки ответа оригинальному document без context replacement или ручной навигации; последующие existing-tab/new-tab/new-window assertions также PASS. При 8-секундном startup первая navigation завершилась за **5.167 s (install)** / **5.089 s (upgrade)**, затем был ровно один automatic GET reload (finishCount=2), реальный page initResp и оба этапа pixel `reload_started` / `response_received`. Эти измерения включают локальную загрузку документа после прежнего 5-секундного gate; его лимит не увеличен.
|
||
|
||
CI опубликованного **`e970e8c90e` завершён: 48 SUCCESS / 21 SKIPPED**, failures/pending нет. GitHub readback подтвердил оба remote heads, прежнюю базу #7376 / `713d67419a` и точное сохранённое PR body. Production worktree возвращён на `alex/cpm-fix-init-loss`, tracked/untracked diff пуст; обе ветки up to date. Новых production изменений после validation нет. Формы оставлены как согласовано; independent late-recovery flag default disabled.
|
||
|
||
Артефакты `/private/tmp/cpm-pr7379-uninstall-fix-20261019/`: `validation-summary.json`, три `app-*-summary.json` и `.xcresult`, exported attachment folders, `app-on-evidence-summary.json`, три `hashes-*.json`, `pr-final.json`, `remote-heads.txt`. Прежние промежуточные snapshots выше сохранены как история.
|