9519b37788
~80% дублированных тел generate_channel_rss / generate_channel_html сведены в одну корутину _prepare_feed_posts (validate limit → cached_get_chat → cached_get_chat_history → опц. _reply_enrichment → to_thread(_render_pipeline) с _flush_pending_media_ids в finally). channel_name_prepare/cached_get_chat/ cached_get_chat_history/_render_pipeline/_flush_pending_media_ids теперь по одной копии (было по две). Новые @dataclass PreparedFeed + ChannelNotFound (→ create_error_feed, байт-эквивалентно). Форматтеры тонкие: RSS (history_limit=limit*2, enrich_replies=False), HTML (limit, enrich_replies=True). _reply_enrichment (живой client.get_messages) перенесён в _prepare_feed_posts (гейт enrich_replies, HTML-only; RSS намеренно пропускает). §3.9 FloodWait из истории ре-райзится (→429) вместо ValueError (→400); §3.10 тексты ValueError унифицированы. Enrichment закрыт unit-тестом (по требованию мейнтейнера в #30): батчинг по chat_id (2 вызова get_messages на 3+2 реплая, не 5), проставление таргета на правильный пост, рендер <div class="message-reply"> end-to-end; плюс RSS-не-обогащает и error-path тесты обоих форматтеров. Golden — БЕЗ изменений (чистый структурный дедуп; harness no-op'ит enrichment на bare-клиенте). Оракул зелёный и детерминирован (PYTHONHASHSEED 1/42). Полный сюит 365 passed (+12). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>