373 KiB
Executable File
title, date, tags, updated
| title | date | tags | updated | |||
|---|---|---|---|---|---|---|
| CPM Web Extension — единый статус (PR, данные, гипотезы, телеметрия) | 2026-10-15 |
|
2026-10-30 |
CPM Web Extension: единый статус
Error-6 restart — PR 7730
Сверено 30 октября 2026: #7730 снова OPEN, не draft, MERGEABLE, head cf668e29382df79f1a94961e39067beac85c6128, ветка alex/cpm-error6-restart. Есть label privacy review required. Не merged и не shipped; локальный review и MERGEABLE не заменяют privacy review, CI или rollout. Историческое закрытие draft 28 октября больше не является текущим статусом.
Что делает фикс. Для новой error 6 текущего embedded context manager запоминает исходный failed background view. Если к выполнению recovery WebKit всё ещё держит именно его, выполняется full embedded-context restart с trigger cpmBackgroundLoadFailure; при освобождённом view остаётся обычный P2 background-only retry. Новый replacement view, даже ещё загружающийся, не перезапускается. Дополнительного page reload/re-init фикс не добавляет; в контролируемом harness исходный документ получает настоящий initResp после restart.
Background/wake и очередь. Попытка хранит context/view слабо. Новая ошибка заменяет pending-попытку также в foreground, если прежняя recovery ещё стоит в очереди. На iOS отложенная recovery получает возможность на didBecomeActive, на macOS — на didWake; повторные события не воспроизводят завершённую попытку. Cooldown 60 s отсчитывается от фактически применённой recovery: ошибка сохраняется до истечения срока и запускается таймером без новой навигации/notification. Если срок истёк в background, попытка ждёт foreground. Перед выполнением снова проверяются актуальность context/view, runtime flags и concurrent-recovery guard. Исторические равные errors не запускают restart; история сбрасывается при создании нового background view, чтобы задержанные clear/add notifications не скрыли новую одинаковую error 6.
Flags. webExtensions.cpmBackgroundLoadRecovery разрешает P2/recovery целиком; отдельный cpmBackgroundLoadRestart — только retained-view restart, code default ON на iOS/macOS, default OFF в manager API. OFF у restart сохраняет plain retry. M4 использует собственный cpmMessagingHangRecovery. Registry task; задача PR. Live remote config этой работой не менялась и не проверялась.
Телеметрия реализована. Новый web_extension_cpm_background_load_restart имеет одно имя на iOS/macOS, без _macos, частоту dailyAndCount и единственный custom parameter outcome. Stages: restart_started, background_ready; terminal outcomes: response_received, no_response_before_deadline, unobserved, superseded, reload_failed, reload_timed_out. Наблюдение отдельно от failure episode: быстрый restart может завершиться раньше первого navigation timeout. Успех требует атрибутируемого dashboard response нужного поколения; background callback — только промежуточный этап. Deadline — пять активных минут, suspension исключается, после resume остаётся минимум 30 s. Без измеренного timeout/ответа — unobserved; другой reload или загрузка новой embedded-версии завершает старое наблюдение как superseded. Повторный loaded того же identifier и другой extension type его не закрывают. Ошибка reload уже даёт reload_failed и существующий reload-error pixel с cpm_background_load_failed. URLs, tab IDs и raw errors новым pixel не отправляются. Для outcome rates использовать count, не daily: daily dedupe работает по имени и обычно оставляет первый restart_started. Отсутствие terminal row после завершения процесса не считать failure.
Проверки на текущем head. 1064 штатных WebExtensions tests PASS, strict SwiftLint и pixel-schema/format/wide-event checks обеих платформ PASS. Финальный source review: 0 существенных findings; отдельный полный прогон со 8 временными edge-case probes — 1072 PASS / 0 failures. Probes проверили burst ошибок, повторный background перед queued recovery, смену context/flags во время cooldown, upgrade после measured failure и позднее завершение прежнего reload. Временные probes удалены; постоянные manager, monitor и platform-adapter regressions входят в обычный PR CI. В unit-регрессии без cooldown timer автоматический retry действительно падает. Exact-head CI заново в этом обновлении документации не запрашивался.
Harness отдельно от main. Production worktree /Users/admin/DuckDuckGo/apple-browsers.git/cpm-error6-restart; review harness — cpm-error6-restart-harness, ветка codex/cpm-error6-restart-harness, опубликованный head 5348c7254efb23e44a4f53e5150dc95cc5edd938, синхронизирован с cf668e2938. Отличия — только 17 fixture/test Swift files + run guide, production code идентичен. Инструкции OFF/ON.
Последний реальный macOS OFF/ON выполнен на production d1a2e1de65, а не повторно на финальном cf668e2938: одинаковый binary SHA256 5369bd0105cc8c1f8257db374a631cae0a4f6146ca038e1e0aa4c75da6f2d141. OFF — ожидаемый functional FAIL: настоящая error 6, retained=true, исходный timeout, потерянный busy 0, restarts/responses=0; позднее помогает M4. ON — PASS: настоящий dashboard response и page initResp, первый ответ 0.084 s, lost=[], restarts=1, responses=1, два следующих idle/wake цикла здоровы. Более поздние queue/cooldown/upgrade-attribution изменения проверены unit/regression tests, не новым OFF/ON запуском.
Границы. Natural production trigger и доля retained-view failures неизвестны; новые production metrics не запрашивались. Контролируемый worker-start exception доказывает этот механизм, не всю iOS interruption/error-6 когорту. Физический iOS suspend/wake финальным прогоном не проверен. Старый draft использовал full reload шире; нынешний restart ограничен тем же retained failed view. Не переносить прежний отрицательный результат full-context P2 из 16 октября на этот более узкий случай как доказательство неработоспособности; он остаётся историческим ограничением, а положительный harness — отдельным контролируемым доказательством.
Артефакты: cpm-error6-verification-2026-10-30/final-review/{verification.json,tests.log}, pending-recovery/, foreground-publish/ в корне bare repo; финальный штатный run и validators — /private/tmp/cpm-upgrade-observation-tests.log, cpm-upgrade-observation-lint.log, cpm-upgrade-{iOS,macOS}-{validate-pixel-defs,check-wide-events}.log. Карта кода — cpm-web-extension-code-guide-2026-10-09#Error-6 restart — PR 7730; reviewer steps — CPM Breakage Testing Steps#Error-6 restart — PR 7730 (30 October 2026).
Текущее состояние — 30 октября 2026, 16:48 Бишкек
Upgrade-before-load: PR открыт, CI зелёный
#7767 — Upgrade CPM before loading installed extensions, ветка codex/cpm-upgrade-before-load, опубликованный head 45593bbb4e631b5daae9ed77e95f4bd760f738fa, base main 3046f67631d5655f227cd2e7e7ebf4ef60d0d90a. Актуальный main влит в ветку PR; конфликт stable-origin policy устранён с сохранением OS 26.6+ gate и pending-origin cleanup/rotation. PR OPEN, MERGEABLE, не merged и не shipped. Проверено на этом head: 43 checks SUCCESS, 15 SKIPPED, 0 failed/pending; итоговые iOS/macOS/BSK/DBP, Danger и SwiftLint — SUCCESS. mergeStateStatus=BLOCKED при зелёных checks не означает merge conflict.
Порядок запуска: при включённом cpmUpgradeBeforeLoad coordinator сначала подготавливает только CPM (.embedded), затем загружает установленные extensions, затем выполняет обычный общий sync. Предварительная фаза обновляет/устанавливает включённый CPM либо удаляет выключенный, не запускает scriptlet handling и не синхронизирует остальные bundled types. Другие contexts ждут завершения этой подготовки, но для них сохраняется собственный порядок load → sync. Scriptlet downloads начинаются после загрузки contexts; зависшая загрузка ad-block scriptlets не держит их незагруженными.
Fallback и совместимость: новая версия устанавливается до удаления старой; ошибка pre-load upgrade оставляет прежнюю установку для следующего load. Обычный post-load sync затем может повторить попытку. Если повреждённая current-version установка была пропущена pre-load, load удаляет её и общий sync восстанавливает в том же запуске. При выключенном флаге действует прежний порядок: load установленной версии → общий sync/upgrade.
Origin: переносится только для CPM при включённом upgrade-before-load, включённой stable-origin policy и отсутствии загруженного старого context. Для присвоения stable baseURL также нужны OS 26.6+ и приватный CPM manifest без web-accessible resources. Runtime upgrade или post-load retry при живом старом context использует отдельные origins, потому что установка ненадолго загружает обе версии. Ротации после background failures сохраняются; неподходящий manifest очищает унаследованный pending origin, а отключённая policy/неподдерживаемая ОС не сохраняет abandoned origin. JSON key cpmStableOriginIdentifier и installation-UUID fallback из main сохранены.
Откат: webExtensions.cpmUpgradeBeforeLoad — remote-releasable failsafe, code default ON на iOS/macOS. Feature Flag Registry. Значение фиксируется при создании manager; изменение применяется к следующему manager, обычно после перезапуска приложения. OFF возвращает старый launch order и прекращает перенос origin в upgrade. Отдельный cpmStableBaseURL по-прежнему управляет stable origin. Live remote configuration в этом обновлении не менялась и не проверялась.
Диагностика failures без нового пикселя
Используется существующий embeddedInstallError / CPM install-error pixel обеих платформ. Только ошибка CPM pre-load sync получает суффикс .before_load в error domain; code, userInfo и доступная underlying-error chain сохраняются. Обычный post-load sync передаёт исходную ошибку без суффикса. Новых success/outcome pixels, stage parameters, таймеров и launch tracking нет.
Отсутствующий bundle и несовпадение типа теперь тоже сообщаются через существующий error pixel: EmbeddedSyncError: DDGError, domain WebExtensionEmbeddedSync, codes 1 = missingBundle, 2 = typeMismatch. typeMismatch перехватывается внутри sync и не прерывает загрузку остальных типов. Ошибки WebKit нормализуются в обёртках самих WebKit API; повторный webKitSafe в telemetry helper удалён. Для изменения domain используется error as NSError.
Ранее выполненный ClickHouse-срез за 29 октября UTC, без суммирования standard и daily legs: 518 iOS / 16 macOS install-error events по всем версиям; на macOS 14 из 16 — invalid archive (WKWebExtensionErrorDomain:9), на iOS преобладали storage и invalid-archive errors. Это события ошибок, не affected-user rate. Проверка корреляции с CPM failure episodes не установила причинную связь: общих installation/session IDs нет, а почасовые totals зависят от трафика. extension_updated в истории означает успешное обновление и само по себе не связывает episode с install error. Этот срез не перезапрашивался при обновлении документации; он предшествует rollout нового domain suffix.
Проверки, производительность и границы
- После первого merge main (
f479f5deb2): 1037 package tests / 0 failures, shared iOS Simulator compilation и strict lint PASS. - На опубликованном
fafcb1ffda: 11 real-WebKit harness scenarios / 0 failures / 0 skips, включая fallback, flag OFF, disabled CPM, runtime upgrade, повторные launch upgrades и persistent profile с 2000 cookie-bearing sites. Это package harness, не full-app E2E. - После перехода на DDGError и исправления теста: 89 manager tests PASS, strict lint PASS. Тест телеметрии упал в iOS CI из-за обращения к отсутствующему
Bundle.module;00ddded2ffзаменил настоящий CPM archive минимальным ZIP fixture, создаваемым тестом. Исправленный тест реально прошёл на iOS 27 Simulator, в том числе при намеренно убранном resource bundle. В production code ради этого падения ничего не менялось. Новый файл тестов не добавлялся; сохранён один telemetry regression method. - На последнем merge
45593bbb4e— зелёный exact-head CI, как указано выше. Исторические локальные counts относятся к своим heads; это не новые прогоны на последнем merge.
Измерение 29 октября: по 20 launches на сценарий, три документа на launch. Медиана времени до ответов всех трёх документов: прежний load-before-sync — 180.9 / 173.6 / 208.3 / 205.8 ms; последний CPM-only sample — 181.7 / 172.3 / 179.5 / 178.4 ms для first install / unchanged / upgrade / upgrade + 2000 sites. Во всех 240 measured replies последней серии — 0 потерь, один context. Это отдельные harness samples; в них не видно регрессии первого/обычного запуска, но они не измеряют полный старт приложения, большой HTTP cache или production hang rate. Сырые результаты: /Users/admin/DuckDuckGo/apple-browsers.git/cpm-upgrade-before-load-results-2026-10-29/, summary.json, cpm-only-summary.json, CPM_ONLY_REVIEW.md.
Что остаётся неизвестным: production-доля failures, устраняемых новым порядком; эффект после rollout; полный iOS app startup/upgrade runtime и full-app UI этого PR локально не проверялись. iOS Simulator telemetry test не является таким app-level прогоном. Error-page fix #7752/#7753 уже merged в release branches, но macOS wake/other baseline до него может содержать false positives. iOS полезен как отдельный срез, однако его нельзя считать доказанно незагрязнённым контролем без соответствующей runtime-проверки. Сравнивать платформы, версии, ОС и rollout/flag states отдельно; новые production metrics в этом обновлении не получались.
Код и worktree: cpm-web-extension-code-guide-2026-10-09#Upgrade-before-load — PR 7767. Приоритеты и границы старого production baseline: cpm-post-release-analysis-2026-10-27#Upgrade-before-load — 30 October 2026.
Обновление 30 октября 2026 — release PR merged
iOS #7753 merged в release/ios/7.244.0, macOS #7752 — в release/macos/1.214.0. Последние публичные GitHub-релизы при проверке 30 октября — 7.243.0 / 1.213.0: merge не означает rollout или подтверждённый эффект.
Итог: фильтр error pages; stable baseURL только на ОС 26.6+ при включённом флаге; при создании контекста на неподдерживаемой ОС или с выключенным флагом сохранённый origin ротируется, а WebKit получает nil. Само переключение флага живой контекст не меняет. Ключ JSON изменён с cpmOriginIdentifier на cpmStableOriginIdentifier; миграции, автозаполнения UUID и хранения версии ОС нет. Без нового ключа используется уже сохранённый installation UUID, который далее переиспользуется; обычная ротация сохраняет замену под новым ключом.
Невосстановимая поломка после обновления ОС из-за сохранённого baseURL не доказана. В двухпроцессном baseline перезапуск восстановил messaging даже с прежним origin. Историческое утверждение «extension workers никогда не писались на диск» исправлено: upstream запретил новые записи, но этим изменением старые не удаляются.
Полный итог, проверенные тесты, merge commits и OS-разрез данных 26–28 октября: cpm-post-release-analysis-2026-10-27#Release update — 30 October 2026. Ниже остаются датированные исторические срезы; их open/shipped-статусы не заменяют это обновление.
P5 #7473 — merged 20 октября 2026, 14:17:11 Бишкек (08:17:11 UTC): merge
9bcce0264eвrelease/macos/1.213.0, финальный PR head574e9288ae. Фикс shared-pinned window close включён по умолчанию и имеет remote failsafe. Подробности реализации, review и проверок — cpm-status-2026-10-15#P5 — merged 20 октября 2026. Merge в release branch подтверждён; публикация сборки, rollout и production effect этим не установлены. Эта сверка касается #7473.
#7379 — независимый P2 flag, 20 октября 2026: production
55a42802a6, harnessa6e6835a5f; новыйwebExtensions.cpmBackgroundLoadRecoverydefault ON на iOS/macOS отделяет same-context error-6 retry отcpmMessagingHangRecovery(full-context restart). 271 selected unit PASS, 47 новых shared regression methods; четыре новых independence regressions дают 4 FAIL с прежними связанными проверками и PASS с новым flag. Signed macOS harness и полный iOS app build PASS. CI нового head: 39 SUCCESS / 13 SKIPPED / 4 IN_PROGRESS, без failures (20 октября, 14:16 UTC). Late GET reload по-прежнему default OFF; P4 detection независим. Подробности — в последнем разделе про independent P2 flag.
Сверено 20 октября 2026, 11:40–11:45 Бишкек (05:40–05:45 UTC): опубликованные heads, состояния PR, exact-head CI и исходники. Актуальны §1–9; короткие прежние обновления перенесены в §9а, подробные журналы ниже сохраняют результаты своих дат и ревизий.
#7376 закрыт без мержа 20 октября в 03:30 Бишкек (19 октября 21:30 UTC), head 5d1cb4abd2; CI зелёный и review APPROVED не означают, что изменение попало в релиз. macOS #7484 открыт на старом контракте и конфликтует с базой. Текущий состав episode report — cpm-pr0-episode-telemetry-summary-2026-10-16. Ни один из этих статусов не доказывает rollout.
Внутренний документ. Цифры ClickHouse не переносить в публичные PR/comments/issues. Каждый production cut сохраняет свои даты и знаменатели. Исторические результаты тестов относятся к указанным heads; факт merge/approval/зелёного CI сам по себе не устанавливает rollout. Старые краткие сводки и прежний план сохранены в §9а.
1. Коротко
- Телеметрия #7376 реализована и опубликована, но PR закрыт без мержа. Один
web_extension_cpm_episode, daily+count, вместо 11 отдельных failure/stuck/recovery/reload-failed пикселей.stuckразличает подтверждённость повторного сбоя и одну/несколько наблюдавшихся failing tabs;bg_eventsдля stuck содержит начальный снимок и продолжение до первого подтверждения. Все параметры, частота и удаления — в cpm-pr0-episode-telemetry-summary-2026-10-16. - #7379: head
55a42802a6содержит install/upgrade readiness, P1 wake, P2 retry и P4 detection. НовыйwebExtensions.cpmBackgroundLoadRecoverydefault ON на iOS/macOS, независим от full-context restart flag; late GET recovery default OFF. 271 selected unit PASS / 47 новых shared regression methods, 6 review threads resolved. CI нового head: 39 SUCCESS / 13 SKIPPED / 4 IN_PROGRESS, 0 failures (20 октября, 14:16 UTC); прежний head9effd97877завершился 43 SUCCESS / 17 SKIPPED. - Network recovery #7459 опубликован с отдельным
web_extension_cpm_network_process_recovery_applieddaily+count без custom parameters. Это измерение выполненного restart, не доказательство восстановления и не замена network-атрибуции каждого failure report. - P3 #7493 реализует стабильный приватный origin, CI зелёный; полный production no-background failure не воспроизведён. P5 #7473 merged 20 октября в
release/macos/1.213.0(9bcce0264e), CI зелёный на финальном head574e9288ae; это один доказанный механизм, не вся P5-когорта. Публикация/rollout ещё не подтверждены. - Полная диагностика Others и отсечение всех ложных срабатываний ещё не обеспечены. Нет page ACK/последнего подтверждённого этапа, конечного исхода изначально pending load и
network_replaced_since_viewв episode report (§6).reason=other— контекст навигации, не историческая причинная группа Others. - Production
bg_eventsдействительно терял начало. В срезе 14 октября исходныйloadотсутствует у 40.86% iOS и 61.62% macOS stuck.daily; это нижняя оценка, не точная частота всех обрезаний. Новый маркер показывает потерю, но общий бюджет может вытеснить весь initial snapshot (§6). - P1/P2, P5/tab=false и условный P6 подтверждены контролируемыми macOS tests. Это не устанавливает их долю среди реальных iOS failures. Естественные listener-metadata loss, P5/tab=true, iOS suspend/resume и occurrence main-flag transitions остаются открытыми. Все прежние отрицательные проверки и красные прогоны сохранены в журналах.
2. PR и quality gates
Сверка 20 октября 2026, 05:40–05:45 UTC. «Зелёный CI» означает success всех четырёх итоговых checks (iOS, macOS, BSK, DBP), Danger success, без failing/pending на указанном head; skipped jobs не считаются выполненными тестами. Задачи Asana из тел PR в этой сверке не читались.
- #7376,
alex/cpm-messaging-readiness-telemetry, head5d1cb4abd2→release/ios/7.243.0: CLOSED, не merged, review APPROVED, CI зелёный (65 check-run entries). Локальный worktree:alex/cpm-telemetry-pr0. Полный пакет на этом исходнике: 777 tests / 0 failures, strict lint 6 файлов / 0 violations, pixel definitions обеих платформ PASS. Тесты выполнялись в изолированной копии с актуальным dependency resolution; пользовательскийPackage.resolvedв worktree сохранён. - #7484,
alex/cpm-messaging-readiness-telemetry-macos-release,3fb13b81fe→release/macos/1.213.0: OPEN, CI зелёный (68 entries), merge conflict / dirty. Это копия более раннего #7376: ещёwas_stuck, нет four-statestuckи двухфазногоbg_events. Называть PR идентичными сейчас нельзя. - #7379,
alex/cpm-fix-init-loss,55a42802a6→alex/cpm-messaging-readiness-telemetry / da8e714b33: OPEN, CI 39 SUCCESS / 13 SKIPPED / 4 IN_PROGRESS, 0 failures (20 октября, 14:16 UTC). Добавлен independent default-enabled P2 retry flag; 271 selected unit PASS / 47 новых shared regressions. Signed macOS harness и полный local iOS app build PASS. Harnesscodex/cpm-fix-init-loss-integration-tests / a6e6835a5fсодержит production head. Старый9effd97877CI 43 SUCCESS / 17 SKIPPED сохранён как история; новый полный app ON/OFF/ON не проводился. - #7459,
alex/cpm-network-process-recovery,f41c7b7047→alex/cpm-messaging-readiness-telemetry: OPEN, CI зелёный (59 entries). Network recovery default ON на обеих платформах, remote kill switch сохранён; добавлен applied pixel. Review-only harness —codex/cpm-network-process-integration-tests. - #7493,
alex/cpm-p3-stable-base-url,dd96018da9→alex/cpm-messaging-readiness-telemetry: OPEN, CI зелёный (60 entries). P3 stable-origin policy, default ON с remote kill switch; harnesscodex/cpm-p3-integration-tests. PR содержит результаты 784 package tests и signed ON 10/10 → OFF ровно 3 origin-continuity failures → ON 30/30 executions. Это проверка origin/migration и CPM controls на macOS, не воспроизведение полного P3 failure и не iOS runtime test. - #7473,
alex/cpm-p5-pinned-window-close, финальный head574e9288ae→release/macos/1.213.0: MERGED 20 октября 2026, 08:17:11 UTC / 14:17:11 Бишкек, merge9bcce0264e. На финальном PR head: 47 SUCCESS / 23 SKIPPED, без failures/pending по сверке после merge; skipped entries не считаются выполненными тестами. Default-ON failsafewebExtensionSharedPinnedTabWindowClose. В merge только 5 production/unit-test paths; real-window fixture и его configuration hook остались локально. Подробности — cpm-status-2026-10-15#P5 — merged 20 октября 2026. - Старые ветки: #7378 harness CLOSED, не merged (
dfb9627919); #7384 baseline CLOSED, не merged (99ecf8b127). #7385 init progress OPEN (46f25175c7), всё ещё на ветке закрытого #7384; privacy-extension #3727 OPEN (b00cc22711), не merged. Их старые Danger/review результаты не перепроверялись как текущие gates. - Старый #7381 OPEN,
39f94e12ea→alex/cpm-fix-init-loss; его прежнюю Network implementation не путать с актуальным #7459. Native GitHub stack удалён по решению пользователя; обычные base-branch зависимости сохранены.
Таким образом, #7379/#7459/#7493 всё ещё ссылаются на ветку закрытого #7376. Закрытие PR не удалило эту ветку и не включило её изменения в release. Worktree большого telemetry rewrite alex/cpm-episode-telemetry — альтернативный prototype, не состав #7376.
P5 — merged 20 октября 2026
- Merge подтверждён через GitHub: #7473, merge
9bcce0264e,release/macos/1.213.0; финальный reviewed/tested head574e9288ae9611ea935fc2a66abfa8cc0d62fb6d. Задача P5. - Что исправлено: после наших явных
didCloseTabWebKit сам запрашиваетtabs(for:)внутриdidCloseWindowи раньше забывал выживший shared pinned tab. Теперь оба пути используют один ownership helper; окно сначала снимается с регистрации, затем отправляетсяdidCloseWindow. Проверяется и app registry, иopenWindowsзапрашивающего context, поэтому открывающиеся/ещё не известные context окна не теряют pins. Нового хранимого closing-state нет.activeTabвыбирается только из closing list, для пустого списка — nil; реальный выбор пользователя не меняется. - Failsafe:
webExtensionSharedPinnedTabWindowClose, default ON; remote keywebExtensions.sharedPinnedTabWindowClose. Registry task. OFF возвращает прежниеtabs/activeTabпри последующих закрытиях; уже забытые WebKit вкладки сам по себе не восстанавливает. - Review и scope: четыре замечания исправлены и threads resolved; Danger warning устранён. В merge 5 paths: shared subfeature, macOS controller/delegate/flag и unit tests. Не вошли
PR.md, real-window app fixture, schemes/DEBUG hooks, ZIP/pin/telemetry changes. Неиспользуемый sharedWebExtensionManager.controllerConfigurationAPI из production PR удалён. - Проверки финального head: 209 selected unit PASS / 0 failed / 0 skipped. Десять close-state/flag регрессий теперь проверяют публичный
context.openTabs, включая preconditions, register → didOpenWindow → unregister → didCloseWindow и реальную потерю shared-pin регистрации при OFF. Обычная macOS build, deep/strict signing и strict lint PASS. CI финального PR head зелёный; ручные шаги reviewer подтвердил в GitHub review. - Отдельный локальный app harness:
alex/cpm-p5-integration-tests,codex/cpm-p5-release-integration-tests@add6e514fe; 13 review-only paths, включая isolated controller-configuration hook. Проверены active pinned/regular, повторные закрытия, последнее окно → новое окно, move/merge, separate pins, реальный background idle, diagnostics OFF и runtime enable. На одинаковых бинарниках: ON 9 PASS → OFF 5 ожидаемых registration-loss FAIL + 1 healthy merge control → ON 27 PASS; 0 skipped, все 4 886 файлов app/XCTest byte-identical. Успешные фазы без active-tab contract errors и неожиданных host restarts; stale tabs дополнительно проверяются черезbrowser.tabs.get. - Исходные summaries, hashes, build/signature и CI evidence:
/private/tmp/cpm-p5-pr-comments-20261019/, manifestvalidation.json. Точки входа и локальные worktree — cpm-web-extension-code-guide-2026-10-09#P5 после merge. - Границы: это один доказанный macOS shared-pinned/tab=false механизм, а не устранение всей P5-когорты, iOS lifecycle или natural listener loss. Восстановление закрытого окна через History отдельно не проверялось; открытие нового окна после последнего закрытия проверено. Merge в release branch не доказывает публикацию сборки, rollout либо production recovery rate.
Исторический реестр PR — 15–19 октября
Следующий реестр сохранён для истории heads, красных прогонов и прежних решений; его OPEN/CI/«локально не опубликовано» не являются текущим статусом.
Телеметрия:
Статусы ниже проверены через GitHub 15 октября в 17:26 UTC. Все семь перечисленных PR открыты; это не означает merge или shipped release. «CI зелёный» означает отсутствие failing/pending среди опубликованных checks; skipped jobs отдельно не считаются выполненными. Счётчики — записи GitHub statusCheckRollup, а не число уникальных тестов: имена checks из разных workflows могут повторяться.
| PR | Ветка | База | Что | Статус |
|---|---|---|---|---|
| #7376 (PR0) | alex/cpm-messaging-readiness-telemetry |
main | параметры готовности и тайминга на failure/recovery пикселях (без новых пикселей) | OPEN, head 619869ee84c6; CI: 39 success / 18 skipped, без pending/failure; REVIEW_REQUIRED. JA1–JA3 и неотправленный ответ — прежняя запись сессии; новые review comments в этой сверке не перечитывались |
| #7384 (0b) | alex/cpm-readiness-baseline |
#7376 | ежедневные базлайн-пиксели cpm_background_ready_{context_load,wake}, нагрузка устройства, возраст возобновления |
OPEN, head 99ecf8b12787; 36 success / 23 skipped / 1 failure: Danger; pending нет. Причина Danger здесь не устанавливалась |
| #7385 (0c) | alex/cpm-init-progress-native |
#7384 | init_stage на failure-пикселях (нужен JS из privacy-extension#3727) |
OPEN, head 46f25175c7c4; 36 success / 23 skipped / 1 failure: Danger; pending нет. Причина Danger здесь не устанавливалась |
| privacy-extension #3727 | alex/cpm-init-progress |
main | уведомления cpmInitProgress из встроенного расширения |
OPEN, head b00cc227112d; 32 success, без skipped/failure/pending; REVIEW_REQUIRED |
Harness и фиксы (красный коммит, затем зелёный; не ребейзить и не форс-пушить, иначе пропадёт доказательство красного прогона):
| PR | Ветка | База | Что | Статус |
|---|---|---|---|---|
| #7378 | alex/cpm-repro-harness |
main | harness (только тесты); 4 быстрых теста в CI около 3 с, остальное локально через CPM_REPRO=1 |
OPEN, head dfb96279196d; 37 success / 18 skipped, без failure/pending; REVIEW_REQUIRED. Предыдущее Danger-предупреждение о 1570 строках сохранено как историческое; в ветке есть коммит другой сессии «Require initResp in the page…» |
| #7379 | alex/cpm-fix-init-loss |
harness | фикс A: ожидание первой загрузки background после установки/апгрейда посреди сессии | OPEN, head 7ad771e227bb; 35 success / 20 skipped, без failure/pending; прежние красный и зелёный прогоны сохраняются |
| #7379 — актуально 19 Oct | alex/cpm-fix-init-loss, published 8275bcce36; production worktree alex/cpm-fix-init-loss-production содержит параллельные незакоммиченные правки вне validation; чистый validation checkout /private/tmp/cpm-pr7379-error6-fix-20261019/source возвращён на production head |
#7376 / telemetry 39fe56af4c |
install/upgrade + P1/P2 + late recovery + independent P4 detection; 38 новых shared regressions, 201 штатный PASS; error-6/P4 finding закрыт fc124938e0 |
OPEN, 10 commits / 23 paths. CI 43 SUCCESS / 17 SKIPPED, без failures/pending. Harness 6d04496882, 12 fixture-only paths; signed app ON 9 PASS → OFF 6 functional FAIL → restored ON 9 PASS (первый restored 7/2 teardown/SIGINT сохранён). Формы/default late OFF сохранены |
| #7381 | alex/cpm-fix-network-crash |
фикс A | фикс B: перезапуск background при замене Network-процесса и мёртвом worker'е | OPEN, remote head 39f94e12eaa4; 28 success / 24 skipped / 6 failure, включая Test (Non-Sandbox) и итоговые отчёты. Прежний красный repro testNetworkProcessCrashKeepsEveryTabWorking с 3 ретраями подтверждён. Исправление f1085846e7ff — единственный локальный commit поверх remote head; в PR его нет, зелёного CI для него нет |
| (нет PR) | alex/cpm-fix-pixels |
фикс B + PR0 | пиксели background_restart и first_load_wait для измерения эффекта |
держим до слияния PR0, A, B |
Локальные проверки фикса A: на коде без фикса оба repro-теста красные (5 из 5 итераций), с фиксом 0 из 10 потерь, 761 тест пакета зелёный. Фикс B: на main красные тесты падения Network-процесса и смерти worker'а, 5 из 5 итераций.
Новые P5/P6 тесты остаются локально в alex/cpm-web-extension-remaining-failures; в PR #7378/#7379/#7381 они не переносились. Весь CPMRepro/ этого worktree untracked, поэтому одного HEAD недостаточно для восстановления тестов. Нужны сами файлы (§12 и актуальная карта кода).
Недоделано в harness и CI: CI гоняет тесты с -test-iterations 3 -retry-tests-on-failure, красный тест должен падать стабильно; тесты WebExtensions идут внутри схем macOS Browser и iOS Browser на osx-tahoe-27. Медленные эксперименты (заморозка 45 с, заполнение кэша около 10 минут, медленный старт worker'а) локальные и не для коммита.
3. Данные (ClickHouse, 12–14 октября, версии iOS 7.240+ и macOS 1.210+)
Единица: daily-пиксель = устройство-день на причину, не пользователь и не эпизод. initialization_failed только .daily; stuck, recovered_*, reload_failed: daily+count. Параметры снимка есть только на init_failed и stuck.
| iOS | macOS | |
|---|---|---|
initialization_failed.daily |
3 273 025 | 119 950 |
stuck.daily |
511 660 | 40 842 |
| init восстановились без reload | 1 135 477 (34.7%) | 30 738 (25.6%) |
| stuck → восстановились после M4 reload | 293 557 (57.4%) | 21 865 (53.5%) |
reload_failed.daily |
140 921 (27.5% от stuck) | 17 796 (43.6%) |
Когорты (по приоритету: error 6 → нет view → вкладка не подключена → Network заменён → пробуждение (view <30 с после dealloc) → недавняя загрузка (load/base_url_changed ≤2 мин) → other). Доля init → stuck:
| Когорта | iOS | macOS | Смысл роста или падения |
|---|---|---|---|
| error 6 | 6.1% → 21.5% | 1.0% → 1.9% | остаётся сломанной |
| нет background view | 48.1% → 10.9% | 3.3% → 1.8% | самоизлечивается |
| вкладка не подключена | 0.03% → 0.05% | 6.2% → 8.9% | остаётся сломанной |
| Network заменён | 16.8% → 39.7% | 13.6% → 26.6% | остаётся сломанной |
| пробуждение | 7.9% → 9.5% | 32.5% → 32.2% | без изменений |
| недавняя загрузка контекста | 15.7% → 6.0% | 20.0% → 6.4% | самоизлечивается |
| other | 5.3% → 12.5% | 23.4% → 22.2% |
Объёмы stuck.count в разных версиях на порядки выше daily: дашборды сравнивать только по версии и частоте.
4. Основание фиксов
- B: доля «Network заменён» растёт в 2–2.4× при переходе init → stuck.
reload_failedвысок: reload расширения мёртвый worker не лечит. Оговорка:network_restartedсравнивает PID с загрузкой контекста, а не с последним view. Если view пересоздан после смены процесса, worker свежий и механизм неприменим. По заметке 8 октября механизм объясняет в лучшем случае 20–45% «здоровых» stuck. Причинность подтвердятnetwork_replaced_since_view(нет ни в одной ветке) иinit_stage=none(#7385). - A: сигнатура «загрузка контекста ≤2 мин, не при запуске, первый view»: iOS 129k init (3.9%) → 4.6k stuck (0.9%); macOS 505 → 118. Разовая потеря одной навигации (0.23× при переходе в stuck). Сигнатура неточна (захватывает M4-reload). Название связанной Asana-задачи («init loss on background unload») относится скорее к группе пробуждения, чем к фиксу A; несоответствие надо снять с автором задачи.
5. Картины событий (stuck.daily, 14 октября, хвост bg_events без возрастов) и воспроизведение
| Картина | Хвост | iOS / macOS | Воспроизведено? | Рычаг в приложении |
|---|---|---|---|---|
| P1. Цикл пробуждения | view>proxy_on>dealloc>view>proxy_on |
16.0k / 4.3k (wake) + 16.3k / 2.4k (старше 30 с) | только с искусственно занятым worker'ом (5 с: init_failed и поздний ответ; 12 с: stuck → M4); органическое пробуждение в тестах проходит. WebKit-баг 324743 (исправлен 22 сентября, нет в 26.7/27.0) | гипотезы: отсчёт 4 с от готовности background («фаза 2 детектора»), ожидание готовности при пробуждении, предпрогрев |
| P2. «Пробуждение → error 6», циклы | proxy_on>dealloc>view>proxy_on>error_E:6 (23.6k), error>dealloc>view>proxy_on>error (5.7k), после died_crash (1.5k) |
iOS; macOS 118 | macOS: заморозка нового процесса на ≥30 с даёт error 6, корзина 25–35 с. iPhone: 4 сценария безуспешно. Главный iOS-хвост без unresponsive (в воспроизведении он есть) |
корень в WebKit (wakeUpBackgroundContentIfNecessary взводит 30-секундный таймер до загрузки worker'а). Гипотеза: перезагрузка расширения при возврате на передний план |
| P3. Контекст загружен, view нет | load / load>base_url_changed |
17.1k + 0.9k / 162 + 81 | задержка очистки при смене baseURL воспроизведена на macOS при большом кэше (около 10 минут заполнения); iOS-гонка запуска (bg_view_creations=0, восстановление сессии, первые 30 с) не воспроизведена: холодный старт в harness'е отвечает за миллисекунды |
кандидат: стабильный baseURL (ветка cpm-stable-base-url: не доведён, меняет URL всем расширениям, не прогнан через harness, старая база). Фикс WebKit 318756@main, номер сборки ОС не проверен |
| P4. Первая загрузка | load>view>proxy_on |
6.8k + 2.3k / 570 | да (фикс A) | фикс A |
| P5. Фон выгружен в момент сбоя | proxy_on>dealloc>view>proxy_on>dealloc |
1.9k / 494 | да: закрытие окна с shared pinned tab через production listener → WebKit сам удаляет сохранённый tab; точная сигнатура с tab=false (§11). Пустые listener metadata пока только test-only (§10) | shared-pinned Mac/tab=false путь исправлен #7473, merged 20 октября в macOS release: delegate исключает выжившие tabs при didCloseWindow. Rollout/effect не измерены; для tab=true и iOS естественный вход не найден |
| P6. macOS: вкладка без расширения | load>view>proxy_on>dealloc / load>view>proxy_on; варианты с extension_updated отдельно |
iOS ≈0 / 627 + 235 | да: цикл главного webExtensions flag через production handler создаёт replacement manager, прежний WKWebView сохраняет старый controller и даёт точную сигнатуру (§11); nil controller до/после idle — test-only (§10) | условный production-путь найден; фактическое переключение главного flag в выборке не подтверждено. Embedded-only cycle сохраняет controller; reload контекста старую привязку не чинит |
| Network заменён | см. раздел 4 | да (краш, worker мёртв при том же view) | фикс B | |
| «Здоровый» остаток, other | iOS 12.5%, macOS 22% | нет | нет гипотезы |
Сверка определения P5: строка выше сохраняет исходные 494 macOS. Повторная выборка 14 октября по хвосту P5 без error 6, network_restarted=true и scripts=false дала 1953 iOS / 499 macOS (§10). Разница 494/499 не устранена сверкой всех дополнительных фильтров: эти знаменатели не подменяют друг друга. В этом повторном Mac-срезе 68 снимков имеют tab_in_context=false, 374 — все три tab-флага true, 57 — неполную/неразрешённую диагностику вкладки. На iOS 646 снимков имеют все три tab-флага true, у 1299 вкладка не разрешилась, ещё 8 имеют неизвестный tab_in_context. Найденный shared-window механизм применим к сигнатуре Mac/tab=false, но происхождение всех 68 событий не доказано. Неразрешённая вкладка не тождественна tab_in_context=false.
P6 не равен P4 по одному хвосту: load>view>proxy_on при controller/scripts=false — P6, при корректной привязке и потерянном раннем init может быть A/P4. 235 событий P6 — хвост без dealloc, а не группа extension_updated. В 468/627 и 234/235 P6-снимках запуск моложе двух минут. Поэтому main-flag cycle проверяет достижимость, но его частоту и соответствие раннему запуску нужно подтвердить отдельно.
Размер группы не равен возможности её починить: P1 и P3 в основном опаздывающие ответы, а часть «сбоев» это срабатывание детектора («нет ответа за 4 с», опоздавший ответ тоже считается). iOS-когорта «other» на самом деле тот же рисунок P1, но со view старше 30 секунд.
6. Телеметрия
Диагностическое покрытие Others — проверка 20 октября 2026
#7376 5d1cb4abd2 улучшает измерение исходов, но не обеспечивает полное объяснение Others. Он объединяет сведения об одном эпизоде без соединения отдельных failure/recovery events. Сравнивать новые episode counts напрямую со старыми отдельно deduplicated failure/stuck/recovery totals нельзя: нужен новый baseline. reason=other означает обычную навигацию вне restoration/crash/reload; это не причинная группа Others из §3–5.
Что реально различается. stuck=false-one/false-many/true-one/true-many объединяет persistence и число разных tabs с наблюдавшимися timeouts. Только последующая eligible non-restoration navigation, начатая после первого failure, может дать true; concurrent/restoration failures увеличивают one/many, но сами не подтверждают persistence. Одна вкладка может дать true-one после повторной навигации. false не означает ровно один timeout или false positive. one не доказывает здоровье остальных tabs; many не доказывает смерть всех tabs или всего окна. Активного all-tabs/window probe в PR нет.
outcome=failed_measurement_response вместе с reload_state=none выделяет поздний native ответ на ту же measurement без reload. failed_tab_response — ответ более поздней measurement в одной из failing tabs; other_tab_response не доказывает восстановление failing tab. no_response_before_deadline с false-* означает, что persistence не подтверждён; отсутствие следующей eligible navigation тоже возможно. Срок наблюдения — не доказательство постоянной поломки.
Ложные срабатывания отфильтровываются только частично. Страница могла получить initResp и работать, а dashboard-подтверждение до native monitor не дошло. Даже true-* не исключает этот случай; он воспроизведён контролируемо. Ни dashboard response, ни успешный background load не являются page ACK.
Три недостающих измерения:
- Последний подтверждённый этап обмена и page ACK. Чтобы разделить init loss, задержку native settings, потерю ответа странице и потерю dashboard confirmation, нужны изменения extension JS и native handler/локальной агрегации. Одной записи в
bg_eventsили только extension-side change недостаточно. #7385/#3727 — старые отдельные предложения; в #7376 нетinit_stage,cpmInitProgressили page ACK. - Конечный исход первоначально pending load. Четыре load-поля и восемь boolean facts заморожены при первом failure. Поздняя error 6 теперь может попасть в
bg_eventsмежду failure и первым stuck; она не обновляетbg_ready_after/bg_load_error_*. У non-stuck episodes continuation не отправляется; после первого stuck она больше не собирается в payload. Явного final-load outcome той же попытки и нового ready-completion token нет. Это частичное закрытие прежнего пробела, не его полное устранение. network_replaced_since_viewв episode. Поля нет; старыйnetwork_restartedудалён из payload. Отдельный applied pixel #7459 считает выполненные background restarts при наблюдаемой смене PID относительно watched view, но не все crashes, не untreated failures и не успешные CPM recoveries. Unknown/нулевой baseline, недоступный текущий PID, исчезнувший view/context или выключенный flag оставляют ненаблюдаемые случаи. Поэтому он не воспроизводит старую causal cohort classification и не заменяет этот diagnostic gap.
Healthy load/wake baseline (#7384 закрыт) и targeted startup/no-view app_state/open_tabs тоже отсутствуют в #7376; loading_tabs не добавлен. Graveyard treatment исключён по решению пользователя, mitigation не менялась.
bg_events: что отправляется и что теряется
В #7376 5d1cb4abd2 initial history на первом failure отделяется ; от событий после него до первого stuck confirmation. Continuation замораживается до вызова его automatic recovery; последующие события в неё не попадают. У non-stuck episodes отправляется только initial history. Начальные ages считаются от первого failure, последующие — от stuck confirmation.
Обе фазы делят 12 событий / 255 символов вместе с separators/markers. Standalone ... в начале фазы означает отброшенные старые события. Текущая политика сначала выбрасывает oldest initial entries, затем oldest continuation entries: достаточно длинное продолжение может полностью вытеснить initial snapshot. Отдельной квоты или приоритета «важных» событий нет. Резервирование бюджета для каждой фазы обсуждалось как улучшение, не реализовано.
Общего удаления дубликатов нет: initial snapshot повторно не добавляется в continuation, но новые одинаковые tokens сохраняются, даже если age bucket совпадает. Только одинаковые последовательные load-error notifications в пределах одной секунды подавляются recorder. Циклы view/dealloc/view диагностически значимы.
Parser должен разделять по , и ;, игнорировать пустые entries и standalone .... Старый comma-only parser неверно прочитает границу. Отсутствие error/death token в обрезанном таймлайне не доказывает отсутствие события.
Обрезание уже было в production — проверен срез 14 октября UTC. Daily attribution reports с непустым context_loaded, iOS major=7/minor≥240, macOS major=1/minor≥210, pixel_id=debug и соответствующие agents:
- iOS init: 1,103,215 reports с bg_events; 192,070 (17.41%) содержат 12 событий; 166,327 (15.08%) без исходного load.
- iOS stuck: 169,115; 79,217 (46.84%) содержат 12 событий; 69,097 (40.86%) без load.
- macOS init: 41,462; 20,328 (49.03%) содержат 12 событий; 19,561 (47.18%) без load.
- macOS stuck: 13,992; 8,924 (63.78%) содержат 12 событий; 8,622 (61.62%) без load.
В shipped tags 7.240.0+ios / 1.210.0+macos context load сбрасывает recorder и записывает load, snapshot сохраняет последние 12 и ограничивает строку 255 символами. Поэтому отсутствие load — свидетельство потери начала, нижняя оценка, не точная доля всех обрезаний: более ранний extension_updated может исчезнуть при ещё сохранённом load. Число строк ровно 255 символов также не измеряет всю character truncation: удаляются целые entries. Максимальная длина iOS 255, macOS init 252 / stuck 253. Запрос агрегатный, individual reports не выгружались. SQL, filters и агрегаты: /private/tmp/cpm-bg-events-truncation-20261019/{query.sql,aggregates.json,summary.json}; ключевые результаты сохранены здесь, так как tmp не постоянное хранилище.
Privacy и границы согласованного контракта
Нет новых URL, tab/document/context IDs, PID или точных timestamps. Идентификаторы measurements и tabs связываются только локально; на сервер уходят allowlisted enums/booleans и bucketed ages. Один terminal report устраняет необходимость склеивать разные lifecycle events, но daily+count первого эпизода всё равно отправляет два одинаковых payload. bg_events и сочетания полей могут быть редкими: daily deduplication не даёт гарантии некоррелируемости или анонимности.
Переданный пользователем privacy-triage summary перечислял was_stuck; последний wire contract использует stuck из четырёх значений, а формат bg_events теперь двухфазный с .... Эти изменения нужно учитывать в review контракта; отдельное подтверждение их privacy approval в этом проходе не получено. Метка/прежний approval не подменяет проверку различимости новой комбинации. Анализ уникальности ниже относится к старому payload и не является оценкой нового.
Полный contract, частоты, 11 retired pixels и 9 удалённых полей: cpm-pr0-episode-telemetry-summary-2026-10-16. Источник текущей сериализации: WebExtensionPixelFiring.swift на 5d1cb4abd2; bg_events encoder.
Проверка актуальности документации — 20 октября 2026
Сверены все 18 содержательных CPM-заметок в vault: текущие sections описывают опубликованный contract, старые proposals/PR states/test results явно датированы как история. PR summary остаётся на английском, без таблиц. Проверкой diff/schema подтверждены наличие описаний всех 18 wire fields, частота daily+count, все 11 retired definitions и перечисление 9 удалённых старых полей. Текст и frontmatter повторно прочитаны после записи; исходные cohorts, красные прогоны и ограничения сохранены. Это документационный проход: код, branches, PR states и dependency lockfile не менялись; новые app/unit tests не запускались.
Историческая оценка до перехода на единый эпизод
Следующее описание относится к прежнему дизайну PR0, до замены отдельных failure/recovery-пикселей единым эпизодом.
Что делает PR0 (#7376): на 8 failure-пикселей и 2 recovery-пикселя добавлены параметры bg_ready_after, bg_view_age, bg_load_error_after, bg_load_error_app_state, cpm_app_state, cpm_open_tabs, cpm_loading_tabs (failure) и cpm_recovery_document, cpm_recovery_after (recovery). Новых пикселей нет. Recovery-пиксели получают только свои два параметра, extension_reload_failed без параметров.
Замечания ревьюера (JA1–JA3): корреляция между пикселями из-за множества перечислений; сколько пикселей на ошибку; константность cpm_open_tabs/cpm_app_state; что такое background view.
Что верно по пикселям (исправление моей прежней ошибки): initialization_failed только daily (не больше одного на устройство в день на причину); stuck daily+count (daily и count одного события идут с одинаковыми параметрами); snapshot пересобирается при каждой отправке, поэтому быстро меняющиеся поля (bg_view_age, bg_ready_after) между init_failed и stuck различаются, медленные (app_state, open_tabs) повторяются.
Уникальность строк stuck.daily внутри дня (доля уникальных по всем параметрам):
| Вариант | iOS | macOS |
|---|---|---|
сейчас, bg_events из 12 событий |
8.3% | 30% |
| информативные токены, последние 4 | 2.1% | 5.2% |
скаляры вместо bg_events |
0.96% | 4.4% |
без bg_events |
0.25% | 2.0% |
Верхняя граница прироста от новых полей (синтетическое независимое равновероятное поле): со старым bg_events при десяти значениях iOS 21%, macOS 65%; со скалярами 3.6% / 15.8%. Граница этого исторического расчёта: скаляры снижали наблюдаемую уникальность старого payload; это не гарантия, что добавление новых полей не увеличит различимость. bg_events — существенный источник редких сочетаний, а не доказанный идентификатор пользователя. Оценка нового двухфазного payload отдельно не выполнена.
bg_events: единственный источник давности загрузки (load, base_url_changed) и причины смерти (died_*). view, dealloc, proxy_on, error_* дублируют bg_view_creations и context_errors; hold/release/proxy_off равны нулю. Скаляры воспроизводят когорты загрузки и пробуждения на 100% строк. Теряется: поиск неожиданных последовательностей в остатке (смягчить удалённым флагом).
Минимум данных по гипотезам:
| Поле / пиксель | Вопрос | Где |
|---|---|---|
bg_ready_after, bg_view_age |
идёт ли загрузка, пробуждение или давно | PR0 |
bg_load_error_after, bg_load_error_app_state (только при error 6) |
таймер 30 с, состояние приложения | PR0 |
recovery_document, recovery_after |
опоздавший ответ или потерянный | PR0 |
app_state, open_tabs (грубо, 1 / 2–5 / >5), только на initialization_failed |
когорта «нет view» на запуске | PR0 |
новое: context_load_cause, context_load_age (вместо load@ и extension_updated) |
точная сигнатура A | PR0 |
новое: bg_died_reason, base_url_changed |
смерть процесса, очистка при смене baseURL | PR0 |
новое: мелкие корзины app_launch_age для первых 30 с (<4 / 4–10 / 10–30) |
гонка запуска | PR0 |
новое на stuck: stuck_after |
сколько от первого сбоя до stuck | PR0 |
новое, срочно: network_replaced_since_view |
доля реальных мёртвых worker'ов в когорте B (рекордер вычисляет, но только логирует; предложено 8 октября, нигде не реализовано) | PR0 |
init_stage |
дошёл ли init до background | #7385 (срочно) |
cpm_background_ready_{context_load,wake} |
базлайн здоровых загрузок и пробуждений | #7384 |
background_restart (причина, исход; убрать из него bg_ready_after, он дублирует соседний failure-пиксель), first_load_wait |
эффект B и A | удержанная ветка |
| убрать | loading_tabs; из таймлайна view, dealloc, proxy_on, error_*, hold/release/proxy_off |
Связность стека: #7384 добавляет ещё 5 измерений на те же 8 failure-пикселей (context_load_to_background_view, cpm_low_power_mode, cpm_thermal_state, cpm_lockdown_mode, cpm_resume_age); оценку склейки надо повторять и по нему. background_restart срабатывает вместе с первым initialization_failed: общий ключ bg_ready_after.
7. Как оценивать результат изменений
- Для #7376 построить новый episode baseline: daily — installations с законченным эпизодом, count — законченные episodes. Разрезы
stuck/outcome/reload_stateотносятся к одному report; частоты старых раздельных пикселей напрямую с ними не сравниваются. Незаконченные episodes теряются при уничтожении monitor/process. - Для P1/P2/P4 #7379 различать background_ready, reload_started и response_received. Последний — attributed dashboard response, не page ACK; независимый startup_response_missing измеряется и при выключенном late reload.
- Для B/#7459 applied_count показывает применение restart, applied_daily — installations с применением. Снижение CPM failures в treatment требует отдельного denominator/control и сопоставимых версий; applied сам по себе не показывает success rate. Нельзя соединять два самостоятельных daily reports как доказанную пару одного пользователя.
- Для P3/#7493 подтверждено устранение лишней origin migration в контролируемой среде. Уменьшение production no-view/startup failures ещё нужно измерить; полного P3 repro нет.
- Для P5/#7473 оценивать доказанный shared-pinned-window mechanism, не приписывать ему всю P5-когорту. Для P6 сначала установить реальный main-flag transition.
- Полную causal attribution Others не считать готовой до закрытия трёх пробелов §6. Отсутствующие поля и здоровый baseline не считать уже доступными измерениями.
8. Открытые вопросы реализации и публикации
- #7376 закрыт без merge; зависимые #7379/#7459/#7493 по-прежнему основаны на его ветке. Это незавершённая публикация, а не shipped telemetry.
- macOS #7484 нужно синхронизировать с текущим episode contract и устранить release-base conflict; сейчас его payload отличается. В этом документационном проходе код и PR не менялись.
- На предыдущем head
9effd97877все CI завершились зелёными; новый flag head55a42802a6на 20 октября, 14:16 UTC имеет 39 SUCCESS / 13 SKIPPED / 4 IN_PROGRESS, без failures. Точная причина исходного ConfigurationFetcher crash не установлена; последнее изменение отделяет P2 retry flag и не исправляет его повторный WebKit error 6. - Для
bg_eventsостаётся риск вытеснения всего initial snapshot; резервирование бюджета двух фаз не реализовано. Аналитические consumers должны поддержать;и.... - Нужны целевые измерения §6: end-to-end stages/page ACK, final pending-load outcome, network replacement относительно view; текущий applied pixel закрывает только учёт применения B. Healthy baseline и targeted startup/no-view measurements не реализованы.
- Для нового
stuckи двухфазногоbg_eventsтребуется актуальный privacy contract и оценка комбинированной различимости; прежние расчёты не переносить на новый payload. - Реальное iOS error-6/lifecycle воспроизведение, P5/tab=true, естественная потеря listener metadata и P6 production transitions всё ещё не доказаны. Исторические результаты controlled tests и отрицательных проверок ниже сохранены.
9. Методика и инструменты
SQL ниже — историческая классификация старых production pixels за 12–14 октября. Он использует удалённые из episode поля и прежний comma-only timeline. Для нового payload его нельзя применять без адаптации; актуальные ограничения и parser — §6. Live PR/CI evidence этой сверки: /private/tmp/cpm-merge-readiness-20261019/; точные heads и существенные результаты сохранены в §2.
- Доступ:
~/Developer/grafana-data-tools(Grafana → ClickHousemetrics.pixels, read-only; авторизация черезgrafana-auth). Ad-hoc SQL запускался отдельным раннером через~/DuckDuckGo/grafana-dashboards/tools/grafana-query/read-dashboard.py. Сканы поpixel LIKEбез фильтра по дате, агенту иpixel_id='debug'превышают лимит строк. Временные запросы лежали в папке сессии и могут пропасть; ключевая классификация ниже. - Версии с диагностикой: iOS 7.240+, macOS 1.210+. Окно: 12–14 октября (3 полных дня). Ограничения: сигнатуры эвристические;
network_restartedтолько прокси; устройства нельзя проследить от init до stuck, сравниваются только доли; доля не равна пользовательскому эффекту.
-- классификация когорт для init_failed.daily / stuck.daily
multiIf(
errs LIKE '%ContextErrorDomain:6%', '1_error6',
creations = '0', '2_no_background_view',
scripts = 'false', '3_tab_not_connected',
net = 'true', '4_network_replaced',
has(ev,'view@30s') AND arrayExists(x -> startsWith(x,'dealloc@'), ev), '5_wake_view_lt30s',
arrayExists(x -> x IN ('load@30s','load@1m','load@2m','base_url_changed@30s','base_url_changed@1m','base_url_changed@2m'), ev), '6_context_load_recent',
'7_other')
-- параметры: network_restarted, context_errors, bg_view_creations, tab_has_ext_scripts, bg_events (decodeURLComponent)
9а. Архив прежних кратких обновлений и плана
Все «актуально», «осталось», CI counts и worktree states в этом архиве относятся к моменту записи и указанным heads, а не к 20 октября. Актуальный статус — §1–9.
Актуально — 19 октября 2026, error-6/P4 finding закрыт в опубликованном PR. #7379
8275bcce36, база #737639fe56af4c, содержит fixfc124938e0+ 2 штатные регрессии. 201 shared PASS, strict lint 4 files / 0 violations. Signed app: ON 9 PASS → OFF 6 ожидаемых functional FAIL → restored ON 9 PASS, 0 SKIP, те же бинарники; первый restored attempt 7 PASS / 2 FAIL (teardown timeout / SIGINT) сохранён, повтор выполнен без изменений и отключения проверок. CI завершён: 43 SUCCESS / 17 SKIPPED, failures/pending нет. Harness6d04496882содержит production head и ровно 12 review-only paths; 38 новых shared regressions относительно базы. Проверенный shipping scope: P1/P2 и независимый P4 detection; late reload остаётся default OFF, формы не менялись. Прежние открытые findings ниже — история до fix.
История ревью — 19 октября 2026, до исправления error-6/P4 в
fc124938e0. Проверен опубликованный после rebasea00dc4afeb/ база #7376c7adf28e12, 7 commits / 22 paths. Остаётся 1 P2: error-6 startup может ошибочно попасть в P4 detection после позднего callback успешного retry. На текущем head: 196 существующих shared tests PASS + 2 дополнительных probes PASS / 1 новый probe FAIL. Проверочная однострочная правка только в изолированной копии даёт 199 PASS; в PR она не внесена. Pixel validators обеих платформ PASS. CI завершён: 53 SUCCESS / 25 SKIPPED, failures и pending нет; повторный probe подтвердил тот же незакрытый дефект. Предыдущие 197 unit и app 9→6→9 относятся кfc87237a47; новая база убрала один retired-telemetry тест. Подробности и границы воспроизведения — в разделе «Полное ревью после P4 detection — error-6 attribution». История ниже сохранена.
Актуально — 19 октября 2026, P4 detection без reload опубликована и проверена. #7379: production
fc87237a47, 7 commits / 22 paths; review-only harness475095b335, 12 paths вне main. Новый dailyAndCountweb_extension_cpm_startup_response_missingсчитает P4 candidates независимо от late recovery flag, без reload и safety JavaScript. 197 unit PASS, signed macOS и iOS app builds PASS, pixel validators PASS, strict lint 6 production + 2 harness / 0. На одних подписанных app/XCTest binaries: ON 9 PASS → OFF 6 ожидаемых functional FAIL → ON 9 PASS, без skips/expected failures, SHA256 совпали. Новый detection-only case дважды подтвердил detection = 1 / finishCount = 1 / late recovery OFF / no late reload outcomes; оба late OFF controls также дали по detection = 1. CI нового head: 48 SUCCESS / 21 SKIPPED, pending/failures нет. Формы не менялись, late reload flag default disabled. PR steps и все три документа актуализированы; worktree возвращён на чистую production branch. Более ранние heads/results сохранены как история.
Актуально — 19 октября 2026, uninstall fix опубликован. #7379: production
e970e8c90e, 6 commits / 22 paths; отдельный review-only harnesse380cf98f7, 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 совпали. CIe970e8c90e: 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 harness4308751d59синхронизирован; его новый ON/OFF/ON ещё не запускался. Подробности — в конце; прежние heads/results ниже относятся к истории.
Актуально — 16 октября 2026, PR review fixes. В #7379 проверены оба inline замечания: cancellation уже покрыта, повторные 5 секунд на каждую navigation исправлены общим deadline на startup. Production
08866c7eda, 3 commits / 21 paths; review-only harness3f4d755511, 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 восстановлена проверка выключенных 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 PRda126120a9, 2 commits / 21 files, не менялся; harness остаётся вне main (2 test-only commits / 12 paths). Пользовательские правки PR сохранены. Подробные результаты и artifacts — в конце; прежние односхемные steps и harness head6e4e39e67cотносятся к истории.
Актуально — 16 октября 2026, harness вынесен из PR. #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/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 теперь поверх #7376 (
alex/cpm-messaging-readiness-telemetry), headaec5be9a83, ровно 3 коммита / 32 файла. Старый worktreecpm-fix-init-lossудалён; текущая директорияalex/cpm-fix-p1-p2теперь на веткеalex/cpm-fix-init-loss. Checkpoint367759cbc1и 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-onlyto-be-disabled before mergeannotations обновлены; GitHub CI ещё не объявляется green. Подробная финальная сверка — в конце документа. Ниже сохранена датированная история: прежние «не закоммичено», старые head/base и over-budget limitation не являются текущим статусом.
Итог замены #7379 — 16 октября 2026 (Бишкек). В
alex/cpm-fix-p1-p2исправлены оба замечания: ожидание конкретного нового context при install/upgrade и background guard независимо от diagnostics flag. Финальный signed app-набор 4/4 PASS, install/upgrade 10+10 PASS, targeted unit 170 PASS, strict lint 16 files / 0 violations, общий WebExtensions module для iOS Simulator BUILD SUCCEEDED. На тех же бинарниках с выключенными flags 4/4 функциональных negative controls FAIL, без deallocation/infrastructure errors. Коммитов нет, scratch не изменён, ZIP/schema/dependencies не менялись. Сохраняется исходный cap 5 s: startup fixture 5000 ms с WK overhead даёт callback после cap и может потерять первый ответ; этот режим остаётся воспроизводимым известным пределом. Подробности, история красных прогонов и machine summary — в конце cpm-status-2026-10-15.
Отдельная ветка P1/P2 — финал 16 Oct, 13:33 UTC. Новый worktree
alex/cpm-fix-p1-p2от текущегоalex/cpm-messaging-readiness-telemetry(01d4a0ec22): только P1/P2-кандидаты и необходимые тесты. Новых методов 2 app + 4 unit; 22 — полный старый research app-набор. На новой signed app-сборке 2/2 PASS, unit 165 PASS, strict lint 0; те же два app-теста при выключенных flags дают ожидаемые функциональные FAIL. Текущая база использует episode contract: P2failed_measurement_response,reload_state=none, плюс реальная доставка initResp в тот же документ без навигации. История исправленного test teardown сохранена. Коммитов нет; scratch не изменён; подробности и артефакты — в конце документа.
Дополнения и исходные сводки — 15–16 октября
Актуальный локальный итог P1/P2 — 16 октября, 12:30 UTC / 18:30 Бишкек. В
cpm-e2e-initдобавлены точечные production-кандидаты и две app-регрессии: 22/22 текущих app methods PASS, 0 failed/skipped/expected failures в positive runs; P2 same-document повторён дважды. С выключенными фиксами обе новые регрессии красные по функциональным причинам. Пакет: 806 tests, 14 skipped, 0 failures; strict SwiftLint — 0 нарушений. P2 в окончательном варианте повторяет только background load и сохраняет context; full-context prototype отвергнут как нестабильный. Подробный итог и ограничения — раздел «Окончательный итог точечных P1/P2-кандидатов». Это macOS с управляемыми fault conditions, не доказательство natural iOS production cause. Коммитов/PR изменений нет; прежние разделы и красные прогоны сохранены.
Внутренний документ. Цифры из ClickHouse, в публичные PR, комментарии и issues не переносить (репозитории duckduckgo/apple-browsers и duckduckgo/duckduckgo-privacy-extension публичные). Заменяет заметку cpm-telemetry-data-requirements-2026-10-15 (теперь ссылка сюда). Исторические срезы с собственными датами и знаменателями: cpm-web-extension-public-attribution-2026-10-05, cpm-web-extension-data-analysis-summary-2026-10-08, cpm-web-extension-repro-and-fix-2026-10-08. Актуальная карта worktree и точек входа добавлена в cpm-web-extension-code-guide-2026-10-09; её прежние разделы сохранены как карта на 9 октября.
Дополнение 16 октября: app-hosted integration tests. Добавлены 20 методов в cpm-e2e-init, схема CPM Integration Tests, штатная Apple Development signing, без commits. Финальный тестовый результат — 20/20 PASS в группах 18+1+1, 0 failed/skipped/expected failures. Это не «все production-кейсы кроме P3 воспроизведены»: часть методов использует явные fault injections, а все app methods — тестовое окружение и инструментированный bundle. Связь с production-причинами доказана не для всех групп. Все результаты, включая красные совместные прогоны, отрицательный P3 и ограничение app P5, находятся в §13 и #Финальный app-hosted итог — 16 октября 2026. Финальную сводку §13 считать текущей; ранние «пока нет результатов» — сохранённая хронология. PR/CI и ClickHouse в рамках app-тестов заново не запрашивались.
Предыдущая сверка — 15 октября, 17:26 UTC / 23:26 Бишкек. Разделы 1–9 — сводный статус; §10–11 — сохранённая хронология экспериментов, где «пока», «не найдено» и красные ожидания относятся к моменту записи. Текущий итог всех 14 P5/P6-проверок, границы доказательства и команды — §12. PR/CI сверены read-only через GitHub; результаты локальных тестов сверены с существующими логами и исходниками, в этом документационном проходе тесты и ClickHouse-запросы заново не запускались.
План до перехода на episode report — история
8. Открытые решения и следующие шаги
- Правки PR0 (ждут вашего «да»): скаляры вместо
bg_events,context_load_cause,stuck_after,network_replaced_since_view, мелкие корзиныapp_launch_age, убратьloading_tabs, константы только наinitialization_failed,open_tabsгрубо,bg_load_error_*только при error 6; после этого ребейз #7384 и #7385. Альтернатива «оставитьbg_events» требует ответа ревьюеру без утверждения «хуже не будет» и обязательства перемерить после релиза. - Ответ ревьюеру (публично, без цифр): JA1 (init_failed daily-only; stuck daily+count с одинаковыми параметрами; recovered несут только
recovery_*), JA2 (медленные поля повторяются, быстрые различаются; планируется сократить набор), JA3 (background view = скрытыйWKWebView, в котором WebKit запускает фоновый скрипт расширения; выгружается через около 30 с простоя и пересоздаётся при пробуждении). Обязательство: перемерить различимость строк после первого релиза. - Запушить фикс B (
f1085846e7): красный прогон подтверждён, ждёт зелёного. - Судьба фикса A: слабое основание в данных, не совпадает с названием задачи; решить, сливать ли без зелёного измерения эффекта через
first_load_wait. - P3: довести стабильный
baseURL(ограничить встроенными расширениями безweb_accessible_resources, прогнать harness, перенести на свежийmain), проверить номер сборки ОС с фиксом 318756@main. - Локальные repro для P1–P3 и новые P5/P6 находятся в
alex/cpm-web-extension-remaining-failures; отдельный PR без фиксов ещё не создавался. В этом worktree весь базовый класс gatedCPM_REPRO=1, новые 14 методов дополнительноCPM_REPRO_P5_P6=1;requireLocalRunотносится к выделенному PR-harness. Не путать opt-in и ветки при переносе. - iOS: воспроизведение error 6 на реальном устройстве. Для Mac P5/tab=false найден и проверен путь shared pinned window → didCloseWindow → внутренний didCloseTab WebKit; для P6 проверен условный путь цикла главного flag (§11). Не установлены P5/tab=true, iOS lifecycle и реальность flag-transition в P6-сессиях.
- Блокеры по свежей сверке: #7376/#7378 и JS #3727 ждут ревью; #7384/#7385 стоят на #7376 и имеют failing Danger; #7381 остаётся на красном remote head без локального fix B.
- Найденный P5 tab lifecycle: проверить исправление полного window-close контракта, а не только списка явных didCloseTab. Направления — исключить сохраняемые shared tabs из delegate закрываемого окна либо восстановить регистрацию после window callback. Это варианты для отдельной реализации и проверки, не выполненный фикс. Контроли: закрытие второго/последнего окна, merge, перемещение и новые/существующие документы.
- P5 с исправной регистрацией и P6 attribution: естественная потеря listener metadata и фактический main-flag transition остаются без доказательства. Задержка main actor, длинный документ и один пропуск listener не заменяют такое доказательство; результаты отрицательных проверок сохранены в §10–12.
10. Журнал воспроизведения P5/P6 — 15 октября 2026
Хронологический журнал первоначальных controlled экспериментов; дополнительные результаты с привязкой к production-путям и текущие ограничения — в §11.
Работа начата по запросу: добавить тесты новых гипотез и попытаться воспроизвести наблюдаемые цепочки. Продакшен-фиксы и публичные PR этим запросом не меняются.
Выбор worktree: /Users/admin/DuckDuckGo/apple-browsers.git/alex/cpm-web-extension-remaining-failures, ветка alex/cpm-web-extension-remaining-failures, HEAD a0717cf098cd1cbd32d5bc9d19aa7db0fab2399f. Здесь общий harness, production recorder, локальные эксперименты пробуждения, error 6 и очистки baseURL (P1–P3), без watchdog/фиксов A/B. Полного единого набора всех сценариев сейчас нет: базовые A/B находятся в cpm-repro-harness/фикс-ветках, init-progress E2E — в cpm-e2e-init. Существующие dirty-изменения сохранены; добавления этой работы ограничиваются тестовым harness.
Среда: macOS 26.7.1 (25G241), Xcode 27.1 (27A9275), системный WebKit. Прогон на macOS проверяет механизм; перенос на iOS требует отдельной проверки.
Уточнение данных перед тестами: read-only перепроверка ClickHouse за 14 октября, stuck.daily, iOS 7.240+/macOS 1.210+:
- P5: в выборке хвоста
proxy_on>dealloc>view>proxy_on>deallocбез error 6/Network replacement/scripts=false — 1953 iOS и 499 macOS,bg_view_alive=falseво всех снимках; последняяviewвезде старше 30 с. Доли от всех stuck.daily за тот же день: iOS 1.15% (1953 / 169115), macOS 3.57% (499 / 13992). Знаменатели — сохранённый исходный запрос volumes за 14 октября, те же версии с диагностикой; это не трёхдневные totals. P5 среди init в том анализе не считался. Это определение чуть шире строки P5 выше, поэтому 499 не подменяет прежние 494 без сверки дополнительных фильтров. На macOS 68 снимков сtab_in_context=false; на iOS у 1299 не разрешилась вкладка для табличных диагностических полей. - P6:
load>view>proxy_on>dealloc— 627 macOS;load>view>proxy_on— 235. В обеих подгруппахcontext_loaded=true,tab_in_context=true,tab_controller_match=false,tab_has_ext_scripts=false. В первых двух минутах запуска — 468/627 и 234/235. 235 — это хвост без dealloc, а не маркер extension_updated; варианты с extension_updated существуют отдельно.
Проверяемые гипотезы:
- P5: потеря регистрации вкладки или runtime listener приводит к отсутствию пробуждения после штатного idle; сравнить content-script send/error, background receive, текущую view и параметры failure-пикселя.
- P5: долгий документ/пауза приложения позволяют background выгрузиться до диагностического снимка; отличить запоздалый снимок от неработающего worker.
- P6: вкладка создана без CPM controller, затем попала в openTabs при загрузке контекста.
- P6: существующая вкладка сохранила controller предыдущего менеджера; новый контекст видит вкладку через openTabs, но не внедряет в неё скрипты.
Текущий статус: читаю точки расширения harness; тесты и результаты ещё не получены. Контролируемая инъекция состояния будет явно отделена от естественного воспроизведения.
P5/P6 — тесты добавлены, начат первый прогон
В CPMReproLifecycleHypothesisTests.swift добавлены 7 локальных XCTest-проверок, отдельный opt-in CPM_REPRO_P5_P6=1 поверх CPM_REPRO=1:
- P6: отсутствующий controller до idle; тот же случай после штатной выгрузки и после reload контекста; перенос существующего WKWebView в replacement manager с сохранением прежнего controller.
- P5: забытая в WebKit, но оставшаяся открытой вкладка после двух idle-циклов; отсутствие runtime listener после следующего пробуждения; два здоровых документа с реальной задержкой image-subresource на 35 с; те же документы с искусственно потерянными dashboard-уведомлениями.
Harness позволяет отдельно выбирать controller вкладки, переносить существующую view и подавлять конкретный native method только в тестах. CPMReproDelayedResource.swift удерживает реальный subresource через WKURLSchemeHandler; навигационные finish-события не подделываются. Проверки требуют actual stuck от production monitor, полный набор диагностических признаков, content-script initResp для здоровых контролей и восстановление после снятия инъекции.
Первым запущен testTabWithoutControllerMatchesP6BeforeIdleUnload; пока идёт сборка, результата нет. Логи сессии: /private/tmp/cpm-p5-p6-20261015/. Команда сборки: CPM_REPRO=1 CPM_REPRO_P5_P6=1 swift test --disable-automatic-resolution --filter CPMReproLifecycleHypothesisTests/testTabWithoutControllerMatchesP6BeforeIdleUnload.
Первый результат P6 до idle
Сборка прошла. Первый тест получил реальный initialization_failed и затем stuck с load>view>proxy_on, context_loaded=true, tab_in_context=true, tab_controller_match=false, tab_has_ext_scripts=false, bg_view_alive=true, bg_view_creations=1. Content marks пустые: CPM не внедрён. Новая вкладка с current controller получила initResp и вызвала recovery.
Прогон пока красный (1 assertion): дополнительное ожидание network_restarted=false не выполнено — поле true. Падения/замены Network-процесса тест не инъецирует. Проверяю холодный старт Network до контекста: recorder сравнивает PID с context-load, что может смешивать первичный запуск и замену. Результат нельзя объявлять полным совпадением всех признаков до этой проверки. Лог: build-and-p6-before-idle.log. SwiftLint пяти изменённых файлов прошёл с --no-cache, 0 нарушений; обычный режим упёрся в недоступный для записи кэш.
Уточнение harness и первый зелёный P6
Проверенный прогрев default WKWebsiteDataStore перед context-load даёт ненулевой Network PID; в последующем failure network_restarted=false. Это сняло лишнее отличие первого холодного прогона. testTabWithoutControllerMatchesP6BeforeIdleUnload теперь зелёный: совпадают хвост и все проверяемые признаки, новая правильно подключённая вкладка работает. Механизм получен контролируемым отсутствием controller; естественный путь создания такой вкладки в приложении ещё не найден.
Проверка replacement manager сначала не дошла до failure-пикселя: teardown первого harness снимает navigationDelegate, и при переносе view его надо восстановить. Исправлен только тестовый adoptTab; это ошибка постановки эксперимента, не результат о CPM. Начат полный прогон семи тестов с исправлением: p5-p6-full.log.
Дополнительный контроль P6
Добавлен восьмой тест testCurrentControllerBeforeContextLoadRemainsHealthy: создать вкладку до завершения context-load, но сразу с правильным controller, и потребовать initResp, скрипты и отсутствие initialization_failed. Это отделяет обычный ранний startup от отсутствующей привязки controller. Он будет выполнен после текущего запущенного бинарника; результат пока не получен.
Подтверждены два контролируемых механизма
P6, replacement manager — зелёный, 9.186 с. Сначала существующая вкладка получила initResp со старым менеджером. После teardown старого менеджера её тот же WKWebView и прежний controller зарегистрированы в replacement manager. Получены два failure и actual stuck с load>view>proxy_on, context/tab=true, controller/scripts=false, view=true, creations=1, errors=none, network_restarted=false. Новая вкладка replacement manager получила initResp. Это воспроизводит сохранённую привязку; естественный триггер замены менеджера не доказан.
P5, забытая живая вкладка — зелёный, 72.744 с. Два нормальных initResp и две реальные idle-выгрузки, затем test-only didCloseTab без удаления WKWebView. Получен actual stuck с load>view>proxy_on>dealloc>view>proxy_on>dealloc, creations=2, view=false, context=true, tab=false, controller/scripts=true, errors=none, network_restarted=false. Content script отправляет init и получает init-error. После didOpenTab эта же вкладка снова получила initResp. Механизм подходит под P5-подгруппу с tab_in_context=false; его доля в проде и естественное действие приложения, создающее нарушение lifecycle, этим тестом не установлены.
Оба результата находятся в p5-p6-full.log; другие сценарии продолжают выполняться.
Полный первый прогон: положительные и отрицательные результаты
Прогон p5-p6-full.log завершён: 7 тестов, 4 зелёных и 3 красных (11 assertions), 273.8 с. Не все красные результаты означают дефект harness:
- P6 после idle — зелёный, 38.658 с. Получена цепочка
load>view>proxy_on>deallocс исходной сигнатурой controller/scripts=false, tab/context=true. Reload контекста не исправил существующую вкладку; новая правильно подключённая вкладка получила initResp. - Потеря runtime listener только на одном wake не воспроизвела P5. Инъекция действительно потеряла первый init (native dashboard не пришёл), но после восстановления файла и следующего idle новый worker принял следующий init и прислал initResp, stuck=0. Ожидание устойчивого P5 в тесте было неверным; превращаю его в отрицательную characterization-проверку этого результата.
- Долгий документ: первый image-subresource действительно удерживался 35 с. Background выгрузился примерно на 32 с, но на finish навигации в 35 с WebKit снова создал background; на failure в 39 с view уже жива. При искусственной потере dashboard получен stuck с живой view, а не P5. Здоровый документ получил initResp и не вызвал failure.
- Ошибка постановки второго долгого документа: одинаковый URL image взялся из кэша, поэтому второй load не был долгим. Исправляю уникальный URL на каждую навигацию и повторю оба контроля.
- Наблюдение о trace: watcher держал strong reference на view через 50-мс await. При переходе сразу после
context._backgroundWebView=nildealloc старой view иногда попадал после создания новой. Убираю удержание через await и буду ждать одновременно отсутствия WebKit background и dealloc в production recorder, чтобы не менять исследуемую последовательность самим наблюдением.
Все неподтверждённые результаты выше остаются отрицательными/ограниченными; совпадение P5 пока подтверждено только для test-only потери регистрации живой вкладки.
Уточнённые проверки P5 — начат повтор
Теперь 9 тестов. В новом testNoRuntimeListenerMetadataFromFirstStartupMatchesP5 runtime listener не регистрируется с самого первого старта; затем background явно загружается второй раз и оба раза штатно выгружается. Это отдельно проверяет пустые listener metadata, которые нельзя было получить простым пропуском регистрации на одном wake после здорового старта. Условия искусственные; естественная утрата metadata не доказана. После проверки инъекция снимается, выполняются explicit background load и проверка initResp.
Первоначальный listener-тест переименован в testMissingRuntimeListenerOnOneWakeRecoversAfterNextIdle и проверяет реально наблюдённое восстановление, отсутствие stuck и третью view.
Оба 35-секундных документа теперь используют разные URL image. Ожидания изменены по наблюдению первого прогона: здоровый длинный документ — no failure; потеря dashboard — stuck с живой view, не P5. Расширен test-only background trace на все типы autoconsent-сообщений, чтобы установить, какое сообщение повторно будит worker на finish.
Watcher больше не удерживает WKWebView через await, а idle-helper требует и освобождения background в WebKit, и dealloc по recorder. Повтор пяти новых/уточнённых проверок: p5-refined-and-controls.log.
Здоровый startup-контроль подтверждён
testCurrentControllerBeforeContextLoadRemainsHealthy — зелёный, 1.190 с: вкладка с правильным controller проходит стартовый gate, получает initResp и скрипты, initialization_failed=0. Одной ранней загрузки контекста для P6 в этом сценарии недостаточно.
Повтор one-wake listener подтвердил восстановление и отсутствие stuck. Остался только ошибочный assertion формата счётчика: pixel bg_view_creations для трёх view равен 3_to_5, а не строке 3. Проверка заменена на raw backgroundWebViewCreateCount == 3; будет повторена. Расширенный trace действительно показывает дополнительные report/eval после init — проверяю их связь с повторным пробуждением длинного документа.
P5 с исправными tab/controller/scripts — воспроизведён
testNoRuntimeListenerMetadataFromFirstStartupMatchesP5 зелёный, 70.149 с. Пропуск runtime listener с первого старта, две реальные idle-выгрузки (между ними explicit background load), затем две навигации дали actual stuck:
load>view>proxy_on>dealloc>view>proxy_on>dealloc, view=false, creations=2, context/tab/controller/scripts=true, errors=none, network_restarted=false. Content script отправляет init; новые view не создаются. После разрешения регистрации listener в установленной тестовой копии скрипта, explicit background load и навигации получен initResp.
Это соответствует P5 с внешне исправной вкладкой. Доказан механизм пустых listener metadata при test-only инъекции; естественная потеря metadata в приложении/WebKit не найдена. В отличие от него, одноразовый пропуск listener после здорового старта восстанавливается на следующем wake.
Два долгих документа с потерянными dashboard после устранения image-cache — зелёная отрицательная проверка, 80.181 с: оба load действительно по 35 с, в обоих документах есть initResp, production monitor получает stuck, но в его snapshot view=true и цепочка заканчивается view>proxy_on. Это воспроизводит сбой диагностического канала, но не P5. Здоровый контроль ещё выполняется. Лог: p5-refined-and-controls.log.
Долгие документы: повтор завершён
Здоровый контроль с двумя разными 35-секундными image — зелёный, 70.683 с: оба документа получили initResp, initialization_failed=0 и stuck=0. На finish background снова просыпается: trace показывает recv report и recv eval примерно на 35.39 с после background none на 32.35 с. Таким образом, в проверенном сценарии длинная загрузка сама по себе не даёт P5.
Повтор пяти тестов завершён: четыре зелёных, один красный исключительно из-за ранее описанного сравнения bucket со строкой «3» (исправлено). Начат финальный прогон оставшихся сценариев и исправленного assertion: p5-p6-final-regressions-and-stall.log.
Добавлен десятый эксперимент testMainActorStallDelaysActualFailureSnapshots: после реального initResp искусственно теряется dashboard, затем test host main actor дважды блокируется на 35 с при ожидающем production timeout. Тест требует actual failures/stuck и восстановления; совпадение P5 отдельно записывается как matchesP5, а не предполагается. Это исследование задержанного выполнения host callbacks, не реальная iOS suspend/resume.
Задержка host main actor: P5 не получен
testMainActorStallDelaysActualFailureSnapshots — зелёный, 71.027 с; результат классификации matchesP5=false. При двух блокировках main actor на 35 с реальные failure-пиксели пришли через 35.42 и 70.68 с, второй дал stuck. В обоих снимках view=true, creations=1, хвост остаётся load>view>proxy_on, context/tab/controller/scripts=true, errors=none, network_restarted=false. InitResp пришёл в документы до пауз; dashboard терялся намеренно. После снятия инъекции новая вкладка работает.
Это подтверждает задержку deadline, но в проверенном порядке callbacks не воспроизводит P5. Настоящие iOS suspend/resume и остановка всего процесса не проверялись; блокировка main actor не подменяет их.
Финальный прогон также повторно подтвердил replacement-manager P6 (9.167 с) и P5 с забытым live tab (72.737 с) после устранения удержания view watcher'ом.
Итог локальных repro-проверок
Финальный прогон шести сценариев — зелёный: 6 тестов, 0 failures, 262.676 с. Исправленный one-wake listener-тест зелёный (62.633 с). P6 до/после idle повторно зелёные (8.569 / 38.543 с).
С учётом четырёх зелёных независимых проверок из предыдущего прогона все 10 актуальных методов добавленного класса имеют зелёный результат. Это проверено несколькими запусками; единого прогона всех десяти после последних добавлений не было. Сводка по каждому методу и исходному логу: /private/tmp/cpm-p5-p6-20261015/results.json, генератор summarize.py.
Что воспроизведено:
- P6: отсутствующий controller до и после idle; сохранённый controller существующего WKWebView после replacement manager. Reload контекста не исправляет такую вкладку; новая вкладка работает.
- P5: потеря WebKit-регистрации сохранившейся живой вкладки; отсутствие runtime listener metadata с первого старта. Совпадают хвост, actual stuck и диагностические признаки; после адресного снятия инъекции работа восстанавливается.
Что P5 не дало на macOS 26.7.1: однократная потеря listener на wake после здорового старта, два действительно долгих документа, две задержки main actor на 35 с. Потеря dashboard при дошедшем initResp даёт диагностический stuck с живой view.
Ограничения: все положительные воспроизведения требуют test-only условия; естественные production-триггеры отсутствующего controller/утраты listener metadata не найдены. Настоящая iOS suspension не проверена. Фиксы поведения приложения этим исследованием не добавлялись.
Запущен обычный пакетный прогон swift test --disable-automatic-resolution --skip-build без repro-флагов; результат будет записан ниже. Финальный strict SwiftLint семи изменённых файлов: 0 нарушений (--no-cache). SHA-1 существующего dirty Package.resolved не изменился: 7ccce20acd452656c9bf2d2fa186d044e4a8185f.
Финальная проверка пакета и файлы
Обычный пакетный прогон завершён успешно: swift test --disable-automatic-resolution --skip-build, 474 XCTest, из них 24 opt-in repro пропущены, 0 failures, 11.344 с. Лог: /private/tmp/cpm-p5-p6-20261015/package-tests.log. Отдельный Swift Testing runner: 0 тестов. Длительные эксперименты запускались отдельно с обоими repro-флагами; все 10 текущих методов имеют зелёный результат в сводке.
Изменены только тестовые файлы этой работы внутри выбранного worktree:
- добавлены
CPMRepro/CPMReproLifecycleHypothesisTests.swift(10 тестов) иCPMRepro/CPMReproDelayedResource.swift; - расширены
CPMReproHarness.swift,CPMReproTabs.swift,CPMReproSupport.swift,CPMReproPrelude.swift,CPMReproTestCase.swift; - existing dirty production/telemetry changes не включались в эту работу, Package.resolved сохранил первоначальную контрольную сумму. Commit/PR/push не делались.
Команда полного повторения новых экспериментов из SharedPackages/WebExtensions:
CPM_REPRO=1 CPM_REPRO_P5_P6=1 swift test --disable-automatic-resolution --filter CPMReproLifecycleHypothesisTests
Эта полная команда приведена для повторения: в текущей сессии десять методов проверены раздельными группами, а не одним финальным запуском всех десяти.
| Текущий тест | Последний результат, с | Лог |
|---|---|---|
| CurrentControllerBeforeContextLoadRemainsHealthy | pass, 1.190 | p5-refined-and-controls.log |
| TabWithoutControllerMatchesP6BeforeIdleUnload | pass, 8.569 | p5-p6-final-regressions-and-stall.log |
| TabWithoutControllerMatchesP6AfterIdleUnload | pass, 38.543 | p5-p6-final-regressions-and-stall.log |
| ExistingTabKeepsPreviousManagerController | pass, 9.167 | p5-p6-final-regressions-and-stall.log |
| ForgottenLiveTabDoesNotWakeBackgroundAndMatchesP5 | pass, 72.737 | p5-p6-final-regressions-and-stall.log |
| MissingRuntimeListenerOnOneWakeRecoversAfterNextIdle | pass, 62.633 | p5-p6-final-regressions-and-stall.log |
| NoRuntimeListenerMetadataFromFirstStartupMatchesP5 | pass, 70.149 | p5-refined-and-controls.log |
| SlowHealthyDocumentsWakeBackgroundAgainAtFinishWithoutFailure | pass, 70.683 | p5-refined-and-controls.log |
| SlowDocumentsWithLostDashboardDoNotMatchP5 | pass, 80.181 | p5-refined-and-controls.log |
| MainActorStallDelaysActualFailureSnapshots | pass, 71.027; matchesP5=false | p5-p6-final-regressions-and-stall.log |
Зелёный результат characterization-теста означает, что проверенное условие и ожидаемый исход подтверждены. Он не означает исправление CPM или доказательство естественного production-триггера.
11. Привязка P5/P6 к достижимым production-путям
Продолжение после замечания: положительный test-only fault доказывает механизм, но не объясняет его возникновение у пользователя. Дальше нужны конкретные caller → browser state → WebKit side effect.
Найден кандидат P5: закрытие окна с shared pinned tab
Проверено в исходниках текущего repro-worktree и macOS release tag 1.210.0+macos:
MainWindowController.windowWillCloseотфильтровывает сохранённые общие pinned tabs перед явнымиdidCloseTab, но послеwindowControllersManager.unregister(self)всё равно вызываетeventsListener.didCloseWindow(self).MainWindowController+WKWebExtensionWindow.tabs(for:)возвращаетloadedPinnedTabs + loadedTabs;MainViewController.windowWillCloseиWindowControllersManager.unregisterэти коллекции не очищают.- В локальном WebKit checkout
99051d5d08cd9f19ec76ce599f7539d070b1ae09,WebExtensionContextCocoa.mm:1351–1367,didCloseWindowвызываетdidCloseTabдля каждого элементаwindow.tabs(). Фильтрация явных callbacks браузером не предотвращает этот внутренний вызов. didCloseTabудаляет tab изm_tabMap;runtimeSendMessageпроверяет tab до пробуждения background и при отсутствии возвращаетtab not found. Это возможная связь с P5/tab_in_context=false.
Начат эксперимент на системном WebKit: один и тот же живой tab в двух windows; закрыть одну через production WebExtensionEventsListener.didCloseWindow, сохранить tab в другой, без прямого didCloseTab, изменения listener или выгрузки context. До выполнения это кандидат, не подтверждённое воспроизведение.
Проверка истории также отсекла старые объяснения: исправление Close Other Tabs и фильтр shared pinned tabs уже находятся в 1.210.0+macos; синхронное создание manager до state restoration существует с февраля 2026. Их прежние версии нельзя выдавать за текущий production-триггер.
P5: production callback → WebKit side effect подтверждены
Два новых теста зелёные, 0 failures:
testClosingWindowForgetsSharedPinnedTabThroughWebKitAndMatchesP5— 73.033 с. Один живой tab предоставлен двумя window delegates, как shared pinned tab в macOS. Две успешные инициализации и две штатные idle-выгрузки. Затем только unregister закрываемого окна и productioneventsListener.didCloseWindow— без явногоdidCloseTab, изменения listeners, ручной выгрузки context или замены manager. Сразу после callbacktab_in_contextменяется true → false, WKWebView остаётся тем же и tab сохраняется в другом окне.- Две последующие навигации дали actual initialization failures и stuck:
load>view>proxy_on>dealloc>view>proxy_on>dealloc, creations=2, view=false, context=true, tab=false, controller/scripts=true, errors=none, network_restarted=false. Content script получает init-error. Новая вкладка работает;didOpenTabвосстанавливает исходную. testClosingUnrelatedWindowPreservesRegisteredTab— 1.205 с: закрытие окна, не возвращающего эту вкладку, сохраняет tab_in_context=true, initResp и 0 failures.
Лог: /private/tmp/cpm-p5-p6-20261015/production-window-close.log. Это source-linked воспроизведение на системном WebKit под macOS 26.7.1 через production listener и неизменённую логику CPM, а не произвольное закрытие живого tab. Harness использует window/tab delegates, повторяющие контракт macOS; сам MainWindowController/AppKit целиком в SPM-тест не загружен. Следовательно, browser call chain подтверждён чтением исходников, внутренний эффект WebKit и P5 — исполнением теста.
Область объяснения: P5/tab_in_context=false (в выбранном Mac-срезе 68 событий). Это не доказательство, что все эти события были caused by shared pinned windows, и не объяснение P5/tab_in_context=true либо iOS.
Тесты WindowsManagerPopupTests в macOS проверяют только список явных didCloseTab и поэтому не проверяют повторное закрытие tabs внутри системного didCloseWindow.
P6: найден условный production-путь, начата проверка
В AppDelegate.setupWebExtensions главная webExtensions flag подключена к production WebExtensionFeatureFlagHandler. Disable вызывает uninstallAllExtensions, затем callback очищает coordinator/manager. Enable вызывает initializeWebExtensions: если manager отсутствует, создаётся новый controller, а существующие WKWebView не пересоздаются. WebKit WebExtensionTab::webView отвергает view, чей controller не равен controller контекста.
Добавлены отдельные source-linked тесты CPMReproProductionFlagTests:
- явное true → false → true главного flag через реальный handler, с host callbacks по AppDelegate и существующей вкладкой, предоставленной WebKit при загрузке replacement context;
- negative control: цикл только embedded flag, при неизменном главном, должен сохранить controller и initResp;
- внутри первого теста есть контроль true → true: повторный одинаковый флаг не должен заменять manager.
Это проверка достижимости механизма через production flag handler, не утверждение, что flag переключался у пользователей. И macOS, и iOS имеют defaultValue enabled для главного flag; подтверждения реального false → true в рассматриваемой выборке нет. Обычная задержка скачивания config сама по себе не доказывает этот путь.
Первый прогон flag-путей: P6 получен, контроль обнаружил известную startup race
production-flag-cycle.log: главный flag-тест зелёный, 9.244 с. Реальный handler по false удалил contexts и host references; по true создан новый manager при сохранённом старом WKWebView/controller. Получены два actual failures и stuck с точным load>view>proxy_on, context/tab/view=true, controller/scripts=false, creations=1, errors=none, network_restarted=false. true → true manager не заменил; новая вкладка replacement manager работает. Это подтверждает условный production-путь P6 при реальном переключении главного flag.
Embedded-контроль красный, 6.119 с, 3 assertions: controller/scripts/tab=true, но первая навигация в 0.704 с ушла до sw activate в 0.747 с и потеряла init. Таким образом, «embedded cycle всегда здоровый сразу после sync» — неверное ожидание: здесь воспроизводится уже известный механизм A / P4, а не P6. Не считаю красный результат ошибкой постановки.
Для изоляции именно controller-binding уточняю контроль: после sync явно дождаться loadEmbeddedExtensionBackgroundContent, затем требовать initResp без failures при прежнем controller. Этот readiness wait — контрольное условие теста; production fix A данной работой не добавляется.
P6: уточнённый прогон завершён
production-flag-cycle-final.log: 2 tests, 0 failures, 10.237 с:
testMainFeatureFlagCycleRecreatesManagerAndMatchesP6— 9.252 с; повторное подтверждение P6 через true → false → true главного flag.testEmbeddedFeatureFlagCycleKeepsControllerAfterBackgroundBecomesReady— 0.985 с; прежний controller и scripts=true, после explicit readiness wait initResp и 0 failures. Первоначальный красный контроль с потерей первой навигации сохранён в предыдущем логе и описан выше как известный A.
Таким образом, добавлены четыре новых source-linked проверки: две window-close и две flag-cycle; все четыре текущих метода зелёные в двух запусках. Они выполняют production listener/flag handler/manager/CPM и системный WebKit; app-side host callbacks и window delegates реализованы harness по исходникам macOS, приложение целиком не запущено.
Оставшиеся неизвестные: происхождение P5 с tab_in_context=true; реальные iOS lifecycle-условия; было ли переключение главного flag в P6-сессиях. Главный flag default enabled, поэтому config fetch с неизвестной задержкой не считается доказанным триггером.
Проверка после source-linked дополнений
- Обычный пакет:
swift test --disable-automatic-resolution --skip-build— 478 XCTest, 28 opt-in skipped, 0 failures, 10.469 с. Логpackage-tests-production-paths.log. - Strict SwiftLint всех восьми файлов этой работы — 0 violations (
--no-cache). summarize.pyиresults.jsonобновлены: 14 текущих методов (10 первоначальных + 4 source-linked) имеют pass по последним соответствующим логам. Это несколько групп запусков, не единый прогон всех 14.- Изменения этой работы остались в
SharedPackages/WebExtensions/Tests/WebExtensionsTests/CPMRepro/: дополнен harness windows/initial-tabs, два P5 window tests вCPMReproLifecycleHypothesisTests.swift, новыйCPMReproProductionFlagTests.swift. Новых production-правок нет; первоначальный tracked dirty diff — те же 10 файлов / 164 additions / 8 deletions. SHA-1 Package.resolved прежний7ccce20acd452656c9bf2d2fa186d044e4a8185f.
Точки для проверки call chain:
- browser:
macOS/DuckDuckGo/MainWindow/MainWindowController.swift:611–640,macOS/DuckDuckGo/WebExtensions/MainWindowController+WKWebExtensionWindow.swift:31,macOS/DuckDuckGo/Windows/View/WindowControllersManager.swift:178; - WebKit:
Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionContextCocoa.mm:1351–1367(window closes tabs),:1419(forgetTab),Cocoa/API/WebExtensionContextAPIRuntimeCocoa.mm:129–141(tab check before wake); - P6 browser:
macOS/DuckDuckGo/Application/AppDelegate.swift:2153–2166,2209–2221; shared production handler:Sources/WebExtensions/FeatureFlag/WebExtensionFeatureFlagHandler.swift; WebKit validates controller inWebExtensionTabCocoa.mm:339–360.
Повторить именно четыре новых source-linked теста из alex/cpm-web-extension-remaining-failures/SharedPackages/WebExtensions:
CPM_REPRO=1 CPM_REPRO_P5_P6=1 swift test --disable-automatic-resolution --filter 'CPMReproProductionFlagTests|CPMReproLifecycleHypothesisTests/testClosing'
12. Актуальный итог P5/P6: доказательства, полный реестр и повторение
Границы доказательства и сигнатуры
| Механизм | Что проверено | Чего это не доказывает |
|---|---|---|
| P5: закрытие окна с общим pinned tab | Browser call chain прочитан в текущем worktree и tag 1.210.0+macos; production listener и системный WebKit исполнены. После window callback tab сохраняется в другом window delegate, но tab_in_context становится false; два failure → stuck с нужным хвостом |
Полное приложение/AppKit в SPM не запускались; неизвестно, какая доля production P5 связана с этим действием. Не объясняет P5/tab=true или iOS |
| P5: нет runtime listener metadata | При подавлении регистрации с первого старта, двух настоящих idle-выгрузках и explicit load между ними получен P5 с исправными tab/controller/scripts | Это test-only исходное условие. Естественное исчезновение metadata и подходящий production caller не найдены |
| P6: главный flag меняет manager | Реальные handler/uninstall/load/context, существующая view при загрузке replacement context; прежний controller сохраняется и воспроизводит P6 | Смена состояния flag задаётся тестом; события false→true в production-выборке не установлены. Default главного flag enabled на обеих платформах |
| P6: nil controller | До и после idle контекст знает tab, но scripts отсутствуют; context reload не исправляет конфигурацию созданного WKWebView | Это controlled construction. Синхронный manager до state restoration уже есть с февраля 2026; обычный ранний startup сам по себе причиной не оказался |
| Длинные документы / потерянный dashboard / задержка main actor | Требуется отдельно смотреть page initResp, native arrival и actual failure snapshot. Длинные здоровые документы работают; lost dashboard/stall дают stuck при живой view | Ни один из этих сценариев не воспроизвёл P5 на проверенной macOS. Host main-actor stall не равен настоящему iOS suspend/resume |
Две положительные сигнатуры:
- P5:
load>view>proxy_on>dealloc>view>proxy_on>dealloc; creations=2, view=false, context=true, errors=none, network_restarted=false. Shared-window/прямой didCloseTab: tab=false, controller/scripts=true. Отсутствие listener с первого старта: tab/controller/scripts=true. - P6:
load>view>proxy_on(view=true) либоload>view>proxy_on>dealloc(view=false); creations=1, context/tab=true, controller/scripts=false, errors=none, network_restarted=false. Main-flag source-linked тест проверяет вариант с живой view; вариант после idle проверен с nil controller.
tab_controller_match=false означает nil или другой controller: recorder требует ненулевую identity-совместимость. tab_in_context проверяет identity tab среди context.openTabs; tab_has_ext_scripts ищет user scripts в content world текущего UUID контекста. Эти поля не являются взаимозаменяемыми и не доказывают page receipt.
Все 14 текущих P5/P6-проверок
Все результаты ниже извлечены из локальных логов; название без суффикса файла — метод XCTest. Первые 12 находятся в CPMReproLifecycleHypothesisTests.swift, последние 2 — в CPMReproProductionFlagTests.swift.
| Метод | Последний результат, с | Наблюдённый смысл pass | Исходный лог |
|---|---|---|---|
testCurrentControllerBeforeContextLoadRemainsHealthy |
pass, 1.190 | Контроль P6: correct controller до context-load → initResp, 0 failures | p5-refined-and-controls.log |
testTabWithoutControllerMatchesP6BeforeIdleUnload |
pass, 8.569 | Инъекция nil controller → P6, view=true; новая вкладка работает | p5-p6-final-regressions-and-stall.log |
testTabWithoutControllerMatchesP6AfterIdleUnload |
pass, 38.543 | nil controller → P6 после idle; context reload не чинит эту view | p5-p6-final-regressions-and-stall.log |
testExistingTabKeepsPreviousManagerController |
pass, 9.167 | Прямой controlled replacement → P6; прежние view/controller сохранены | p5-p6-final-regressions-and-stall.log |
testClosingWindowForgetsSharedPinnedTabThroughWebKitAndMatchesP5 |
pass, 73.033 | Browser callback → WebKit: shared-window close → P5/tab=false; восстановление и healthy новая вкладка | production-window-close.log |
testClosingUnrelatedWindowPreservesRegisteredTab |
pass, 1.205 | Контроль window close: без общей вкладки initResp и 0 failures | production-window-close.log |
testMainActorStallDelaysActualFailureSnapshots |
pass, 71.027 | Две паузы host main actor по 35 с → actual stuck, но matchesP5=false; view жива | p5-p6-final-regressions-and-stall.log |
testForgottenLiveTabDoesNotWakeBackgroundAndMatchesP5 |
pass, 72.737 | Прямой test-only didCloseTab → P5/tab=false; didOpenTab восстанавливает | p5-p6-final-regressions-and-stall.log |
testMissingRuntimeListenerOnOneWakeRecoversAfterNextIdle |
pass, 62.633 | Один wake без listener после здорового старта → следующий wake здоровый, stuck=0 | p5-p6-final-regressions-and-stall.log |
testNoRuntimeListenerMetadataFromFirstStartupMatchesP5 |
pass, 70.149 | Test-only отсутствие listener с первого старта; два idle и explicit load между ними → P5/all tab flags=true | p5-refined-and-controls.log |
testSlowHealthyDocumentsWakeBackgroundAgainAtFinishWithoutFailure |
pass, 70.683 | Два 35-секундных image → initResp, report/eval снова будят background на finish, 0 failures | p5-refined-and-controls.log |
testSlowDocumentsWithLostDashboardDoNotMatchP5 |
pass, 80.181 | Два 35-секундных image + потеря dashboard → initResp и stuck с живой view; не P5 | p5-refined-and-controls.log |
testMainFeatureFlagCycleRecreatesManagerAndMatchesP6 |
pass, 9.252 | Production flag handler true→false→true → P6; true→true controller не меняет | production-flag-cycle-final.log |
testEmbeddedFeatureFlagCycleKeepsControllerAfterBackgroundBecomesReady |
pass, 0.985 | Embedded-only cycle сохраняет controller; initResp после explicit background-ready wait | production-flag-cycle-final.log |
Зелёный тест с ожидаемым сбоем означает воспроизведение механизма; зелёный negative control означает отсутствие предполагаемой сигнатуры в проверенных условиях. Исходные неудачные ожидания, ошибки harness и их исправления не удалены из §10–11. Единого итогового запуска всех 14 не было: green подтверждён раздельными группами. 478 XCTest обычного пакета включают 28 пропущенных opt-in; они не заменяют выполненные длительные repro.
Среда, исходники и устройство harness
- Worktree:
/Users/admin/DuckDuckGo/apple-browsers.git/alex/cpm-web-extension-remaining-failures, HEADa0717cf098cd1cbd32d5bc9d19aa7db0fab2399f; новые тесты untracked, поэтому checkout одного commit их не восстановит. - macOS 26.7.1 (25G241), Xcode 27.1 (27A9275), системный WebKit; bundled embedded extension 2026.9.29. Проверка source:
/Users/admin/Downloads/WebKit_sparse, HEAD99051d5d08cd9f19ec76ce599f7539d070b1ae09. Этот checkout не доказывает, что весь его код соответствует shipped binary; нужный эффект отдельно проверен на system WebKit. - Harness запускает production extension manager/coordinator, recorder, health monitor и native autoconsent handler, реальные WKWebView и WKWebExtension contexts. Host window/tab delegates и callbacks flag enable/disable собраны по исходникам macOS, не импортированы из целого приложения.
- База Options: startup navigation gate и M4 включены. В P5/P6-тестах M4 выключен, чтобы actual stuck не был сразу затёрт automatic reload; navigation gate остаётся включён. Перед context load прогревается Network-процесс default data store. Без прогрева PID=0→ненулевой может дать network_restarted=true без crash; это было причиной первого несовпадения.
- Временная распакованная копия bundled extension содержит test prelude/DOM marks и trace. Production zip не менялся. В source-linked window/flag сценариях listener не подавляется, dashboard не теряется намеренно, background startup не задерживается. Это не byte-identical bundle без instrumentation.
- Отдельные controlled сценарии используют: nil/старый controller, suppressed runtime listener, suppressed native dashboard method, задержанный image через WKURLSchemeHandler или блокировку main actor. Их нельзя выдавать за найденный естественный production-trigger.
- Content marks требуют
initrespдля здоровых контролей; native dashboard arrival отдельно измеряет production detector. Trace не должен удерживать view: watcher исправлен, чтобы strong reference не пересекал 50-мс await. Idle-helper требует одновременноcontext._backgroundWebView == nilиbackgroundWebViewAlive == falseв recorder. - Долгие image имеют уникальный URL на каждую навигацию, иначе второй документ берётся из cache.
adoptTabвосстанавливает navigationDelegate после teardown предыдущего harness. Счётчик 3 view в pixel — bucket3_to_5; raw assertion используетbackgroundWebViewCreateCount == 3. - Три новых файла:
CPMReproLifecycleHypothesisTests.swift,CPMReproDelayedResource.swift,CPMReproProductionFlagTests.swift. Пять дополненных:CPMReproHarness.swift,CPMReproTabs.swift,CPMReproSupport.swift,CPMReproPrelude.swift,CPMReproTestCase.swift. Существующая dirty telemetry/Package.resolved не включалась в эту работу; тестовыми изменениями production поведение не исправлялось.
Артефакты и история красных прогонов
Все перечисленные файлы находились в /private/tmp/cpm-p5-p6-20261015/ на момент документирования. Папка временная: долговечная сводка значимых исходов, причин красных assertions и ограничений сохранена здесь и в §10–11; сами raw logs могут исчезнуть.
| Файл | Результат и как его читать |
|---|---|
build-and-p6-before-idle.log |
P6 получен, но network_restarted=true из-за холодного PID attribution; не полное совпадение |
p6-fast.log |
2 теста / 1 failure: перенос старой view упёрся в снятый navigationDelegate; исправлен test adoptTab. P6 до idle уже green |
p5-p6-full.log |
7 тестов / 11 failing assertions, 273.808 с: one-wake listener сам восстановился; finish длинного документа разбудил background; повторный image был cached. Эти результаты разделены на отрицательные выводы и ошибку постановки, а не скрыты |
p5-refined-and-controls.log |
5 тестов / 1 failure, 289.130 с: 4 green, оставшийся assertion ожидал строку «3» вместо bucket «3_to_5». Исправлен raw count assertion и повторён |
p5-p6-final-regressions-and-stall.log |
6 тестов / 0 failures, 262.676 с; задержка main actor даёт matchesP5=false |
production-window-close.log |
2 теста / 0 failures, 74.238 с; source-linked P5 и unrelated-window control |
production-flag-cycle.log |
2 теста / 3 assertions, 15.363 с: main flag P6 green; embedded-only first init потерян до worker activate — известный A/P4, не P6 |
production-flag-cycle-final.log |
2 теста / 0 failures, 10.237 с; embedded control проверен после explicit ready wait |
package-tests.log |
Промежуточный пакет: 474 XCTest, 24 skipped, 0 failures, 11.344 с |
package-tests-production-paths.log |
Последний пакет: 478 XCTest, 28 skipped, 0 failures, 10.469 с |
summarize.py, results.json |
Сводка последних результатов 14 текущих методов, имена исходных логов и SHA-256 source файлов; это не лог одного общего прогона |
Контрольные суммы актуальных тестов:
CPMReproLifecycleHypothesisTests.swift:60c2f3081c08a40cfc0244e023652013f0abf939151a63c14addc400a40ed413.CPMReproProductionFlagTests.swift:240c3828e3fac13357c6e80a954f3d07fc5dd26376d36308258ecc984bfd860e.- Существующий dirty
Package.resolved, SHA-1:7ccce20acd452656c9bf2d2fa186d044e4a8185f.
Команды повторения и границы следующей работы
Из /Users/admin/DuckDuckGo/apple-browsers.git/alex/cpm-web-extension-remaining-failures/SharedPackages/WebExtensions:
# Все 14 P5/P6 методов; эта общая команда дана для повторения, единым прогоном ещё не выполнялась.
CPM_REPRO=1 CPM_REPRO_P5_P6=1 swift test --disable-automatic-resolution --filter 'CPMReproLifecycleHypothesisTests|CPMReproProductionFlagTests'
# Только четыре новых source-linked метода.
CPM_REPRO=1 CPM_REPRO_P5_P6=1 swift test --disable-automatic-resolution --filter 'CPMReproProductionFlagTests|CPMReproLifecycleHypothesisTests/testClosing'
# Обычные тесты после актуальной сборки: repro без флагов пропускаются.
swift test --disable-automatic-resolution --skip-build
Strict SwiftLint запускался для восьми перечисленных Swift-файлов из корня worktree: mint run swiftlint lint --strict --force-exclude --no-cache <paths>, 0 violations. --no-cache нужен, чтобы не писать во внешний SwiftLint cache; это не пропуск правил.
Карта P1–P3/A/B/init-progress, file names, параметры тяжёлых экспериментов и актуальные worktree — cpm-web-extension-code-guide-2026-10-09. Нельзя предполагать, что один worktree содержит все эксперименты или что HEAD включает локальные untracked файлы.
Эта актуализация — документация. Новые app/WebKit fixes, telemetry/schema changes, commit/push/PR, сообщения ревьюерам, переключения remote flags и новые iOS/device проверки не выполнялись. Старые результаты до исправления ожиданий сохраняются, а causal attribution production-групп не повышена до доказанной.
13. Integration tests в настоящем приложении — реализация и проверки
Запрос: без commit добавить app integration tests, по возможности воспроизвести известные сбои, обязательно разделить slow load, одну потерянную навигацию с recovery, persistent single-tab, несколько tabs одного окна и все окна.
Выбран cpm-e2e-init (HEAD 46f25175c7c42766cab23ea39f447f075c7bdd24): здесь уже PR0/baseline/init_stage и изменённый до этой работы JS bundle для init-progress. Существующие dirty bundle и SPM harness сохраняются. Новый слой — macOS app-hosted Integration Tests, реальный DuckDuckGo.app/AppDelegate/Tab/MainWindowController, production navigation/native/diagnostics. Это отличается от прежнего standalone SPM harness с собственными window delegates.
Исходная запись перед реализацией (история, не текущий итог). Планируется отдельная схема без destructive UI-scheme clean-app pre-action. Внешние сервисы/config и pixel transport изолируются, production monitor/navigation callbacks вручную не подделываются. Естественные P5 shared-window / условный P6 flag / process failure и controlled diagnostic scopes будут явно различаться. Начата предварительная сборка в /private/tmp/cpm-app-integration-20261015/; результатов новых integration tests пока нет.
App integration: реализация, 16 октября
В cpm-e2e-init/macOS/IntegrationTests/CPM/ добавлены app-hosted harness и отдельные методы для slow HTTP, slow native settings (received → recovery), одной потерянной навигации, persistent one-tab, нескольких tabs одного окна, всех окон и dashboard-transport negative control (initResp получен, detector timeout). Естественные/условные пути вынесены в CPMAppKnownFailureIntegrationTests: реальные AppDelegate flag callbacks, shared pinned window close, process termination, первая embedded install, slow wake, opt-in 30s WebKit timeout.
Тесты используют реальные Tab, MainWindowController, WindowsManager, macOS provider/native handler/delegate, navigation callbacks и системный WebKit. Monitor events вручную не вызываются. CPM Integration Tests.xcscheme не содержит pre/post actions для очистки Review или killall tests-server; тесты запускаются только при explicit opt-in и не параллельно. Собственные HTTP listener на loopback, временная распакованная extension copy, in-memory installation store, nonpersistent WebKit data store и recorder transport. Существующий dirty ZIP не менялся этой работой.
Минимальные seams: DEBUG-only AppDelegate factory, доступная лишь в .integrationTests при CPM_APP_INTEGRATION=1; необязательная controllerConfiguration у shared manager для изоляции data store (default production прежний). Контролируемые scoped faults меняют доставку настоящих autoconsent messages в extracted test copy; они не являются доказательством естественной причины production-событий.
Xcode bootstrap: исходный frozen lockfile отсутствовал на workspace-level; resolver подготовил зависимости. Попытка ad-hoc build упёрлась в provisioning app/VPN/helpers; начата unsigned build. В sandbox Xcode ошибочно объявлял существующий workspace не-workspace из-за доступа к build services, повтор выполняется с разрешённым доступом. Strict SwiftLint новых 6 Swift + 2 seams: 0 violations (--no-cache). Исполнение новых integration tests ещё не подтверждено; результаты будут дописаны после сборки.
App host bootstrap: проверенные блокеры и границы, 16 октября
Unsigned app/test bundle успешно собран (build-app-integration-2.log), но первый запуск не дошёл до XCTest: stack sample остановился в ConfigurationStore.loadData → NSFileCoordinator → open shared app-group config. Тестовый host остановлен отдельно, без killall и без очистки профиля. Добавлен отдельный directory initializer ConfigurationStore, прежний initializer сохранён; только при DEBUG + .integrationTests + CPM opt-in AppDelegate использует собственный temporary configuration cache и test defaults. Production default не меняется. Промежуточные compile/link ошибки из-за type inference/incremental symbol устранены; build-app-integration-5.log — TEST BUILD SUCCEEDED.
Повтор baseline-2.xcresult дошёл до реального XCTest и штатного cpm_background_ready_context_load (under_1s, ctx_load_to_view under_250ms). Затем создание настоящего Tab упёрлось в независимый сервис: SpecialPagesUserScript → OnboardingActionsManager → SecureVaultFactory.makeVault; main thread ждал lock, background CredentialsDatabaseCleaner ждал SecItemCopyMatching/SecurityServer. Это блокировка unsigned host при доступе к Keychain, не CPM failure и не зелёный baseline. Отдельный test process остановлен; пользовательское запущенное приложение не останавливалось.
Для suite добавлен DEBUG-only SecureVaultFactory.isDisabledForIntegrationTesting; AppMain выставляет его до создания app environment лишь для .integrationTests + CPM opt-in. Autofill/vault этим suite не тестируется и Keychain для него не требуется; monitor, Tab/navigation, native CPM и WebKit остаются production. Выполняется пересборка. Первый и второй запуск пока считаются bootstrap failures, не результатами новых CPM assertions. В коде уже 18 отдельных сценариев, включая M4 recovery и embedded-only negative control; P3 persistent cache / естественная утрата listener / iOS остаются отдельными непроверенными здесь областями.
После DEBUG-only vault isolation baseline-3.xcresult дошёл до реального Tab.willStart для HTTP fixture, где сработал существующий fatalError: обычным integration tests разрешён только WKURLSchemeHandler, настоящая HTTP-навигация запрещена. Добавлено узкое DEBUG-исключение: .integrationTests + CPM_APP_INTEGRATION=1 + host 127.0.0.1. Удалённые hosts и остальные suites сохраняют прежнюю защиту. Это требование инфраструктуры для настоящего HTTP и штатных navigation callbacks; это не CPM fix. Baseline №3 — infrastructure failure, не доказательство сбоя расширения. Пересборка и повтор продолжаются.
Baseline №4: настоящие HTTP navigation callbacks/native dashboard успешно проходят для двух реальных tabs (≈0.40/0.31 с), diagnostics: context/tab/controller/scripts/handler=true, errors=none, network_restarted=false, ready under_1s. Два assertions page receipt были красными: test storage перехватывал copyExtension, но штатная ZIP install использует extractExtension на background task, поэтому DOM bridge вообще не был внедрён. Исправлен override extraction с делегированием production extractor, затем instrumentation только временной копии. Content mark теперь оборачивает существующий runtime listener, не добавляя ещё одного listener для ответов. Этот красный результат сохранён (baseline-4.xcresult) как ошибка harness, не CPM init loss. Выполняется повтор с проверкой control bridge.
Исправление signing-обхода — 16 октября
После вопроса пользователя проверены исходные настройки: Debug app использует Automatic signing, DEVELOPMENT_TEAM=HKE973VLUW; валидные identities/profile доступны. Unsigned был моим обходом, а не требованием среды. Первую provisioning-ошибку вызвали мои CLI overrides CODE_SIGN_STYLE=Manual, пустой DEVELOPMENT_TEAM/profile и ad-hoc identity. Повтор без этих overrides — build-signed-default.log, TEST BUILD SUCCEEDED, Apple Development: Alexey Martemyanov. Дальнейшие результаты считаются по подписанному приложению; unsigned command убрана из README. Ранние bootstrap-гипотезы о невозможности штатной подписи неверны.
Добавленные test-only изоляции cache/vault не являются CPM fixes; необходимость их сохранения пересматривается после signed проверки. Временная extraction instrumentation теперь подтверждена строкой CPM APP INSTRUMENTED; baseline с native response, но без page mark всё ещё красный, control bridge не подтверждён. Добавлена фиксация DOM marks/native methods. Прямой async импорт WebKit Any-result заменяется callback continuation с String-only границей после runtime abort «freed pointer was not the last allocation» в диагностическом прогоне. Результаты не объявляются зелёными до проверки page receipt и реальных failures/recovery.
Signed baseline и one-navigation — первые подтверждённые результаты
signed-baseline-and-navigation.xcresult / .log: 2 tests, 0 failures, baseline 1.864 с и single-navigation/recovery 5.360 с. На здоровых страницах есть реальные DOM marks initresp (≈13–17 мс) и forwarded native dashboard; при ровно одной потерянной навигации mark отсутствует, есть штатный timeout, следующая навигация отвечает и даёт recovery_document=later_navigation. Это signed DuckDuckGo.app, TeamIdentifier=HKE973VLUW, hardened runtime.
Прежний unsigned vault-обход удалён из SecureVaultFactory.swift и AppMain.swift: эти два файла снова без diff. Последующая signed build без обхода успешна (build-signed-core.log). Recorder в harness теперь использует реальный macOS runtime feature-flag adapter (видимость internal для app-hosted теста), добавлен отдельный test переключения proxy flag с проверкой здоровых навигаций и omission readiness на новом view при off. Всего 19 методов. Запущены все 10 core diagnostic scope/recovery методов. Slow wake/freeze задают busy wait до первой загрузки контекста и ждут исходную готовность, чтобы проверка следующего wake не зависела от чтения изменённого JS из уже закэшированного ресурса.
Core suite завершён: 10/10, signed, штатный Vault
signed-core.xcresult / signed-core.log: 10 tests, 0 failures, все выполнены, без skip. Последовательные методы: M4 11.571 с; dashboard transport 5.195; runtime proxy flag 1.719; healthy baseline 1.149; one navigation/recovery 5.292; all windows + new tab + recovery 23.995; one tab + healthy controls 11.127; several tabs + controls 19.648; slow HTTP 8.872; slow native settings/recovery 8.961. Десять JSON attachments экспортированы в signed-core-evidence/.
Проверенные поля: controlled loss → init_stage=none; native settings delay → received, recovery=failed_navigation, 4s_to_10s; потеря dashboard подтверждения при наличии page initResp → response_sent; scoped loss → actual init_failed/stuck и recovery=later_navigation; M4 recovery идёт after_extension_reload. При proxy off новый view не имеет readiness-параметра, CPM отвечает; при on готовность снова измеряется. Vault-обход удалён, подпись штатная.
Known-failures suite начат с включённым expensive flag. Первый background-SIGKILL автоматически восстановился в том же контексте: actual page initResp + native ответ, timeline содержит died_crash/dealloc/new view. Следующий explicit reload потерял первую навигацию (init_stage=none, ready уже under_1s) — известная startup race A, а не доказательство неустранимого worker death. Recovery-контроль уточнён: после explicit reload дождаться production loadEmbeddedExtensionBackgroundContent, как уже сделано в embedded-only negative control. Исходный красный прогон сохраняется. Embedded-only cycle, first embedded install и first main-enable уже прошли; выполняется реальный WebKit load timeout.
Known paths: P1/P2/P6 и Network подтверждены; P5 требует повторной диагностики
Первый signed-known-failures.xcresult:
- Embedded-only negative control pass 1.243 с; первая embedded install pass 11.934 с: первая навигация потеряна, view/handler/tab/scripts корректны, readiness=pending, затем CPM работает — controlled A/P4.
- First main-enable pass 9.130 с и main true→false→true pass 14.616 с: реальные AppDelegate callbacks оставляют nil/старый controller у существующих Tab; новые tabs здоровы — app-level P6.
- Frozen background pass 68.319 с: настоящая idle-выгрузка, SIGSTOP нового background process, WebKit error 6;
bg_load_error_after=25s_to_35s, app_state=inactive, context_errors=WKWebExtensionContextErrorDomain:6, хвост содержит unresponsive/dealloc/error, creations=2. SIGCONT отправлен в defer/teardown. - Slow idle wake pass 50.469 с: busy startup 8 с задан до context load; после healthy baseline/idle получены pending + timeout и поздний ответ, затем здоровая навигация.
- Network SIGKILL дал 4/4 отказавших tabs двух окон, actual
init_stage=none, observation=global failure reproduced. Первый recovery-контроль после explicit reload попал в startup race A; добавлен readiness wait, идёт повтор. - Background SIGKILL сам восстановился (не утверждается persistent failure); первый post-reload контроль тоже попал в A; уточнён тем же readiness wait.
- Реальный shared-pinned-window close дошёл до WebKit
Tab not foundпосле двух здоровых idle cycles. Однако XCTest host завершился signal INT без финальных assertions/attachment; это пока не green app-level P5. Запущен отдельный повтор со stack sample, причина SIGINT ещё не установлена.
Добавлен 20-й метод P3 testLargePersistentCacheRecordsContextReloadInjectionGap: opt-in CPM_APP_INTEGRATION_CACHE=1, свежие собственные UUID website/controller stores, 20k loopback cache entries, настоящий baseURL-changing reload и проверка actual no-view failure. Сборка signed успешна, strict SwiftLint всех 11 текущих изменённых Swift — 0 violations. P3 ещё не исполнялся; normal suite остаётся nonpersistent. Persistent данные удаляются WebKit API только для UUID данного теста.
Recovery controls исправлены; источник INT в P5 установлен по коду
signed-known-retests.xcresult: background crash/reload control pass 3.328 с, Network global crash/reload control pass 21.972 с после explicit production readiness wait. Никакой browser fix для этих проверок не добавлялся. Повтор P5 опять завершился INT спустя около 4 с после window close, до timeout assertion. Отложенный stack sample не успел: к моменту съёмки test host уже завершился.
Найден штатный DEBUG caller: MainWindowController.windowWillClose вызывает self.ensureObjectDeallocated(after: 4.0, do: .interrupt); NSObjectExtension → breakByRaisingSigInt → raise(SIGINT). Тест держал let controller в async-method scope и закрытое NSWindow в harness.windows; это тестовое удержание, не CPM-сигнатура. Исправлено: pinning в ограниченном autoreleasepool, close удаляет окно из owned collection немедленно, surviving window index скорректирован. Проверка приложения не отключается. Signed build успешна, запущен повтор только P5.
Уточнение P5 после освобождения явной ссылки: первый actual failure уже был получен, но второй вызов helper снова мог выбрать закрытое окно. WindowControllersManager.windowController(for: Tab) сначала возвращает controller из tab.webView.window, даже если тот уже unregister. Поэтому automation могла повторно открыть/удержать closed window вместо пользовательского действия в surviving window и попасть в DEBUG deallocation interrupt. Исправлено только в harness: выбор из registered mainWindowControllers; shared pinned tab заранее выбирается в surviving window через настоящий TabCollectionViewModel.select. Production lifecycle/registration не подделывается, guard приложения остаётся включён. Ещё один повтор P5 необходим, прежний SIGINT не объявляется доказательством app crash/CPM failure.
P5 interrupt: LLDB дал точный объект
Первая попытка attach до idle изменила условия: idle-helper не дождался выгрузки за 45 с; этот debugger run — отдельный infrastructure failure, не результат P5. Attach перенесён только после двух idle cycles и window close.
pinned-lldb-after-idle.log: breakpoint в Common.breakByRaisingSigInt, stack NSObject.DeallocationCheckAction.interrupt → ensureObjectDeallocated, parameter description = MainWindow has not been deallocated, line NSObjectExtension.swift:80. Это уточняет прежнюю гипотезу: controller уже прошёл deinit, сработала следующая 8-секундная проверка его окна. Исправления только harness: создание NSWindow/MainWindowController целиком в отдельном autoreleasepool (как в существующих DeallocationTests), lookup/selection не держит controllers через await, closing window немедленно удаляется из owned list. Guards приложения и production window-close path не отключаются. Нужен повтор без debugger; прежние app-level P5 результаты пока red/incomplete, хотя actual first failure/tab-not-found уже получены.
16 Oct: signed cache/P5 follow-up (intermediate, not a green suite)
signed-pinned-autorelease.xcresult: real shared pin + two normal WebKit idle unloads + real window closure again producedruntime.sendMessage(): Tab not found, missing page initResp, production timeoutinit_stage=none,tab_in_context=false, matching controller andbg_view_creations=2. The host still interrupted before the second probe/attachment. Prior LLDB identifies the DEBUG deallocation guard for MainWindow, not an assertion from the CPM detector. Wrapping creation/selection in autorelease pools alone did not resolve that retention; the test is still red.signed-large-cache.xcresult: fresh persistent UUID store, 20,000 cacheable loopback resources, actual disk-cache records, fill 42.705 s. Real extension reload changed baseURL; immediate next navigation succeeded in 1.058 s, page initResp present, background created once and ready under 1 s.p3NoBackgroundFailure=false: P3 not reproduced in this run. The readiness control also succeeded. The test failed only in teardown (WKWebSiteDataStore: Data store is in use (by network process)), while its own controller/store references were still retained. Cleanup is being corrected before a new run.- These runs use the project's normal Apple Development signing. The earlier unsigned CLI workaround was incorrect and removed; no SecureVault bypass remains. Current verified distinct passing methods remain 18/20, with the two red methods above; there is no combined all-green result.
P5: memory-reference evidence and harness correction
pinned-lldb-retainers.log again stops in breakByRaisingSigInt for MainWindow. leaks --noContent --nostacks --traceTree=<closed-window> <owned-test-host> saved pinned-window-retainers.txt: a direct chain is NSApplication._currentEvent → NSEvent._window → closed MainWindow. SDK declares NSApplication.currentEvent strong, and Apple documents it as the last dequeued event. TestRunHelper.loadedViews is weak (confirmed in source), not a strong owner. The graph also contains autorelease-pool and system/weak references; not every reported root proves retention.
The harness now posts and dequeues one neutral application-defined event after window closure, using public AppKit APIs, to advance the event queue while async XCTest is awaiting. It does not disable the DEBUG guard, detach tabs, or send manual extension lifecycle events. Verification is pending. Persistent-store teardown now clears owned website/controller data, releases controllers and the last harness store reference, then retries WebKit's asynchronous removal for at most 10 seconds. The orphan from the first failed cleanup was removed only at its verified fresh UUID 83CBE3E6-4B0E-41F0-B53F-22CD306B8DEE (creation time and extension State.plist matched that test), after the owner process exited.
Signing verification detail
The latest signed build (build-signed-event-loop.log) invokes codesign with Apple Development: Alexey Martemyanov (9SAT8X9S84), team HKE973VLUW, hardened runtime. codesign -dvv with normal keychain access confirms the Apple Development → WWDR → Apple Root CA chain. App debug dylib and Integration Tests.xctest independently pass codesign --verify --strict. codesign --verify --deep --strict on the entire Debug app fails in nested DuckDuckGo VPN.app/Contents/Frameworks/Subscription.framework: the seal lists four missing Modules/Subscription.swiftmodule artifacts (abi.json/swiftdoc/swiftmodule/Project/swiftsourceinfo). This is a separate nested Debug packaging limitation, not an unsigned main app or test bundle. No manual re-signing or production packaging changes were made for it.
Verification of cleanup/event-queue changes: still red
signed-pinned-final-control.xcresult: posting/dequeuing a neutral event alone did not resolve the DEBUG window interruption. The first real P5 failure is again recorded in stdout, but no completed green test. The memory graph's currentEvent chain is a real reference; it was not established as the sole cause. Harness synchronousTab.setContent, diagnostics collection and polling conditions are now also confined to autorelease pools, pending verification.signed-large-cache-cleanup.xcresult: the 20k cache scenario again completed its body with healthy post-reload/readiness controls, but static store removal still failed after the bounded retry. Merely clearing data and dropping harness references was insufficient: the pre-existing Integration Tests process-pool swizzle outlives each test. Teardown now requests_terminateNetworkProcesson the owned store after evidence capture and data clearing, before UUID removal. This is cleanup, not a causal fault used to claim P3. The previous fresh UUID0FB56422-72D5-4CCB-AC66-86F3C50DE165is recorded explicitly for cleanup auditing; no user-store identifier is selected.
WebKit cleanup contract checked against primary sources: profiles API documents that removal can fail while an active view uses the profile; WKWebsiteDataStore.mm implements _terminateNetworkProcess by forwarding to its WebsiteDataStore. Current upstream source is reference material, not an assertion that this macOS release has the identical revision. The actual runtime selector is checked before test-only cleanup.
P5 app test scope narrowed explicitly, not a hidden green result
signed-pinned-scoped-pools.xcresult again interrupted after the first lost navigation. The harness printed currentEventStillOwnsWindow=false, so advancing the event queue demonstrably does not solve the remaining lifetime problem. That neutral-event workaround is removed. The earlier graph also showed the closed MainWindow directly retained in an autorelease pool while the async XCTest was running; the sole original autorelease call site was not established. Synchronous harness navigation/diagnostics/polling remain inside scoped pools as normal lifetime hygiene.
The app P5 method is now deliberately limited to one real failed navigation (tab_in_context=false, matching controller, bg_view_creations=2) after two healthy/idle cycles and actual shared-pin window closure, followed by healthy regular/new-tab controls. It no longer attempts the second four-second timeout that reaches the app's eight-second DEBUG window guard before XCTest can finish. The guard is not disabled, no controller/tab detach is fabricated, and no claim is made that app-hosted persistent P5/stuck passed. The separate source-linked SPM P5 tests retain the existing two-failure/stuck proof. A new app run is still required for the narrowed method.
Persistent cache cleanup verified
signed-large-cache-session-end.xcresult: PASS, 47.519 s, no skip. The 20,000-resource persistent-cache scenario completed; teardown printed CPM APP REMOVED PERSISTENT STORE F705D072-DDD7-4ED7-BA23-C57A6193EA7E after clearing owned data and terminating this test host's Network session. The earlier orphan UUID from the second red cleanup was separately removed only after its owner exited, based on the harness's explicit UUID log. P3 remains a negative reproduction result; this passing characterization is not evidence of the no-background mechanism. There are now 19 distinct passing methods from corresponding signed runs, while P5's narrowed one-navigation method still requires its final run. A final build/regression is starting; it will keep the cache opt-in separate from the ordinary nonpersistent suite.
P5 one-navigation app characterization verified
signed-pinned-single-probe.xcresult: PASS, 73.806 s, no skip, signed final build. Two healthy page/native handshakes with real idle unloads establish background creation count 2. The real first-window close leaves the shared pinned Tab in the app but removes it from WebKit: the next real navigation receives no initResp, triggers the production failure (init_stage=none, tab_in_context=false, matching controller, creation count 2). The surviving regular tab and a newly created tab both receive page initResp and forwarded native dashboard. JSON attachment saved in signed-pinned-single-probe-evidence/.
The earlier longer attempts remain red and are not reclassified: this app method proves one lost navigation and unaffected controls, not two app failures/stuck. The DEBUG guard stays enabled. Combined with the separate successful persistent-cache characterization, all 20 distinct methods now have corresponding signed passes, but the final shared 18-method regression and a current-source cache rerun are still running. No single combined 20-method run is claimed.
Final combined regression was red: do not claim 20/20 together
signed-regression-final.xcresult: 18 methods, 16 passed, 2 failed, 0 skipped (XCTest summary, not just stdout). All ten core diagnostics methods passed again. Signed P1/P2/flag/crash controls preceding Network also passed, including the real WebKit timeout in 68.464 s. Network termination produced real failure in 4/4 tabs across two windows, but explicit recovery threw extensionNotFound for its owned extracted extension. The shared TestRunHelper subsequently timed out waiting for view deallocation; the next slow-wake method crashed with signal INT before it could attach evidence. Therefore those two latest combined outcomes are red, despite their earlier isolated passes. Exported 17 JSON reports and the summary remain in signed-regression-final-evidence/ and signed-regression-final-summary.json.
Source check: production WebExtensionLoader.loadWebExtension throws this error when storageProvider.resolveInstalledExtension returns nil, after the context unload. Harness resources used the globally swizzled FileManager.temporaryDirectory/com.duckduckgo.macos.tests; TestRunHelper initialization and test-bundle completion delete that shared root. The deleting actor for this run was not identified, so this is an isolation hazard, not yet a proven cause. Harness extraction is now placed under unswizzled NSTemporaryDirectory()/cpm-app-UUID, outside the helper's global cleanup folder; resolution still delegates to the production storage implementation and logs folder/root existence on failure. No browser reload/failure expectation is weakened. A new signed build/retest is required.
signed-large-cache-final.xcresult: PASS, 47.232 s on the preceding final build, owned store AFFE4DCD-59E5-4DFC-9D2C-6874C2FE498C removed successfully. P3 remains negative. These cache and P5 passes do not make the red combined run green.
Resource isolation recheck: 18/18 together
signed-isolated-regression.xcresult: TEST EXECUTE SUCCEEDED, 18/18 methods passed, 0 failures, 0 skipped, on signed build-signed-resource-isolation.log. This repeats the same selection that previously went red, with extension files outside the helper's global cleanup root. There are zero CPM APP STORAGE MISSING messages. The Network method now completes its real 4-tab fault/reload recovery in 21.692 s, and the next slow-wake method completes in 49.651 s, without the earlier INT. Real WebKit load-timeout method passes in 68.303 s. All ten core scope/diagnostics methods pass in the same invocation.
This verifies the new isolation under the observed conditions; it does not identify the actor that deleted files in the previous run. The prior 16/18 red result remains part of the record. P5 and cache are being repeated separately against this exact build, and will be included in the final matrix rather than described as a single 20-method invocation.
Финальный app-hosted итог — 16 октября 2026
Проверено 15 Oct 19:48 UTC / 16 Oct 01:48 Бишкек: 20/20 методов PASS, 0 failures, 0 skipped, 0 expected failures в трёх запусках одной окончательной signed build. Это 18 вместе + P5 отдельно + cache отдельно, а не один 20-method invocation.
signed-isolated-regression.xcresult: 18/18, включая все 10 основных scope/diagnostics tests, P1/P2, first install/flags и Network recovery.signed-isolated-pinned.xcresult: 1/1, 72.644 s, настоящий app P5 в явно ограниченном one-navigation scope.signed-isolated-cache.xcresult: 1/1, 43.053 s; 20 000 ресурсов за 39.113 s, post-reload probe примерно 0.900 s,p3NoBackgroundFailure=false. Собственный persistent store удалён через WebKit; дополнительно проверено отсутствие его каталога.
Сводки получены через xcresulttool get test-results summary, все 20 JSON reports экспортированы. Корень артефактов: /private/tmp/cpm-app-integration-20261015/; актуальные evidence directories — signed-isolated-regression-evidence/, signed-isolated-pinned-evidence/, signed-isolated-cache-evidence/. app-test-results.json содержит per-method latest result, сохранённую историю красных/зелёных попыток, summaries трёх финальных групп и SHA-256 исходников. Старые красные logs/results сохранены, включая joint 16/18 и длинные P5 attempts.
Worktree /Users/admin/DuckDuckGo/apple-browsers.git/cpm-e2e-init, branch scratch/cpm-e2e-init, HEAD неизменён 46f25175c7c42766cab23ea39f447f075c7bdd24, commits нет. macOS 26.7.1 (25G241), Xcode 27.1 (27A9275), system WebKit 21624.5.1.11.3, arm64, GUI session. Финальная сборка — build-signed-resource-isolation.log. Apple Development: Alexey Martemyanov (9SAT8X9S84), team HKE973VLUW, hardened runtime; normal project signing defaults. final-signing.txt содержит цепочку сертификатов; app debug dylib и XCTest bundle проходят strict signature verification. Весь Debug app имеет отдельно описанное выше ограничение deep verify вложенного VPN Subscription.framework; distribution-валидность не заявляется. Unsigned/vault-bypass workaround в окончательном коде отсутствует.
Полный реестр 20 app methods
Первые 10 методов — CPMAppIntegrationTests.swift, остальные 10 — CPMAppKnownFailureIntegrationTests.swift, оба в macOS/IntegrationTests/CPM/.
| Метод | Финальный результат | Что проверено |
|---|---|---|
testHealthyBaselineUsesRealTabsAndNativeDashboard |
PASS · 1.131 s | Baseline: реальные page initResp и native dashboard; tab/controller/handler/readiness/tabs |
testDiagnosticProxyFlagCanChangeWithoutBreakingMessaging |
PASS · 1.705 s | Proxy flag off/on через macOS adapter: readiness исчезает/возвращается, messaging healthy |
testSlowHTTPDocumentDoesNotProduceCPMFailure |
PASS · 8.883 s | Медленная HTTP-загрузка (8 s image): handshake healthy, ложного failure нет |
testSlowNativeSettingsProducesReceivedStageThenSameDocumentRecovery |
PASS · 8.964 s | Медленный native settings (8 s): received → timeout → same-document recovery |
testOneNavigationFailureRecoversOnNextNavigation |
PASS · 5.307 s | Одна потерянная навигация: none → healthy на следующей, later_navigation, без stuck |
testPersistentFailureOfOneTabLeavesOtherTabsAndWindowsHealthy |
PASS · 11.167 s | Persistent одна таба: два failure/stuck; same-window/other-window/new-tab controls healthy; clear → recovery |
testPersistentFailureOfSeveralTabsInOneWindowLeavesControlsHealthy |
PASS · 19.657 s | Две табы одного окна: persistent failure/stuck; оставшаяся таба/другое окно healthy; clear → recovery |
testPersistentFailureAcrossAllWindowsIncludesNewTabsAndRecoversAfterClear |
PASS · 24.966 s | Все окна: 4 табы и новая таба fail; clear → recovery во всех |
testAutomaticHangRecoveryReloadsExtensionAndRestoresMessaging |
PASS · 12.870 s | Настоящий M4 после подтверждения hang заменяет context; messaging и recovered_after_reload |
testDashboardTransportLossIsDistinguishableFromMissingInitResponse |
PASS · 5.293 s | Потеря dashboard confirmation при полученном page initResp: response_sent, затем recovery |
testMainFeatureOffOnRetainsOldControllerAndBreaksExistingTabs |
PASS · 14.562 s | P6: реальные main-flag off/on callbacks; старый controller у existing tabs, новая таба healthy |
testFirstMainFeatureEnableLeavesExistingTabsWithoutController |
PASS · 9.137 s | Первое main enable: existing tabs с nil controller fail; новая таба healthy |
testEmbeddedOnlyFlagCyclePreservesControllerAfterReadiness |
PASS · 1.256 s | Embedded-only cycle сохраняет controller; healthy после readiness |
testSharedPinnedTabSurvivesClosingWindowButDisappearsFromWebKit |
PASS · 72.644 s | P5: реальное shared-pin window close → один failure/tab_in_context=false; regular/new-tab healthy |
testNetworkProcessTerminationRecordsActualCrossWindowOutcome |
PASS · 21.692 s | Реальный Network SIGKILL → 4/4 tabs fail в двух окнах; explicit reload/readiness → все healthy |
testBackgroundProcessTerminationRecordsCrashAndCanRecoverAfterReload |
PASS · 2.518 s | Реальный background SIGKILL: died_crash/dealloc/new view; в этом WebKit самоизлечился; reload healthy |
testFirstEmbeddedInstallDuringSessionRecordsStartupRace |
PASS · 11.882 s | Реальный first embedded install с 6 s startup delay: первый init потерян/pending; следующий healthy |
testLargePersistentCacheRecordsContextReloadInjectionGap |
PASS · 43.053 s | P3 experiment: 20k persistent-cache resources + baseURL-changing reload; P3 НЕ воспроизведён; cleanup/recovery healthy |
testSlowBackgroundWakeReportsPendingThenRecovers |
PASS · 49.651 s | P1: реальный idle unload + 8 s wake delay; pending/none → timeout → поздний ответ/recovery |
testFrozenBackgroundHitsWebKitLoadTimeout |
PASS · 68.303 s | P2: новый worker SIGSTOP → настоящий WebKit error 6 около 30 s (25s_to_35s); SIGCONT в cleanup |
Границы доказательства и окончательная реализация
- Зелёный characterization test не означает исправление CPM или доказанную production-причину. Scoped drops/delays действуют только на extracted test copy и проверяют диагностику/масштаб отказа. Навигации, app window/tab callbacks, native provider/delegate, monitor и recorder настоящие; прямые
didCloseTab/synthetic monitor events для получения failure не вызываются. - P3 не воспроизведён на этой машине/20k cache; этот результат не исключает другие размеры, timing или iOS. Background SIGKILL здесь самоизлечился — зафиксирован crash/recovery, не выдуман persistent failure.
- App P5 доказывает одну потерянную навигацию после реального закрытия окна и здоровые controls. Две app-навигации/stuck не объявляются зелёными: прежние попытки достигали DEBUG MainWindow lifetime guard, который остаётся включён. Persistent P5/stuck проверен отдельными SPM methods (§12).
- P1/P2 — управляемые macOS-механизмы. Естественная потеря listener metadata, P5 с tab_in_context=true, iOS suspend/resume/device-only ошибки и production main-flag transitions всё ещё не доказаны.
- Устранён риск общего test temp root: extension resources теперь в
NSTemporaryDirectory()/cpm-app-UUIDвне глобального swizzledcom.duckduckgo.macos.tests, который очищает TestRunHelper. Старый deleting actor не установлен; после изоляции тот же joint selection прошёл 18/18, missing-resource logs нет. Это не выдаётся за установленную внешнюю причину прежнего удаления. - Teardown закрывает только окна своего host, восстанавливает pinned mode/flags, возобновляет остановленные PID и удаляет собственные extension files. Для cache сначала сохраняет evidence, очищает owned website/controller data, завершает Network session этого test host через test-only SPI, освобождает refs и удаляет только свежий UUID store с bounded retry. Пользовательский browser process не выбирается; глобального
killall/Review-profile wipe нет. - Минимальные seams: optional
controllerConfigurationс прежним production default; guarded DEBUG AppDelegate factory; собственный ConfigurationStore directory; loopback-only opt-in exception в Tab; internal macOS diagnostics flag adapter для@testable. Новая pixel schema/production fix не добавлялись. Шесть новых Swift files включены в оба Integration Tests targets, scheme XML/PBX валидны. - Strict SwiftLint 11 Swift files: 0 violations,
git diff --checkчистый. Прежний dirty bundle сохранён (2026.10.14, SHA-2564254967bb447aa45deb371b26fea1dd93850a8e6a654f8ebecd70fccc3859936) вместе с прежними untracked SPM files. Другой remaining-failures worktree не менялся.
Повторение: схема CPM Integration Tests с project signing defaults; полная инструкция в macOS/IntegrationTests/CPM/README.md, карта файлов — cpm-web-extension-code-guide-2026-10-09#5. App-hosted CPM integration tests — 16 октября 2026. Default scheme пропускает два expensive opt-ins. Реальный timeout включать test-host env CPM_APP_INTEGRATION_EXPENSIVE=1. Cache выбирать отдельным методом с CPM_APP_INTEGRATION_CACHE=1, execution allowance 2400 s (count override CPM_APP_INTEGRATION_CACHE_ENTRIES); cache switch применяется ко всем harness instances выбранного invocation. Финальные .xctestrun планы сохранены в DerivedData/Build/Products/CPM-App-Final.xctestrun и CPM-App-Cache.xctestrun. PR/CI/ClickHouse snapshot выше не обновлялся этим локальным тестовым проходом.
16 Oct: explicit interpretation correction and requested rerun
The statement “all cases from production data reproduced except P3” is not supported. The 20 green methods establish their assertions, not 20 identified production causes. All run inside the actual app/system WebKit, but in an app-hosted test environment with isolated stores, mocked feature/config inputs, intercepted pixel transport and an instrumented extracted bundle (the runtime listener wrappers/DOM observation are present even when a scoped fault is disabled). This is not a black-box test of an unchanged shipping binary.
- P5 closure, conditional P6 flag transitions and real Network-process termination exercise actual browser/WebKit lifecycle paths. P5 app scope is one failed navigation; the original two-timeout app attempt remains unverified/red because of the DEBUG window guard. Actual main-flag transitions in the affected production sessions are not established. SIGKILL is a forced process failure, not an observation of its production trigger.
- P1 worker delay and P2 SIGSTOP reproduce a timing/timeout mechanism under imposed conditions; they do not identify why a production worker is slow/frozen. First embedded install uses the real app callback with an injected startup delay.
- Single-/multi-tab/all-window message filtering, native-method delay and dashboard suppression are explicit fault injections for diagnostics/recovery. Those injection hooks are not shipped production behavior. Matching a failure signature is not causal attribution.
- Still unproven: P3, natural listener-metadata loss, P5 with tab_in_context=true, real iOS lifecycle/device chains, and production occurrence of the P6 transition. Background SIGKILL self-healed on this WebKit.
The user requested rerunning failed selections. Current unchanged source hashes match the final prior build. Repeat launched in /private/tmp/cpm-app-integration-20261015/rerun-failed-20261016-042858/: the prior red shared selection of 18 methods, current one-navigation P5 method, and current persistent-cache method. Historical removed unsigned/bootstrap variants and the old longer P5 source are not being presented as rerun/fixed. Results pending; no source or submodule changes.
Submodule source check: .gitmodules lists SnapshotReferences and privacy-reference-tests, both uninitialized here. SnapshotTestingSupport resolves its image/data references under SnapshotReferences; privacy-reference-tests is used by BrowserServicesKit reference tests and is listed in Xcode resource phases. No direct reference to either exists in the new CPM integration files. These app integration runs are not the macOS/XCUI scheme; that scheme's Review cleanup pre-action is not invoked.
Requested failed-selection rerun — completed 16 Oct, 04:37 UTC
Rerun folder /private/tmp/cpm-app-integration-20261015/rerun-failed-20261016-042858/. Same signed build and all 14 source hashes unchanged, no source/submodule edits or commits. xcresulttool summaries confirm 20 current methods PASS in 18+1+1 invocations; 0 failures, 0 skips, 0 expected failures. Exported 20 JSON attachments; rerun-summary.json indexes the summaries, immutable source hashes, cache outcome and interpretation limits.
The 18-method selection includes successful Network/reload (22.198 s) and slow wake (49.764 s); no missing-resource log. P5 reruns the current one-navigation method, not the historical two-timeout variant. Cache PASS 44.162 s: 20k resources, fill 40.129 s, post-reload probe 0.963 s, p3NoBackgroundFailure=false; UUID store removal completed. Thus P3 is again not reproduced, although the characterization/cleanup assertions pass.
Nonfatal infrastructure warning remains: cache teardown emits BUG IN CLIENT OF libsqlite3.dylib / vnode unlinked while in use while removing its owned UUID website store. The warning is preserved in cache.log and rerun-summary.json; there is no XCTest assertion failure or claimed clean-runtime result. No code was changed to hide it. The older long app P5 still has its separate DEBUG lifetime limitation. It is therefore wrong to call all historical red runs “only infrastructure” or to mark their original source variants fixed: earlier reload controls also lost the first CPM navigation (page/native response missing), and some controls were subsequently changed to await actual background readiness.
Submodule mapping verified from PBX: privacy-reference-tests resources belong to Unit Tests / Unit Tests App Store. SnapshotReferences is used by SnapshotTestingSupport (unit/package snapshot suites); XCUI accessibility snapshot() calls are a different API. Neither submodule is consumed by the new CPM app test cases; both remain uninitialized here. The macOS XCUI scheme and its Review-profile cleanup were not run.
Interpretation unchanged: this rerun demonstrates test repeatability in the instrumented app-test environment, not reproduction/attribution of all production-data cases. P1/P2 and scoped drops remain imposed faults; P5/P6/Network exercise real browser/WebKit paths with the trigger/production-evidence limits stated above.
Точечные фиксы P1/P2 — начато 16 октября 2026
Пользователь разрешил локальные фиксы и проверку регрессионными тестами, без коммитов. В последнем запросе написано p0/1; в существующей классификации P0 нет, поэтому работа относится к двум обсуждавшимся картинам P1 (slow idle wake) и P2 (wake → error 6). Worktree cpm-e2e-init, исходный HEAD 46f25175c7c42766cab23ea39f447f075c7bdd24; прежние dirty-изменения сохранены. Снимок исходников до работы: /private/tmp/cpm-p1-p2-fixes-20261016/before-changes.tar.gz и before-source-hashes.json.
Добавлены app-регрессии testSlowBackgroundWakeKeepsFirstNavigationHealthy (первый документ после реального idle + 8 s startup должен получить page initResp и native dashboard без detector failure) и testFrozenBackgroundRecoversAutomaticallyForNextNavigation (реальный WebKit error 6 после SIGSTOP, затем SIGCONT; автоматическая замена контекста без второй сломанной навигации, существующие tabs в двух окнах и новая tab здоровы). Начинается прогон до production-фикса; результат ещё не получен. SIGSTOP/JS delay остаются принудительными условиями, не доказательством естественной iOS-причины. Старые 20 зелёных characterization methods не считаются проверкой этих новых исправлений.
Первый красный app-прогон и реализация кандидатов
/private/tmp/cpm-p1-p2-fixes-20261016/red.xcresult: обе новые проверки красные на исходном production-коде. P1 действительно дал detector timeout и отсутствие page/native ответа в момент проверки. Дополнительно teardown попал в DEBUG deallocation deadline, поскольку тест завершался до позднего ответа; добавлено ожидание его завершения после функционального assertion. P2 пока не считается чистым red-доказательством recovery: тест ждал error 6, пока процесс был SIGSTOP; в этом запуске WebKit сообщил error 6 лишь после SIGCONT из defer. Исправлен порядок: держать процесс 32 s, отправить SIGCONT, наблюдать реальное error-notification (сохранённое до возможного unload), затем требовать автоматическую замену контекста. Исторический первый красный результат не удалён.
Локально добавлен кандидат P1: перед каждой HTTP(S) main-frame navigation подготовка background через публичный loadBackgroundContent, совместная задача для параллельных навигаций одного context; ожидание ограничено прежними 5 s и отменяется независимо от WebKit. При этом прямой withTimeout { await Task.value } не годится для неотменяемого callback: task group ждёт завершения дочерней операции. Использован отменяемый AsyncStream, добавлены отдельные unit tests deadline/cancellation. Старый P1 characterization сохранён с webExtensionStateRestorationGate=false.
Кандидат P2: текущий, а не уже заменённый context при настоящем backgroundContentFailedToLoad запускает штатный context reload через существующий recovery kill switch. Общий in-progress guard с M4 и 60 s cooldown для load-error recovery; после reload явно запускается background. При известном app_state=background восстановление не выполняется. Приватные process SPI в production-фикс не добавлены; SIGSTOP остаётся только способом воспроизведения. Оба кандидата ещё собираются/проверяются; зелёный результат пока не заявляется. Добавлен disabled-fix режим только новых app-tests для сравнения того же бинарника с явно заданными флагами.
Проверка кандидата: P1 зелёный, P2 fixture пока не воспроизводит ошибку
fixed-initial.xcresult: новый функциональный P1 PASS (49.628 s), первый документ после idle + 8 s busy worker получил page initResp/native confirmation без timeout. P2 в том же прогоне FAIL на ожидании реального error 6, не на проверке recovery. После учёта 5 s navigation gate пробовались отключение P1 gate и 45 s SIGSTOP (fixed-p2-observer.xcresult): снова healthy late wake без error 6. Этот расширенный fixture имел два окна/два baseline-документа; он не считается воспроизведением P2 и не подтверждает его фикс. Запущен исходный одновкладочный testFrozenBackgroundHitsWebKitLoadTimeout для сравнения с прежним зелёным сценарием. Все отрицательные результаты сохранены.
P2 реализация уточнена: manager наблюдает публичное WKWebExtensionContext.errorsDidUpdateNotification, независимо от navigation gate и пассивного recorder. В прочитанном WebKit source (WebExtensionContextCocoa.mm:2793–2802) service-worker failure записывает error 6 и возвращается без выполнения ожидающих loadBackgroundContent callbacks. Поэтому callback-ожидание дополнительно завершается по error notification и при willReload; recovery guard не удерживается бесконечным ожиданием нового startup. Автоматическая обработка ошибок чужого/старого/не-embedded context исключена. Восстановление пока не объявляется проверенным на app P2.
Пакет после текущих изменений: 806 XCTest, 14 skipped, 0 failures (unit-package-callback.log); strict SwiftLint шести Swift-файлов — 0 violations. Перегенерированные SPM resolver изменения версий CSS/privacy-dashboard сохраняются только в артефактах и будут исключены из итогового diff; это не часть фикса.
P2: исходный fault воспроизведён, автоматический reload есть; page receipt ещё проверяется
p2-original-control.xcresult: исходный one-tab P2 PASS 69.049 s, реальный error 6 ровно через ~30 s после второго background view. Поэтому новый P2 regression возвращён к одному исходному tab/window; проверка нового окна/вкладки перенесена после recovery. Два baseline окна до fault не воспроизводили тот же error 6 — этот вариант остаётся отрицательным результатом.
fixed-final.xcresult: P1 снова PASS 49.908 s. P2 получил настоящий WKWebExtensionContextErrorDomain:6 и автоматический context reload; но регрессия осталась красной: existing-tab probe имел native dashboard=true и 0 detector failures, но page initResp mark=false. Новое окно и новая вкладка получили оба подтверждения. Нельзя объявлять восстановление старого tab по одному native ack. В harness фиксированный sleep 150 ms заменён bounded polling реального page initResp (до 2 s); идёт повтор fixed-page-ack.xcresult. Проверка page receipt сохранена строгой, зелёный P2 пока не заявлен.
Оговорка о пользе P2: исходный standalone testBackgroundStalledPastUnloadTimerFailsToLoad уже проверяет, что следующий wake может восстановиться без нового фикса. Новый кандидат делает recovery заранее, по error notification; его проверка сама по себе не доказывает, что SIGSTOP-модель объясняет persistent iOS stuck или что без reload следующая навигация обязательно сломана.
Первый зелёный P2 regression на окончательном production-кандидате
fixed-page-ack.xcresult: P2 PASS, 85.562 s, 1/1, 0 failures/skips/expected failures. Exported evidence: fixed-page-ack-evidence/755EA182-FC24-479A-95B5-CFF1493D0404.json. Реальный error 6 записан до recovery: context_errors=WKWebExtensionContextErrorDomain:6, bg_load_error_after=25s_to_35s, app_state=inactive, view_creations=2, view_alive=false. Первая fault-navigation: native=false/page=false, 1 initialization failure. После автоматического reload, без явного test reload: existing tab native=true/page=true, 0 failures; новое окно и новая tab также native=true/page=true, 0 failures. Existing-tab probe занял 0.149 s: сам по себе этот повтор не доказывает, что предыдущий page-negative был только недостаточными 150 ms; intermittent/order-dependent delivery остаётся причиной дополнительной проверки повторяемости.
Сборка build-page-ack.log — project signing defaults, TEST BUILD SUCCEEDED; подписи app dylib/XCTest проверены, bundled ZIP сохранён с исходным SHA-256 4254967bb447aa45deb371b26fea1dd93850a8e6a654f8ebecd70fccc3859936. Начат общий прогон оставшихся 19 app methods на этой же сборке (новый P1 включён, P2 уже проверен отдельно, P5/cache отдельно). Контроль с выключенными фиксами и итоговая полная матрица пока не завершены. HEAD не изменился, commits/PR изменений нет.
Повтор выявил нестабильность полного P2 reload; кандидат изменён
p2-repeat.xcresult: FAIL 90.533 s. Настоящий error 6 и автоматический reload опять произошли, но existing tab снова имел native=true/page initResp=false, уже после bounded 2 s ожидания. Значит, первый зелёный fixed-page-ack не доказывал устойчивость: полный context reload как готовый фикс P2 отклонён. Проверка page receipt не ослабляется.
Новый P2 кандидат: после ошибки повторить только loadBackgroundContent с сохранением WKWebExtensionContext, чтобы не инвалидировать ожидающий ответ исходного content script. Pending readiness очищается по реальному error notification до повторного API-вызова, иначе новая попытка могла бы присоединиться к уже завершённой старой задаче. Существующий M4 full reload остаётся fallback для confirmed stuck; kill switch/cooldown/own-current-context checks сохранены. Новый regression требует native dashboard и page initResp на том же failed document до любой новой навигации, тот же объект context, recovery_document=failed_navigation; затем проверяет обычные следующие/new-window/new-tab навигации. Идёт пересборка, зелёного результата для этого кандидата пока нет.
pinned.xcresult: один assertion FAIL: bg_view_creations ожидался 2, стал 3_to_5. Остальные P5 assertions (реальная потеря tab registration, controller match, lost navigation и healthy controls) прошли. Причина дополнительного view установлена по коду: новый P1 явно будит background перед тем, как WebKit отвергает сообщение неизвестной pinned tab. Для включённого navigation gate актуализирован строгий ожидаемый bucket на 3_to_5; сам сбой не спрятан и не исправлен этим изменением.
cache.xcresult: PASS 45.702 s, P3 по-прежнему не воспроизведён. В teardown снова есть nonfatal libsqlite3 vnode unlinked while in use для owned UUID store 71644ce9-6351-4c46-b915-825beb961ee6, в этот раз EnhancedSecuritySites.db. Это отдельное ограничение cleanup, не зелёная проверка целостности SQLite и не сбой CPM assertion. Исторические прогоны и warning сохранены.
Background-only retry: первый строгий P2 PASS без смены документа/контекста
background-retry-points.xcresult: 2/2 PASS, 0 failed/skipped/expected failures. P1 49.734 s, P2 85.234 s. Для P2 теперь проверено: реальный error 6 → публичный повторный loadBackgroundContent → исходный failed document получает native dashboard и page initResp до любой следующей навигации, context object остаётся тем же. Затем существующая tab, новое окно и новая tab здоровы. Доставку не имитировали и не переинициализировали документ из теста.
Фактический pixel в background-retry-points-evidence/A7C288DA-DCCE-42DC-BC30-6E8D6865526D.json: debug_web_extension_cpm_messaging_recovered_without_extension_reload, recovery_from=initialization_failed, recovery_document=failed_navigation, recovery_after=30s_to_2m. Это новый кандидат, прежний once-green full-context prototype не входит в его подтверждение. Пакет: 806 tests / 14 skips / 0 failures (unit-package-background-retry.log), strict lint — 0 violations. Идёт общий app-run с повтором P2, затем новые negative controls и P5/cache на этой же сборке. Окончательная source matrix пока не завершена.
Окончательный итог точечных P1/P2-кандидатов — 16 октября 2026, 12:30 UTC
Завершено локально, без commits и PR mutations. Worktree /Users/admin/DuckDuckGo/apple-browsers.git/cpm-e2e-init, HEAD 46f25175c7c42766cab23ea39f447f075c7bdd24. Сводка: /private/tmp/cpm-p1-p2-fixes-20261016/validation-summary.json; отдельная delta этой работы — point-fixes.diff, предыдущие dirty contents — before-changes.tar.gz. Предыдущие tracked/untracked harnesses и dirty embedded ZIP сохранены; ZIP SHA-256 по-прежнему 4254967bb447aa45deb371b26fea1dd93850a8e6a654f8ebecd70fccc3859936. Проверены отсутствие неожиданных изменений прежних файлов, неизменность final source hashes после сборки, normal Apple Development signing app dylib/XCTest и git diff --check.
Что реализовано
- P1: перед каждым HTTP(S) main-frame navigation production coordinator через manager готовит embedded background, включая повторный wake. Shared task привязана к объекту context; навигация ждёт максимум прежние 5 s и освобождается при cancellation, даже если WebKit не завершает callback. Остальная lifecycle chain не блокируется. Управляет существующий
webExtensionStateRestorationGate. - P2: настоящий
WKWebExtensionContext.errorsDidUpdateNotification, own/current/embedded context и domain/code 6 запускают один повторloadBackgroundContentбез смены context/Tab/controller. Старый pending startup завершается/очищается до retry, cooldown 60 s, существующий recovery kill switch и M4 in-progress guard. При known app_state=background retry не выполняется; отдельный resume handler не добавлен. M4 full reset остаётся fallback для confirmed stuck, и не держит guard бесконечным ожиданием readiness callback. - P2 full-context prototype отвергнут: после once-green результата повтор опять терял page initResp в existing tab при native dashboard=true. Эти красные результаты сохранены, а не объявлены инфраструктурными. Финальный background-only кандидат требует действительного восстановления исходного документа.
- Harness: bounded polling реального page initResp вместо sleep 150 ms, без подделки response/monitor events. P5 с включённым P1 ожидает дополнительный view (bucket 3_to_5), но всё ещё требует actual tab_in_context=false, controller match, failed pinned navigation и healthy regular/new-tab controls. Raw P1/P3 и fault-фаза P2 явно отключают P1 gate там, где нужно сохранить исходные условия/таймер.
Проверенная матрица окончательного исходника
Run (.xcresult в общей папке) |
Итог | Что доказывает |
|---|---|---|
background-retry-points |
2/2 PASS, 0 skips/expected failures | P1 first document после idle+8 s startup здоров; P2 error 6 → native+page ответ на том же failed document до новой navigation и без context replacement |
background-retry-regression |
19/19 PASS, 0 skips/expected failures | Все 10 core diagnostics/scopes, известные lifecycle/flag/Network/background paths; P2 same-document повторно PASS 84.776 s |
background-retry-pinned |
1/1 PASS, 75.409 s | Реальный P5 browser→WebKit tab-registration failure с актуальным bg-view bucket; one failed navigation + healthy controls |
background-retry-cache |
1/1 PASS, 61.906 s | 20,000 actual cache responses/records; fill 55.918 s, reload→probe 2.149 s, p3NoBackgroundFailure=false |
background-retry-disabled |
0/2 PASS, 2 FAIL — ожидаемый negative control | Тот же signed binary, P1 gate/P2 retry выключены только в новых test methods: P1 снова timeout; P2 error 6 есть, same-document recovery отсутствует (timeout), а не просто «context не заменился» |
Итого 22 уникальных app methods PASS, 0 failed/skipped/expected failures в положительных прогонах. Это 2 + 19 + 1 + 1 = 23 положительных выполнения, поскольку P2 повторён в двух runs; не один combined run из 22 methods. Две deliberate-red проверки не скрыты в этом счёте. Их официальный XCTest expectedFailures=0 — красный результат ожидается самим экспериментом, это не XCTExpectFailure.
P2 first pass 85.234 s, повтор 84.776 s. Фактический recovered pixel: recovered_without_extension_reload, recovery_from=initialization_failed, recovery_document=failed_navigation, recovery_after=30s_to_2m. Test также проверяет DOM initResp и тот же объект WKWebExtensionContext; новый документ из теста не создаётся до этих assertions. Исходный fault: реальная idle-выгрузка, owned SIGSTOP 45 s, WebKit error 6 около 30 s. SIGCONT/teardown ограничены owned PID.
Unit/package: unit-package-background-retry.log — 806 XCTest, 14 skipped, 0 failures. Новые lifecycle unit tests проверяют bounded/cancellable ожидание при callback, игнорирующем cancellation. lint-background-retry.log — strict SwiftLint 6 файлов, 0 violations. Временные resolver-изменения CSS/privacy-dashboard сохранены в artifact diff и исключены из repo diff. Final hashes — background-retry-source-hashes.json.
Практические границы
- P1 проверен при injected 8 s startup. Ожидание 5 s не ускоряет worker и не доказывает устранение всех более долгих P1 delays; 4 s detector threshold не изменялся.
- P2 проверен в одном исходном окне/tab с controlled SIGSTOP. Новый window/tab и existing-tab subsequent navigation здоровы; вариант с двумя baseline окнами не воспроизвёл error 6 и остался отрицательным экспериментом. Natural iOS suspend/resume, deferred foreground recovery и причины основных iOS production rows не доказаны. Старый standalone P2 уже мог сам восстановиться на следующем wake; текущая регрессия строже — восстановление того же документа до следующего wake/navigation.
- Все app methods работают в test environment с instrumented extracted bundle. Это реальные AppDelegate/Tab/window/provider/monitor/WebKit пути, но не unchanged shipping environment и не исчерпывающее causal attribution production cohorts.
- P3 по-прежнему не воспроизведён; app P5 всё ещё ограничен одной потерянной navigation, прежний long app P5 red сохранён. Отдельные SPM P5/P6 эксперименты и исторические цифры не подменяются новой app-матрицей.
- Cache cleanup снова вывел nonfatal libsqlite3
vnode unlinked while in useдля только тестового UUID store3ac0b3e6-8945-43dc-aad4-864b08bcc469(EnhancedSecuritySites.db/-wal/-shm). XCTest assertions и публичное UUID removal завершились; целостность этой удаляемой SQLite базы этим не подтверждается. Warning не исправлялся и не скрыт. - CI/PR/ClickHouse в рамках финальной локальной проверки не обновлялись; численные production-срезы выше сохраняют свои даты. #7379 и остальные PR не менялись, никаких commits/push не было.
Выделение P1/P2 в отдельный worktree — 16 октября 2026
По запросу создан /Users/admin/DuckDuckGo/apple-browsers.git/alex/cpm-fix-p1-p2, ветка alex/cpm-fix-p1-p2, от текущего alex/cpm-messaging-readiness-telemetry (01d4a0ec22b4702ca9f9ea37757e060772ec7fa9, уже содержит merge main). Исходный scratch worktree сохранён без изменений. Коммитов нет.
22 — полный исследовательский app-набор, а не число обязательных тестов этих двух фиксов. В новую ветку выделены ровно два app-теста (testSlowBackgroundWakeKeepsFirstNavigationHealthy, testBackgroundLoadFailureRecoversOriginalDocument) и четыре новых lifecycle unit-теста (wake после launch, ограниченное ожидание неотменяемого callback, отмена навигации, выключенный gate). App-harness сокращён до необходимого: реальные AppDelegate/Window/Tab, native handler и system WebKit, изолированные stores, loopback HTTP, наблюдение реального initResp; задержка worker и SIGSTOP/SIGCONT только собственного background PID. Scoped drop/control, Network crash, cache/P3/P5 и остальные 20 сценариев не перенесены.
Фикс P1 сохраняет предел 5 с и существующий gate flag. P2 повторяет background load в том же контексте по настоящей notification error 6, с существующим recovery flag, ограничением частоты и запретом в background; полный context reset не перенесён. На этой базе отсутствует generic applicationState из более поздних telemetry PR, поэтому добавлен только read-only isApplicationInBackground у recorder, читающий уже существующий appSession provider. ZIP расширения и pixel schemas не менялись, более поздние telemetry PR не включены. Изменения macOS AppDelegate/ConfigurationStore/Tab и optional controllerConfiguration нужны для изоляции app-hosted тестов; shipping defaults сохранены.
Проверка новой ветки запущена: signed build-for-testing и lifecycle/navigation unit suites. Strict SwiftLint: 14 Swift-файлов, 0 violations; project.pbxproj проходит plutil, все пять новых Swift-файлов зарегистрированы в обоих Integration Tests targets. Артефакты: /private/tmp/cpm-p1-p2-clean-20261016/. Финальные результаты будут дописаны после прогонов; предыдущие 22/22 относятся к scratch, а не автоматически к этой новой сборке.
Адаптация к текущему контракту базовой ветки
При выделении проверено, что текущий alex/cpm-messaging-readiness-telemetry уже использует единый cpmMessagingEpisode, а не прежние initialization_failed/stuck/recovered events. Первый build выявил несовместимость перенесённого test adapter с удалёнными enum cases (build.log, exit 65). Исправлен исключительно test adapter, без возврата старой telemetry schema: CPMAppFailureRecorder прозрачно делегирует настоящий CPMMessagingDiagnosticsProviding.collectDiagnostics и фиксирует реальный first-failure snapshot, terminal events пишет существующий pixel sink. P2 требует outcome=failed_measurement_response, reload_state=none, плюс фактический page initResp и идентичность context. Это соответствует новой модели: сам episode dashboard response отдельно не доказывает доставку в страницу, поэтому DOM-подтверждение сохранено.
Повторный build-episode-adapter.log: TEST BUILD SUCCEEDED. Подтверждена Apple Development: Alexey Martemyanov (9SAT8X9S84), Team HKE973VLUW, strict verification app debug dylib и фактически загружаемого XCTest bundle успешна. 31 lifecycle unit test + 134 существующих manager/background-load/health-monitor unit tests = 165 PASS, 0 failures. App positive run запущен (enabled.xcresult), отрицательный контроль запланирован на той же сборке. Исходный bundled ZIP 2026.10.14 не изменён, SHA256 ff98a2b0c07819b2ee5ee6de67d6dd8d28696363c08c9fbc2c32255e9d9efa94.
Первый app-прогон выделенной ветки и cleanup fixture
enabled.xcresult: 1 PASS / 1 FAIL. P1 PASS (49.389 с). P2 функциональные assertions прошли: реальная error 6, first-failure snapshot, восстановление native dashboard и page initResp исходного документа, web_extension_cpm_episode(outcome=failed_measurement_response,reload_state=none), healthy existing tab/new tab/new window. Итоговый P2 FAIL (84.990 с) вызван штатным TestRunHelper.swift:453 deallocation guard: оставшиеся AppKit window-chrome views. Это не зелёный app-прогон и не считается PASS.
Для исправления fixture добавлены локальные autoreleasepool вокруг проксируемого collectDiagnostics и синхронного unload/configuration teardown; deallocation guard не отключён, исключения не добавлены. build-cleanup.log: TEST BUILD SUCCEEDED. Повтор обоих app-тестов выполняется в enabled-cleanup.xcresult; результат cleanup ещё проверяется. Исходный scratch сверён с сохранёнными hash предыдущего 22/22 прогона: ни один из его файлов не изменён.
Повтор app-прогона: оба положительных сценария PASS
enabled-cleanup.xcresult: 2/2 PASS, 0 skips, 0 failures, exit 0. P2 84.950 с; P1 49.376 с. Штатные deallocation checks включены и прошли после локальных autoreleasepool в fixture. Production-код с момента успешных 165 unit-тестов не менялся. P2 evidence содержит actualBackgroundLoadError [6], failed first probe, healthy existing/new-window/new-tab probes, terminal failed_measurement_response / reload_state=none; отдельные assertions проверяют доставку page initResp тому же исходному документу до следующей навигации и тот же context object. P1 evidence: healthy baseline и первый document после idle wake, no failures/episodes. Strict verification финальных app debug dylib и загружаемого XCTest bundle также PASS.
SwiftPM автоматически обновил только package-local content-scope-scripts pin 17.27→17.29 (текущий base BrowserServicesKit/Package.swift уже требует exact 17.29 и workspace lock уже имеет 17.29). Этот побочный diff сохранён как swiftpm-generated-pins.diff и отменён только в новом worktree; зависимостей в итоговом изменении нет. Baseline ZIP и все исходники финальной сборки сверены по cleanup-source-hashes.json: drift отсутствует.
Отрицательный контроль на тех же бинарниках выполняется в disabled.xcresult, с единственной дополнительной test-env переменной CPM_APP_INTEGRATION_DISABLE_P1_P2_FIXES=1. Финальный итог контроля будет записан отдельно.
Финальный итог отдельной ветки P1/P2 — 16 октября 2026, 13:33 UTC
Готово без коммитов: /Users/admin/DuckDuckGo/apple-browsers.git/alex/cpm-fix-p1-p2, branch alex/cpm-fix-p1-p2, HEAD/base 01d4a0ec22b4702ca9f9ea37757e060772ec7fa9 из alex/cpm-messaging-readiness-telemetry. Шесть новых методов: 2 app + 4 lifecycle unit, не 22; остальные 20 исследовательских app-кейсов оставлены в исходном worktree.
| Проверка этой ветки | Результат | Артефакт |
|---|---|---|
| Signed app: P2 same-document recovery | PASS, 84.950 с | enabled-cleanup.xcresult |
| Signed app: P1 first document after slow wake | PASS, 49.376 с | enabled-cleanup.xcresult |
| Lifecycle (включая 4 новых и существующие navigation-gate проверки) | 31 PASS, 0 skips | unit.log |
| Существующие manager / background-load / health-monitor tests | 134 PASS, 0 skips | unit-related.log |
| Те же бинарники, P2 recovery flag OFF | Ожидаемый functional FAIL, 104.468 с: timeout same-document recovery после реальной error 6 | disabled.xcresult |
| Те же бинарники, P1 gate flag OFF | Ожидаемый functional FAIL, 49.933 с: нет своевременных native dashboard/page initResp, есть first failure | disabled.xcresult |
| Strict SwiftLint | 14 Swift-файлов, 0 violations | lint-final.log |
| Project registration | plutil PASS; 5 файлов по одному разу в каждом из двух Integration Tests targets | project.pbxproj |
Итог положительного app-прогона 2/2 PASS, unit 165/165 PASS. Отрицательные контроли намеренно обычные красные XCTest runs, expectedFailures=0: это проверка чувствительности функциональных assertions, а не инфраструктурное падение. Deallocation checks в финальном положительном прогоне прошли; первая ошибка cleanup сохранена выше как история. Проверки выполнены на текущей базовой episode-телеметрии, без переноса старых pixel cases или более поздних baseline/init-progress слоёв.
Все артефакты: /private/tmp/cpm-p1-p2-clean-20261016/; машинная сводка validation-summary.json, source hashes cleanup-source-hashes.json, JSON evidence в enabled-cleanup-evidence/ и disabled-evidence/. Все 18 зафиксированных файлов (включая unchanged ZIP) неизменны во время финальных прогонов. Исходный scratch также сверён с hash прежнего 22/22 прогона: изменений нет. HEAD не сдвинут, commitsCreated=0, generated package pin diff убран из новой ветки.
Границы доказательства сохранены: P1 проверяет imposed 8 s worker start при gate cap 5 s; P2 — реальную error 6 на macOS с одним начальным window/tab и остановкой только своего PID. Естественный триггер в затронутых production-сессиях и поведение iOS suspension/resume этим не доказаны; отдельный resume recovery не добавлен. P3/P5 и другие research-сценарии не включены в чистую ветку и их прежние ограничения не сняты.
Полная замена #7379: upgrade context и background guard — 16 октября 2026
В том же alex/cpm-fix-p1-p2 выполняется следующий этап по запросу: объединить P1/P2 с гарантиями #7379 и убрать зависимость recovery safety от diagnostics flag. До изменений сохранены before.tar.gz, before.diff, before-hashes.json в /private/tmp/cpm-p1-p2-replacement-20261016/. Коммитов нет.
Изменения: после успешного install выбран и отслеживается точный новый context по identifier; navigation waiter захватывает именно его, включая период сосуществования старого/нового. Для fallback учитывается порядок installation store (новый append до удаления старого), между несколькими неизвестными contexts больше нет произвольного выбора. Retry error 6 работает только с актуальным выбранным context и повторно проверяет flag/background перед вызовом WK API.
isApplicationInBackground перенесён из diagnostics recorder в самостоятельный lifecycle provider менеджера. iOS factory передаёт прямое чтение UIApplication.shared.applicationState, даже при cpmDiagnosticsRecorder=OFF. Прежний read-only diagnostics getter удалён. Добавлены unit-регрессии coexistence/replacement removal, diagnostics=nil + background→foreground, runtime recovery kill switch и отказ retry superseded context.
Два install/upgrade-сценария #7379 перенесены с сохранением test method names в текущий app-hosted harness (адаптация исходного SPM harness, без копирования остальных research-тестов). Проверяются реальные feature-flag/lifecycle install и version-triggered upgrade, настоящий native dashboard + page initResp; worker новой установки/версии задерживается на 5 s. Отдельно записываются реальные context sets в notifications, чтобы подтвердить coexistence при upgrade. Запущены общий signed build для четырёх app-кейсов и targeted unit suites. Strict lint: 16 Swift files, 0 violations. Результаты предыдущего 2/2 прогона остаются историей и не заменяют проверку новой сборки.
Общий прогон новой реализации — 16 октября, Бишкек
/private/tmp/cpm-p1-p2-replacement-20261016/enabled.xcresult: 4/4 PASS, 0 failures, 0 skips. P2 84.925 s, install-during-session 5.638 s, P1 49.380 s, upgrade-during-session 5.854 s. Upgrade JSON evidence фиксирует настоящий переход context sets [0,1,2,1]: новая версия реально загружена до выгрузки старой; первый документ получает и native dashboard, и page initResp.
Targeted unit suites: 170 PASS, включая 5 новых manager tests (unit-cached.log). Обычный запуск сначала завис на обновлении git dependency, остановлен только собственный SwiftPM процесс; повтор --skip-update успешно использовал уже скачанные зависимости. Это инфраструктурный fetch, не test failure. iOS Simulator WebExtensions module build PASS (ios-module-build.log, generic simulator destination); это компиляция общего модуля, не запуск приложения/iOS lifecycle. Signed macOS app и загруженный XCTest bundle проверены, strict verification PASS (signing.txt).
Начат повтор install/upgrade по 10 iterations (install-upgrade-repeat.xcresult); после него запланирован тот же общий app-набор с выключенными flags. README актуализирован: теперь 4 app methods + 9 новых unit methods (4 прежних lifecycle + 5 manager), а 22 по-прежнему весь отдельный research-набор. Компилируемые исходники с момента сборки не менялись; во время проверки менялся только README.
Повтор выявил граничный upgrade failure — результат не скрыт
install-upgrade-repeat.xcresult: install 10/10 PASS, upgrade 4/10 PASS + 6/10 functional FAIL (native dashboard и page initResp отсутствуют, настоящий first-failure snapshot). Это не teardown/инфраструктура. Первый общий 4/4 не подтверждает устойчивость этого режима. Логи failed upgrade показывают release навигации примерно через 5.0 s и общий failed probe около 9.1 s; worker fixture задержан ровно на 5000 ms, то есть без запаса относительно gate cap 5 s. Выбор правильного context unit-проверен, реальный overlap [0,1,2,1] подтверждён.
Для установления точной причины добавлены обычные локальные debug logs [CPM Background] с request/callback временем и причиной release navigation wait. Они не меняют pixels/schema и не увеличивают cap. Следующий шаг — измерить реальный WK callback относительно deadline на том же 5 s fixture, прежде чем менять ожидания или утверждать, что проблема исправлена. Все 20 iterations и их JSON сохранены в install-upgrade-repeat-evidence/.
Причина граничных upgrade failures установлена по timestamp
upgrade-timing-visible.log: ожидается именно новый context (UUID совпадает с новой extracted installation), но gate отпускает навигацию по TimeoutError(timeout: 5.0s) до WK callback. Измеренные callbacks 5.150336 s и 5.184716 s, соответственно через ~44 ms и ~97 ms после release. Это выход за предусмотренный бюджет 5 s, а не выбор старого context. На 5000 ms busy-loop ошибочно ожидался безусловный healthy first document: полный запуск WK дольше самого busy-loop. Strict assertions не ослаблены и timeout не увеличен.
Для install/upgrade regression в пределах контракта выбран busy-loop 4500 ms (временной запас на WK startup), с теми же требованиями native dashboard + page initResp без first failure. CPM_APP_INTEGRATION_INSTALL_DELAY_MS=5000 сохраняет возможность повторить граничный/выходящий за бюджет эксперимент на той же сборке. Предел записан и в README: новая реализация, как #7379, не гарантирует первый документ, если startup не помещается в 5 s. Артефакты всех красных прогонов сохранены; нельзя представлять их как успешно исправленный режим.
Финальный общий app-прогон и 10× повторы будут выполнены после пересборки с этим явным разделением contract regression и over-budget эксперимента. 170 unit-тестов с актуальной shared logic снова PASS (unit-final-visible.log), 16 Swift lint 0.
Финальная положительная валидация replacement-кандидата
С default install/upgrade fixture 4500 ms, прежним production gate 5 s и неизменными строгими assertions:
install-upgrade-final-repeat.xcresult: 20/20 успешных method executions, по 10 install и 10 upgrade, без skips/failures. Это два уникальных метода, не 20 разных кейсов.enabled-final.xcresult: 4/4 уникальных app methods PASS в одном общем прогоне: P2 84.923 s, install 5.163 s, P1 49.352 s, upgrade 5.434 s. Во всех требуемых точках проверяется и настоящий native dashboard, и page initResp; P2 восстанавливается до другой навигации в том же context, upgrade меняет identifier и подтверждает coexistence.unit-final-visible.log: 170 PASS, 0 failures/skips; в ветке 9 новых unit methods (4 lifecycle + 5 manager).ios-module-final-visible.log: BUILD SUCCEEDED для общего WebExtensions на generic iOS Simulator SDK. Полное iOS app/lifecycle тестирование не запускалось.lint-final.log: strict lint 16 Swift files, 0 violations. Подписи Apple Development и strict проверка фактически загружаемого XCTest и app debug dylib успешны.
Финальные source hashes записаны в final-source-hashes.json перед повторами/общим прогоном и сверены: drift отсутствует, включая README и unchanged ZIP. Побочный SwiftPM pin diff сохранён и удалён из worktree. Начат disabled-final.xcresult на тех же бинарниках, выключающий gate для P1/install/upgrade и recovery flag для P2. Его результат ещё ожидается. Случай 5000 ms + WK overhead выше остаётся красным известным выходом за бюджет; он не был исправлен и не выдаётся за PASS.
Завершение замены #7379 — финальный проверенный результат
Оба запрошенных изменения реализованы в прежнем новом worktree alex/cpm-fix-p1-p2, base/HEAD 01d4a0ec22b4702ca9f9ea37757e060772ec7fa9. trackEmbeddedBackgroundStartup(identifier:) отслеживает новый context, waiter захватывает его; fallback учитывает порядок installation store, а retry игнорирует superseded context. iOS factory передаёт самостоятельный isApplicationInBackground provider из UIApplication; cpmDiagnosticsRecorder=OFF больше не выключает safety guard. Provider и recovery flag повторно проверяются перед queued retry. Прежний diagnostics getter удалён; telemetry schema не менялась.
Два сценария #7379 перенесены в app-hosted suite с исходными method names; worker delay default 4500 ms. Требования не ослаблены: первый документ должен получить native dashboard + page initResp без first failure. Upgrade показывает сосуществование old/new context sets [0,1,2,1] и новый identifier. Это семантическая замена production-фикса #7379 с тем же 5 s bound, плюс P1 idle wake и P2 same-context retry. Сам PR #7379 не менялся и не закрывался.
| Финальная проверка | Результат |
|---|---|
| Общий signed app run: install, upgrade, P1, P2 | 4/4 PASS, 0 skips/failures (enabled-final.xcresult) |
| Повторы install/upgrade на той же сборке | 20/20 executions PASS: install 10/10, upgrade 10/10; 2 уникальных метода (install-upgrade-final-repeat.xcresult) |
| Targeted unit suites | 170 PASS, 0 skips/failures: lifecycle 31, manager 48, остальные связанные 91 |
| Общий WebExtensions для iOS Simulator | BUILD SUCCEEDED (ios-module-final-visible.log); это не полный iOS app/runtime run |
| Strict lint | 16 Swift files, 0 violations |
| Gate/recovery OFF на тех же бинарниках | 0 PASS / 4 functional FAIL, expectedFailures=0, exit 65 (disabled-final.xcresult) |
| Подписи / project structure | Apple Development + strict app debug dylib / загружаемый XCTest PASS; plutil PASS |
Negative controls конкретно: P2 — timeout ожидания восстановления того же документа после настоящей error 6; install/upgrade и P1 — отсутствующие своевременные native dashboard/page initResp и настоящий first-failure snapshot. Ошибок TestRunHelper deallocation в финальных positive/negative runs нет. Это намеренно красный контроль чувствительности, а не четыре нерешённых инфраструктурных падения.
Сохранённое ограничение: исходный gate cap не увеличен, 5 s. Искусственная 5000 ms startup-пауза вместе с overhead WebKit даёт callbacks 5.150336/5.184716 s уже после release; upgrade в этом режиме ранее потерял первый ответ в 6/10. Этот конкретный over-budget режим не исправлен и не считается PASS. Он воспроизводится через CPM_APP_INTEGRATION_INSTALL_DELAY_MS=5000; строгие contract regression cases используют 4500 ms, а bounded/cancellation unit cases отдельно требуют release без callback. Натуральный production trigger и реальный iOS suspension/resume не доказаны этими macOS тестами.
Артефакты: /private/tmp/cpm-p1-p2-replacement-20261016/validation-summary.json, final-source-hashes.json, enabled-final-evidence/, disabled-final-evidence/, install-upgrade-final-repeat-evidence/, все первоначальные failing/timing runs. Всего в ветке 4 новых app + 9 новых unit methods; прежние 22 research app-кейса остаются отдельно. Хэши финальных исходников не изменились во время всех финальных прогонов; bundled ZIP равен HEAD; исходный scratch сверён с прежними hash и не изменён. Побочный generated package pin diff сохранён/убран. HEAD не сдвинут, commitsCreated=0.
Дополнение: 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 recovery: первый подтверждённый app PASS — 16 октября 2026
Первый кандидат дал 0/2 PASS / 2 functional FAIL (late-smoke.xcresult); диагностический повтор install — 0/1 PASS (late-trace.xcresult). Документ действительно оставался без native dashboard/page initResp после 5 s release и WK callback 8.1–8.5 s. Это не infrastructure failure: reload не начинался. Логи подтвердили armed candidate, GET/history identity и включённый flag, но safety check всегда false. Установлена конкретная ошибка Swift overload: evaluateJavaScript(..., in: nil, in: .defaultClient) выбирал synchronous callback overload, возвращающий Void; cast к Bool всегда false. SDK async overload имеет label contentWorld:. Вызов исправлен; аналогичный вызов edit-veto app test тоже переведён на настоящий async API. Локальные decision logs оставлены на debug level, без URL/field values.
На исправленном исходнике 2/2 app PASS (late-fixed.xcresult): 8000 ms worker delay; первая загрузка install 5.230188 s, upgrade 5.141589 s, каждая до готовности background; ровно 2 document finishes (первый + один automatic reload), тот же URL/context, настоящий native dashboard и page initResp. Pixels: соответствующий install/upgrade background_ready, затем late-document reload_started и response_received. Episode закрывается failed_tab_response / reload_state=none: reload здесь относится к документу, не extension context. Targeted unit 179 PASS, 0 failures/skips; strict lint 17 files / 0 violations; shared iOS Simulator + iOS FeatureFlags builds PASS. Полный app run из 8 методов, repeated runs и disabled-new-flag controls ещё выполняются/ожидают очереди. Предыдущие красные artifacts сохранены, assertions не ослаблены.
Итог: opt-in recovery первого документа за пределами 5 s + fix pixels — 16 октября 2026
Это актуальный результат поверх предыдущих progress-записей. Checkpoint прежнего проверенного состояния — dff29dfb1ec7240ee4e2694191a7385dc32dd782 (Fix CPM background wake and installation readiness). HEAD остался на нём; новые recovery/pixels/tests НЕ закоммичены. Ветка/worktree: alex/cpm-fix-p1-p2, /Users/admin/DuckDuckGo/apple-browsers.git/alex/cpm-fix-p1-p2. #7379 не изменялся и не закрывался.
Новый независимый remote flag: webExtensions.cpmLateBackgroundDocumentRecovery, default disabled на macOS и iOS; remote configuration не менялась. Tests явно включают его. Старые gate/recovery flags и navigation timeout 5 s сохранены. Восстановление работает независимо от diagnostics flag.
CPMBackgroundReadinessRecovery получает точный startup UUID от manager и события реального health monitor. Документ привязывается к startup, только если commit произошёл до успешной готовности background. Нужны оба факта: реальный missing-dashboard timeout и успешный public WK background callback. Затем допускается одна GET-перезагрузка в пределах 30 s от начала startup. Проверяются тот же navigation/document и history item, отсутствие загрузки/изменённых main-document form fields/contenteditable, foreground app state и runtime flag; флаг/идентичность проверяются повторно после async JS safety check. Смена/закрытие/ошибка навигации и context replacement отменяют candidate. Healthy late response отменяет reload; ответ исходного документа во время safety check не получает recovery credit. Повторные callbacks не создают цикл.
Это новый документ на том же URL после одного reload, с сохранённым WKWebExtensionContext. Исходный потерянный init не пересылается. P2 по-прежнему восстанавливает именно исходный документ background-only retry; его механизм не заменён этим reload.
| Финальная проверка | Проверенный результат |
|---|---|
| Signed общий app-набор | 8/8 PASS, 0 skips/expected failures, 0 teardown/deallocation errors — enabled-all.xcresult |
| Новые over-budget install + upgrade, первый зелёный прогон | 2/2 PASS — late-fixed.xcresult |
| Те же два over-budget метода, по 5 повторов | 10/10 executions PASS, 2 уникальных метода — late-repeat.xcresult |
| Только новый flag OFF, те же binaries | 0/2 PASS, 2 functional FAIL, expectedFailures=0, exit 65, без teardown errors — late-disabled.xcresult |
| Targeted shared unit suites | 179 PASS, 0 failures/skips: manager 48, lifecycle 31, background-load diagnostics 13, health monitor 78, новый recovery 9 |
| Strict SwiftLint | 17 Swift files, 0 violations; compiler warnings нового recovery отсутствуют после исправления async API |
| Pixel definitions, formatting, wide-event consistency + immutability | PASS macOS + iOS; generated schemas не менялись |
| Shared WebExtensions + FeatureFlags-iOS для iOS Simulator | BUILD SUCCEEDED; полный iOS app/runtime не запускался |
| Подписи | Apple Development: Alexey Martemyanov (9SAT8X9S84); strict verify app debug dylib и загружаемого Integration Tests.xctest PASS |
Восемь методов — 7 реальных app/WebKit scenarios + 1 PixelKitMock adapter test: прежние P1, P2, install, upgrade; два новых 8000 ms install/upgrade; запрет reload изменённой формы; проверка пяти имён/prefix/frequency/parameters через production adapter. Новые 9 unit methods проверяют success/failure prerequisites, once-only, healthy late reply, stale/closed/failed/replaced navigation, runtime flag/background checks, flag OFF→ON, expiry/unsafe veto, ответ во время safety check и отдельные pixel metadata. От checkpoint добавлены 4 app methods и 9 unit methods; всего в чистой ветке 8 app methods и 18 новых unit methods относительно исходной telemetry branch. Исторические 22 research methods не подменяются этим счётом.
Во всех 14 положительных over-budget executions (2 initial + 2 combined + 10 repeat) worker delay 8000 ms, первый document finish 5.085–5.328 s, раньше ответа фона; finish count строго 2 (первая загрузка + один reload), URL/context те же, настоящий native dashboard и page initResp получены. Test допускает 6.5 s на 5 s gate + loopback document load. Новый флаг OFF оставляет обе страницы без ответа после public background readiness; оба fail именно на ожидании автоматического recovery. Это проверяет чувствительность регрессий. P1 остаётся healthy без failure episode; P2 получает настоящую error 6, сохраняет context и доставляет исходному документу initResp без навигации. Проверка изменённого поля сохраняет один finish и отсутствие reload pixels; последующая ручная навигация healthy.
Пиксели пойманных fix cases
Общий prefix web_extension_cpm_; macOS добавляет _macos, iOS использует стандартные platform/form-factor suffixes. Каждый case — отдельный pixel, frequency dailyAndCount:
| Case | outcome и точный смысл |
|---|---|
install_readiness |
background_ready: успешный медленный WK callback после использования navigation wait при install |
upgrade_readiness |
background_ready: такой же callback именно replacement context при upgrade |
background_wake |
background_ready: медленный shared wake после использования navigation wait (P1) |
background_load_retry |
background_ready: успешный public background retry после настоящей error 6 (P2), включая быстрый callback |
late_document_reload |
reload_started: WK принял reload; response_received: monitor однозначно атрибутировал dashboard response после начала replacement navigation |
background_ready не означает page ACK. response_received — dashboard evidence, без доказательства причинности reload или initResp/all-tab recovery; настоящий page ACK отдельно проверяет app harness. Daily считает affected devices по имени, count/stage позволяет различать фактические операции; daily dedup не разделён по outcome. Параметры: outcome + стандартные appVersion/pixelSource/(macOS channel), без URL/tab ID/form values/raw errors. Пять definitions присутствуют на обеих платформах; adapters используют injected PixelKit sink. Существующий episode pixel/schema сохранён: для document reload observed failed_tab_response / reload_state=none, поскольку reload_state относится к extension context.
Артефакты и границы
/private/tmp/cpm-late-recovery-20261016/validation-summary.json содержит группы прогонов, timings, source/binary SHA256 и ограничения; рядом все .xcresult, summaries, JSON attachments, build/unit/lint/pixel/signing logs и summarize.py. Красные late-smoke (2 fail) и late-trace (1 fail) сохранены: они выявили неверно выбранный Void callback overload; async label исправлен на contentWorld:. Assertions не ослаблялись. Финальные source hashes и app/XCTest binary hashes стабильны во всех зелёных/повторных/disabled runs.
Bundled ZIP равен checkpoint, SHA256 ff98a2b0c07819b2ee5ee6de67d6dd8d28696363c08c9fbc2c32255e9d9efa94; задержка/DOM observation добавляются только в extracted test copy. Реальные AppDelegate, tabs/windows, lifecycle coordinator, handler, health monitor и системный WebKit; нет искусственного отвязывания табов, фиктивных ответов или scoped drops. P2 останавливает только свой background PID и гарантированно возобновляет его. Побочный SwiftPM package pin diff сохранён в artifacts и убран; зависимости не изменены. Исходный research scratch не редактировался.
Старое ограничение «over-budget first response loss не исправлен» снято для этого opt-in GET recovery в описанных условиях. Постоянный/неответивший background, unsafe/edited/POST documents и startup за пределами recovery window этим механизмом не исправляются. Натуральные причины всех production cohorts, P3, старые P5-ограничения и реальный iOS suspension/resume этой новой проверкой не установлены и не закрыты.
Перенос в 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:
05b499e355— P1/P2, ожидание нового install/upgrade context и необходимые app/unit tests.c661f805b1— late GET document recovery, независимый flag, пять fix pixels, регрессии.aec5be9a83— маркировка local-only fault-injection suite/scheme/README какto-be-disabled before merge.
PR #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.
Уточнение reviewer instructions в #7379 — 16 октября 2026
После пользовательских правок прочитана актуальная PR description; они сохранены. Из Testing Steps удалены внутренние instructions для intentional-red controls, таблица local validation и пространный local-only/cleanup paragraph, а также manual flag step без способа воспроизвести slow startup. Reviewer steps теперь: macOS 15.4+, открыть DuckDuckGo.xcworkspace, выбрать CPM Integration Tests / My Mac, Product → Test (⌘U), ожидать 8 PASS / 0 skips; затем запустить указанную команду shared unit suites и ожидать PASS. Dedicated scheme сама задаёт opt-in и fixture flags. Проверки OFF/ON и их результаты остаются исследовательским evidence в этом документе, а не заданием reviewer получать красный Test run. Код, annotations и HEAD не менялись. Последний remote текст сверён после записи; snapshots/diff: /private/tmp/cpm-pr7379-final-20261016/pr-user-edited.json, pr-reviewer-steps-20261016.diff, pr-reviewer-steps-after.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 headda126120a9, один дополнительный 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 и исходная история сохранены; дополнительный backupcodex/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 подпись, teamHKE973VLUW; 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, harness6e4e39e67c64907127bd17197a932b0117a4cb09; 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 — отдельная незавершённая проверка, snapshotci-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, база #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: исходное замечание уже было закрыто реализацией cancellable AsyncStream bridge и regressiontestBackgroundWakeWaitIsBoundedWhenWebKitIgnoresCancellation. Проверено повторно на текущем коде: timeout освобождает navigation, даже когда реальный startup task не реагирует на cancellation; физический WebKit startup не отменяется. - Повторные пять секунд на каждом navigation: замечание оказалось актуальным по поведению, несмотря на устаревшее имя файла в тексте. Раньше каждый 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и iOSTabViewControllerвызов 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:
Оба 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, база #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 выше сохранены как история.
19 октября 2026 — P4 detection независимо от late-document reload
Запрос: считать затронутые документы/установки при выключенном webExtensions.cpmLateBackgroundDocumentRecovery, без автоматической перезагрузки страницы.
Опубликовано: production alex/cpm-fix-init-loss / fc87237a476cca58a17fde69b3091b8e99602e03 в #7379, база #7376 / 713d67419a, 7 commits / 22 changed paths. Отдельный review-only harness codex/cpm-fix-init-loss-integration-tests / 475095b33535467f9d63eda6d593cf95242d0449, по-прежнему 12 harness-only paths вне main. Обе ветки опубликованы обычным fast-forward push. Ручные правки PR description сохранены; reviewer steps обновлены на 9 PASS → 6 ожидаемых functional FAIL → 9 PASS.
Что именно считается
Новый web_extension_cpm_startup_response_missing (macOS: web_extension_cpm_startup_response_missing_macos), частота dailyAndCount, без custom parameters. Daily оценивает число затронутых установок за день; count — число подходящих документов. Это не число уникальных людей за всё время.
Сигнал возникает один раз на документ, если HTTP(S)-документ закоммичен во время известного ещё незавершённого первого monitored background startup данного embedded context, ожидание CPM dashboard response завершилось timeout, а WebKit startup callback успешно завершился в пределах 30 секунд от начала startup. Порядок callback и timeout не важен. Обычные последующие idle wakes того же context, здоровые/сменившиеся/отменённые документы, invalidated context, failed startup и истёкшее 30-секундное окно исключаются. Это P4 candidate signature, не доказательство потери именно initResp и не доказательство единственной первопричины; исторические P4/P6 и bg_events наблюдения ниже остаются в силе.
Детекция не зависит от late recovery flag, GET/form reload eligibility и нахождения приложения на переднем плане. Она не запускает safety JavaScript, reload или дополнительную WebKit background load. URL, tab ID, значения форм и raw errors в pixel не передаются. First-startup identity учитывается слабым набором WKWebExtensionContext; проверено, что WKWebExtensionContext использует NSObject identity equality. Это отдельный счётчик, а не переименование late_document_reload/response_received: прежние recovery pixels продолжают означать действия/результат восстановления.
Возможность reload по-прежнему проверяет flag и условия безопасности. Для документа, отслеживаемого при выключенном флаге, позднее включение флага не разрешает retroactive reload. Navigation wait limit не увеличен. Исходный form predicate не изменён, ранее отложенное усиление защиты форм не добавлялось; default late recovery flag остаётся disabled.
Проверки production и app fixture
- 197 shared unit tests PASS / 0 failures: 13 diagnostics + 20 recovery + 78 monitor + 35 lifecycle + 51 manager. В этом изменении добавлено 7 regression methods; всего 36 новых shared regressions относительно базы.
- Покрыты OFF без preflight/reload closure, обе очередности timeout/readiness, once-per-document и несколько табов, idle wake exclusion, healthy/navigation/cancel/invalidate/failed/expired exclusions, независимость detection/recovery outcomes и PIIFree daily/count metadata.
- Production strict lint: 6 изменённых Swift files / 0 violations; harness strict lint: 2 / 0. macOS/iOS pixel definitions и обе wide-event validation/schema immutability проверки PASS.
- Полные signed macOS и iOS Simulator app builds PASS. Обычная Apple Development signing сохранена.
- App fixture: новый
CPMBackgroundRecoveryIntegrationTests.testLateStartupIsDetectedWithRecoveryDisabledWithoutReloadявно выключает production late recovery flag, задерживает реальный background startup на 8 секунд, требует реальный dashboard timeout, detection count = 1, один finish исходного документа, отсутствие late_document_reload outcomes и здоровую следующую ручную навигацию. P1/P2 fixture также проверяет, что idle wake не увеличивает P4 counter; pixel adapter проверяет имя/частоту/параметры. - Signed app ON 9 PASS → OFF 6 functional FAIL → ON 9 PASS, без skips/expected failures, на одних бинарниках.
app-on.xcresult,app-off.xcresult,app-restored.xcresultи их summaries/attachments сохранены. SHA256 app debug dylib =7d3a3c9f1a772a24b2e502a3e8f07db78603e8a4d44a9cfd68de12f2f9dca52e; XCTest executable =4c28d4bf9a69ccd4f0c24b67d25bf5b492d47a68f343270bfc74a7505071194a, до OFF / после OFF / после ON совпали. - Новый detection-only positive case дважды PASS при late recovery flag OFF: реальный first-document timeout, detection = 1, finishCount = 1, отсутствуют
late_document_reloadoutcomes; следующая ручная навигация получает native dashboard и page initResp, счётчик остаётся 1. Доказательства:detection-only-evidence.json, ON attachment9567542F-EF42-4C5A-8FFC-ACF716ABC09F.json, restored attachmentCA7E1D06-9140-4D8E-B119-363B43D4CA8B.json. - Все шесть OFF failures проверены по именам и причинам: install/upgrade/slow wake теряют первый ответ, P2 получает настоящий WebKit error 6 и timeout same-document recovery, late install/upgrade получают timeout автоматического восстановления. Оба late OFF controls дали detection = 1 и ни одного late reload event. Setup/host failures не использованы как red controls. Доказательства:
off-control-evidence.json,validation-summary.json. - CI production head
fc87237a47: 48 SUCCESS / 21 SKIPPED, pending и failures нет. PR head/base/description проверены чтением после публикации;pr-final.json. Worktree чистый и возвращён наalex/cpm-fix-init-loss; обе локальные ветки совпадают с origin.
Локальные artifacts: /private/tmp/cpm-p4-detection-20261019/: unit.log, unit-evidence.json (96 production source files byte-identical), lint.log, harness-lint.log, четыре pixel validation logs, mac-build.log, ios-build.log, context-identity.log, heads.json, pr-body.md, app result bundles, detection-only-evidence.json, off-control-evidence.json, validation-summary.json, три SHA256 snapshots, pr-final.json. Исторические результаты предыдущего head e970e8c90e (190 / 8→6→8) выше/ниже сохранены и не являются результатами нового head.
Полное ревью после P4 detection — error-6 attribution (19 октября 2026)
Проверен полный diff #7379 относительно #7376: manager/install/upgrade/uninstall/reload, общий launch/navigation deadline и cancellation, WebKit navigation identity, обе платформенные интеграции, runtime flags, P4/health-monitor evidence и pixel adapters/definitions, а также shared regressions и reviewer instructions. Ревью начато на fc87237a47 / 713d67419a; во время проверки опубликовано перебазирование на a00dc4afeb95b3fbbbb95dc50ce450c49004f3e2 / c7adf28e1254da79d788125033474db737b11b6d. Затронутая логика manager/recovery/lifecycle не изменилась; замечание отдельно повторено на новом head. Полный diff по-прежнему 7 commits / 22 paths.
Открытое замечание P2 — failed startup с error 6 не инвалидируется в P4 detector. WebExtensionManager.swift:319–320: error notification завершает только manager startup task через clearEmbeddedBackgroundStartup(), сохраняя startup и его документы в CPMBackgroundReadinessRecovery. Если WebKit сохраняет pending load callbacks до удачного retry, старый callback приходит с nil; backgroundCompleted(oldID, succeeded: true) делает уже провалившийся first startup успешным для P4. При timeout документа и завершении retry до 30 секунд detector посылает web_extension_cpm_startup_response_missing. Это смешивает известный error-6 failure/retry с P4 candidates, вопреки заявленному исключению failed startups.
Порядок соответствует upstream WebKit implementation: didFailLoadWithError записывает error и выгружает background view, recordError асинхронно публикует notification; pending actions исполняются после успешной background load. Исходники сохранены вместе с review artifacts. Для исправления нужно инвалидировать данные именно провалившегося startup до освобождения его wait, а поздние callbacks этого startup игнорировать.
Доказательство: временный CPMP4FullReviewTests.testError6FirstStartupMustNotBecomeP4WhenOldCallbacksCompleteAfterRetry проходит через production manager, notification observer, реальный health monitor/timeout и pixel instrumentation. Публичные WebKit completions управляются в subclass context; известный error 6 вызывает production retry, затем retained callbacks завершаются с nil. На текущем head получен detection count = 1 вместо 0. Это детерминированный regression probe порядка public API, не новый natural-trigger signed-app reproduction. Предыдущие настоящие app 9→6→9 сохранены как отдельное доказательство на прежнем head и не подменяют этот probe.
В изолированной копии вставка backgroundRecovery?.invalidate() перед clearEmbeddedBackgroundStartup() в ветке совпадающего context устраняет дефект. На актуальном head весь выбранный набор с тремя probes: 199 PASS / 0 failures. Затем проверочная production-правка из копии убрана; production worktree/PR не изменены, commit/push не делались. Это проверка причины, а не опубликованный fix.
Проверки и artifacts
- Опубликованный прежний
fc87237a47: 197 штатных shared tests PASS; probes 2 PASS / 1 FAIL; временный candidate fix 200 PASS. - Актуальный
a00dc4afeb: 196 штатных shared tests PASS, probes 2 PASS / 1 FAIL, всего 199 tests / 1 FAIL. Разница 197→196 — удалённый retired-telemetry тест в обновлённой базе #7376, не потеря P1/P2/P4 регрессий. - Актуальный head с candidate fix только в копии: 199 PASS. Два дополнительных positive probes подтверждают detection при late flag OFF + background app state и независимость текущего startup от failed callback предыдущего startup.
- macOS/iOS pixel definitions, formatting, wide-event consistency и generated schema immutability PASS.
git diff --checkPASS; production worktree чистый. Исходники и штатные тесты копии сверены с текущим Git head (153 tracked paths); временный probe — единственный дополнительный test file. - Новый head CI на момент завершения проверки: 31 SUCCESS / 14 SKIPPED / 4 IN_PROGRESS / 3 QUEUED, failures нет, завершение не заявлено. Старый green CI 48/21 относится к
fc87237a47. - Review-only harness tracking head остаётся
475095b335, содержит прежний productionfc87237a47и 12 harness-only paths. App ON 9 PASS → OFF 6 functional FAIL → ON 9 PASS здесь повторно не запускался; после текущего rebase/correction точный новый head этими runs не объявляется проверенным. - Формы сохранены в ранее явно согласованном scope: исходная проверка и default-disabled late recovery flag. Отложенное усиление формы повторно не предъявлялось как новый finding. Других новых actionable дефектов по полному diff не подтверждено.
Artifacts: /private/tmp/cpm-pr7379-after-p4-review-20261019/: review-summary.json, CPMP4FullReviewTests.swift, candidate-fix.diff, shared-tests.log, probes.log, probes-with-candidate-fix.log, rebased-head-with-probes.log, rebased-head-with-candidate-fix.log, четыре pixel validation logs, snapshots WebKit implementation и pr-final.json. Исходная тестовая копия после проверки возвращена к production source; candidate patch сохранён отдельно.
Финальный контроль #7379 — 19 октября 2026
Head остаётся a00dc4afeb, база c7adf28e12; production diff не изменился после полного review. Остаётся один открытый P2 error-6/P4 attribution: проверочная production-правка в PR отсутствует. Повторный запуск трёх изолированных probes на точном head: 2 PASS / 1 FAIL, detection count 1 вместо 0 в ранее найденном случае. Это тот же управляемый public-API regression probe, не новый natural-trigger app run.
CI актуального head завершён: 53 SUCCESS / 25 SKIPPED, failures и pending нет. Два GitHub review threads resolved, новых открытых threads нет; это не означает, что локально воспроизведённый P4-дефект исправлен. Краткое описание PR и 10 однострочных reviewer steps ON → OFF → ON проверены; таблица feature flags удалена по запросу пользователя. Production worktree чистый; код PR в ходе финального контроля не менялся.
Повторный log: /private/tmp/cpm-pr7379-final-review-20261019/probes.log; PR/CI snapshot: /private/tmp/cpm-pr7379-final-review-pr.json; review threads snapshot: /private/tmp/cpm-pr7379-final-review-threads.json. Детали причины, предыдущие 196 shared PASS и candidate-only 199 PASS — в предыдущем разделе.
Исправление error-6/P4 finding в production PR — 19 октября 2026
Первоначальная публикация fc124938e0
Опубликован fc124938e040533e8ea9ef04df9d313d0e9d5dcb в #7379, поверх a00dc4afeb: 2 изменённых файла, 8 commits / 22 paths относительно #7376. Observer принимает только background-load error 6 из WKWebExtensionContextErrorDomain. Для совпадающего pending context он инвалидирует startup/document recovery до clearEmbeddedBackgroundStartup(); retained successful callbacks после retry уже не могут объявить этот failed startup успешным P4 или запустить document preflight.
Штатные тесты теперь в PR, в WebExtensionManagerTests.swift:
testFailedFirstStartupCannotReportP4OrReloadAfterRetainedCallbacksSucceed: actual production observer/manager/health-monitor path, retained public WebKit callbacks, late recovery flag OFF и ON; после error 6 нет P4 detection и document preflight.testUnrelatedErrorPreservesCurrentFirstStartupDetection: error 6 superseded context и code 6 другого error domain не сбивают текущий startup и его корректный P4 detection.
До production fix официальный первый regression method красный при обоих состояниях late flag (3 assertion failures), obsolete-context control PASS. После исправления 198 штатных shared tests PASS / 0 failures: 13 diagnostics + 20 recovery + 77 monitor + 35 lifecycle + 53 manager. Всего 38 новых shared regressions относительно текущей базы. Strict SwiftLint: 2 changed files / 0 violations. Полный iOS Simulator app build PASS с обычными настройками проекта.
Harness обновлён 475095b335 → f4f135766a7fd67096ebc6a973919cb5a464d7ec обычным merge/push, без переписывания истории. Конфликты в manager/shared tests разрешены точным production-кодом. Актуальный production head — предок harness; diff ровно 12 harness-only paths, старые telemetry/test deletions больше не возвращаются. README обновлён: 38 новых shared regressions, 10 manager regressions. Тестовые steps в PR остаются 9 PASS → OFF 6 functional FAIL → ON 9 PASS.
Первоначальный app этап: первый build-for-testing выявил ссылки fixture на удалённые telemetry API. Исправлен только harness (eb3865b594): evidence строится через текущий public episode metadata adapter, добавляется локальный raw view count без отправки synthetic pixel. Затем signed ON дал 9 PASS на production fc124938e0. Фикс уже опубликован; предыдущая candidate-only правка больше не описывает актуальное состояние. Формы и default-disabled late recovery flag сохранены.
Artifacts: /private/tmp/cpm-pr7379-error6-fix-20261019/: red-before-fix.log, shared-tests.log, lint.log, ios-build.log, heads-and-checks.json, app-build.log/app-build.xcresult, pr-progress.json; все последующие app bundles также сохранены в этой папке.
Итог после обновления telemetry/main и WebKit error API
Пока выполнялась проверка, production обновлён обычными merge до 8275bcce368ea63b3777b42b6007beb48d768409, база #7376 — 39fe56af4c35bf034d570f04dd58f59bd41eb2b6. fc124938e0 — предок опубликованного PR, его fix и обе штатные регрессии сохранены. Текущий diff: 10 commits / 23 paths. Safe WebKit error copying и изменения base/main учтены; retired EventsListener mock удалён из изолированной package-копии.
На обновлённом production 201 selected shared tests PASS / 0 failures: 14 diagnostics + 20 recovery + 77 health monitor + 35 lifecycle + 55 manager. Относительно текущей базы #7376 — 38 новых shared regressions, включая обе новые error-6/P4 проверки; первая покрывает late recovery OFF и ON. Strict SwiftLint на 4 изменённых Swift files: 0 violations. Первоначальный полный iOS Simulator app build PASS относится к fc124938e0; для текущего 8275bcce36 завершились зелёным iOS PR Checks и iOS UI Tests в CI.
Harness опубликован 6d04496882ad25e05dceb622305a50c7e51aeb37, содержит актуальный production 8275bcce36; diff ровно 12 fixture-only paths, вне main. История синхронизации сохранена: f4f135766a → eb3865b594 → 6d04496882, обычные merge/push. README отражает 38 новых shared regressions и 10 manager regressions. Параллельные незакоммиченные правки alex/cpm-fix-p1-p2 и отдельного alex/cpm-fix-init-loss-production не трогались и не включались в validation; app запускался из чистого checkout /private/tmp/cpm-pr7379-error6-fix-20261019/source. После тестов он возвращён на detached production 8275bcce36; production-ветка уже занята параллельным worktree, её повторный checkout был безопасно отклонён Git.
После смены базы app build сначала остановился на устаревшем локальном workspace lockfile; выполнено разрешение зависимостей. Затем выяснилось, что reused DerivedData импортирует старые standalone .swiftmodule вместо свежих PackageFrameworks (конкретно отсутствовал DataClearingActionSkipping из нового BrowserServicesKit). Сохранены 11 подтверждённых устаревших модулей в stale-standalone-modules/ с manifest; исходники не менялись. После этого подписанная app-сборка прошла. Подпись: Apple Development, Team HKE973VLUW; host macOS 26.7.1 (25G241).
Фактическая app-матрица на одном build:
- ON:
current-app-on-3.xcresult— 9 PASS / 0 FAIL / 0 SKIP. - OFF:
current-app-off.xcresult— 0 PASS / 6 functional FAIL / 0 SKIP; install, upgrade и slow wake теряют first response; error-6 recovery, late install и late upgrade завершаются ожидаемым recovery timeout. Host/setup failures отсутствуют; это отрицательный контроль, не успешный test run. - Первый restored ON:
current-app-restored.xcresult— 7 PASS / 2 FAIL / 0 SKIP: P2 functional recovery завершился, но views не освободились за teardown budget; следующий install host остановился с SIGINT. Этот результат сохранён как failed, retained root cause не установлен; подробности ниже. - Полный restored ON retry:
current-app-restored-retry.xcresult— 9 PASS / 0 FAIL / 0 SKIP. Выполненtest-without-building, проверки/assertions не ослаблялись. SHA256 app debug dylib и XCTest executable совпали до OFF, после OFF и после restored ON; compilation между фазами не было.
P4 detection-only method прошёл в обоих успешных ON runs: late flag OFF, detections=1, finishCount=1, нет late reload/recovery events. Это счётчик candidate signature, а не доказательство конкретного root cause каждого пользовательского инцидента. Новый shared regression отдельно исключает из P4 известный failed startup после retained successful callback.
CI текущего head завершён: 43 SUCCESS / 17 SKIPPED, 0 failures / 0 pending; macOS PR Checks, iOS PR/UI Checks, BSK, lint и pixel schema зелёные. Сохранены final-pr.json и current-ci-runs.json. Пользовательские title и лаконичное PR description сохранены: 10 точных однострочных steps, отдельный harness, ON → OFF → ON, без таблицы feature flags и служебных local-validation секций.
Итог для rollout: error-6/P4 review blocker закрыт в production PR; P1/P2 и независимый P4 detection проверены. Late document reload остаётся default OFF. Shadow DOM / controlled form hardening по просьбе пользователя отложен; включение late reload в production — отдельный scope. Flaky teardown/host failure review-only harness не скрыт и не считается функциональным PASS.
Полный evidence index: /private/tmp/cpm-pr7379-error6-fix-20261019/validation-summary.json; logs + .xcresult всех попыток, exported app attachments, current-app-restored-first-diagnostics/, p4-detection-only-evidence.json, три SHA256 manifests, harness-only-paths.txt, final-pr.json. Старые результаты и failed attempts сохранены.
Промежуточный app-run: teardown / SIGINT — 19 октября 2026
На чистом harness 6d04496882 (production 8275bcce36) первый ON завершился 9 PASS, OFF — 0 PASS / 6 ожидаемых functional FAIL / 0 SKIP, SHA256 app/test modules совпали. Первый restored-ON (current-app-restored.xcresult) завершился 7 PASS / 2 FAIL / 0 SKIP: testBackgroundLoadFailureRecoversOriginalDocument прошёл CPM assertions, но TestRunHelper.swift:453 не дождался освобождения window views за 5 секунд; следующий testInstallDuringSessionInitIsDelivered остановился с SIGINT, runner перезапустил host. P2 evidence подтверждает native dashboard + page initResp после recovery для existing tab, new window и new tab; этот run всё равно считается неуспешным. В production DEBUG есть ensureObjectDeallocated(..., .interrupt) → raise(SIGINT); связь с конкретным удерживающим reference пока не доказана, retained root cause не установлен. Функциональные ожидания и deallocation checks не отключались. Полные attachments, diagnostics, summary и логи сохранены в /private/tmp/cpm-pr7379-error6-fix-20261019/current-app-restored-first-*; начатый полный restored-ON retry на тех же бинарниках (current-app-restored-retry.xcresult) затем завершился 9 PASS / 0 FAIL / 0 SKIP. SHA256 app/test modules совпали; source и assertions между попытками не менялись. Это не отрицательный контроль, failures не исключаются из истории.
Проверка отчёта о трёх падениях late-startup app tests — 19 октября 2026
Исходный screenshot: failed testLateStartupIsDetectedWithRecoveryDisabledWithoutReload, testOverBudgetInstallRecoversFirstDocumentWithoutExtendingNavigationWait, testOverBudgetUpgradeRecoversFirstDocumentWithoutExtendingNavigationWait; остальные 6 methods зелёные. Assertion text и macOS/Xcode reviewer run не предоставлены, поэтому идентичность конкретной причины его каждого failure не утверждается. На Virfield macOS 27 воспроизведены все три имени и найдены ошибки timing assumptions в harness.
Опубликованный harness: 29da2c1587571e91f85915c35c8322f273e07eb0, branch codex/cpm-fix-init-loss-integration-tests. Обычный fast-forward push от d26a1c55d1; локальный чистый worktree alex/cpm-fix-p1-p2 также fast-forward обновлён. Новые изменения затрагивают ровно три существующих файла: CPMAppIntegrationHarness.swift, CPMBackgroundRecoveryIntegrationTests.swift, README.md. Production PR #7379 остаётся на 674bb809f2, base alex/cpm-messaging-readiness-telemetry (#7376); production files, flags/defaults, reload safeguards и 5-second navigation timeout не менялись. Production head — ancestor harness; полный diff harness относительно production по-прежнему содержит только 12 harness paths, без новых submodule changes. Оба shared schemes автоматически задают flags: normal CPM_APP_INTEGRATION=1; disabled дополнительно задаёт оба DISABLE overrides=1 и выбирает ровно 6 recovery methods. Ручной шаг включения late recovery в PR instructions не пропущен; текущие 10 шагов PR сохранены, README уточнён.
Исходный d26a1c55d1 на host macOS 26.7.1 (25G241) / Xcode 27.1 (27A9275) дал: reported 3 methods 3 PASS / 0 FAIL / 0 SKIP; полный disabled 0 PASS / 6 ожидаемых functional FAIL / 0 SKIP; следующий normal 9 PASS / 0 FAIL / 0 SKIP. На чистой Virfield macOS 27.0 (26A428), Xcode 27.0 (27A266a), arm64 исходный harness дал normal 5 PASS / 4 FAIL / 0 SKIP: edited-document и over-budget install истекали на 6.5-second first-document precondition; следующие P4-detection и over-budget upgrade падали с SIGINT. Disabled дал 0 PASS / 6 FAIL, но часть была precondition/host failures, поэтому этот исходный VM run не считается успешным отрицательным контролем. Пример normal install: policy callback 16:05:46.736120Z, fixture timeout 16:05:51.770356Z, didStart 16:05:51.806345Z — fixture бросал ошибку за 36 ms до release корректного пятисекундного gate, включив холодную подготовку окна/WebKit в общий бюджет. Незавершённые unstructured navigation tasks продолжали работу во время teardown перед следующими SIGINT.
Промежуточный вариант с прогревом окна, отдельным измерением реального coordinator waiter и structured late navigation cleanup убрал precondition/SIGINT cascade, но на VM дал 6 PASS / 3 FAIL, затем disabled 0 PASS / 6 FAIL, затем normal снова 6 PASS / 3 FAIL на неизменных binaries. Ровно три reported methods требовали failure/recovery от здоровой страницы: при 8-second startup WebKit 27 доставлял настоящий native dashboard и page initResp через ~3 секунды после release gate, до 4-second response deadline. Поэтому отсутствие missing-response/reload было корректным поведением app. Late-startup fixture delay увеличен до 12 секунд, чтобы пересечь и 5-second gate, и 4-second response deadline; обычный P1 idle-wake остаётся 8 секунд. Используется реальное приложение, AppDelegate/Tab/lifecycle coordinator, system WebKit, CPU delay в extracted test copy extension и настоящие ответы; ACK/ошибки/пиксели не фабрикуются.
Checkpoint 8a27a69776 включил эти harness fixes. Первая локальная 12-second попытка: 2 PASS / 2 FAIL, оба over-budget cases проверяли P4 count до реального background completion; после ожидания callback локальные четыре methods дали 4 PASS / 0 FAIL. Первый полный 12-second VM run: 7 PASS / 2 FAIL — P4-only ошибочно запрещал естественный поздний original response после уже зарегистрированного timeout, а install проверял response_received через ~38 ms после reload finish без отдельного ожидания. Follow-up 0a6d90e4c0 сохраняет исходный failed probe, P4 detection=1, finishCount=1 и отсутствие reload при recovery OFF, но допускает поздний original response. Recovery требует reload_started, ровно два finish, ACK уже нового документа и отдельное ожидание attributed response_received с точным списком outcomes. Локальные четыре methods после этого: 4 PASS / 0 FAIL / 0 SKIP. Несвязанный перевод P2 navigation Task→async let затем убран в ddee0e5be0: финальный P2 method сохраняет исходную Task и timing. Это ограничение diff, а не заявленный фикс повторного WebKit failure. Локальный отдельный P2 после возврата исходной Task: 1 PASS / 0 FAIL / 0 SKIP. 29da2c1587 добавляет README-only описание оставшегося failure. Strict SwiftLint двух изменённых Swift files: 0 violations, diff checks clean.
Финальный VM цикл на коде ddee0e5be0 (published head 29da2c1587 отличается только README): normal 9 PASS / 0 FAIL → disabled 0 PASS / 6 ожидаемых functional FAIL → normal 8 PASS / 1 unexpected P2 FAIL; везде 0 SKIP. Все три исходных reported methods и edited-document PASS в обоих normal runs. Disabled failures проверены по причинам: install/upgrade/slow wake не получают первый ответ в срок, P2 не восстанавливает исходный документ, late install/upgrade не выполняют automatic recovery; host crashes/setup errors не засчитаны как controls. Финальные measured gate waits: install 5.00827 s, upgrade 5.01224 s; после OFF соответственно 5.01076 s, 5.00807 s. При release startup оставался pending; оба recovery cases имеют finishCount=2 и оба reload outcomes. P4-only имеет detections=1, finishCount=1, без reload/recovery outcomes. App/test SHA-256 до/после всех трёх phases совпали и совпали с локальным payload; пересборки между phases не было. Полный green 9 → 6 expected red → 9 на финальной версии не подтверждён; последний enabled run действительно красный из-за P2.
Оставшийся P2: retry тоже иногда получает настоящий background-load error 6. Два отдельных normal runs 0a6d90e4c0 дали 8 PASS / 1 P2 FAIL, но тот же неизменный payload после disabled дал 9 PASS / 0 FAIL. В первой failed попытке второй error 6 пришёл через 30 секунд после первого (16:36:14.455Z → 16:36:44.468Z, retry PID 3001); повтор дал 16:40:38.693Z → 16:41:08.707Z, PID 3265. Контроль единственного P2 на исходном published d26a1c55d1 в той же VM также дал 0 PASS / 1 FAIL / 0 SKIP с повторным error 6, поэтому явление воспроизводится и без новых late-startup fixes. Для baseline retry PID 3952 успешно сняты background/app stack samples: WebKit 22625.1.29.11.27, background footprint 11.3 MB, main thread в XPCServiceMain/xpc_main/NSRunLoop/Mach wait, 0% CPU; app main thread в XCTest/AppKit run loop. Эти стеки не показывают CPU busy-loop и совместимы с неустановившимся XPC/bootstrap connection, но точная root cause не доказана. Console VM была разблокирована/активна; crash/OOM report в собранных логах не найден. Failure не объявляется infrastructure-only и не игнорируется. Финальный normal после OFF снова поймал его; README явно считает это unexpected enabled-scheme failure, а не валидный отрицательный контроль. Production-изменений для второго error 6 в этой работе нет.
Все raw xcresults, logs, JSON attachments, generated .xctestrun/flag environment, app/test hashes, export manifests и samples сохранены в /private/tmp/cpm-pr7379-three-late-failures-20261019/: vm-baseline/, vm-eight-second-intermediate/, vm-twelve-intermediate/, vm-verified-first/, vm-confirmation/ (включая baseline-p2.xcresult и samples), vm-final/. SHA-256 manifests экспортированных VM bundles совпали с оригиналами; финальный экспорт проверен по 3820 files. Локальные failed/positive attempts также сохранены отдельно. Первая transfer попытка пересеклась с Xcode build и завершилась rsync exit 23 до test execution; stable copy затем успешно запустила baseline. Deep signature verification локального Debug build сообщал missing/invalid sealed resource во вложенном VPN helper PrivacyConfig.framework; это не засчитано как причина reported failures. Использовался Apple Development signed host app/test build, guest signing identities отсутствуют, private signing keys в VM не передавались. Virfield lease lease-671a00b65fc4efdfc9b0df677a0ff51f, expiry 2026-10-19T17:49:35Z; VM не удалялась без отдельного разрешения.
#7379: review fixes и CI — 20 октября 2026
Область этой сверки — только #7379; она не меняет статус merge/rollout других CPM PR. Production head 9effd97877bbaf5918f59083b17a8c5e7d11492c, review fix f08aefe1b8, база alex/cpm-messaging-readiness-telemetry / da8e714b33. Все шесть human review threads получили ответы и resolved.
- Удалены
CPMRecoveryInstrumentation,WebExtensionCPMRecoveryInstrumentationи manager wrapper. Recovery и тесты используют непосредственноWebExtensionPixelFiring/ реальныеWebExtensionPixelEvent. - iOS/macOS adapters снова вызывают статический
PixelKit.fire: shared instance определяется при отправке, не при создании struct. Имена, параметры и частоты CPM pixels сохранены. MacOSCPMDiagnosticsFeatureFlagsснова private в production; доступ и pixel injection нужны только отдельному review harness.- У
cpmLateBackgroundDocumentRecoveryсобственный doc comment; описание hang recovery возвращено кcpmMessagingHangRecovery. - По последнему явному указанию пользователя применён предложенный reviewer focus guard: допустим только отсутствующий focus или
document.body; страницы с любым iframe консервативно исключены из автоматического reload. Это предотвращает проверенные focused Shadow DOM / iframe случаи, но не является журналом всех document edits: синхронизация mutable defaults и ранее изменённый, затем потерявший focus shadow content полностью не закрыты. - Добавлены 5 обычных shared-package WKWebView регрессий, остающихся в production PR: edited input; edited iframe после ухода focus; focused edited Shadow DOM input; non-GET veto; pristine GET с реальным завершившимся reload. Загружается локальный URL через WKURLSchemeHandler, затем вызывается реальный production
registerDocument(in:webView:isGET:)и safety JavaScript. Отказ от reload не подавляет independent P4 detection.
Проверки: до обновления базы 260 selected PASS; после merge базы и окончательного выделения main-actor CPM setup — 267 PASS / 0 FAIL (CPM, lifecycle, manager), strict SwiftLint 8 files / 0 violations. В отдельной source copy с исходными guards оба новых iframe/Shadow DOM теста дают нежелательный реальный reload и 2 failed tests / 6 assertions; с исправлением проходят. Это red controls, не проигнорированные CI failures. Pixel validators и formatting обеих платформ PASS; wide-event consistency/schema checks PASS после merge commit.
CI: прежний failed workflow 39272483366 на 674bb809f2 падал crash процесса xctest в ConfigurationFetcherTests.testFetchAllWhenAssetsAreModifiedThenAllFetchedConfigurationsAreReturned. Извлечённый .ips: SIGBUS / EXC_BAD_ACCESS, triggered stack swift_retain → Foundation Locale.preferredLanguages → APIRequest.Headers.acceptLanguage → ConfigurationFetcher.fetch(all:); второй thread тоже читает Locale. Это установленное место падения, не установленная причина corruption/race и не доказательство infrastructure-only. Код Configuration/Networking не менялся. Attempt 2 SUCCESS без изменения binaries/source этого head. На новом 9effd97877 все checks завершены: 43 SUCCESS / 17 SKIPPED, 0 FAILURE, 0 pending; merge state CLEAN. Зелёные workflow: BSK, macOS PR checks, iOS PR checks, а также UI/PIR, pixel validation, SwiftLint и Danger. Итог повторно проверен по опубликованному head; 0 открытых review threads.
После f08aefe1b8 GitHub не запускал checks из-за DIRTY merge state с текущей базой. Merge da8e714b33 разрешил два конфликта (manager и pixel enum), сохранив обе стороны; CPM setup вынесен в @MainActor configureCPMMonitoring(), чтобы соблюсти strict complexity lint. Force push не использовался. Ранее существовавшие локальные Package.resolved изменения не включены в commits.
Harness: codex/cpm-fix-init-loss-integration-tests / 0b1d6771fe опубликован и содержит production 9effd97877 как ancestor; ровно 14 fixture-only paths, 43 новых production shared regressions унаследованы. Служебные visibility/pixel seams остаются только там; live PixelKit lookup в harness тоже выполняется при fire. Signed build-for-testing PASS, Apple Development / Team HKE973VLUW, strict harness lint 2 files / 0 violations; использован новый locked artifact cache. Новый полный app ON/OFF/ON пока не выполнялся: прежний macOS 27 результат 9 PASS → 6 expected FAIL → 8 PASS / 1 unexpected P2 FAIL остаётся историческим фактом; повторный настоящий WebKit error 6 на P2 retry этим review fix не исправляется.
webExtensions.cpmLateBackgroundDocumentRecovery по-прежнему default disabled на iOS и macOS; P4 detection работает независимо и не требует reload или safety-script evaluation. Этот проход не включает rollout flag и не доказывает готовность включать late reload для всех документов.
Все промежуточные результаты сохранены в /private/tmp/cpm-pr7379-comments-ci-20261020/: CI .xcresult/.ips и raw log, replies/resolution JSON, normal/red-control test logs, lint/validators, merge logs и harness build logs. При подготовке WK fixture loadHTMLString сначала дал 9 assertions из-за отсутствия history item; заменён реальной navigation. Промежуточный synthetic WKNavigation() в reload spy вызвал локальный SIGTRAP (CFRetain(NULL) в WKNavigation dealloc); fixture теперь вызывает super.reload() и ждёт реальный finish. Первая harness build попытка использовала stale absolute artifact paths; повторная подготовлена со свежим locked package cache.
#7379: independent P2 background-load recovery flag — 20 октября 2026
По запросу пользователя P2 same-context retry отделён от полной перезагрузки extension context. Production 55a42802a6bf1210a51642c3ae59504078ad82d3 опубликован в #7379. Review-only harness a6e6835a5f9eac783f6c64ec9467deb9302535b2 содержит этот head и по-прежнему не предназначен для main.
- Новый remote subfeature —
webExtensions.cpmBackgroundLoadRecovery,Config(defaultValue: .enabled)на обеих платформах, macOS category.webExtensions, local override разрешён стандартным default. ОбщийWebExtensionsSubfeatureи обе production factories подключены к отдельномуisCPMBackgroundLoadRecoveryEnabledclosure. - Новый flag проверяется при решении о retry и повторно перед deferred execution. Он управляет только retry фонового контента после error 6 в том же context; прежние rate limit, foreground checks и защита от конкурентного restart сохранены.
webExtensions.cpmMessagingHangRecoveryуправляет full-context restart после подтверждённого messaging hang. Retry работает при restart OFF; restart работает при retry OFF.stateRestorationGate, late GET recovery default OFF и независимый P4 detection не менялись. Этот проход не исправляет известный повторный error 6 на P2 retry в macOS 27 и не объявляет полный app ON/OFF/ON зелёным.- Remote override добавляется в
duckduckgo/privacy-configurationпод subfeaturecpmBackgroundLoadRecoveryродителяwebExtensions. Пока subfeature отсутствует в remote config, применяется новый enabled default; если remote config явно задаёт состояние, он имеет приоритет. Generated embedded config здесь не редактировался; remote rollout не выполнялся. По уточнению пользователя комментарии нового flag на обеих платформах и вWebExtensionsSubfeatureссылаются на отдельную feature-flag задачу; она заменяет первоначальную ссылку на задачу P1/P2 fix. Comment-only update опубликован: production8d8b5bee0f, harnessb1b9a047e3; изменены только три URL-комментария, strict SwiftLint 3 files / 0 violations. Результаты unit/build и CI выше относятся к указанному55a42802a6; для comment-only обновления повторный build/tests не запускался.
Регрессии: обновлён existing runtime kill-switch test; добавлены 4 метода: actual background retry при hang recovery OFF, confirmed-hang restart при retry OFF, veto queued retry после runtime disable, и error-6 notification при retry OFF / restart ON. 271 selected CPM/lifecycle/manager PASS / 0 FAIL; в production PR теперь 47 новых shared test methods относительно базы. Изолированный контроль с прежними isCPMMessagingHangRecoveryEnabled() в обоих retry guards дал 4 failed methods / 10 assertions; исходники production для контроля не менялись.
Платформы и CI: strict production SwiftLint 7 files / 0 violations, fixture lint 2 / 0; signed macOS app + integration test build PASS, Apple Development / Team HKE973VLUW. Полный local iOS app build на concrete iOS 27 simulator destination PASS (ios-build-verified.log / ios-build-verified.xcresult); app не запускался. Новый flag и production iOS factory также успешно компилировались для arm64 и x86_64 в первом generic build. Первый generic build остановился в существующем packaging script из-за отсутствия TARGET_DEVICE_OS_VERSION; concrete retry первоначально упёрся в заполненный диск. Удалены только собственные воспроизводимые compiler intermediates, app/test products и исходники сохранены; финальный concrete build успешно завершился. Промежуточный sandbox-запуск не получил доступ к Xcode simulator services; успешный финальный запуск использовал разрешённый доступ к build services. CI точного 55a42802a6 на 20 октября, 14:16 UTC: 39 SUCCESS / 13 SKIPPED / 4 IN_PROGRESS, 0 failures, merge state UNSTABLE из-за незавершённых проверок; это ещё не CI GREEN. Прежние 43 SUCCESS / 17 SKIPPED относятся к 9effd97877.
Harness: P2 метод переключает .cpmBackgroundLoadRecovery; .cpmMessagingHangRecovery остаётся false, так что P2 positive теперь отдельно проверяет retry без full restart. Negative scheme отключает новый P2 flag через прежний CPM_APP_INTEGRATION_DISABLE_P1_P2_FIXES; остальные controls и девять app methods сохранены. README (включая mapping P2 flag и 47 production regressions) обновлён; PR description дополнен одной строкой, существующие reviewer steps сохранены. Новый полный app ON/OFF/ON не запускался.
Артефакты: /private/tmp/cpm-pr7379-independent-retry-flag-20261020/ — normal/red-control test logs, lint, signed harness build/signature, iOS build и CI watch, PR description before/after. Существовавшие до работы локальные Package.resolved изменения не включены в commits; production dependency diff SHA256 по-прежнему 66123735bfe51a00cb1775b5526cf6b9c0310aee79d23fc0e4e47b1b62cbdcdb.