Files
vtube/docs/MIGRATION_PLAYBOOK.md

12 KiB
Raw Blame History

tubev — Site Migration Playbook

Призначення: 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.


Modes

  • 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.

Phase 1 — Discovery (ОБОВ'ЯЗКОВО спершу)

Перед будь-якими правками — 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
B4 Sidebar layout (vi-side-col? інший?) grep -n 'embed_sidebar|vi-side-col|sidebar_adspy' views/id_index.etlua
B5 Cols-change button (для bannersBlocks observer scope) grep -rn 'changeNumberCols|data-changecolumns' views/
B6 CSS layers — index.css + критичні views/css/css_<page>.etlua ls views/css/css_*.etlua
B7 Shared lib references — views/static/js/lib/ git submodule? cd views/static/js/lib && git remote -v
B8 Cooldown regex у layout.etlua (popunder spot ID hardcoded) grep 'asgsl|_popRr' layout.etlua
B9 c counter call безпеки (if(typeof c==="function")) grep tb.start_events_v2 у id_index.etlua
B10 lazyLoadFunc defined inline grep -c 'function lazyLoadFunc' layout.etlua views/static/js/main.min.js

Output Discovery phase: короткий summary — "Site X: player Y, banners Z (mapping pending), sidebar W, cols-button A/N, etc." → юзер confirms scope перед Phase 2.


Phase 2 — Conditional Task Branches

Кожна гілка SKIP-нулагається якщо Discovery показав feature відсутньою. Кожна = окремий atomic commit.

Branch P (Player migration) — якщо A1=yes

→ повний 30+ checkpoint список у ADS.md § Migration checklist.

  • Inline lazysizes wrapper якщо main.min.js його не містить (R3).
  • if(typeof c==="function") guard (counter race fix).
  • foreign 8148 classes (vdo-blk-*) → site-native (vi-limiter etc.) (R5).
  • Playwright probe → pageerrors empty.

Branch S (Spot ID rotation) — якщо A2=yes

  • Replace IDs у views/static/js/ad-config.js (popunder.spot, vast.spot).
  • Replace (?:^|\|)<id>= regex у layout.etlua (must sync з ad-config!).
  • Replace IDs у views/modules/banners/*adspy*.etlua (footer/header/middle/sidebar/native).
  • 1thumb_a..f spots якщо site має those modules.
  • Per-site inventory таблицю — додати у ADS.md § Per-site spot inventory.
  • Build bundle → cache-bust md5.
  • 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.
  6. Wrapper height = scaled height (250 * scale) для row consistency.
  7. CSS hide empty sidebar: @media(max-width:<activateWidth>px){.<sidebar-class>{display:none}} у обидва index.css + critical css_.etlua.
  8. MutationObserver:
    • mainBlock — childList + class/style attrs (lazy-load thumbs)
    • 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).
  • Console clean: no ASGB loader errors.

Ad pipeline

  • ad-test "<test_url>" → 6/6 PASS.
  • VAST endpoint returns <Ad> (curl) — не empty <VAST/> no-fill.
  • Popunder endpoint returns renderSpot JSON.
  • CDN serves fresh bundle (curl t1/static/js/ad-bundle.min.js | grep spot: reflects новий config).
  • adspyglass dashboard show графік не падає (ручний check 1-2h після deploy).

Responsive (якщо Branch R7 активний)

  • Width > activateWidth: banners у sidebar slots, sidebar visible.
  • Width <= activateWidth: banners у grid (зайве sidebar hidden), no overlap з thumbs, row heights consistent.
  • Resize transition (drag window narrow → wide → narrow): banners move properly, no duplicate, no orphan.
  • Cols-change button click: banners recalc + reposition.
  • Hard-refresh test (browser cache max-age=3600): нові правила apply.

Player (якщо Branch P активний)

  • Click play → VAST pre-roll shows (after popunder cooldown set on first visit).
  • After VAST done → main video starts via _revealPjs() callback.
  • Tooltip likes/dislikes над player chrome (z-index battle won).
  • Timeline thumb hover → play icon shown.
  • iOS Safari: .no-js/.js swap robitь native controls hidden.

Diff / commit hygiene

  • Atomic commits — 1 commit per logical change (Branch P / S / R7 / C / X = окремий commit).
  • Commit messages пояснюють "WHY", не лише "WHAT".
  • ~/git-save-all.sh (якщо є backup_) виконано.
  • Push до origin (якщо SSH key authorized).

Phase 4 — Production verification

  • Юзер натискає button у https://rss.g--o.info/admin/site/control для prod deploy.
  • 5-10 хв — adspyglass dashboard продовжує показувати impressions.
  • Кейс real-юзер test (попросити reload + перевірити likes/timeline/banners visually).

Anti-patterns (не повторювати помилок 8161 migration)

  1. Скопіювати foreign classes (vdo-blk-lmtr) тільки тому що 8148 так робить. Site має native equivalent — використовуй його. R5 урок.
  2. Append external JS file (tb.append_script) коли можна inline. Зайвий HTTP request. R3 урок.
  3. Edit тільки index.css без критичного css_<page>.etlua. FOUC на first paint. Юзер скаржиться. Урок 2026-05-04.
  4. Compound bash chains з fail-prone steps (cd && head wrong-file). Червоні errors дратують юзера. Урок 2026-05-04.
  5. Single-line snippet output форсити. Терминал word-wrap ламає copy → file workflow краще. Урок 2026-05-04.
  6. Early-return у calc() тільки на widthLine — не зловити thumb width зміну (button cols toggle). Має track обидва. R7 урок.
  7. observe лише mainBlock без body — cols-button updates body class, banners stale. R7 урок.
  8. Брати template з memory без verify — пишемо у memory одразу коли юзер каже "це знадобиться", не після compaction. Урок 2026-05-04.

Cheat-sheet команд

# Discovery
grep -rn 'PATTERN' /home/nosfortube/frontend_<port>/views/

# Test ad pipeline
ad-test "https://t1.<domain>/v/<id>/<slug>"

# Empirical browser probe (write to /tmp first, не до /home/w4/playwright-tests/)
node /tmp/probe-X.mjs

# Build ad bundle (8148-only — інші sites копіювати pipeline)
bash views/static/js/build-ad-bundle.sh

# Snippet delivery до VSCode
cat > /home/nosfortube/orest/snippet.txt << EOF
...
EOF

Memory rules що activate-яться під час migration

  • feedback_critical_inline_css_sync — CSS dual-layer
  • feedback_clean_bash_no_red — Read tool > head/cat
  • feedback_use_bots_proactively — gocc1/gocc2 для audit/research parallel
  • feedback_pre_push_subagent — code-reviewer на diff перед push
  • feedback_clipboard_snippet_workflow — snippet → file
  • reference_native_banner_templates — ASG ExoClick / TrafficStars wrappers
  • reference_ad_regression_tool — ad-test usage
  • project_player_roadmap — generation map sites