From e9c66c60c59459897f4967ac22e1274544a5ad84 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Tue, 22 Sep 2026 13:40:05 +0600 Subject: [PATCH] [2026-09-22] eagle: work/projects/cpm-web-extension-breakage-validation.md --- .../cpm-web-extension-breakage-validation.md | 105 ++++++++++-------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/work/projects/cpm-web-extension-breakage-validation.md b/work/projects/cpm-web-extension-breakage-validation.md index 89812045..5e3c7748 100644 --- a/work/projects/cpm-web-extension-breakage-validation.md +++ b/work/projects/cpm-web-extension-breakage-validation.md @@ -1,58 +1,52 @@ # CPM Web Extension Breakage — статус и PR-план -> Актуально на 2026-09-21. Полная трассировка: [[cpm-web-extension-breakage-findings]]. +> Актуально на 2026-09-22. Полная трассировка: [[cpm-web-extension-breakage-findings]]. +> Experiment Definition: [[cpm-background-graveyard-experiment-definition]]. -## Итог +## Решение Есть два независимых класса проблем: -- **All-tabs:** после смерти background WebContent process CPM может перестать работать во всех вкладках. Нужна последовательность telemetry → production validation gate → bounded graveyard → A/B. -- **Per-tab:** ошибки lifecycle заставляют WebKit забыть ещё живую вкладку. Исправляются независимо от graveyard. +- **Per-tab lifecycle** исправлен в M1 и уже merged. +- **All-tabs CPM outage** идёт stack: M3 telemetry → M5 bounded graveyard → M6 A/B. -## Статус гипотез +M4 как отдельной ветки/PR нет: production validation — rollout gate после M3. Старый M4 worktree удалён и из topology исключён. -| Проблема | Статус | Результат | -| ------------------------------------------------------------------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| Forced death background process → persistent all-tabs CPM outage | **Подтверждено воспроизведением** | Kill background PID ломает CPM; reload и новая вкладка не помогают, Fire восстанавливает. | -| Natural crash / memory / CPU termination в production | **Не проверено** | Механизм доказан, реальная частота и связь с outage требуют M3 и последующей production validation. | -| Close Other Tabs forget-ит retained tab | **Подтверждено и исправлено в M1** | Сохраняется тот же экземпляр Tab без ложного didCloseTab. | -| Duplicate Tab из popup переносит original | **Подтверждено и исправлено в M1** | В main window уходит копия, source остаётся отдельным объектом. | -| Pin/unpin/reorder/move/merge теряют lifecycle tab | **Подтверждено и исправлено в M1** | Tab сохраняет identity; WebKit получает `didMoveTab` с old index/window вместо ложных close/open. Pinned tabs имеют корректный flattened index. | -| Закрытие окна forget-ит shared pinned/other-owned tabs | **Подтверждено и исправлено в M1** | `didCloseTab` получают только реально закрытые tabs; shared pinned сохраняются и при закрытии последнего окна. | -| Нестабильный WebExtension base URL | **Исправлено и локально проверено в M2** | URL детерминирован по identifier и сохраняется между load/reload; production migration/storage validation остаётся rollout-задачей. | -| Startup/session-restore content-script miss | **Подтверждено как transient** | Не объясняет устойчивый all-tabs outage. | -| Failed background load навсегда застревает в m_backgroundWebView | **Исходный вывод отвергнут** | WebKit способен повторно загрузить background; устойчивый outage требует повторяемой причины/traffic. | -| Event starvation / JSC hang / native messaging stall | **Не проверено** | Оставлено в diagnostic buckets M3. | -| Critical memory pressure как конкретный trigger | **Не воспроизведено** | Не считать root cause без production correlation. | -| Network process restart как trigger | **Отвергнуто** | На проверенной сборке CPM восстановился. | -| removeAllUserScripts удаляет WebExtension scripts | **Отвергнуто** | WebKit использует RemoveWebExtensions::No. | -| Feature-flag transition / scriptlet reload / обычный page JS как all-tabs cause | **Отвергнуто** | Это другой context или tab-local execution. | +## Статус проблем -## Worktrees +| Проблема | Статус | Что делаем | +|---|---|---| +| Forced background-process death → persistent all-tabs CPM outage | **Подтверждено** | M3 измеряет production incidence; M5 лечит; M6 сравнивает control/treatment. | +| Natural crash/memory/CPU termination → production outage | **Не проверено в production** | Один production release M3 до включения M5/M6. | +| Close Other Tabs, popup duplicate, pin/unpin/reorder/move/merge/window close | **Исправлено, merged** | M1 PR #6827. | +| Нестабильный Web Extension base URL | **Исправлено локально** | M2 задаёт deterministic base URL до load/reload и отвергает malformed identifier без crash. | +| Startup/session-restore content-script miss | **Подтверждено как transient** | Не объясняет persistent all-tabs outage. | +| Failed background load навсегда блокирует WebKit | **Отвергнуто** | WebKit повторяет load; нужна повторяемая причина/traffic. | +| Critical memory pressure как root cause | **Не воспроизведено** | Остаётся diagnostic correlation M3. | +| Event starvation / JSC hang / native messaging stall | **Не проверено** | Остаётся в M3 diagnostic buckets. | +| Network process restart | **Отвергнуто** | На проверенной сборке CPM восстановился. | +| removeAllUserScripts / flag transition / scriptlet reload / page JS как all-tabs cause | **Отвергнуто** | Это не persistent global failure mode. | -Все созданы от локального `origin/main` `570c2f8ef4`, актуального на момент создания. M1 перенесён на текущий `origin/main` `2ac23b22af`, закоммичен как `3df78ebfb2` и опубликован в PR #6827. Остальные milestones не закоммичены. Исходный `cpm-breakage-pixel` не изменялся этим PR. +## Milestones -| Milestone | Branch / worktree | Статус | -| ------------------------ | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Milestone | Branch / worktree | Готовность | +|---|---|---| +| M1 tab lifecycle | `codex/cpm-tab-lifecycle-fixes` | **Merged:** [PR #6827](https://github.com/duckduckgo/apple-browsers/pull/6827), CI green. | +| M2 stable base URL | `alex/cpm-stable-base-url` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-stable-base-url` | **Production-ready:** 2 files; 11/11 targeted tests. Добавлена safe URL construction и negative validation вместо force-unwrap. | +| M3 attribution telemetry | `alex/cpm-failure-telemetry` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-failure-telemetry` | **Production-ready code:** passive PII-free buckets, app-update/launch attribution, stable-base-URL transition fact, iOS/macOS schema validation green. Privacy triage: [[cpm-m3-telemetry-privacy-triage]]. | +| M5 graveyard mitigation | `alex/cpm-graveyard-mitigation` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-graveyard-mitigation` | **Production-ready, default off:** one retained view maximum, 3-second bounded hold, stale release cancellation, unload/reload/runtime kill-switch cleanup. 24/24 targeted tests; earlier real forced-kill iOS E2E recovered with release at ≈3.15 s. | +| M6 graveyard A/B | `alex/cpm-graveyard-ab` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-graveyard-ab` | **Production-ready code:** lazy exposure enrollment, stable cohorts, control/treatment isolation, bounded pending queue, operational pixels and standard `experiment_metrics` outcome. 421/421 WebExtensions tests; iOS/macOS app builds; iOS/macOS pixel validators. | +All milestone diffs pass `git diff --check`; changed Swift lines are within 150 columns. -| M1 tab lifecycle | `codex/cpm-tab-lifecycle-fixes` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-tab-lifecycle-fixes` | **PR открыт:** [#6827](https://github.com/duckduckgo/apple-browsers/pull/6827), reviewer `@dus7`, commit `3df78ebfb2`. Close Other Tabs, popup duplicate, pin/unpin, reorder, cross-window/new-window move, merge, pinned indices и shared pinned при закрытии окна. Проверки: 229/229 targeted unit, 10/10 pinned-tabs integration, SwiftLint 0 violations. Широкий macOS unit run: 6708/6710 pass, 30 skipped; 2 failures воспроизводятся изолированно в неизменённых тестах из-за их собственного teardown (`AIChatSelectionActionPageContextTests`, `AutoClearHandlerTests`). Старый `CBRCompileTimeReporterTests` отдельно не запускается в широком gate: его dry-run pixel expectations системно timeout-ятся и не относятся к M1. | -| M2 stable base URL | `codex/cpm-stable-base-url` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-stable-base-url` | **Готово:** 11/11 tests; iOS/macOS builds проходят. | -| M3 attribution telemetry | `alex/cpm-failure-telemetry` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-failure-telemetry` | **Готово к PR:** passive PII-free facts, forwarding proxy, runtime kill switch; 23/23 tests; schemas iOS/macOS валидны. | -| M5 graveyard mitigation | `alex/cpm-graveyard-mitigation` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-graveyard-mitigation` | **Готово к PR, выключено по умолчанию:** только M5 поверх M3; максимум один WKWebView, bounded 3 s hold без скрытого retention из scheduled release, synchronous unload/reload/kill-switch cleanup. Реальный iOS Simulator kill-E2E: CPM background WebContent получил SIGKILL, delegate зафиксировал crash, старый view освободился через ≈3.15 s, новый process обработал следующие страницы; 113/113 targeted tests, полные iOS/macOS app builds, pixel schema и formatting validators проходят. | -| M6 graveyard A/B | `alex/cpm-graveyard-ab` — `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-graveyard-ab` | **Готово к PR:** control/treatment, stable cohort, denominator/outcome pixels, bounded pending queue; 30/30 tests; iOS/macOS builds и pixel validators проходят. | +## Stack and Git state -Во всех шести worktree проходит `git diff --check`; случайный `Package.resolved` churn удалён. +Independent PRs: -## PR topology +1. M2 — stable base URL. +2. M3 — production telemetry. -Независимые PR от `origin/main`: - -1. M1 — tab lifecycle fixes. -2. M2 — stable base URL. -3. M3 — production telemetry. - -Последовательный feature stack: +Stacked PRs: ``` M3 telemetry @@ -60,11 +54,32 @@ M3 telemetry └── M6 A/B experiment ``` -Поскольку коммитов пока нет, refs зависимых branches указывают на тот же base SHA, а зависимость представлена накопленным dirty diff. При оформлении: commit M3 → перенести/перебазировать M5 на M3 → M6 на M5. Production validation — rollout gate после M3, а не отдельная ветка или PR. +Current refs intentionally preserve the earlier no-commit workflow: -## Rollout gates +- M3 and M5 branch refs: `814bce5d11`; their milestone changes are unstaged. +- M5 ghost commit used only as M6 base: `cefad244c5`. +- M6 branch ref: `cefad244c5`; M6 changes are unstaged above it. +- M5’s final combined pixel description has been pulled into M6. +- Local `origin/main` advanced from the M3 merge base by one unrelated pixel-definition commit (`67a2f2b7b2`); it does not overlap CPM files. -- M3: privacy review, schema review, затем один production release. -- После production rollout M3 отдельно по iOS/macOS проверить termination reason → initialization failure/stuck и долю per-tab failures. -- M5: включать только если production validation после M3 подтверждает process-death hypothesis; kill switch и graveyard по умолчанию выключены. -- M6: internal/review → небольшой 50/50 sample → проверить cohort balance, denominator/outcome coverage и crash/retention guardrails. +Before opening PRs: commit M2 and M3, update them onto current `origin/main`, recreate/rebase the M5 commit onto M3, then rebase M6 onto that M5 commit. Do not include generated `Package.resolved` churn; Cursor currently regenerates it in the open M3 workspace. + +## Experiment contract + +- Experiment: `cpmBackgroundGraveyardExperiment`. +- Cohorts: `control`, `treatment`. +- Enrollment: only on an actual eligible CPM background-process termination. +- Primary framework metric: `cpmBackgroundGraveyardOutcome`, window `0...1`, values `healthy`, `initialization_failed`, `not_measured`. +- Rollback: disable both `cpmBackgroundGraveyardExperiment` and `cpmBackgroundGraveyard`. + +Full definition and config draft: [[cpm-background-graveyard-experiment-definition]]. + +## Remaining gates + +1. Open M2/M3/M5/M6 PRs in the topology above. +2. Create separate iOS and macOS experiment-log tasks. +3. Create Privacy Configuration + privacy/schema review tasks. +4. Create Data Science sample-size/design and Native Apps Experiment Analysis task. +5. Ship M3 first; enable M5/M6 only if production telemetry confirms the process-death hypothesis. + +No Asana tasks, comments, or configuration were created during this review.