220 lines
33 KiB
Markdown
Executable File
220 lines
33 KiB
Markdown
Executable File
---
|
||
title: CPM Web Extension — карта кода по worktree и строкам
|
||
date: '2026-10-09'
|
||
tags:
|
||
- work
|
||
- cpm
|
||
- webkit
|
||
---
|
||
|
||
# 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://<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.
|