**Призначення:** robust план для міграції / refactor наступного site (player, banners, sidebar responsive, etc.). Не лінійний — **discovery-driven** з conditional branches. Кожен пункт може бути SKIP якщо feature відсутня.
> Спирається на досвід з 8148 (PlayerJS pilot), 8161 (full migration + R7 responsive sidebar), 8081 (videojs10 + bannersBlocks reference). Доповнення — у [ADS.md § Migration checklist](ADS.md#migration-checklist--legacy-videojs4--modern-playerjs--ad-bundle).
- **Interactive** (default) — Claude робить Discovery, потім confirm scope з юзером перед Phase 2.
- **Autonomous batch** — юзер дає пакет sites + global instructions; Claude робить sam без апрувів, per-site report + master `~/comms/batch-summary.md`. Halt тільки на critical ambiguity. Memory: `feedback_autonomous_batch_migration`.
Перед будь-якими правками — Claude **запитує юзера** + інспектує наявний site, фіксує scope. Без цього — risk hidden assumptions.
### A) Scope decisions (запитати юзера)
| # | Питання | Why it matters |
|---|---------|---------------|
| A1 | **Чи мігруємо player?** (videojs4 → PlayerJS / videojs10 / залишаємо?) | Якщо ні — skip Phase 3 player. |
| A2 | **Чи замінюємо ad spot IDs?** (rotation / new account) — якщо так, дай mapping таблицю. | Якщо ні — skip banner spot rewrite. |
| A3 | **Чи треба responsive sidebar→grid swap (R7-style)?** | Tільки якщо site має sidebar з ads AND junior-grid (related/recommended thumbs). |
| A4 | **Чи зробити уніфікацію критичних inline + index.css?** | За замовчуванням так — запобігає FOUC. |
| A5 | **Spec for site** — щось унікальне (custom player, ASG-bypass, GDPR overlay, etc.)? | Невідомі вимоги дізнаємось ранo, не пізно. |
### B) Site inspection (Claude робить empirically)
| # | Check | Tool / Command |
|---|-------|----------------|
| B1 | URL pattern для video page | grep `id_index` references у layout, парсинг `mysettings.location_css == "id"` block |
| B2 | Player generation у DOM | `curl t1.<domain>/<video-url>` → grep `videojs\|PlayerJS\|pjs_container` |
| B3 | Native banners серед thumbs (1thumb_a-f чи інший module чи нема) | `grep -rn 'native_allpg_1thumb\|render.*banners' views/modules/related_video.etlua views/modules/recommended_video.etlua` |
- ad-test → 6/6 PASS на новий popunder/vast spots.
### Branch R7 (Responsive sidebar→grid) — якщо A3=yes
Передумови: site має sidebar (`.vi-side-col` чи аналог) з ad slots AND grid (`#related_video_block` чи similar) з thumbs.
1.**Уніфікувати DOM IDs** sidebar slots (mobile + desktop templates обидва використовують ті самі ID — `sdd_a`/`sdd_b`).
2.**Move banner-load logic** з template `<script>`у`id_index.etlua` inline → один місто, server-side `isMobile()` choose spot pair, callback `() => bannersBlocks.calc()`.
3.**Inject `bannersBlocks` JS** з activateWidth (1024 default), main block selector (`#related_video_block`), thumb selector (`.video-item:not(.js-iframe_banner)`).
4.**Banner wrapper class** — match site thumb structure (8161 = `<div class="video-item">`, 8081 = `<figure>`). Inspect 1 existing thumb DOM.
5.**iframe transform scale** = thumbW / banner_natural_width (default 300) для banner content fit.
-`document.body` — attributes ['class'] (cols-change button updates body class — без observer banners stale)
9.**Defer init** до DOMContentLoaded якщо script render-иться перед grid block.
10.**Debounce calc** ~150ms на frequent events.
### Branch C (Critical CSS sync) — якщо A4=yes (default)
Кожна правка `views/static/css/index.css` → перевірити чи selector існує у відповідному `views/css/css_<page>.etlua` → дзеркалити rule. Без цього FOUC. Memory: `feedback_critical_inline_css_sync`.
### Branch X (Site-specific) — якщо A5 показав щось
Custom план залежно від спецификy. Документуй у session notes.
---
## Phase 3 — Universal post-checks (ALWAYS, незалежно від Branch)
Усі ці checkpoints — обов'язкові, незалежно від scope. Вони catch генеричні regressions.
### CSS / DOM
- [ ]`index.css` + critical inline `css_<page>.etlua` мають однакові rules для торкнутих selectors.
- [ ] No duplicate DOM IDs на сторінці (Playwright probe `Array.from(document.querySelectorAll('[id]')).map(e=>e.id)` + count duplicates).
- [ ] No foreign-site classes (8148 `vdo-blk-*` etc.) у site-specific files.
### JS / errors
- [ ] Playwright headless `pageerror` array empty при load video page (timeout 8s).
- [ ] No `c is not defined`у inline `start_events_v2` (counter race).
- [ ] No `lazyLoadFunc is not defined`у timeline-pjs (якщо використовується).
- [ ] No `c2.min.js: Unexpected identifier` (re-minify якщо source edit).