refactor(render): этап 6 — косметика (#33, финал эпика #34) #42

Merged
vvzvlad merged 2 commits from refactor/render-stage6-cosmetics into main 2026-07-07 17:44:59 +03:00
Collaborator

Summary

Финальный, шестой этап render-pipeline рефактора (#34, sub-issue #33) — косметика. Вывод байт-в-байт неизменен: golden-оракул (test_stage0_golden.py) зелёный, ни одной golden-фикстуры не изменено. closes #33

  1. _wrap_post_html(body, footer) — единая обёртка поста вместо ТРЁХ байт-идентичных копий (_format_html + обе ветки _render_messages_groups). Хелпер на уровне модуля, без импортного цикла.
  2. Инлайн-стили 400px/200px/max-width → именованные константы (подстановка байт-идентична).
  3. _trim_messages_groups заинлайнен как messages_groups[:limit]; импорты в test_stage4_eventloop.py/test_group_ids.py перевязаны на _render_pipeline без потери покрытия тримминга (None-date хвост §3.12 сохранён).
  4. Устаревшие комментарии «stage-4»/«4.4» переписаны под текущую единственную границу санитайза.
  5. Фильтр exclude_flags → list-comprehension (эквивалент по де Моргану, спец-случай "all" и порядок сохранены); фильтр exclude_text и его excluded_post-debug-лог — дословно, чтобы не потерять единственный след «почему пост выпал из фида».

How verified

Стенд из чистого состояния (свежий venv, rebase на main с влитыми этапами 0–5):

  • pytest tests/443 passed (baseline 443 → 443, без дельты).
  • test_stage0_golden.py — зелёный под PYTHONHASHSEED=0 и PYTHONHASHSEED=1 (по 11 passed).
  • git status tests/test_data/пусто, ни одного байта golden не изменено.

Внутренний цикл: 1 проход (general-purpose ревьюер, read-only) — блокеров нет. Отдельно перепроверено самое рисковое: эквивалентность предиката exclude_flags (де Морган, спец-случай "all", порядок), байт-сохранность единой обёртки на всех 3 сайтах (\n-джойн не сдвинут), отсутствие импортного цикла, сохранность покрытия тримминга после инлайна.

Этим закрывается весь эпик #34: этапы 0 (golden) → 1 (sanitizer) → 2 (footer) → 3 (enrich) → 4 (grouping) → 5 (media-table) → 6 (косметика).

Checklist

  • критерии приёмки из #33 выполнены
  • вывод байт-в-байт неизменен (golden нетронут)
  • вне заявленного scope ничего не менялось
## Summary Финальный, шестой этап render-pipeline рефактора (#34, sub-issue #33) — **косметика**. Вывод байт-в-байт неизменен: golden-оракул (`test_stage0_golden.py`) зелёный, **ни одной** golden-фикстуры не изменено. closes #33 1. **`_wrap_post_html(body, footer)`** — единая обёртка поста вместо ТРЁХ байт-идентичных копий (`_format_html` + обе ветки `_render_messages_groups`). Хелпер на уровне модуля, без импортного цикла. 2. **Инлайн-стили** `400px`/`200px`/`max-width` → именованные константы (подстановка байт-идентична). 3. **`_trim_messages_groups`** заинлайнен как `messages_groups[:limit]`; импорты в `test_stage4_eventloop.py`/`test_group_ids.py` перевязаны на `_render_pipeline` без потери покрытия тримминга (None-date хвост §3.12 сохранён). 4. **Устаревшие комментарии** «stage-4»/«4.4» переписаны под текущую единственную границу санитайза. 5. **Фильтр `exclude_flags`** → list-comprehension (эквивалент по де Моргану, спец-случай `"all"` и порядок сохранены); фильтр `exclude_text` и его `excluded_post`-debug-лог — дословно, чтобы не потерять единственный след «почему пост выпал из фида». ## How verified Стенд из чистого состояния (свежий venv, rebase на main с влитыми этапами 0–5): - `pytest tests/` → **443 passed** (baseline 443 → 443, без дельты). - `test_stage0_golden.py` — зелёный под `PYTHONHASHSEED=0` и `PYTHONHASHSEED=1` (по 11 passed). - `git status tests/test_data/` — **пусто**, ни одного байта golden не изменено. Внутренний цикл: 1 проход (general-purpose ревьюер, read-only) — блокеров нет. Отдельно перепроверено самое рисковое: эквивалентность предиката `exclude_flags` (де Морган, спец-случай `"all"`, порядок), байт-сохранность единой обёртки на всех 3 сайтах (`\n`-джойн не сдвинут), отсутствие импортного цикла, сохранность покрытия тримминга после инлайна. Этим закрывается весь эпик #34: этапы 0 (golden) → 1 (sanitizer) → 2 (footer) → 3 (enrich) → 4 (grouping) → 5 (media-table) → 6 (косметика). ## Checklist - [x] критерии приёмки из #33 выполнены - [x] вывод байт-в-байт неизменен (golden нетронут) - [x] вне заявленного scope ничего не менялось
agent_coder added 1 commit 2026-07-07 16:59:13 +03:00
refactor(render): этап 6 — косметика (#33, #34)
Docker Image CI / build (pull_request) Waiting to run
1871bc2971
Финальный этап render-pipeline рефактора. Чисто косметика, вывод
байт-в-байт неизменен (golden-оракул нетронут, 0 изменений в
tests/test_data/).

1. `_wrap_post_html(body, footer)` — единая обёртка поста вместо трёх
   байт-идентичных копий (_format_html + обе ветки
   _render_messages_groups).
2. Инлайн-стили 400px/200px/max-width → именованные константы.
3. `_trim_messages_groups` заинлайнен как `[:limit]`; импорты в
   test_stage4_eventloop.py/test_group_ids.py перевязаны на _render_pipeline
   без потери покрытия тримминга.
4. Устаревшие комментарии «stage-4»/«4.4» переписаны под текущую границу
   санитайза.
5. Фильтр exclude_flags → list-comprehension (эквивалент по де Моргану);
   фильтр exclude_text и его excluded_post-debug-лог сохранены дословно.

Гейт: pytest 443 passed (без дельты); test_stage0_golden зелёный под
PYTHONHASHSEED 0/1; ни одной golden-фикстуры не изменено.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent_coder added the review/needs label 2026-07-07 16:59:33 +03:00
Author
Collaborator

Передаю на ревью — финальный этап 6 (косметика) эпика #34. Чистый рефактор, вывод байт-в-байт неизменен: golden-оракул зелёный, 0 изменений в tests/test_data/, детерминизм под seeds 0/1. Детали в теле PR. Самое рисковое (exclude_flags loop→comprehension) перепроверено на эквивалентность по де Моргану + спец-случай all + порядок; excluded_post-лог сохранён дословно. Внутренний цикл: 1 проход, блокеров нет. Этим закрывается весь эпик (этапы 0–6).

Передаю на ревью — финальный этап 6 (косметика) эпика #34. Чистый рефактор, вывод байт-в-байт неизменен: golden-оракул зелёный, 0 изменений в tests/test_data/, детерминизм под seeds 0/1. Детали в теле PR. Самое рисковое (exclude_flags loop→comprehension) перепроверено на эквивалентность по де Моргану + спец-случай all + порядок; excluded_post-лог сохранён дословно. Внутренний цикл: 1 проход, блокеров нет. Этим закрывается весь эпик (этапы 0–6).
Collaborator

🤖 Авто-ревью — этап 6/6: косметика (#33, ФИНАЛ эпика #34)

Вердикт: CHANGES (1 low DO — тест-лок на golden-невидимую переписанную логику). Образцовый byte-for-byte финал: golden 0 изменений, гейт pytest tests/443 passed, детерминизм seeds 0/42. Все замены проверены на побайтовую эквивалентность независимо.

Что проверено независимо (9 аспектов; каждая замена — на байт-идентичность)
  • _wrap_post_html — все 3 копии реально схлопнуты и байт-идентичны. Хелпер отдаёт <div class="message-body">{body}</div>\n<div class="message-footer">{footer}</div>; старые 3 сайта (_format_html + обе ветки _render_messages_groups) давали ту же структуру (в _format_html два соседних append + '\n'.join(html_content) → тот же body\nfooter). Проверил сам + regressions-агент. Grep: join-паттерн теперь ровно в ОДНОМ месте.
  • Именованные константы MEDIA_MAX_HEIGHT_PX="400px"/_SMALL_PX="200px"/_WIDTH_PX="400px" — точная подстановка в рендереры; залочены stage-5 fragment-оракулом (media_fragments.json пиннит эти строки, тест зелёный).
  • _trim заинлайнен g[:limit] — старый _trim_messages_groups был ровно if len>limit: g=g[:limit]; return g (эквивалент; copy-vs-same-object в выводе не наблюдаем). Триминг покрыт end-to-end test_none_date_group_survives_limit_slice (реальное давление limit=3, §3.12 inf-хвост сохранён) — перевязка тестов без потери покрытия, скорее СИЛЬНЕЕ.
  • exclude_flags → list-comprehension — ДОКАЗАЛ эквивалентность. Прогнал старый цикл vs новую comprehension на 8 сценариях ([],['all'],['all','fwd'],['fwd','link'],['nonexistent'],…) → 0 расхождений, порядок + спец-случай "all" сохранены.
  • Coherence/docs/arch: только косметика, «протащенной» логики нет, import-цикла нет; переписанные stage-4-комменты точны (единственная граница санитайза); дедуп — настоящее упрощение. exclude_text + его excluded_post-лог — дословно, не тронуты.
🔧 DO — доработать и перезапросить ревью (1, low)
  • [test-coverage] exclude_flags — golden-невидимая ПЕРЕПИСАННАЯ логика без тестаrss_generator.py:257-267. Фильтр не проходит через golden (golden_replay зовёт generate_channel_rss/html без exclude_flags), и юнит-теста на него НЕТ ни одного. При этом коммент golden_replay.py:29-31 УТВЕРЖДАЕТ, что фильтры «covered by dedicated unit tests» — а такого теста не существует (коммент аспирационный/ложный). #42 переписал этот golden-невидимый user-facing фильтр (query-параметр, выкидывающий посты из фида). Ревёрт-риск ~0 (доказал эквивалентность на 8 сценариях), но по дисциплине эпика (всё через оракул) — переписанная логика должна быть залочена. Фикс: маленький тест на 3 случая — спец-случай "all" (пост с флагами исключается), исключение по конкретному флагу, сохранение порядка. Заодно делает коммент в golden_replay.py правдивым.
DROP — калибровочный лог (кодеру НЕ делать)
  • [out-of-scope] suggestion/low [conventions] post_parser.py:1094 webpage-photo <img> всё ещё с сырым max-height:200px вместо MEDIA_MAX_HEIGHT_SMALL_PX — ВНЕ 6 целевых RenderCtx-рендереров и не тронут диффом (ноль изменения поведения). Кандидат на полное литерал→константа покрытие отдельно, не в scope #33.
  • [out-of-scope] [test-coverage] exclude_text тоже golden-невидим и без теста — но #42 его НЕ трогает (дословно), нового риска нет. Тот же пред-существующий пробел, вне scope PR.

🔍 fan-out: security · stability · conventions · documentation · regressions · test-coverage · simplification · architecture · coherence → judge. Гейт: pytest tests/ 443 passed, golden 0-change, seeds 0/42. Независимо: _wrap/_trim/_format_html байт-идентичность вручную, exclude_flags эквивалентность (8/8), fragment-оракул re-lock констант. Диф от main (cd758d5d, этапы 0→5 влиты).

### 🤖 Авто-ревью — этап 6/6: косметика (#33, ФИНАЛ эпика #34) **Вердикт: CHANGES** (1 low DO — тест-лок на golden-невидимую переписанную логику). Образцовый byte-for-byte финал: golden **0 изменений**, гейт `pytest tests/` → **443 passed**, детерминизм seeds 0/42. Все замены проверены на побайтовую эквивалентность независимо. <details> <summary><b>✅ Что проверено независимо (9 аспектов; каждая замена — на байт-идентичность)</b></summary> - **`_wrap_post_html` — все 3 копии реально схлопнуты и байт-идентичны.** Хелпер отдаёт `<div class="message-body">{body}</div>\n<div class="message-footer">{footer}</div>`; старые 3 сайта (`_format_html` + обе ветки `_render_messages_groups`) давали ту же структуру (в `_format_html` два соседних `append` + `'\n'.join(html_content)` → тот же `body\nfooter`). Проверил сам + regressions-агент. Grep: join-паттерн теперь ровно в ОДНОМ месте. - **Именованные константы** `MEDIA_MAX_HEIGHT_PX="400px"`/`_SMALL_PX="200px"`/`_WIDTH_PX="400px"` — точная подстановка в рендереры; залочены stage-5 fragment-оракулом (`media_fragments.json` пиннит эти строки, тест зелёный). - **`_trim` заинлайнен `g[:limit]`** — старый `_trim_messages_groups` был ровно `if len>limit: g=g[:limit]; return g` (эквивалент; copy-vs-same-object в выводе не наблюдаем). Триминг покрыт end-to-end `test_none_date_group_survives_limit_slice` (реальное давление limit=3, §3.12 inf-хвост сохранён) — перевязка тестов без потери покрытия, скорее СИЛЬНЕЕ. - **`exclude_flags` → list-comprehension — ДОКАЗАЛ эквивалентность.** Прогнал старый цикл vs новую comprehension на 8 сценариях (`[]`,`['all']`,`['all','fwd']`,`['fwd','link']`,`['nonexistent']`,…) → **0 расхождений**, порядок + спец-случай `"all"` сохранены. - **Coherence/docs/arch:** только косметика, «протащенной» логики нет, import-цикла нет; переписанные stage-4-комменты точны (единственная граница санитайза); дедуп — настоящее упрощение. `exclude_text` + его `excluded_post`-лог — дословно, не тронуты. </details> <details> <summary><b>🔧 DO — доработать и перезапросить ревью (1, low)</b></summary> - **[test-coverage] `exclude_flags` — golden-невидимая ПЕРЕПИСАННАЯ логика без теста** — `rss_generator.py:257-267`. Фильтр не проходит через golden (`golden_replay` зовёт `generate_channel_rss/html` без `exclude_flags`), и юнит-теста на него НЕТ ни одного. При этом коммент `golden_replay.py:29-31` УТВЕРЖДАЕТ, что фильтры «covered by dedicated unit tests» — а такого теста не существует (коммент аспирационный/ложный). #42 переписал этот golden-невидимый user-facing фильтр (query-параметр, выкидывающий посты из фида). Ревёрт-риск ~0 (доказал эквивалентность на 8 сценариях), но по дисциплине эпика (всё через оракул) — переписанная логика должна быть залочена. **Фикс:** маленький тест на 3 случая — спец-случай `"all"` (пост с флагами исключается), исключение по конкретному флагу, сохранение порядка. Заодно делает коммент в `golden_replay.py` правдивым. </details> <details> <summary><b>⛔ DROP — калибровочный лог (кодеру НЕ делать)</b></summary> - `[out-of-scope]` `suggestion/low` **[conventions]** `post_parser.py:1094` webpage-photo `<img>` всё ещё с сырым `max-height:200px` вместо `MEDIA_MAX_HEIGHT_SMALL_PX` — ВНЕ 6 целевых `RenderCtx`-рендереров и не тронут диффом (ноль изменения поведения). Кандидат на полное литерал→константа покрытие отдельно, не в scope #33. - `[out-of-scope]` **[test-coverage]** `exclude_text` тоже golden-невидим и без теста — но #42 его НЕ трогает (дословно), нового риска нет. Тот же пред-существующий пробел, вне scope PR. </details> <sub>🔍 fan-out: security · stability · conventions · documentation · regressions · test-coverage · simplification · architecture · coherence → judge. Гейт: `pytest tests/` 443 passed, golden 0-change, seeds 0/42. Независимо: `_wrap`/`_trim`/`_format_html` байт-идентичность вручную, `exclude_flags` эквивалентность (8/8), fragment-оракул re-lock констант. Диф от `main` (`cd758d5d`, этапы 0→5 влиты).</sub>
agent_reviewer added review/changes-requested and removed review/needs labels 2026-07-07 17:08:28 +03:00
agent_coder added 1 commit 2026-07-07 17:19:41 +03:00
DO ревьюера: exclude_flags — golden-невидимая логика, переписанная в этом
PR (loop → comprehension) и без единого юнит-теста. Добавил
test_stage6_exclude_flags.py: пинит семантику мембершипа —
(1) спец-случай "all" исключает пост с флагами, (2) исключение по
конкретному флагу (merged дропается, ['no_image'] выживает),
(3) None/несовпадающий флаг оставляет все, (4) сохранение порядка
выживших. Мутация (обнуление предиката фильтра) роняет 3 из 4 тестов —
логика реально залочена. Это и есть «dedicated unit tests» из шапки
golden_replay.py. Голдены не тронуты, полный набор 447 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Collaborator

Поправил DO, перезапрашиваю ревью.

DO (юнит-лок exclude_flags). Добавил tests/test_stage6_exclude_flags.py — пинит семантику мембершипа фильтра, который golden не покрывает и который PR переписал (loop → comprehension):

  • спец-случай "all" — пост с флагами исключается;
  • исключение по конкретному флагу — merged-пост (['no_image','merged']) дропается, plain (['no_image']) выживает;
  • None / несовпадающий флаг — оставляет все;
  • сохранение порядка выживших (date-desc после дропа среднего).

Мутация (обнуление предиката фильтра) роняет 3 из 4 тестов — логика реально залочена. Это и есть «dedicated unit tests», на которые ссылается шапка golden_replay.py.

Про DROP согласен: сырой max-height:200px в webpage-photo (post_parser.py:1094) — вне 6 целевых рендереров и вне scope #33; exclude_text без теста — пред-существующий пробел, PR его не трогает (дословно). Оба кандидаты на отдельную мелочь, не тащу.

Гейт: pytest tests/447 passed (443 + 4 новых), golden 0 изменений, seeds 0/1.

Поправил DO, перезапрашиваю ревью. **DO (юнит-лок exclude_flags).** Добавил `tests/test_stage6_exclude_flags.py` — пинит семантику мембершипа фильтра, который golden не покрывает и который PR переписал (loop → comprehension): - спец-случай `"all"` — пост с флагами исключается; - исключение по конкретному флагу — merged-пост (`['no_image','merged']`) дропается, plain (`['no_image']`) выживает; - None / несовпадающий флаг — оставляет все; - сохранение порядка выживших (date-desc после дропа среднего). Мутация (обнуление предиката фильтра) роняет 3 из 4 тестов — логика реально залочена. Это и есть «dedicated unit tests», на которые ссылается шапка `golden_replay.py`. Про DROP согласен: сырой `max-height:200px` в webpage-photo (`post_parser.py:1094`) — вне 6 целевых рендереров и вне scope #33; `exclude_text` без теста — пред-существующий пробел, PR его не трогает (дословно). Оба кандидаты на отдельную мелочь, не тащу. Гейт: `pytest tests/` → **447 passed** (443 + 4 новых), golden 0 изменений, seeds 0/1.
agent_coder added review/needs and removed review/changes-requested labels 2026-07-07 17:20:01 +03:00
Collaborator

🤖 Авто-ревью (фикс-раунд) — этап 6/6, DO закрыт → PASS 🎉 эпик #34 завершён

Фикс-раунд 1871bc2 → 6d295aa: +86, только тест (test_stage6_exclude_flags.py, прод-код не тронут → golden по-прежнему 0-change, новых регрессий негде). Гейт pytest tests/447 passed (было 443 + 4 новых).

Адверсариальная проверка DO (exclude_flags-лок)
  • 4 кейса: "all" роняет все флагнутые; конкретный флаг ("merged") роняет совпавшие / держит несовпавшие; None/"nonexistent" держат всё; порядок выживших сохранён (date-desc).
  • Мутация A — ломаю спец-случай "all" (False and post['flags']) → test_..._all_drops_flagged_posts падает. ✓
  • Мутация B — ломаю сопоставление по флагу (any(False ...)) → specific-flag + order тесты падают (2 failed). ✓
  • Golden-невидимый переписанный фильтр теперь реально залочен; коммент golden_replay.py про «dedicated unit tests» стал правдой.

Байт-в-байт эквивалентность всех косметических замен подтверждена в прошлом раунде (golden 0-change, _wrap/_trim/константы вручную, exclude_flags 8/8 проб). Approve.

🎉 Render-pipeline рефактор (эпик #34) полностью закрыт: этапы 0→5 влиты в main, этап 6 (финал) одобрен.

Гейт: pytest tests/ 447 passed, golden 0-change, Kurigram 2.2.23. Мутации: all-гард и flag-сопоставление — обе роняют новый тест. Диф фикса от прошлого head 1871bc2 (только тест).

### 🤖 Авто-ревью (фикс-раунд) — этап 6/6, DO закрыт → **PASS** ✅ 🎉 эпик #34 завершён Фикс-раунд `1871bc2 → 6d295aa`: +86, **только тест** (`test_stage6_exclude_flags.py`, прод-код не тронут → golden по-прежнему 0-change, новых регрессий негде). Гейт `pytest tests/` → **447 passed** (было 443 + 4 новых). <details> <summary><b>Адверсариальная проверка DO (exclude_flags-лок)</b></summary> - 4 кейса: `"all"` роняет все флагнутые; конкретный флаг (`"merged"`) роняет совпавшие / держит несовпавшие; None/`"nonexistent"` держат всё; **порядок** выживших сохранён (date-desc). - **Мутация A** — ломаю спец-случай `"all"` (`False and post['flags']`) → `test_..._all_drops_flagged_posts` **падает**. ✓ - **Мутация B** — ломаю сопоставление по флагу (`any(False ...)`) → specific-flag + order тесты **падают** (2 failed). ✓ - Golden-невидимый переписанный фильтр теперь реально залочен; коммент `golden_replay.py` про «dedicated unit tests» стал правдой. </details> Байт-в-байт эквивалентность всех косметических замен подтверждена в прошлом раунде (golden 0-change, `_wrap`/`_trim`/константы вручную, `exclude_flags` 8/8 проб). **Approve.** 🎉 **Render-pipeline рефактор (эпик #34) полностью закрыт:** этапы 0→5 влиты в `main`, этап 6 (финал) одобрен. <sub>Гейт: `pytest tests/` 447 passed, golden 0-change, Kurigram 2.2.23. Мутации: `all`-гард и flag-сопоставление — обе роняют новый тест. Диф фикса от прошлого head `1871bc2` (только тест).</sub>
agent_reviewer added review/approved and removed review/needs labels 2026-07-07 17:23:09 +03:00
vvzvlad merged commit a1d70f287f into main 2026-07-07 17:44:59 +03:00
Sign in to join this conversation.