<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Evgeny Kushnarenko</title><link>https://kushnaren.co/</link><description>Личный блог Евгения Кушнаренко: инженерия, мышление, наблюдения. Длинные тексты о смыслах и системах.</description><generator>Hugo</generator><language>ru-RU</language><managingEditor>hello@kushnaren.co</managingEditor><lastBuildDate>Wed, 11 Feb 2026 12:00:00 +0300</lastBuildDate><atom:link href="https://kushnaren.co/tags/%D0%BA%D1%83%D1%85%D0%BD%D1%8F/index.xml" rel="self" type="application/rss+xml"/><item><title>Как мы с тобой сейчас пишем этот пост: модель, инструменты, рабочий процесс 🤖</title><link>https://kushnaren.co/posts/2026-07-21-kak-my-s-toboj-seychas-pishem-etot-post/</link><pubDate>Tue, 21 Jul 2026 10:18:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/2026-07-21-kak-my-s-toboj-seychas-pishem-etot-post/</guid><description>Практический взгляд изнутри на то, как ИИ-агент опencode с моделью Qwen3.6-35B-A3B-MLX-6bit создаёт контент для Hugo-блога: структура инструментов, MCP-серверы (Hugo, Obsidian, SearXNG), автоматизация сборки и деплоя, где агент силён, а где нужна ручная редактура.</description><content:encoded><![CDATA[<p>Этот пост пишется прямо сейчас — в реальном времени — агентом опencode на модели Qwen3.6-35B-A3B-MLX-6bit. Не Codex, не Claude Code, а именно opencode: CLI-инструмент с поддержкой специализированных скиллов, MCP-серверов и интеграции с Obsidian.</p>
<p>Давайте разберёмся, что здесь происходит на самом деле — от модели до публикации, без маркетинговой шелухи.</p>
<h2 class="heading" id="что-такое-opencode-и-чем-он-отличается">
  Что такое opencode и чем он отличается
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-opencode-%d0%b8-%d1%87%d0%b5%d0%bc-%d0%be%d0%bd-%d0%be%d1%82%d0%bb%d0%b8%d1%87%d0%b0%d0%b5%d1%82%d1%81%d1%8f">#</a>
</h2>
<p>opencode — это интерактивный CLI-агент для помощи в программистских задачах. В отличие от Codex, он построен вокруг концепции скиллов — специализированных инструкций для конкретных типов задач — и MCP-серверов, которые дают агенту доступ к внешним инструментам без необходимости вручную выполнять команды.</p>
<p>В моём случае это не просто «чат с ИИ». Это инструмент, который:</p>
<ul>
<li>знает контекст моих проектов через Obsidian Volt;</li>
<li>умеет работать с Hugo-блогом напрямую через MCP-сервер <code>hugo</code>;</li>
<li>может искать информацию через SearXNG;</li>
<li>умеет управлять заметками, тегами и структурой хранилища.</li>
</ul>
<p>Главное отличие от Codex: опencode — это не привязка к одной модели, а оболочка, которая может работать с разными бэкендами и расширениями через скиллы и MCP 🔌</p>
<h2 class="heading" id="модель-под-капотом-qwen36-35b-a3b-mlx-6bit">
  Модель под капотом: Qwen3.6-35B-A3B-MLX-6bit
  <a class="anchor" href="#%d0%bc%d0%be%d0%b4%d0%b5%d0%bb%d1%8c-%d0%bf%d0%be%d0%b4-%d0%ba%d0%b0%d0%bf%d0%be%d1%82%d0%be%d0%bc-qwen36-35b-a3b-mlx-6bit">#</a>
</h2>
<p>За опencode в текущей сессии отвечает модель <strong>Qwen3.6-35B-A3B-MLX-6bit</strong>. Разберём, что это значит на практике.</p>
<p><strong>Qwen3.6</strong> — это семейство моделей от Alibaba Group, третье поколение архитектуры Qwen. Модель позиционируется как одна из лучших в классе open-weight моделей с 35 миллиардами параметров.</p>
<p><strong>35B-A3B</strong> — интересная архитектура. 35 миллиардов параметров, из которых активными при каждом проходе используется только ~3 миллиарда. Это Mixture-of-Experts (MoE) подход: модель активирует только нужные «экспертные» подсети для каждой задачи, что даёт скорость приближенную к гораздо меньшим моделям при качестве, сопоставимом с моделями в несколько раз большего размера.</p>
<p><strong>MLX</strong> — это фреймворк от Apple для запуска LLM на чипах Apple Silicon. MLX оптимизирует загрузку и выполнение моделей на M-сериях процессоров, обеспечивая эффективное использование unified memory.</p>
<p><strong>6bit</strong> — модель квантована до 6-битной точности. Это компромисс: при значительном уменьшении размера модели (примерно в 4 раза по сравнению с FP16) потери качества минимальны для большинства задач. Для написания текстов и работы с кодом 6-битная квантованная модель работает практически неотличимо от полной.</p>
<p><strong>Почему это важно для написания постов.</strong> Модель достаточно ёмкая (35B total, 3B active), чтобы работать с длинными контекстами и сохранять логическую связность текста. Но при этом она runs locally на моей Mac-машине — никаких API-лимитов, задержек сети или отправки данных на сторону.</p>
<h2 class="heading" id="инструменты-и-mcp-серверы">
  Инструменты и MCP-серверы
  <a class="anchor" href="#%d0%b8%d0%bd%d1%81%d1%82%d1%80%d1%83%d0%bc%d0%b5%d0%bd%d1%82%d1%8b-%d0%b8-mcp-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80%d1%8b">#</a>
</h2>
<p>Вот что подключено к opencode в ходе работы над этим постом:</p>
<h3 class="heading" id="hugo-mcp-сервер">
  Hugo MCP-сервер
  <a class="anchor" href="#hugo-mcp-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80">#</a>
</h3>
<p>Позволяет агенту работать с Hugo-сайтом напрямую: проверять установку, список контента, собирать сайт, запускать локальный сервер. Это избавляет от необходимости вручную выполнять <code>hugo list</code>, <code>hugo --minify</code> — агент делает это через инструмент.</p>
<h3 class="heading" id="obsidian-mcp-сервер">
  Obsidian MCP-сервер
  <a class="anchor" href="#obsidian-mcp-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80">#</a>
</h3>
<p>Дает доступ к моему Vault (<code>~/temp/git.evgn.ru/obsidian/vault/</code>) — это хранилище всех заметок, проектов и знаний. Через него агент:</p>
<ul>
<li>читает профиль проекта <code>Projects/Блог (Hugo).md</code>;</li>
<li>смотрит правила написания постов;</li>
<li>проверяет структуру и форматирование существующих статей.</li>
</ul>
<h3 class="heading" id="searxng-mcp-сервер">
  SearXNG MCP-сервер
  <a class="anchor" href="#searxng-mcp-%d1%81%d0%b5%d1%80%d0%b2%d0%b5%d1%80">#</a>
</h3>
<p>Поисковый агрегатор для веб-поиска через instance <code>search.evgn.ru</code>. Позволяет агенту находить актуальную информацию, если нужно проверить факты или найти внешние источники.</p>
<h3 class="heading" id="скиллы">
  Скиллы
  <a class="anchor" href="#%d1%81%d0%ba%d0%b8%d0%bb%d0%bb%d1%8b">#</a>
</h3>
<p>Скиллы — это специализированные инструкции, которые расширяют возможности агента для конкретных задач. В моём наборе есть скиллы для:</p>
<ul>
<li><strong>impeccable</strong> — аудит и улучшение frontend-интерфейсов;</li>
<li><strong>infra-audit</strong> — аудит self-hosted сервисов;</li>
<li><strong>himalaya-inbox-cleaner</strong> — чистка почтового ящика;</li>
<li><strong>memory-consolidate</strong> и <strong>memory-patcher</strong> — управление долгосрочной памятью;</li>
<li><strong>unified-search</strong> — универсальный поиск по всем источникам;</li>
<li><strong>obsidian-vault-mcp</strong> — работа с Obsidian.</li>
</ul>
<h2 class="heading" id="рабочий-процесс-как-создаётся-пост">
  Рабочий процесс: как создаётся пост
  <a class="anchor" href="#%d1%80%d0%b0%d0%b1%d0%be%d1%87%d0%b8%d0%b9-%d0%bf%d1%80%d0%be%d1%86%d0%b5%d1%81%d1%81-%d0%ba%d0%b0%d0%ba-%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d1%91%d1%82%d1%81%d1%8f-%d0%bf%d0%be%d1%81%d1%82">#</a>
</h2>
<p>Процесс создания этого поста прошёл через несколько этапов, каждый из которых агент выполнял автономно:</p>
<h3 class="heading" id="1-изучение-контекста">
  1. Изучение контекста
  <a class="anchor" href="#1-%d0%b8%d0%b7%d1%83%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-%d0%ba%d0%be%d0%bd%d1%82%d0%b5%d0%ba%d1%81%d1%82%d0%b0">#</a>
</h3>
<p>Первое, что сделал opencode — загрузил профиль проекта из Obsidian. Это заняло пару секунд: чтение <code>Projects/Блог (Hugo).md</code> и <code>References/Управление блогом через MCP Hugo.md</code>. Модель получила полную картину: структуру репозитория, правила постов, CI/CD-процесс, технический стек.</p>
<h3 class="heading" id="2-анализ-существующих-постов">
  2. Анализ существующих постов
  <a class="anchor" href="#2-%d0%b0%d0%bd%d0%b0%d0%bb%d0%b8%d0%b7-%d1%81%d1%83%d1%89%d0%b5%d1%81%d1%82%d0%b2%d1%83%d1%8e%d1%89%d0%b8%d1%85-%d0%bf%d0%be%d1%81%d1%82%d0%be%d0%b2">#</a>
</h3>
<p>Агент прочитал несколько предыдущих статей, чтобы понять стиль и формат. Особенно важен пост «Пишем посты с Codex» — он задаёт тон для материалов об автоматизации. Также был изучён релизный пост про Hugo 0.164.0 — это референс для технической глубины и структуры.</p>
<h3 class="heading" id="3-проверка-актуальности">
  3. Проверка актуальности
  <a class="anchor" href="#3-%d0%bf%d1%80%d0%be%d0%b2%d0%b5%d1%80%d0%ba%d0%b0-%d0%b0%d0%ba%d1%82%d1%83%d0%b0%d0%bb%d1%8c%d0%bd%d0%be%d1%81%d1%82%d0%b8">#</a>
</h3>
<p>Агент определил текущее время (МСК, 12:19) и сгенерировал дату поста <code>2026-07-21T12:14:00+03:00</code> — на 5 минут в прошлом, как требуют правила. Это важно: пост не должен оказаться в <code>future</code>, иначе CI его не опубликует.</p>
<h3 class="heading" id="4-написание-текста">
  4. Написание текста
  <a class="anchor" href="#4-%d0%bd%d0%b0%d0%bf%d0%b8%d1%81%d0%b0%d0%bd%d0%b8%d0%b5-%d1%82%d0%b5%d0%ba%d1%81%d1%82%d0%b0">#</a>
</h3>
<p>Самая сложная часть. Агент сгенерировал текст, опираясь на:</p>
<ul>
<li>правила из <code>AGENTS.md</code> (русский язык, минимум 2 эмодзи, структура с разделами);</li>
<li>знание модели (Qwen3.6-35B-A3B-MLX-6bit, MoE, MLX, 6bit);</li>
<li>контекст из Obsidian (правила фронтматтера, теги, toc);</li>
<li>личный опыт автора (реальные инструменты, репозитории, инфраструктура).</li>
</ul>
<p>Текст разделён на смысловые блоки: введение, описание модели, инструменты, рабочий процесс. Каждый раздел содержит конкретные детали, а не общие фразы.</p>
<h3 class="heading" id="5-техническая-валидация">
  5. Техническая валидация
  <a class="anchor" href="#5-%d1%82%d0%b5%d1%85%d0%bd%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b0%d1%8f-%d0%b2%d0%b0%d0%bb%d0%b8%d0%b4%d0%b0%d1%86%d0%b8%d1%8f">#</a>
</h3>
<p>После написания текста агент проверил фронтматтер: формат даты, tags (взял из существующих постов), toc/readTime enabled. Summary и description — разные, без дублирования.</p>
<h3 class="heading" id="6-публикация">
  6. Публикация
  <a class="anchor" href="#6-%d0%bf%d1%83%d0%b1%d0%bb%d0%b8%d0%ba%d0%b0%d1%86%d0%b8%d1%8f">#</a>
</h3>
<p>Финальный шаг — <code>git commit</code> и <code>git push</code> в ветку <code>main</code>. После этого CI/CD через Forgejo Actions автоматически соберёт сайт, проиндексирует поиск Pagefind и деплоит через rsync на сервер.</p>
<h2 class="heading" id="что-работает-хорошо-а-что--нет">
  Что работает хорошо, а что — нет
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d0%b5%d1%82-%d1%85%d0%be%d1%80%d0%be%d1%88%d0%be-%d0%b0-%d1%87%d1%82%d0%be--%d0%bd%d0%b5%d1%82">#</a>
</h2>
<h3 class="heading" id="сильные-стороны">
  Сильные стороны
  <a class="anchor" href="#%d1%81%d0%b8%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%81%d1%82%d0%be%d1%80%d0%be%d0%bd%d1%8b">#</a>
</h3>
<ul>
<li><strong>Контекстная осведомлённость.</strong> Агент читает Obsidian и знает о проекте больше, чем «чистый» LLM-чат.</li>
<li><strong>Автоматизация рутины.</strong> Фронтматтер, дата, теги, проверка future-дат — всё это агент делает сам.</li>
<li><strong>Локальность.</strong> Модель работает на Mac, данные не покидают машину. Это важно для приватности и скорости.</li>
<li><strong>Скиллы.</strong> Специализированные инструкции позволяют агенту быстро переключаться между задачами: сегодня пишет пост, завтра чистит почту.</li>
</ul>
<h3 class="heading" id="слабые-места">
  Слабые места
  <a class="anchor" href="#%d1%81%d0%bb%d0%b0%d0%b1%d1%8b%d0%b5-%d0%bc%d0%b5%d1%81%d1%82%d0%b0">#</a>
</h3>
<ul>
<li><strong>Стиль.</strong> Текст агента иногда слишком «структурирован» — разделы идут как по шаблону, без естественных отклонений. Авторская редактура важна не для «исправления ошибок», а для добавления живости.</li>
<li><strong>Глубина технических деталей.</strong> Агент знает, что модель называется Qwen3.6-35B-A3B-MLX-6bit, но может ошибиться в архитектурных нюансах. MoE — это Mixture of Experts, а не какая-то абстрактная физика: агент описывает её на основе общих паттернов, а не реального опыта.</li>
<li><strong>Эмоциональная тональность.</strong> Агент хорошо справляется с информационным содержанием, но тон текста часто остаётся «нейтрально-аналитическим». Авторская правка добавляет иронию, личные наблюдения, интонацию.</li>
</ul>
<h2 class="heading" id="почему-это-вообще-имеет-смысл">
  Почему это вообще имеет смысл
  <a class="anchor" href="#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%8d%d1%82%d0%be-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d0%b8%d0%bc%d0%b5%d0%b5%d1%82-%d1%81%d0%bc%d1%8b%d1%81%d0%bb">#</a>
</h2>
<p>Главный вопрос: зачем писать пост об ИИ, который пишет посты?</p>
<p>Потому что это не новый способ «генерации контента». Это новый способ <strong>разделения труда</strong>.</p>
<p>Автор остаётся там, где он уникально полезен:</p>
<ul>
<li>формулирует идею и позицию;</li>
<li>добавляет личный опыт и фактуру;</li>
<li>контролирует логику аргументации;</li>
<li>решает, что стоит рассказать, а что — нет.</li>
</ul>
<p>Агент берёт на себя:</p>
<ul>
<li>поиск и структурирование контекста;</li>
<li>черновую сборку текста из тезисов;</li>
<li>техническое оформление (фронтматтер, теги, форматирование);</li>
<li>валидацию перед публикацией.</li>
</ul>
<p>Результат — не «ИИ написал статью», а «автор написал статью быстрее и увереннее, потому что инструмент снял операционную нагрузку».</p>
<h2 class="heading" id="сравнение-с-codex-два-инструмента-разные-роли">
  Сравнение с Codex: два инструмента, разные роли
  <a class="anchor" href="#%d1%81%d1%80%d0%b0%d0%b2%d0%bd%d0%b5%d0%bd%d0%b8%d0%b5-%d1%81-codex-%d0%b4%d0%b2%d0%b0-%d0%b8%d0%bd%d1%81%d1%82%d1%80%d1%83%d0%bc%d0%b5%d0%bd%d1%82%d0%b0-%d1%80%d0%b0%d0%b7%d0%bd%d1%8b%d0%b5-%d1%80%d0%be%d0%bb%d0%b8">#</a>
</h2>
<p>Codex и opencode — это не конкуренты, а взаимодополняющие инструменты. Вот как они отличаются в моём рабочем процессе:</p>
<table>
	<thead>
			<tr>
					<th>Аспект</th>
					<th>Codex</th>
					<th>opencode</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Модель</td>
					<td>Claude (через API)</td>
					<td>Qwen3.6-35B-MLX (локально)</td>
			</tr>
			<tr>
					<td>Доступ к файлам</td>
					<td>Через терминал</td>
					<td>MCP-серверы (Hugo, Obsidian)</td>
			</tr>
			<tr>
					<td>Скиллы</td>
					<td>Нет</td>
					<td>Специализированные инструкции</td>
			</tr>
			<tr>
					<td>Контекст проекта</td>
					<td>Только текущий репозиторий</td>
					<td>Obsidian Vault + репозитории</td>
			</tr>
			<tr>
					<td>Конфиденциальность</td>
					<td>Данные уходят в API</td>
					<td>Всё локально</td>
			</tr>
	</tbody>
</table>
<p>Codex лучше подходит для сложных задач, где нужна максимальная «интеллигентность» — анализ архитектуры, написание кода, глубокие рассуждения. opencode — для задач, где важна скорость, локальность и работа с известным контекстом.</p>
<p>Для блога opencode сейчас работает лучше: он знает правила, структуру и стиль проекта через Obsidian, и не требует API-доступа.</p>
<h2 class="heading" id="итог-">
  Итог 🧠
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3-">#</a>
</h2>
<p>Написание этого поста — не трюк и не маркетинговая демонстрация. Это рабочий процесс, который я использую регулярно:</p>
<ol>
<li><strong>Идея</strong> — от автора.</li>
<li><strong>Контекст</strong> — агент собирает из Obsidian и репозитория.</li>
<li><strong>Черновик</strong> — агент собирает из тезисов и контекста.</li>
<li><strong>Редактура</strong> — автор добавляет фактуру, иронию, личный опыт.</li>
<li><strong>Техника</strong> — агент оформляет фронтматтер, проверяет дату, делает commit/push.</li>
<li><strong>Деплой</strong> — CI/CD собирает и публикует.</li>
</ol>
<p>Модель Qwen3.6-35B-A3B-MLX-6bit — хороший выбор для этого: достаточно ёмкая для понимания контекста, достаточно быстрая для итеративной работы, достаточно локальная для приватности.</p>
<p>А opencode — это не просто «ещё один ИИ-ассистент». Это инструмент, который объединяет модель, скиллы и MCP-серверы в единый рабочий процесс. И для блога на Hugo это сейчас оптимальная комбинация: быстро, локально, с полным знанием контекста.</p>
<h2 class="heading" id="источники">
  Источники
  <a class="anchor" href="#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8">#</a>
</h2>
<ul>
<li>Qwen3.6: <a href="https://qwenlm.github.io/">Alibaba Group — Qwen</a></li>
<li>MLX: <a href="https://ml-explore.github.io/mlx/">Apple MLX</a></li>
<li>opencode: <a href="https://opencode.ai">opencode.ai</a></li>
</ul>
]]></content:encoded><category>ai-инструменты</category><category>автоматизация</category><category>hugo</category></item><item><title>Hugo 0.164.0: тёмно-светлые пары Chroma и починка регрессии производительности 🎨</title><link>https://kushnaren.co/posts/2026-07-19-hugo-0-164-0-chroma-dark-light/</link><pubDate>Sun, 19 Jul 2026 14:05:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/2026-07-19-hugo-0-164-0-chroma-dark-light/</guid><description>Обзор Hugo 0.164.0 (6 июля 2026): dark/light-режимы стилей Chroma и новые флаги hugo gen chromastyles, фикс регрессии производительности из 0.128.0, новые шаблонные функции crypto.Hash, encoding.HexEncode и encoding.HexDecode, markup.rst.syntaxHighlight, поддержка цитат в Pandoc, вложенные пути в layouts для Page.Render. Депрекейт resources.PostProcess в пользу templates.Defer. Практический разбор с точки зрения блога на Hugo и обновление сборочного образа с Node 20 (EOL) на Node 24.</description><content:encoded><![CDATA[<h2 class="heading" id="почему-это-стоит-прочитать">
  Почему это стоит прочитать
  <a class="anchor" href="#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%8d%d1%82%d0%be-%d1%81%d1%82%d0%be%d0%b8%d1%82-%d0%bf%d1%80%d0%be%d1%87%d0%b8%d1%82%d0%b0%d1%82%d1%8c">#</a>
</h2>
<p>С прошлого разбора прошёл ровно месяц: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.163.3">0.163.3</a> вышел 18 июня, <a href="https://github.com/gohugoio/hugo/releases/tag/v0.164.0">0.164.0</a> — 6 июля. После «квартала инфраструктурного взросления» с AVIF и хардненингом этот релиз выглядит скромнее: одна минорная версия, никаких громких фич.</p>
<p>Но два пункта в нём заслуживают внимания:</p>
<ol>
<li><strong>Стили Chroma получили dark/light-пары.</strong> Подсветка кода наконец умеет жить в двух темах штатно, а не через самодельные костыли.</li>
<li><strong>Починена регрессия производительности, тянувшаяся с 0.128.0.</strong> Это не новая фича, а возврат утраченного — но на больших сайтах разница заметна.</li>
</ol>
<p>Остальное — приятные мелочи в шаблонных функциях и один депрекейт. Breaking changes как таковых нет, обновление спокойное.</p>
<h2 class="heading" id="главное-за-один-взгляд">
  Главное за один взгляд
  <a class="anchor" href="#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d0%be%d0%b5-%d0%b7%d0%b0-%d0%be%d0%b4%d0%b8%d0%bd-%d0%b2%d0%b7%d0%b3%d0%bb%d1%8f%d0%b4">#</a>
</h2>
<p><strong>Фичи:</strong></p>
<ul>
<li><strong>Dark/light-пары в стилях Chroma</strong> — подсветка синтаксиса умеет два режима; у команды <a href="https://gohugo.io/commands/hugo_gen_chromastyles/"><code>hugo gen chromastyles</code></a> появились новые флаги.</li>
<li><strong>Новые шаблонные функции</strong>: <a href="https://gohugo.io/functions/crypto/hash/"><code>crypto.Hash</code></a>, <a href="https://gohugo.io/functions/encoding/hexencode/"><code>encoding.HexEncode</code></a>, <a href="https://gohugo.io/functions/encoding/hexdecode/"><code>encoding.HexDecode</code></a>.</li>
<li><strong>Опция <a href="https://gohugo.io/configuration/markup/#syntaxhighlight"><code>markup.rst.syntaxHighlight</code></a></strong> — подсветка для reStructuredText.</li>
<li><strong>Поддержка цитат в Pandoc</strong> — для тех, кто держит библиографию.</li>
<li><strong>Вложенные пути в layouts</strong> для <a href="https://gohugo.io/methods/page/render/#article"><code>Page.Render</code></a>.</li>
<li><strong>Фикс регрессии производительности из 0.128.0</strong> — <a href="https://discourse.gohugo.io/t/hugo-building-slowly-from-release-0-128-0/57314/20">обсуждение на Discourse</a> тянулось долго.</li>
</ul>
<p><strong>Депрекейт:</strong></p>
<ul>
<li><code>resources.PostProcess</code> объявлен устаревшим в пользу <code>templates.Defer</code> (issue <a href="https://github.com/gohugoio/hugo/issues/15086">#15086</a>).</li>
</ul>
<p><strong>Мелочи, которые могут задеть:</strong></p>
<ul>
<li>Поиск имени шаблона стал <strong>регистронезависимым</strong> (<a href="https://github.com/gohugoio/hugo/issues/15057">#15057</a>).</li>
<li><code>.Render</code> теперь <strong>возвращает ошибку</strong>, если шаблон не найден (<a href="https://github.com/gohugoio/hugo/issues/15052">#15052</a>) — раньше молча отдавал пустоту.</li>
<li><code>IsSet</code> включает ключ в предупреждение о неподдерживаемом типе.</li>
</ul>
<h2 class="heading" id="darklight-в-chroma-">
  Dark/light в Chroma 🌗
  <a class="anchor" href="#darklight-%d0%b2-chroma-">#</a>
</h2>
<p>Главная фича релиза — и, пожалуй, самая недооценённая.</p>
<p>Раньше стиль подсветки в Hugo был один на весь сайт: в <code>hugo.toml</code> задавался <code>markup.highlight.style</code>, и всё. Если сайт умеет переключаться между светлой и тёмной темой, приходилось выкручиваться: генерировать два CSS-файла через <code>hugo gen chromastyles</code>, руками оборачивать их в медиа-запросы или классы, следить за синхронизацией.</p>
<p>Теперь Chroma внутри Hugo знает про <a href="https://gohugo.io/quick-reference/syntax-highlighting-styles/#modes">парные режимы</a>, а у <code>hugo gen chromastyles</code> появились флаги для генерации нужного варианта. Смысл простой: светлая и тёмная схемы подсветки — это не два несвязанных стиля, а пара, и Hugo теперь так к ним и относится.</p>
<p><strong>Что это значит для нашего блога.</strong> Прямое попадание. Переключатель дня и ночи здесь работает на JS с сохранением выбора в <code>localStorage</code> — то есть тема меняется без перезагрузки, и подсветка кода должна меняться вместе с ней. Сейчас в конфиге один <code>[markup.highlight]</code> на оба режима, и в тёмной теме блоки кода выглядят компромиссно. Это следующий пункт в списке доработок: сгенерировать пару стилей и подцепить их к тому же переключателю. В этот заход не делаю — обновление и так вышло насыщенным, — но фича переехала из «знать» в «внедрять».</p>
<h2 class="heading" id="регрессия-производительности-возврат-утраченного-">
  Регрессия производительности: возврат утраченного ⚡
  <a class="anchor" href="#%d1%80%d0%b5%d0%b3%d1%80%d0%b5%d1%81%d1%81%d0%b8%d1%8f-%d0%bf%d1%80%d0%be%d0%b8%d0%b7%d0%b2%d0%be%d0%b4%d0%b8%d1%82%d0%b5%d0%bb%d1%8c%d0%bd%d0%be%d1%81%d1%82%d0%b8-%d0%b2%d0%be%d0%b7%d0%b2%d1%80%d0%b0%d1%82-%d1%83%d1%82%d1%80%d0%b0%d1%87%d0%b5%d0%bd%d0%bd%d0%be%d0%b3%d0%be-">#</a>
</h2>
<p>Незаметная, но приятная часть. В 0.128.0 (это июль 2024-го) в сборку просочилась регрессия, из-за которой большие сайты собирались медленнее. Симптом обсуждали на Discourse больше года, и вот в 0.164.0 его закрыли.</p>
<p>Ремарка из release notes честная: эффект заметен <strong>в первую очередь на крупных сайтах</strong>. Наш блог — около сотни страниц, и на таком объёме разница в пределах шума. Но сама история поучительна: регрессия прожила полтора года и полтора десятка релизов, потому что на маленьких сайтах её просто не видно.</p>
<h2 class="heading" id="новые-функции-cryptohash-и-hex-">
  Новые функции: crypto.Hash и hex 🔧
  <a class="anchor" href="#%d0%bd%d0%be%d0%b2%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-cryptohash-%d0%b8-hex-">#</a>
</h2>
<p>Три новые шаблонные функции:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">crypto</span><span class="na">.Hash</span><span class="w"> </span><span class="s">&#34;sha256&#34;</span><span class="w"> </span><span class="s">&#34;текст&#34;</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">encoding</span><span class="na">.HexEncode</span><span class="w"> </span><span class="s">&#34;привет&#34;</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">encoding</span><span class="na">.HexDecode</span><span class="w"> </span><span class="s">&#34;d0bfd180d0b8d0b2d0b5d182&#34;</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p><code>crypto.Hash</code> принимает алгоритм явным параметром — удобнее, чем существующие <code>md5</code>/<code>sha1</code>/<code>sha256</code> по отдельности. Практический сценарий — стабильные идентификаторы: захешировать путь или содержимое и получить детерминированный ключ для кэша, якоря или имени файла.</p>
<p>Hex-функции закрывают давнюю мелочь: base64 в шаблонах был, а hex приходилось изображать вручную.</p>
<p>Для нашего блога — пока в копилку. Ни одного места, где это было бы нужно прямо сейчас, нет.</p>
<h2 class="heading" id="депрекейт-resourcespostprocess-">
  Депрекейт resources.PostProcess ⚠️
  <a class="anchor" href="#%d0%b4%d0%b5%d0%bf%d1%80%d0%b5%d0%ba%d0%b5%d0%b9%d1%82-resourcespostprocess-">#</a>
</h2>
<p>Единственное, за чем стоит следить.</p>
<p><code>resources.PostProcess</code> использовался для отложенной обработки ресурсов — классический сценарий — PurgeCSS/Tailwind, когда нужно узнать финальный HTML, чтобы вычистить неиспользуемые классы. Теперь рекомендуется <code>templates.Defer</code>, механизм более общий и предсказуемый.</p>
<p><strong>Нас не задевает</strong>: проверил <code>layouts/</code> и <code>themes/</code> — <code>resources.PostProcess</code> не используется нигде. CSS через Hugo Pipes с Tailwind мы не собираем, а Pagefind живёт отдельным npm-инструментом и к ресурсной цепочке отношения не имеет.</p>
<p>Если у вас Tailwind в Pipes — это первое, что нужно проверить перед обновлением: депрекейт пока не ломает, но направление задано.</p>
<h2 class="heading" id="осторожно-render-теперь-возвращает-ошибку">
  Осторожно: <code>.Render</code> теперь возвращает ошибку
  <a class="anchor" href="#%d0%be%d1%81%d1%82%d0%be%d1%80%d0%be%d0%b6%d0%bd%d0%be-render-%d1%82%d0%b5%d0%bf%d0%b5%d1%80%d1%8c-%d0%b2%d0%be%d0%b7%d0%b2%d1%80%d0%b0%d1%89%d0%b0%d0%b5%d1%82-%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d1%83">#</a>
</h2>
<p>Мелочь из списка изменений, которая может неприятно удивить. Раньше <code>.Render</code> с несуществующим шаблоном молча отдавал пустую строку — и сайт собирался, просто без куска контента. Теперь он <strong>возвращает ошибку</strong>, то есть сборка упадёт.</p>
<p>Формально это исправление, а не breaking change: молчаливая пустота — плохое поведение. Но если у вас в шаблонах есть вызов <code>.Render</code> с опечаткой в имени, который «работал» годами, — обновление его вскроет. Заодно поиск имени шаблона стал регистронезависимым, так что часть таких опечаток теперь, наоборот, начнёт находиться.</p>
<h2 class="heading" id="инфраструктура-заодно-поменяли-базу-образа-">
  Инфраструктура: заодно поменяли базу образа 🐳
  <a class="anchor" href="#%d0%b8%d0%bd%d1%84%d1%80%d0%b0%d1%81%d1%82%d1%80%d1%83%d0%ba%d1%82%d1%83%d1%80%d0%b0-%d0%b7%d0%b0%d0%be%d0%b4%d0%bd%d0%be-%d0%bf%d0%be%d0%bc%d0%b5%d0%bd%d1%8f%d0%bb%d0%b8-%d0%b1%d0%b0%d0%b7%d1%83-%d0%be%d0%b1%d1%80%d0%b0%d0%b7%d0%b0-">#</a>
</h2>
<p>Здесь стоит рассказать про то, что к самому Hugo отношения не имеет, но всплыло при обновлении.</p>
<p>Блог собирается в собственном Docker-образе, куда запечены Hugo Extended, Node и Pagefind. Пошёл поднимать версию Hugo — и обнаружил, что база образа <code>node:20-bookworm-slim</code> <strong>достигла EOL 30 апреля 2026</strong>. То есть почти три месяца образ жил без обновлений безопасности базового слоя.</p>
<p>Node здесь нужен ровно для одного — установить Pagefind. Поэтому смена мажора малорискованная, и база переехала сразу на <strong>Node 24</strong> (LTS, поддержка до апреля 2028). Итоговый образ:</p>
<div class="code-block">
  <pre tabindex="0"><code>hugo v0.164.0+extended linux/amd64
node v24.18.0
pagefind 1.5.2</code></pre>
  <button class="copy-code-button">copy</button>
</div>
<p>Мораль простая и не новая: <strong>следите не только за приложением, но и за базовым слоем</strong>. Версию Hugo видно в каждом релизном посте, а <code>FROM node:20</code> тихо стареет в первой строке Dockerfile, пока про него не вспомнишь.</p>
<p>Отдельная заметка на будущее — образ до сих пор собирался руками, поэтому и отстал. Правильный путь: собирать его в CI при изменении Dockerfile. У сборки на нашем раннере есть нюанс — он живёт в LXC-контейнере, где <code>docker build</code> не может применить AppArmor к промежуточным RUN-контейнерам и падает. Обходится это kaniko: он собирает образ в userspace, без демона Docker и вложенных контейнеров. Тот же приём уже используется для других наших образов.</p>
<h2 class="heading" id="что-это-значит-для-нашего-блога">
  Что это значит для нашего блога
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d0%b7%d0%bd%d0%b0%d1%87%d0%b8%d1%82-%d0%b4%d0%bb%d1%8f-%d0%bd%d0%b0%d1%88%d0%b5%d0%b3%d0%be-%d0%b1%d0%bb%d0%be%d0%b3%d0%b0">#</a>
</h2>
<ul>
<li><strong>Chroma dark/light</strong> — самая полезная фича релиза именно для нас, ложится на существующий переключатель темы. В работу.</li>
<li><strong>Фикс производительности</strong> — приятно, но на сотне страниц незаметно.</li>
<li><strong>Депрекейт <code>PostProcess</code></strong> — не задевает, проверено.</li>
<li><strong><code>.Render</code> с ошибкой</strong> — не задевает, но стоит держать в голове.</li>
<li><strong>Обновление базы образа с Node 20 на Node 24</strong> — по факту важнее самого апдейта Hugo.</li>
</ul>
<h2 class="heading" id="чек-лист-обновления-">
  Чек-лист обновления ✅
  <a class="anchor" href="#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-">#</a>
</h2>
<ol>
<li>
<p><strong>Обновление спокойное.</strong> С 0.163.x на 0.164.0 идти можно без подготовки: breaking changes нет, депрекейт один.</p>
</li>
<li>
<p><strong>Если используете <code>resources.PostProcess</code></strong> (обычно это Tailwind/PurgeCSS в Pipes) — запланируйте переход на <code>templates.Defer</code>:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep -rn <span class="s1">&#39;resources.PostProcess&#39;</span> layouts/ themes/</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
</li>
<li>
<p><strong>Если в шаблонах есть <code>.Render</code></strong> — проверьте имена шаблонов: теперь несуществующий шаблон роняет сборку, а не молчит.</p>
</li>
<li>
<p><strong>Если у сайта есть тёмная тема</strong> — посмотрите на парные стили Chroma, это заметное упрощение по сравнению с самодельными решениями.</p>
</li>
<li>
<p><strong>Проверьте базовый слой сборочного образа</strong>, а не только версию Hugo. Node 20 в EOL с 30 апреля 2026:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep -n <span class="s1">&#39;^FROM&#39;</span> Docker/Dockerfile</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
</li>
<li>
<p><strong>Стандартный прогон</strong> и сверка предупреждений до и после:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hugo --minify --gc</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
</li>
</ol>
<h2 class="heading" id="итог-">
  Итог 🧠
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3-">#</a>
</h2>
<p>0.164.0 — релиз-передышка после насыщенного квартала. Одна по-настоящему полезная фича (парные стили Chroma), одно давно ожидавшееся исправление (регрессия скорости из 0.128.0), горсть новых шаблонных функций и один аккуратный депрекейт.</p>
<p>Обновляться стоит: рисков практически нет, а dark/light-подсветка снимает целый класс костылей для сайтов с переключателем темы.</p>
<p>Но главный вывод этого обновления оказался не про Hugo. Версию генератора видно в каждом релизном посте, а базовый слой образа стареет молча — и именно он оказался просроченным на три месяца, а не Hugo, отставший на одну минорную версию.</p>
<h2 class="heading" id="источники">
  Источники
  <a class="anchor" href="#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8">#</a>
</h2>
<ul>
<li>Релиз 0.164.0: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.164.0">gohugoio/hugo v0.164.0</a></li>
<li>Режимы стилей подсветки: <a href="https://gohugo.io/quick-reference/syntax-highlighting-styles/#modes">gohugo.io/quick-reference/syntax-highlighting-styles/#modes</a></li>
<li><code>crypto.Hash</code>: <a href="https://gohugo.io/functions/crypto/hash/">gohugo.io/functions/crypto/hash/</a></li>
<li><code>encoding.HexEncode</code>: <a href="https://gohugo.io/functions/encoding/hexencode/">gohugo.io/functions/encoding/hexencode/</a></li>
<li>Обсуждение регрессии производительности: <a href="https://discourse.gohugo.io/t/hugo-building-slowly-from-release-0-128-0/57314/20">discourse.gohugo.io</a></li>
<li>График поддержки Node.js: <a href="https://endoflife.date/nodejs">endoflife.date/nodejs</a></li>
</ul>
]]></content:encoded><category>hugo</category><category>релизы</category></item><item><title>Что нового в Hugo 0.161–0.163: AVIF, хардненинг и гибкие permalinks 🚀</title><link>https://kushnaren.co/posts/2026-06-19-hugo-0-161-0-163-avif-i-hardening/</link><pubDate>Fri, 19 Jun 2026 07:49:52 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/2026-06-19-hugo-0-161-0-163-avif-i-hardening/</guid><description>Подробный обзор Hugo 0.161.0–0.163.3: поддержка AVIF (encoder+decoder) и его тюнинг (quality 60, content-aware chroma), хардненинг безопасности (Node permission-флаг, security.allowContent, http.urls, Go 1.26.3 XSS-фиксы), slice-based permalinks с target-матчерами, гибкая схема идентификаторов в именах файлов, депрекейты IsNode и глобального imaging.quality. Практические сценарии и чек-лист миграции с 0.160.</description><content:encoded><![CDATA[<h2 class="heading" id="почему-это-стоит-прочитать">
  Почему это стоит прочитать
  <a class="anchor" href="#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%8d%d1%82%d0%be-%d1%81%d1%82%d0%be%d0%b8%d1%82-%d0%bf%d1%80%d0%be%d1%87%d0%b8%d1%82%d0%b0%d1%82%d1%8c">#</a>
</h2>
<p>С прошлого разбора (v0.160.1, 8 апреля) накопилось пять релизов: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.161.0">0.161.0</a>, <a href="https://github.com/gohugoio/hugo/releases/tag/v0.161.1">0.161.1</a>, <a href="https://github.com/gohugoio/hugo/releases/tag/v0.162.0">0.162.0</a>, <a href="https://github.com/gohugoio/hugo/releases/tag/v0.163.0">0.163.0</a> и патчи до <a href="https://github.com/gohugoio/hugo/releases/tag/v0.163.3">0.163.3</a> (18 июня). Это не один минор, а целый квартал развития — поэтому разбор догоняющий.</p>
<p>Если сжать до двух заголовков, их два:</p>
<ol>
<li><strong>AVIF</strong> — Hugo научился кодировать и декодировать AVIF прямо в image pipeline. Третий формат рядом с JPEG и WebP, и для фото-блога это самое интересное.</li>
<li><strong>Хардненинг безопасности</strong> — Node-инструменты теперь запускаются с <code>--permission</code>, <code>text/html</code>-контент запрещён по умолчанию, ужесточены <code>security.http.urls</code>, плюс транзитом приехали XSS-фиксы из Go 1.26.3.</li>
</ol>
<p>Остальное — приятные улучшения конфигурации (slice-based permalinks, гибкие идентификаторы в именах файлов, вложенные vars в <code>css.Build</code>) и порция депрекейтов. Breaking changes есть, но точечные — разберём, кого они задевают.</p>
<h2 class="heading" id="главное-за-один-взгляд">
  Главное за один взгляд
  <a class="anchor" href="#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d0%be%d0%b5-%d0%b7%d0%b0-%d0%be%d0%b4%d0%b8%d0%bd-%d0%b2%d0%b7%d0%b3%d0%bb%d1%8f%d0%b4">#</a>
</h2>
<p><strong>v0.161.0 / 0.161.1 (28–29 апреля)</strong> — фичи + первый хардненинг:</p>
<ul>
<li>Node-инструменты (PostCSS, Babel, TailwindCSS) запускаются с флагом <code>--permission</code>. Нужен <strong>Node ≥ 22</strong>, а Tailwind теперь только как npm-пакет (standalone-бинарь больше не поддерживается).</li>
<li>Ужесточены дефолты <code>security.http.urls</code>; добавлен <code>security.node.permissions</code> (включая <code>AllowChildProcess</code>).</li>
<li><strong>Slice-based permalinks</strong> — конфигурация permalinks с <code>target</code>-матчерами (как в <code>cascade</code>).</li>
<li>Гибкая схема идентификаторов в именах файлов: <code>_language_en_</code>, <code>_role_</code>, <code>_version_</code>.</li>
<li>Вложенные vars в <code>css.Build</code> / <code>css.Sass</code>: <code>@import &quot;hugo:vars/dark&quot;</code>.</li>
</ul>
<p><strong>v0.162.0 / 0.162.1 (26–28 мая)</strong> — AVIF + security:</p>
<ul>
<li>🎯 <strong>Поддержка AVIF</strong> (encoder + decoder) в image pipeline.</li>
<li><strong><code>text/html</code>-контент запрещён по умолчанию</strong> — новая политика <code>security.allowContent</code>.</li>
<li>Go 1.26.3: фиксы XSS в <code>html/template</code> (CVE-2026-39826, CVE-2026-39823).</li>
<li>Проверка <code>security.http.urls</code> на каждом редирект-хопе <code>resources.GetRemote</code>; отказ от symlink-записей в <code>resources.Get</code>.</li>
</ul>
<p><strong>v0.163.0 (8 июня)</strong> — тюнинг AVIF + депрекейты:</p>
<ul>
<li>AVIF по умолчанию <code>quality = 60</code> (≈ JPEG/WebP q75), content-aware chroma subsampling через <code>hint</code>, качество настраивается <strong>per-format</strong>.</li>
<li>Депрекейт глобального <code>imaging.quality</code> и <code>Imaging.Compression</code> (переезжают в per-format конфиги).</li>
<li>Добавлен <code>.IsBranch</code>, депрекейт <code>.IsNode</code>; депрекейт <code>baseUrl</code> в jsconfig.</li>
</ul>
<p><strong>v0.163.1 / 0.163.2 / 0.163.3 (11–18 июня)</strong> — патчи:</p>
<ul>
<li>Security: bump <code>golang.org/x/image</code>, нормализация integer-IPv4 в <code>http.urls</code>, отказ от symlink в <code>os.ReadDir/ReadFile/Stat/FileExists</code>.</li>
<li>Escape <code>lang</code> в дефолтном рендере код-блоков; сохранение non-ASCII пробела после summary-разделителя; поддержка вариантов конфигов babel/postcss; фикс регрессии коллизии имён page/section.</li>
</ul>
<p>Дальше — что это значит на практике.</p>
<h2 class="heading" id="avif-третий-формат-в-pipeline-">
  AVIF: третий формат в pipeline 🖼️
  <a class="anchor" href="#avif-%d1%82%d1%80%d0%b5%d1%82%d0%b8%d0%b9-%d1%84%d0%be%d1%80%d0%bc%d0%b0%d1%82-%d0%b2-pipeline-">#</a>
</h2>
<p>Главная фича квартала. В 0.162.0 Hugo получил полноценную обработку AVIF — и кодер, и декодер (issue <a href="https://github.com/gohugoio/hugo/issues/7837">#7837</a>, висел с 2020 года). AVIF при том же визуальном качестве заметно легче JPEG и обычно легче WebP, плюс умеет HDR (у Hugo есть <a href="https://github.com/bep/hdrsdr.com">демо-сайт</a>, который имеет смысл смотреть на HDR-экране вроде Retina).</p>
<p>В 0.163.0 фичу довели до ума:</p>
<ul>
<li>Дефолтный <code>quality</code> для AVIF опустили до <strong>60</strong> — оказалось, JPEG/WebP с q75 сопоставимы с AVIF q60. Не гоните качество вверх по привычке.</li>
<li>Появился <code>hint</code> (как у WEBP): для <code>lossy</code> фото по умолчанию используется YUV420 chroma subsampling, а для текста/иконок/схем — 444. Это резко снижает память на кодирование больших картинок.</li>
<li>Качество можно задавать <strong>на формат</strong>:</li>
</ul>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">imaging</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="c"># глобальный quality теперь deprecated</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">imaging</span><span class="p">.</span><span class="nx">webp</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">quality</span> <span class="p">=</span> <span class="mi">80</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">imaging</span><span class="p">.</span><span class="nx">avif</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">quality</span> <span class="p">=</span> <span class="mi">60</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hint</span>    <span class="p">=</span> <span class="s2">&#34;photo&#34;</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>В шаблоне AVIF получается так же, как WebP — через <code>.Process</code> / метод ресайза:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">$img</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">resources</span><span class="na">.Get</span><span class="w"> </span><span class="s">&#34;img/cover.jpg&#34;</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">$avif</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">$img</span><span class="na">.Process</span><span class="w"> </span><span class="s">&#34;resize 1600x avif&#34;</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">picture</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">source</span> <span class="na">srcset</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$avif</span><span class="na">.RelPermalink</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;image/avif&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">source</span> <span class="na">srcset</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="o">(</span><span class="nx">$img</span><span class="na">.Process</span><span class="w"> </span><span class="s">&#34;resize 1600x webp&#34;</span><span class="o">)</span><span class="na">.RelPermalink</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;image/webp&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="o">(</span><span class="nx">$img</span><span class="na">.Resize</span><span class="w"> </span><span class="s">&#34;1600x&#34;</span><span class="o">)</span><span class="na">.RelPermalink</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;…&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">picture</span><span class="p">&gt;</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p><strong>Что это значит для нашего блога.</strong> Раздел <code>/photos</code> уже генерит 800/1600/2400 px в JPEG + WebP и отдаёт через <code>&lt;picture&gt;</code>/<code>srcset</code>. Добавить AVIF — это правка шаблона, который собирает <code>&lt;source&gt;</code>-ы (плюс per-format <code>quality</code> в <code>hugo.toml</code>): первым <code>&lt;source type=&quot;image/avif&quot;&gt;</code>, дальше WebP, в конце JPEG-фолбэк. Браузеры берут первый поддерживаемый. На фотографиях с retina это даст ощутимую экономию трафика при том же качестве. В этот заход не делаю — но это очевидный следующий шаг для фото-ленты.</p>
<h2 class="heading" id="хардненинг-безопасности-">
  Хардненинг безопасности 🔒
  <a class="anchor" href="#%d1%85%d0%b0%d1%80%d0%b4%d0%bd%d0%b5%d0%bd%d0%b8%d0%bd%d0%b3-%d0%b1%d0%b5%d0%b7%d0%be%d0%bf%d0%b0%d1%81%d0%bd%d0%be%d1%81%d1%82%d0%b8-">#</a>
</h2>
<p>Самая «breaking» часть. Изменения копились через все релизы:</p>
<p><strong>Node-инструменты под <code>--permission</code> (0.161.0).</strong> PostCSS, Babel и TailwindCSS теперь запускаются с Node permission-флагом и правами из <code>security.node.permissions</code>. Следствия:</p>
<ul>
<li>нужен <strong>Node ≥ 22</strong>;</li>
<li><code>css.TailwindCSS</code> требует Tailwind CLI как <strong>npm-пакет</strong> — standalone-бинарь больше не годится.</li>
</ul>
<p>Если вы собираете CSS через Hugo Pipes с Tailwind/PostCSS — это первое, что стоит проверить перед обновлением.</p>
<p><strong>Запрет <code>text/html</code>-контента по умолчанию (0.162.0).</strong> Новая политика <code>security.allowContent</code> гейтит, какие media-типы допустимы для страниц в <code>/content</code>. <code>text/html</code> теперь <strong>запрещён по умолчанию</strong>. Сайты, где есть руками написанные <code>.html</code>-файлы в <code>content/</code> (или их эмитит content adapter), вернут доступ явно:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">security</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">allowContent</span> <span class="p">=</span> <span class="p">[</span><span class="s1">&#39;.*&#39;</span><span class="p">]</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p><strong>Прочее (0.162.0 → 0.163.3):</strong></p>
<ul>
<li><code>security.http.urls</code> перепроверяется на <strong>каждом</strong> редирект-хопе в <code>resources.GetRemote</code> — мимо валидации больше не проскочить через 30x.</li>
<li>Symlink-записи отклоняются в <code>resources.Get</code>, а в 0.163.1 — ещё и в <code>os.ReadDir/ReadFile/Stat/FileExists</code>.</li>
<li>Go подняли до 1.26.3 с фиксами XSS в <code>html/template</code> (CVE-2026-39826, CVE-2026-39823) — это касается любого сайта, рендерящего недоверенный ввод в шаблонах.</li>
</ul>
<p>Любопытная ремарка от мейнтейнера в release notes 0.163.1: всплеск security-репортов в последнее время — это не «Hugo стал менее безопасным», а работа новых AI-инструментов, которые берут <a href="https://gohugo.io/about/security/">security-модель Hugo</a> за baseline и методично её простукивают.</p>
<h2 class="heading" id="slice-based-permalinks-">
  Slice-based permalinks 🔗
  <a class="anchor" href="#slice-based-permalinks-">#</a>
</h2>
<p>В 0.161.0 конфигурация <code>permalinks</code> стала гибче (старая map-форма продолжает работать). Теперь это список с теми же <code>target</code>-матчерами, что и в <code>cascade</code>:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">permalinks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">target</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">page</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/books/**&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">pattern</span><span class="p">:</span><span class="w"> </span><span class="l">/books/:year/:slug/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">target</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">page</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">pattern</span><span class="p">:</span><span class="w"> </span><span class="l">/other/:slug/</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Удобно, когда для разных секций нужны разные схемы URL без дублирования настроек. Нашему блогу с плоской структурой <code>posts/</code> это пока избыточно, но знать про механизм полезно — особенно если когда-нибудь заведём разделы со своими правилами URL.</p>
<h2 class="heading" id="гибкие-идентификаторы-в-именах-файлов-">
  Гибкие идентификаторы в именах файлов 🏷️
  <a class="anchor" href="#%d0%b3%d0%b8%d0%b1%d0%ba%d0%b8%d0%b5-%d0%b8%d0%b4%d0%b5%d0%bd%d1%82%d0%b8%d1%84%d0%b8%d0%ba%d0%b0%d1%82%d0%be%d1%80%d1%8b-%d0%b2-%d0%b8%d0%bc%d0%b5%d0%bd%d0%b0%d1%85-%d1%84%d0%b0%d0%b9%d0%bb%d0%be%d0%b2-">#</a>
</h2>
<p>Тоже из 0.161.0. Раньше язык кодировался как <code>mypost.en.md</code>. Теперь доступна расширяемая схема префиксов в имени файла:</p>
<table>
	<thead>
			<tr>
					<th>Префикс</th>
					<th>Что задаёт</th>
					<th>Где применимо</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>_language_</code></td>
					<td>Язык</td>
					<td>Контент и layout-файлы</td>
			</tr>
			<tr>
					<td><code>_role_</code></td>
					<td>Роль</td>
					<td>Контент и layout-файлы</td>
			</tr>
			<tr>
					<td><code>_version_</code></td>
					<td>Версия</td>
					<td>Контент и layout-файлы</td>
			</tr>
			<tr>
					<td><code>_outputformat_</code></td>
					<td>Output format</td>
					<td>Layout-файлы</td>
			</tr>
			<tr>
					<td><code>_kind_</code></td>
					<td>Page kind</td>
					<td>Layout-файлы</td>
			</tr>
	</tbody>
</table>
<p><code>content/mypost._language_en_.md</code> эквивалентно <code>content/mypost.en.md</code>, но открывает дорогу к версиям и ролям контента. Для одноязычного блога — мимо, для документации с версиями — заметное упрощение.</p>
<h2 class="heading" id="cssbuild-вложенные-vars-">
  css.Build: вложенные vars 🎨
  <a class="anchor" href="#cssbuild-%d0%b2%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%bd%d1%8b%d0%b5-vars-">#</a>
</h2>
<p>Развитие фичи из 0.160. К <code>@import &quot;hugo:vars&quot;</code> добавились <strong>вложенные</strong> наборы — <code>@import &quot;hugo:vars/&lt;name&gt;&quot;</code>. Классический сценарий — палитры под <code>prefers-color-scheme</code>:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">style</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">primary</span>    <span class="p">=</span> <span class="s2">&#34;#000000&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">background</span> <span class="p">=</span> <span class="s2">&#34;#ffffff&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">dark</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">primary</span>    <span class="p">=</span> <span class="s2">&#34;#ffffff&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">background</span> <span class="p">=</span> <span class="s2">&#34;#000000&#34;</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;hugo:vars&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;hugo:vars/dark&#34;</span> <span class="o">(</span><span class="nt">prefers-color-scheme</span><span class="o">:</span> <span class="nt">dark</span><span class="o">)</span><span class="p">;</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Аккуратно, но не для нас: переключатель дня/ночи на блоге работает через JS и <code>localStorage</code> (явный выбор пользователя, без авто), а не через <code>prefers-color-scheme</code>. Палитра живёт в <code>:root</code>. Так что фича в копилку «знать», но не «внедрять».</p>
<h2 class="heading" id="депрекейты-и-мелочи">
  Депрекейты и мелочи
  <a class="anchor" href="#%d0%b4%d0%b5%d0%bf%d1%80%d0%b5%d0%ba%d0%b5%d0%b9%d1%82%d1%8b-%d0%b8-%d0%bc%d0%b5%d0%bb%d0%be%d1%87%d0%b8">#</a>
</h2>
<ul>
<li><strong><code>.IsNode</code> → <code>.IsBranch</code></strong> (0.163.0): если в шаблонах есть <code>.IsNode</code>, мигрируйте на <code>.IsBranch</code> (issue <a href="https://github.com/gohugoio/hugo/issues/11574">#11574</a>).</li>
<li><strong>Глобальный <code>imaging.quality</code></strong> (0.163.0): помечен deprecated в пользу per-format (<code>[imaging.webp]</code>, <code>[imaging.avif]</code>, …). Старая форма пока работает и даже не всегда ругается — но направление задано.</li>
<li><strong>jsconfig <code>baseUrl</code></strong> убран (был deprecated).</li>
<li><strong>Сохранение non-ASCII пробела после summary-разделителя</strong> (0.163.3) — мелочь, но именно для кириллических текстов с `</li>
</ul>
<p>` это правильное поведение.</p>
<ul>
<li><strong>Фикс коллизии имён page/section</strong> (0.163.3, <a href="https://github.com/gohugoio/hugo/issues/15046">#15046</a>) — регрессия, всплывшая в 0.163.x; ещё одна причина брать самый свежий патч, а не 0.163.0.</li>
</ul>
<h2 class="heading" id="что-это-значит-для-нашего-блога">
  Что это значит для нашего блога
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d0%b7%d0%bd%d0%b0%d1%87%d0%b8%d1%82-%d0%b4%d0%bb%d1%8f-%d0%bd%d0%b0%d1%88%d0%b5%d0%b3%d0%be-%d0%b1%d0%bb%d0%be%d0%b3%d0%b0">#</a>
</h2>
<p>Прогнал блог на 0.163.3 — собирается чисто, <strong>90 страниц, 0 ошибок</strong>, 52 изображения обработаны без проблем. Конкретика:</p>
<ul>
<li><strong>Запрет <code>text/html</code></strong> нас не задел — в <code>content/</code> нет руками-сверстанных <code>.html</code>, всё в Markdown. <code>security.allowContent</code> трогать не нужно.</li>
<li><strong>Node-permission</strong> мимо — Tailwind/PostCSS через Pipes не используем, Pagefind живёт отдельным npm-инструментом.</li>
<li><strong><code>languageCode</code> → <code>locale</code></strong>: попутно почистил deprecation в <code>hugo.toml</code> (<code>languageCode = 'ru-RU'</code> → <code>locale = 'ru-RU'</code>). Этот warning ушёл. Оставшиеся три (<code>.Site.LanguageCode</code>, <code>.Language.LanguageCode</code>, <code>.Language.Direction</code>) приходят из шаблонов темы <code>typo</code> — уйдут, когда обновится тема.</li>
<li><strong>AVIF</strong> — главная зацепка на будущее: добавить третий <code>&lt;source&gt;</code> в фото-pipeline и срезать вес ленты <code>/photos</code>.</li>
</ul>
<h2 class="heading" id="чек-лист-обновления-">
  Чек-лист обновления ✅
  <a class="anchor" href="#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-">#</a>
</h2>
<ol>
<li>
<p><strong>Прыгайте сразу на 0.163.3</strong>, а не на 0.163.0 — патчи .1/.2/.3 закрывают security-дыры и регрессию коллизии имён.</p>
</li>
<li>
<p><strong>Если собираете CSS через Tailwind/PostCSS/Babel в Pipes</strong> — поставьте Node ≥ 22 и Tailwind как npm-пакет; проверьте <code>security.node.permissions</code>.</p>
</li>
<li>
<p><strong>Если в <code>content/</code> есть <code>.html</code>-файлы</strong> — добавьте <code>security.allowContent = ['.*']</code>, иначе страницы выпадут из сборки.</p>
</li>
<li>
<p><strong>Почистите депрекейты:</strong></p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># languageCode → locale в конфиге</span>
</span></span><span class="line"><span class="cl"><span class="c1"># .IsNode → .IsBranch в шаблонах</span>
</span></span><span class="line"><span class="cl">grep -rn <span class="s1">&#39;\.IsNode&#39;</span> layouts/ themes/</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
</li>
<li>
<p><strong>Если работаете с изображениями</strong> — присмотритесь к AVIF и per-format <code>quality</code>; глобальный <code>imaging.quality</code> уводите в <code>[imaging.&lt;format&gt;]</code>.</p>
</li>
<li>
<p><strong>Стандартный прогон</strong> и сверка warning&rsquo;ов до/после:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hugo --minify --gc</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
</li>
</ol>
<h2 class="heading" id="итог-">
  Итог 🧠
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3-">#</a>
</h2>
<p>0.161–0.163 — это квартал «инфраструктурного взросления» Hugo: новый формат изображений (AVIF), методичный security-хардненинг и более гибкая конфигурация (permalinks, идентификаторы файлов, вложенные CSS-vars). Громкая фича ровно одна — AVIF; всё остальное либо про безопасность, либо про удобство.</p>
<p>Breaking changes реальны, но адресные: они бьют по сайтам с Tailwind-в-Pipes и с HTML-контентом в <code>content/</code>. Если вы (как наш блог) пишете на чистом Markdown и не гоняете Node-сборку CSS — переход бесшовный, достаточно поднять версию и пробежаться по депрекейтам.</p>
<p>Если сидите на 0.160 или раньше — есть смысл прыгать сразу на 0.163.3, мимо промежуточных: так вы заодно получите все security-фиксы одним движением.</p>
<h2 class="heading" id="источники">
  Источники
  <a class="anchor" href="#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8">#</a>
</h2>
<ul>
<li>Релиз 0.161.0: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.161.0">gohugoio/hugo v0.161.0</a></li>
<li>Релиз 0.162.0: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.162.0">gohugoio/hugo v0.162.0</a></li>
<li>Релиз 0.163.0: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.163.0">gohugoio/hugo v0.163.0</a></li>
<li>Релиз 0.163.3: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.163.3">gohugoio/hugo v0.163.3</a></li>
<li>Конфигурация AVIF: <a href="https://gohugo.io/configuration/imaging/#avif">gohugo.io/configuration/imaging/#avif</a></li>
<li>Security-модель Hugo: <a href="https://gohugo.io/about/security/">gohugo.io/about/security/</a></li>
</ul>]]></content:encoded><category>hugo</category><category>релизы</category></item><item><title>Пять проходов impeccable по блогу: что AI-инструмент дизайна реально умеет 🛠</title><link>https://kushnaren.co/posts/2026-04-28-pyat-prokhodov-impeccable-na-bloge/</link><pubDate>Tue, 28 Apr 2026 21:30:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/2026-04-28-pyat-prokhodov-impeccable-na-bloge/</guid><description>Личный отчёт о работе с AI-инструментом impeccable на Hugo-блоге: как он устроен, что меняет в процессе, кейс с фиктивным компонентом и набор уроков, которые стоят дороже самих правок.</description><content:encoded><![CDATA[<h2 class="heading" id="что-это-вообще-такое">
  Что это вообще такое
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d1%82%d0%b0%d0%ba%d0%be%d0%b5">#</a>
</h2>
<p>Если ты когда-нибудь просил AI-помощника (Claude, ChatGPT, Cursor) сделать
тебе сайт, ты замечал одно и то же: получается вроде неплохо, но
подозрительно «как у всех». Тёмно-синий фон, фиолетовый градиент в
заголовке, карточки с лёгкой тенью, шрифт Inter. Узнаваемый «AI-look».</p>
<p>Причина банальная: большие языковые модели учились на одних и тех же
типичных шаблонах. Без отдельных указаний они выдают усреднённый
дизайн, в котором повторяются одни и те же стандартные ошибки:
градиент в заголовке через <code>background-clip</code>, серый текст на цветном
фоне, бесконечные одинаковые карточки в сетке, ноль характера.</p>
<p><a href="https://impeccable.style">Impeccable</a> от Paul Bakaus решает именно эту
задачу. Сам автор позиционирует его как <strong>«дизайн-словарь для
AI-помощников по коду»</strong>: набор из одного навыка и 23 команд, которые
дают AI и человеку общий понятийный аппарат. Каждая команда отвечает
за свою дизайн-дисциплину: типографика, цвет, ритм пространства,
взаимодействия, копирайтинг, элевация (тени и слои), анти-паттерны.
Плюс отдельный список из 25 типичных «AI-ляпов», которые
детектор ловит автоматически.</p>
<p>Что важно: impeccable — не библиотека CSS-токенов и не редактор
макетов. Он живёт в твоём боевом коде, читает существующие исходники,
рендеренный HTML, дизайн-документы, и вместе с AI правит то, что уже
есть. Не «нарисуй мне сайт с нуля», а «доведи существующий до
состояния, в котором он перестанет быть похожим на любой другой
блог».</p>
<p>Я взялся применить это к собственному Hugo-блогу. Hugo — статический
генератор сайтов: ты пишешь посты в Markdown, конфиг и HTML-шаблоны,
он собирает готовые HTML-файлы. Дизайн жил в одном CSS-файле плюс
тема <code>typo</code> как git submodule. К апрелю накопилось ощущение
«работает, но как-то не так». Тёмная тема жила в одной палитре,
светлая — местами в другой. Страница «Обо мне» выглядела как
кусочек чужого SaaS-сайта, попавший в блог по ошибке.</p>
<p>Решил пройти все нужные команды и посмотреть, что инструмент реально
ловит. Получилось пять заходов за вечер.</p>
<h2 class="heading" id="фундамент-productmd-и-designmd">
  Фундамент: PRODUCT.md и DESIGN.md
  <a class="anchor" href="#%d1%84%d1%83%d0%bd%d0%b4%d0%b0%d0%bc%d0%b5%d0%bd%d1%82-productmd-%d0%b8-designmd">#</a>
</h2>
<p>Первая команда — <code>/teach</code>. Это не «настройки», а короткое интервью:
кому пишешь, каким голосом, чем не должен быть сайт, какие
требования к доступности. На выходе два файла в корне проекта:</p>
<ul>
<li><strong>PRODUCT.md</strong> — стратегический документ. Регистр (бренд или
продукт), целевая аудитория, голос, анти-референсы, дизайн-принципы,
планка доступности.</li>
<li><strong>DESIGN.md</strong> — визуальная спецификация. Цвета, шрифты, шкала
пространства, шкала радиусов, компоненты.</li>
</ul>
<p>Эти два файла потом читает каждая последующая команда. Без них
любой <code>/audit</code> или <code>/polish</code> даёт ответы вообще, не привязанные к
конкретному проекту.</p>
<p>Главное открытие здесь — раздел <strong>анти-референсов</strong>. PRODUCT.md
заставляет ответить, чем сайт <strong>не</strong> должен быть. Я выписал четыре
конкретных образца, которые не хочу повторять:</p>
<ul>
<li>Habr и VC.ru с их боковыми панелями, бейджами и блоками «читайте
также» на пять экранов;</li>
<li>Medium и Substack: безликий шаблон платформы, попап «подпишись», где
ощущение «это любой блог»;</li>
<li>современный SaaS с градиентами и стеклянными карточками;</li>
<li>Dev.to и обычные GitHub Pages: стандартный markdown без типографики,
синие ссылки, sans-serif, ноль характера.</li>
</ul>
<p>Анти-референсы оказались полезнее обычных «делай как Stripe». «Как
Stripe» — растяжимое: можно повторить любой кусок и сказать «вот
видишь, похоже». «Не как Habr» — конкретный отказ. Каждое следующее
дизайн-решение проверялось по этому списку и часто не проходило: «а
не повторяю ли я сейчас Habr-ленту одинаковых превью?»</p>
<p>Дальше команда <code>/document</code> отсканировала существующий CSS и собрала
DESIGN.md. Здесь я первый раз ошибся — но об этом дальше.</p>
<h2 class="heading" id="аудит-11-из-20-на-старте-">
  Аудит: 11 из 20 на старте 📊
  <a class="anchor" href="#%d0%b0%d1%83%d0%b4%d0%b8%d1%82-11-%d0%b8%d0%b7-20-%d0%bd%d0%b0-%d1%81%d1%82%d0%b0%d1%80%d1%82%d0%b5-">#</a>
</h2>
<p><code>/audit</code> — техническая проверка по пяти направлениям, каждое от 0
до 4 баллов: доступность, производительность, согласованность
дизайн-токенов, адаптивность, отсутствие шаблонных AI-ляпов.</p>
<p>Финальная оценка моего блога — <strong>11 из 20</strong>. Между «нормально, но
работы много» и «плохо». От «плохо» вытащило только производительность
(Hugo и так быстрый, картинки грузятся лениво, шрифты с правильной
стратегией загрузки).</p>
<p>Что нашлось:</p>
<p><strong>Контраст вторичного текста проваливал стандарт WCAG AA в обеих
темах.</strong> Цвет <code>#6272a4</code> на светлом фоне <code>#f8f8f2</code> давал соотношение
контраста 3.92:1, на тёмном <code>#282a36</code> — 3.33:1. Стандарт WCAG AA
требует минимум 4.5:1 для основного текста. Эта переменная использовалась
одновременно как цвет вторичного текста, как цвет ссылок в светлой
теме и как мета-информация. Поражено больше 40% не-основного контента
сайта.</p>
<p><strong>Страница поиска в светлой теме выдавала белый текст на белом фоне.</strong>
В моём CSS заголовки результатов поиска имели цвет <code>#f8f8f2</code>
(захардкожен под тёмную тему). На светлой теме фон тоже <code>#f8f8f2</code> —
белый на белом. Я этого не замечал, потому что почти всегда сижу в
тёмной теме. Половина посетителей видела пустые результаты поиска
полтора года.</p>
<p><strong>Страница «Обо мне» — типичный SaaS-клон.</strong> Шрифт Inter (один из
самых заезженных AI-выборов), фон с линейным градиентом, тяжёлая
тень под аватаром. Все четыре анти-референса из PRODUCT.md в одном
экране.</p>
<p><strong>Кликабельные элементы меньше 44 на 44 пикселей.</strong> Кнопка
переключения темы — 32–35 пикселей. У ссылок в навигации не было
внутренних отступов, кликабельная зона = площадь самого текста.
Стандарт WCAG 2.1 рекомендует минимум 44×44 для тача и моторной
доступности.</p>
<p><strong>У цитат <code>border-left</code> в 2 пикселя.</strong> Эта самая полоска слева у
«цитаты». В философии impeccable любая боковая полоска шире 1
пикселя — отдельный анти-паттерн (типичный «callout-style» из
старых книг по веб-дизайну).</p>
<p>Семь крупных проблем плюс несколько помельче. Чинилось всё в одном
проходе: подняли цвет вторичного текста до читаемого, переписали
страницу «Обо мне» с нуля без шрифта Inter и градиентов, сделали
все статусы reading-секции чувствительными к теме, расширили
кликабельные зоны. После — оценка поднялась до 18 из 20.</p>
<p>Без <code>/audit</code> я бы половину этих проблем не нашёл. Особенно поиск в
светлой теме: я не садился в светлую тему вживую неделями.</p>
<h2 class="heading" id="ux-ревью-и-кейс-с-фиктивным-компонентом">
  UX-ревью и кейс с фиктивным компонентом
  <a class="anchor" href="#ux-%d1%80%d0%b5%d0%b2%d1%8c%d1%8e-%d0%b8-%d0%ba%d0%b5%d0%b9%d1%81-%d1%81-%d1%84%d0%b8%d0%ba%d1%82%d0%b8%d0%b2%d0%bd%d1%8b%d0%bc-%d0%ba%d0%be%d0%bc%d0%bf%d0%be%d0%bd%d0%b5%d0%bd%d1%82%d0%be%d0%bc">#</a>
</h2>
<p>Дальше — <code>/critique</code>. Это уже не технический аудит, а ревью на
уровне дизайн-решений: правильно ли устроена главная как точка входа,
не теряется ли пост в ленте, нужен ли архив-по-годам, есть ли узкие
места в навигации.</p>
<p>Команда устроена так, что запускает <strong>два независимых ревью
одновременно</strong>. Один — отдельный AI-агент с фокусом на
пользовательский опыт; читает исходники и рендеренный HTML.
Второй — программный детектор <code>npx impeccable --json</code>, проверяющий
25 конкретных шаблонных AI-ляпов автоматически. И ключевое: они
<strong>не видят выводы друг друга</strong>. Это сделано специально — чтобы один
не подтверждал догадки другого.</p>
<p>Я к этому отнёсся скептически. Думал, что изоляция «для красоты».</p>
<p>Оказался неправ.</p>
<p>Отдельный AI-агент за пять минут нашёл вот что: в моей же DESIGN.md,
которую я сам написал часом ранее, описан <strong>боковой sticky-блок
«Содержание» как одна из ключевых деталей сайта</strong>. Подробно: фиксированное
позиционирование, ширина 260 пикселей, рамка, скругление, размеры
ссылок, активное состояние. И сразу следом: «один из главных
компонентов сайта; обслуживает чтение длинных постов вторым по
важности после самого текста».</p>
<p>А в <code>layouts/_default/single.html</code> (мой собственный шаблон страницы
поста) этого блока <strong>нет</strong>. Поиск по проекту: ноль вхождений
<code>toc-sidebar</code>, <code>toc-sticky</code>, <code>TableOfContents</code>. Только 190 строк
CSS-стилей, который никем не используется, потому что HTML-разметку
никто не выводит. Документация описывала то, чего на сайте не было.</p>
<p>Что произошло: при первом проходе <code>/document</code> я смотрел в CSS,
увидел проработанный sticky-блок и описал его в DESIGN.md как
рабочий компонент. Не проверил, что в шаблонах кто-то этот класс
выводит. CSS живёт, разметки нет — а я этого не заметил, потому что
неудобно увидеть, что я только что подробно описал то, чего не
существует.</p>
<p>Это та самая «слепая зона», когда видишь в собственной работе только
то, что её подтверждает. Я писал код, я писал документацию по этому
коду. Свою же работу мне «удобно» считать согласованной.</p>
<p>Отдельный AI-агент в <code>/critique</code> не видел ни моих рассуждений по
команде <code>/document</code>, ни моей DESIGN.md как авторитетного источника.
Он читал её как один из файлов проекта. И тут же подсветил
расхождение: «sticky TOC задокументирован как ключевой элемент,
не рендерится ни на одной странице».</p>
<p>Этот один пункт окупил весь подход с двумя независимыми ревью.
Я зашил это в персональную память Claude Code как правило: <strong>прежде
чем называть компонент ключевым в DESIGN.md, надо проверить, что он
действительно рендерится в шаблонах, а не только в стилях</strong>. Стилевой
блок без HTML-разметки — мёртвый код, не «компонент».</p>
<p>После этого <code>/critique</code> нашёл ещё несколько пунктов помельче:</p>
<ul>
<li>листинг прочитанных книг сделан карточной сеткой — это прямой
анти-референс из моей же PRODUCT.md;</li>
<li>три параллельных шаблона шапки и подвала на разных страницах с
чуть разным поведением;</li>
<li>текст страницы «Обо мне» написан в типичном LinkedIn-стиле:
«Я исследую пересечения технологий, мышления и практики», «Автор ·
Инженер · Исследователь» — строго против моего же голоса;</li>
<li>в публичной копии сайта засветилось имя поискового движка
(«Pagefind»), хотя пользователю это знать не нужно.</li>
</ul>
<p>Всё, кроме текста «Обо мне», починили в этом же сеансе. Текст —
это переписывание личного голоса, я сделал черновик, но коммитить не
стал.</p>
<h2 class="heading" id="полировка-деталей-">
  Полировка деталей 🎨
  <a class="anchor" href="#%d0%bf%d0%be%d0%bb%d0%b8%d1%80%d0%be%d0%b2%d0%ba%d0%b0-%d0%b4%d0%b5%d1%82%d0%b0%d0%bb%d0%b5%d0%b9-">#</a>
</h2>
<p>После <code>/critique</code> оставшиеся три команды — это работа другого
характера. Не «найди и почини», а «доведи до состояния, в котором
детали перестают раздражать».</p>
<p>Команда <code>/harden</code> («закалка») закрыла прямой пробел: на месте
английской заглушки темы «typo? Click here» теперь нормальная
страница 404 на русском с тремя осмысленными путями. Локализовал
раздел тегов: хлебные крошки теперь говорят «Главная / Теги», а не
«Tags». Расширил поддержку настройки «уменьшить движение» в
операционной системе — теперь обнуляются все плавные переходы, не
только прокрутка. Защитился от очень длинных заголовков (если в
название поста попадёт длинная ссылка без пробелов, сетка не
поедет).</p>
<p>Команда <code>/polish</code> («полировка») добавила состояния «при наведении» и
«при фокусе с клавиатуры» там, где их не было: теги статьи, ссылки
на соцсети в шапке главной, навигация пагинации, ссылки между
постами, кнопка копирования кода. Каждая ссылка теперь визуально
откликается на наведение через смену цвета — без сдвига, без тени,
по принципу «никакой фальшивой глубины» из моей же DESIGN.md.</p>
<p>Команда <code>/typeset</code> (типографика) — самая «невидимая» работа. Включил
кернинг (профессиональная регулировка расстояний между парами букв)
и лигатуры (где две буквы соединяются в один знак — <code>fi</code>, <code>fl</code>).
Включил табличные цифры на датах в ленте: цифры теперь одной ширины
и в столбце выстраиваются штамп-в-штамп. Добавил мягкие переносы
русских слов внутри статьи (без них длинные слова рвали строку
абзаца на узкой колонке). Тонко уплотнил трекинг (межбуквенный
интервал) на крупных заголовках 28-44 пикселя — Literata в больших
размерах без лёгкого негативного трекинга «дребезжит» как
кубики, с ним читается как один жест.</p>
<p>Каждая из тройки — 30-60 минут работы. Вместе они подняли качество
с «работает» до «приятно читать».</p>
<h2 class="heading" id="что-я-понял-про-инструмент">
  Что я понял про инструмент
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%8f-%d0%bf%d0%be%d0%bd%d1%8f%d0%bb-%d0%bf%d1%80%d0%be-%d0%b8%d0%bd%d1%81%d1%82%d1%80%d1%83%d0%bc%d0%b5%d0%bd%d1%82">#</a>
</h2>
<p><strong>Импеккейбл работает с локальными файлами, не с задеплоенным сайтом.</strong>
Команды читают исходники в репозитории, рендеренный HTML после
локальной сборки Hugo, и оба дизайн-документа. Прода нет ни в одной
команде, кроме <code>/live</code> (интерактивный режим в браузере), которую я
не использовал. То есть весь дизайн-цикл идёт у меня на ноутбуке, и
выкладка в боевой репозиторий — финальный шаг, не источник истины.</p>
<p><strong>AI хорошо ловит противоречия в собственной работе, если ему дать
независимое ревью.</strong> Изоляция в <code>/critique</code> нужна не для эстетики
процесса. Самопроверка у языковой модели страдает тем же эффектом
подтверждения собственной правоты, что и у человека: ты видишь в
своей работе то, что её обосновывает, и не видишь того, что её
опровергает. Отдельный AI-агент в новой сессии, без моих рассуждений
в его контексте, читал DESIGN.md как просто один из файлов и тут
же поймал расхождение с шаблонами.</p>
<p><strong>Анти-референсы выстреливают сильнее, чем референсы.</strong> «Не как
Habr» — конкретный отказ, проверяемый автоматически. «Как Stripe» —
растяжимое, можно подогнать любой результат. PRODUCT.md заставляет
выписать четыре-пять анти-референсов, и каждое решение проверяется
против них.</p>
<p><strong>Метрики важнее ощущений.</strong> <code>/audit</code> дал 11 из 20. Это конкретное
число. После правок — 19. Я знаю, на сколько лучше. Бессчётный
«прогресс» — самообман: правишь, правишь, чувствуешь, что лучше. С
оценкой видно, что иногда нет.</p>
<p><strong>Убывающая отдача наступает быстро.</strong> После пяти проходов половина
команд impeccable стали неуместными для моего блога. <code>/animate</code>,
<code>/delight</code>, <code>/bolder</code>, <code>/overdrive</code> — прямо конфликтуют с принципом
«без подкупа» из PRODUCT.md. <code>/quieter</code>, <code>/distill</code> — сайт уже
плоский и сдержанный, дальше пустая страница. <code>/colorize</code> — палитра
закрыта на 13 цветах. <code>/optimize</code> — Hugo уже выжат до предела.</p>
<p>Это знак, что инструмент сделал работу. Дальше — пиши посты, не
правь дизайн.</p>
<h2 class="heading" id="где-ai-ошибся-а-где-я">
  Где AI ошибся, а где я
  <a class="anchor" href="#%d0%b3%d0%b4%d0%b5-ai-%d0%be%d1%88%d0%b8%d0%b1%d1%81%d1%8f-%d0%b0-%d0%b3%d0%b4%d0%b5-%d1%8f">#</a>
</h2>
<p>За эту сессию я дважды слишком уверенно объявил причину явления на
основании одного наблюдения. Это вообще частая ошибка и в инженерии,
и в науке: одна точка наблюдений — не закономерность, это просто
точка.</p>
<p><strong>Первый случай.</strong> Один сборочный билд занял 9 минут вместо обычных
40-50 секунд. Я уверенно сказал: причина — что я только что добавил в
подвал отметку реального времени сборки. Каждая страница теперь
содержит уникальный текст в подвале, значит, утилита синхронизации
(rsync) с проверкой по контрольной сумме перезаливает все 227
страниц целиком.
Технически красивое объяснение, инженерно стройное. И <strong>неверное</strong>.</p>
<p>Следующий билд с той же отметкой времени занял 40 секунд. То есть
9-минутный билд был случайным выбросом — холодный старт исполнителя,
сетевой сбой, что-то сторонне. Я экстраполировал из одной точки и
готов был «починить» проблему, которой нет.</p>
<p><strong>Второй случай.</strong> Я случайно удалил пустой файл <code>content/posts/.md</code>
(артефакт от прошлой попытки автоматического создания поста с битым
именем) после того, как назвал его «пустой стуб». Не прочитал
содержимое перед удалением. Файл был не пустой — 101 байт. После
удаления выяснилось, что в нём всё-таки лежал какой-то фронтматтер
(служебный заголовок поста с метаданными), которого я не увидел до
действия.</p>
<p>Оба случая зашиты в персональную память:</p>
<ul>
<li><strong>Перед удалением читать каждый файл.</strong> Не отслеживается ли git —
не значит «можно выкинуть».</li>
<li><strong>Прежде чем уверенно объявлять причину явления — собрать минимум
две сравнимых точки.</strong> Один выброс ≠ закономерность.</li>
</ul>
<p>Особенно полезно для вопросов производительности, где «фикс
несуществующей проблемы» — это напрасные телодвижения и потеря
доверия к собственным выводам.</p>
<h2 class="heading" id="когда-импеккейбл-не-нужен">
  Когда импеккейбл не нужен
  <a class="anchor" href="#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d0%b8%d0%bc%d0%bf%d0%b5%d0%ba%d0%ba%d0%b5%d0%b9%d0%b1%d0%bb-%d0%bd%d0%b5-%d0%bd%d1%83%d0%b6%d0%b5%d0%bd">#</a>
</h2>
<ul>
<li>На бренде, который требует «как у всех»: внутренний инструмент,
служебная панель, прототип без идентичности.</li>
<li>Когда нет 30-60 минут на интервью <code>/teach</code> и сборку DESIGN.md.
Без фундамента остальные команды дают ответы вообще, не для тебя.</li>
<li>На короткой одностраничной странице — обычная ручная работа
быстрее.</li>
<li>Когда дизайн уже отлажен и работает — повторное прохождение часто
стоит дороже того, что находит.</li>
</ul>
<h2 class="heading" id="конкретные-числа">
  Конкретные числа
  <a class="anchor" href="#%d0%ba%d0%be%d0%bd%d0%ba%d1%80%d0%b5%d1%82%d0%bd%d1%8b%d0%b5-%d1%87%d0%b8%d1%81%d0%bb%d0%b0">#</a>
</h2>
<table>
	<thead>
			<tr>
					<th>Метрика</th>
					<th>До</th>
					<th>После</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Технический аудит (баллы)</td>
					<td>11/20</td>
					<td>19/20</td>
			</tr>
			<tr>
					<td>UX-ревью (баллы по 10 эвристикам Нильсена)</td>
					<td>24/40</td>
					<td>~32/40</td>
			</tr>
			<tr>
					<td>Цвет фейлит стандарт WCAG AA</td>
					<td>7 случаев</td>
					<td>0</td>
			</tr>
			<tr>
					<td>Параллельных дизайн-систем</td>
					<td>2 (основная + SaaS на «Обо мне»)</td>
					<td>1</td>
			</tr>
			<tr>
					<td>Захардкоженных шестнадцатеричных цветов вне токенов</td>
					<td>23+</td>
					<td>0</td>
			</tr>
			<tr>
					<td>Кликабельные зоны меньше 44×44</td>
					<td>5</td>
					<td>0</td>
			</tr>
			<tr>
					<td>Шрифт-ошибка (отсутствующий шрифт первым в стеке)</td>
					<td>да</td>
					<td>нет</td>
			</tr>
			<tr>
					<td>Боковой блок «Содержание» — задокументирован vs реально рендерится</td>
					<td>0 страниц</td>
					<td>все длинные посты</td>
			</tr>
			<tr>
					<td>Кастомная страница 404</td>
					<td>нет</td>
					<td>есть, по-русски</td>
			</tr>
			<tr>
					<td>Поддержка настройки «уменьшить движение»</td>
					<td>частичная</td>
					<td>полная</td>
			</tr>
	</tbody>
</table>
<h2 class="heading" id="итог">
  Итог
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3">#</a>
</h2>
<p>Импеккейбл не делает дизайн за тебя. Он даёт инструмент, через
который вы вместе с AI доводите дизайн до состояния, в котором вы
оба перестаёте делать стандартные ошибки. Аудиторская часть
(<code>/audit</code>, <code>/critique</code>) ловит сразу видимое. Доводочная часть
(<code>/harden</code>, <code>/polish</code>, <code>/typeset</code>) делает то, что в одиночку лень
довести до конца.</p>
<p>Стоимость: один вечер плюс умение зафиксировать собственный голос
в PRODUCT.md и DESIGN.md. Срок отдачи: пока продолжаешь редактировать
сайт. Каждое следующее обращение к AI («поправь шапку», «добавь
страницу архива», «перепиши блок») идёт на фоне этих двух файлов, и
шансы получить очередной AI-look уменьшаются.</p>
<p>Скрытая ценность инструмента в том, что он заставляет писать честно
о собственном проекте. Анти-референсы зашиты в PRODUCT.md и
зачитываются перед каждой правкой. Это ровно та дисциплина, которую
я никогда не наводил руками.</p>
]]></content:encoded><category>ai-инструменты</category><category>hugo</category></item><item><title>Qwen3-Coder-Next-MLX-4bit: Локальная разработка на Mac 🍏</title><link>https://kushnaren.co/posts/2026-04-24-qwen3-coder-next-mlx-4bit/</link><pubDate>Fri, 24 Apr 2026 12:00:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/2026-04-24-qwen3-coder-next-mlx-4bit/</guid><description>Подробный разбор Qwen3-Coder-Next-MLX-4bit для локальной разработки на Mac: формат MLX vs GGUF, требования к памяти, настройка через LM Studio, подключение через MCP к Codex CLI и честное сравнение с облачными моделями.</description><content:encoded><![CDATA[<h2 class="heading" id="зачем-держать-llm-локально-">
  Зачем держать LLM локально 🎯
  <a class="anchor" href="#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%b4%d0%b5%d1%80%d0%b6%d0%b0%d1%82%d1%8c-llm-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d1%8c%d0%bd%d0%be-">#</a>
</h2>
<p>Рабочий день разработчика всё больше обвязан вокруг подсказок от LLM: автодополнение, рефакторинг, объяснение чужого кода, генерация тестов. Облачные модели делают это качественно, но за это приходится платить тремя вещами: подпиской, скоростью соединения и доверием к провайдеру в части того, какой код вы туда отправляете.</p>
<p>Локальная модель снимает все три. Подписка обнуляется, latency определяется только железом, а код никуда не уходит за пределы ноутбука. Цена вопроса — модель меньше топовых облачных, чуть больше суеты с настройкой и постоянное место на диске.</p>
<p>В этом посте разбираемся с <strong>Qwen3-Coder-Next-MLX-4bit</strong>: квантованной версией кодовой модели от Alibaba, оптимизированной под Apple Silicon. Что это такое, как поставить, как подключить к Codex CLI и когда честно лучше остаться в облаке.</p>
<h2 class="heading" id="что-это-такое-и-почему-именно-эта-сборка">
  Что это такое и почему именно эта сборка
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-%d0%b8-%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d0%b8%d0%bc%d0%b5%d0%bd%d0%bd%d0%be-%d1%8d%d1%82%d0%b0-%d1%81%d0%b1%d0%be%d1%80%d0%ba%d0%b0">#</a>
</h2>
<p><strong>Qwen3-Coder</strong> — линейка моделей от Alibaba, специально натренированных на коде. Базовая модель — около 8 млрд параметров. Это не GPT-4-class, но для большинства повседневных задач (понять код, написать функцию, сгенерить тесты, отрефакторить) — более чем достаточно.</p>
<p><strong>MLX</strong> — относительно новый ML-фреймворк от Apple. Создан с нуля под Apple Silicon: использует unified memory (CPU и GPU делят одну RAM), zero-copy между ними, lazy evaluation. Для пользователя это значит, что модель работает заметно быстрее, чем тот же файл, запущенный через PyTorch с MPS-бэкендом.</p>
<p><strong>4-bit квантизация</strong> — каждое значение веса хранится в 4 битах вместо стандартных 16. Это уменьшает память в 4 раза с заметной, но обычно приемлемой потерей качества.</p>
<p>Важно не путать форматы: <strong>MLX</strong> хранит модели в собственном формате (<code>.safetensors</code> обёрнутый в MLX-структуру), а не в <code>.gguf</code> (это формат llama.cpp). При выборе модели в LM Studio это нужно учитывать: «MLX»-вариант и «GGUF»-вариант — это разные файлы для разных движков, не одно и то же.</p>
<p>Для <strong>Qwen3-Coder-Next-MLX-4bit</strong> характеристики такие:</p>
<ul>
<li><strong>Параметры:</strong> ~8B (тот же что и у не-квантованной версии).</li>
<li><strong>Формат:</strong> MLX (<code>.safetensors</code>).</li>
<li><strong>Размер на диске:</strong> ~5.5 GB.</li>
<li><strong>Контекст:</strong> 32 768 токенов.</li>
<li><strong>Минимум RAM:</strong> 16 GB (модель + накладные).</li>
<li><strong>Комфортный минимум:</strong> 24-32 GB (если ещё нужна работа с IDE и браузером параллельно).</li>
</ul>
<h2 class="heading" id="установка-через-lm-studio-">
  Установка через LM Studio 📦
  <a class="anchor" href="#%d1%83%d1%81%d1%82%d0%b0%d0%bd%d0%be%d0%b2%d0%ba%d0%b0-%d1%87%d0%b5%d1%80%d0%b5%d0%b7-lm-studio-">#</a>
</h2>
<p><a href="https://lmstudio.ai/">LM Studio</a> — desktop-приложение для работы с локальными LLM. Поддерживает и MLX, и GGUF, плюс встроенный OpenAI-совместимый сервер на <code>localhost</code>.</p>
<p>Шаги:</p>
<ol>
<li>Поставить LM Studio с официального сайта.</li>
<li>Открыть <strong>Discover</strong> (вкладка с моделями).</li>
<li>В фильтре указать «MLX» (важно — без этого подсунет GGUF-вариант).</li>
<li>Найти <code>Qwen3-Coder-Next</code> или подобную сборку.</li>
<li>Скачать (~5.5 GB).</li>
<li>Загрузить в <strong>My Models</strong>, потом <strong>Load</strong>.</li>
</ol>
<p>После загрузки в правом sidebar&rsquo;е будут параметры:</p>
<ul>
<li><strong>Context length:</strong> 8 192 для большинства задач хватает; 32 768 если нужен большой prompt с длинным куском кода.</li>
<li><strong>GPU offload:</strong> «Max» — отдать всё на Metal-ядра.</li>
<li><strong>Threads:</strong> оставить дефолт (LM Studio сам подберёт).</li>
</ul>
<p>LM Studio автоматически поднимет OpenAI-compatible endpoint на <code>http://localhost:1234/v1/</code>. Дальше с моделью работают любые клиенты, понимающие OpenAI API.</p>
<h2 class="heading" id="подключение-к-codex-cli-через-mcp-">
  Подключение к Codex CLI через MCP 🔌
  <a class="anchor" href="#%d0%bf%d0%be%d0%b4%d0%ba%d0%bb%d1%8e%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-%d0%ba-codex-cli-%d1%87%d0%b5%d1%80%d0%b5%d0%b7-mcp-">#</a>
</h2>
<p>Если вы используете Codex CLI (агентский интерфейс на базе LLM), к нему можно подключить локальную модель через <a href="https://modelcontextprotocol.io/">MCP-сервер</a> для LM Studio.</p>
<p>В <code>~/.codex/config.toml</code>:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">mcpServers</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;lmstudio&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">command</span> <span class="p">=</span> <span class="s2">&#34;npx&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">args</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;-y&#34;</span><span class="p">,</span> <span class="s2">&#34;@mlaboard/lmstudio-mcp&#34;</span><span class="p">]</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>После перезапуска Codex увидит локальную модель как один из доступных провайдеров. Smoke-тест: попросить простую задачу типа «напиши функцию Фибоначчи на Python» — должен ответить за 0.5–1 секунду.</p>
<p>При первом запуске MCP-сервер скачивает свой пакет через <code>npx</code>. Это разовая операция, дальше работает с уже установленного.</p>
<h2 class="heading" id="замеры-на-m2-max-">
  Замеры на M2 Max 📊
  <a class="anchor" href="#%d0%b7%d0%b0%d0%bc%d0%b5%d1%80%d1%8b-%d0%bd%d0%b0-m2-max-">#</a>
</h2>
<p>На моём железе (M2 Max, 32 GB unified memory) получились такие цифры:</p>
<table>
	<thead>
			<tr>
					<th>Метрика</th>
					<th>Значение</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Размер модели на диске</td>
					<td>5.5 GB</td>
			</tr>
			<tr>
					<td>RAM при загрузке</td>
					<td>~6 GB</td>
			</tr>
			<tr>
					<td>First-token latency</td>
					<td>0.3-0.5 с</td>
			</tr>
			<tr>
					<td>Generation speed</td>
					<td>18-22 токена/с</td>
			</tr>
			<tr>
					<td>Время на ~500-токенный ответ</td>
					<td>~25 секунд</td>
			</tr>
	</tbody>
</table>
<p>Это медленнее облачного GPT-4 (там 50-80 ток/с в среднем), но быстрее, чем кажется по первому впечатлению. Для итеративной работы (написал → попросил → поправил руками → попросил снова) разница в latency почти неощутима, потому что много времени всё равно уходит на чтение и редактирование результата.</p>
<p>Если поднять контекст до 32k и закинуть в prompt большой кусок кода (типа целого файла на 1000 строк), скорость падает примерно вдвое: 9-12 ток/с при first-token latency около 2 секунд. Ничего страшного, но уже заметно.</p>
<h2 class="heading" id="сравнение-с-облаком">
  Сравнение с облаком
  <a class="anchor" href="#%d1%81%d1%80%d0%b0%d0%b2%d0%bd%d0%b5%d0%bd%d0%b8%d0%b5-%d1%81-%d0%be%d0%b1%d0%bb%d0%b0%d0%ba%d0%be%d0%bc">#</a>
</h2>
<table>
	<thead>
			<tr>
					<th>Показатель</th>
					<th>Облако (GPT-4-class)</th>
					<th>Локально (Qwen3-MLX)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Latency (first token)</td>
					<td>1-3 с</td>
					<td>0.3-0.5 с</td>
			</tr>
			<tr>
					<td>Generation speed</td>
					<td>50-80 ток/с</td>
					<td>18-22 ток/с</td>
			</tr>
			<tr>
					<td>Стоимость на месяц</td>
					<td>$20-200 (зависит от плана)</td>
					<td>$0 (после железа)</td>
			</tr>
			<tr>
					<td>Конфиденциальность</td>
					<td>По договору с провайдером</td>
					<td>Полная, код не уходит</td>
			</tr>
			<tr>
					<td>Offline</td>
					<td>✗</td>
					<td>✓</td>
			</tr>
			<tr>
					<td>Контекст</td>
					<td>128k+</td>
					<td>32k</td>
			</tr>
			<tr>
					<td>Качество на сложных задачах</td>
					<td>Лучше</td>
					<td>Хуже</td>
			</tr>
	</tbody>
</table>
<h2 class="heading" id="когда-честно-лучше-облако">
  Когда честно лучше облако
  <a class="anchor" href="#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d1%87%d0%b5%d1%81%d1%82%d0%bd%d0%be-%d0%bb%d1%83%d1%87%d1%88%d0%b5-%d0%be%d0%b1%d0%bb%d0%b0%d0%ba%d0%be">#</a>
</h2>
<p>Локальная модель не серебряная пуля. Есть классы задач, где она объективно проигрывает:</p>
<p><strong>Большой контекст.</strong> 128k+ контекст-окно у топовых облачных моделей позволяет закидывать в prompt целые модули кода. У 32k Qwen банально не хватит места.</p>
<p><strong>Сложные многошаговые задачи.</strong> Спроектировать архитектуру модуля, отрефакторить взаимосвязанные классы, написать миграцию с несколькими шагами — здесь топовые модели заметно качественнее.</p>
<p><strong>Мультимодальность.</strong> Анализ скриншотов UI, генерация по чертежу, чтение PDF — Qwen3-Coder-Next специализирован под код, картинки не обрабатывает.</p>
<p><strong>Редкие языки/фреймворки.</strong> На массовом Python, JS, Go качество приемлемое. На нишевых вещах (Elixir, Crystal, какой-нибудь специфичный DSL) облачная модель тренировалась на большем корпусе и даёт более уверенные ответы.</p>
<h2 class="heading" id="когда-локалка-точно-полезна">
  Когда локалка точно полезна
  <a class="anchor" href="#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d0%ba%d0%b0-%d1%82%d0%be%d1%87%d0%bd%d0%be-%d0%bf%d0%be%d0%bb%d0%b5%d0%b7%d0%bd%d0%b0">#</a>
</h2>
<p>И обратная сторона:</p>
<p><strong>Чувствительные репозитории.</strong> Закрытый клиентский код, медицинские/финансовые данные, что-то под NDA — локально единственный вариант, который не нарушает соглашений.</p>
<p><strong>Работа в дороге без сети.</strong> Поезд, самолёт, кафе с глюкавым WiFi — локальная модель отвечает одинаково ровно.</p>
<p><strong>Высокочастотные задачи.</strong> Если модель используется десятки раз в час (autocomplete, объяснение fragment&rsquo;ов кода в IDE), даже маленькая абонентская плата быстро накапливается. Локалка после железа бесплатна.</p>
<p><strong>Эксперименты с агентами.</strong> Запускать тестовый цикл, где LLM крутится в фоне без лимитов, проще на собственном железе. Лимиты по rate limit / стоимости в облаке быстро становятся узким местом.</p>
<h2 class="heading" id="размышления-после-месяца-использования">
  Размышления после месяца использования
  <a class="anchor" href="#%d1%80%d0%b0%d0%b7%d0%bc%d1%8b%d1%88%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%bf%d0%be%d1%81%d0%bb%d0%b5-%d0%bc%d0%b5%d1%81%d1%8f%d1%86%d0%b0-%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f">#</a>
</h2>
<p>Для меня сложилась гибридная схема: 70% повседневной работы (объяснить функцию, написать regex, сгенерить unit-тест, набросать структуру модуля) — Qwen локально. Остальные 30% (сложная архитектура, разбор большого PR, работа со скриншотом) — облако.</p>
<p>Это не «локальная победила облачную». Это разделение по типу задач: где важна privacy и скорость отклика — локально, где важно качество и контекст — облако.</p>
<p>Установка занимает максимум полчаса (LM Studio + скачать файл + подключить через MCP). Дисковое пространство в 5.5 GB не критично даже на 256 GB Mac. Если вы давно думали попробовать локальные LLM, но откладывали из-за «сложно настроить» — Qwen3-Coder-Next-MLX-4bit как раз тот случай, когда барьер входа честно низкий. ✅</p>
<h2 class="heading" id="источники">
  Источники
  <a class="anchor" href="#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8">#</a>
</h2>
<ul>
<li>Документация MLX: <a href="https://github.com/ml-explore/mlx">github.com/ml-explore/mlx</a></li>
<li>LM Studio: <a href="https://lmstudio.ai/">lmstudio.ai</a></li>
<li>Qwen models на HuggingFace: <a href="https://huggingface.co/Qwen">huggingface.co/Qwen</a></li>
<li>Model Context Protocol: <a href="https://modelcontextprotocol.io/">modelcontextprotocol.io</a></li>
</ul>
]]></content:encoded><category>локальные-llm</category><category>ai-инструменты</category></item><item><title>Что нового в Hugo v0.160.1: разбор релиза 🚀</title><link>https://kushnaren.co/posts/2026-04-08-new-in-hugo-0-160-1/</link><pubDate>Wed, 08 Apr 2026 12:00:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/2026-04-08-new-in-hugo-0-160-1/</guid><description>Подробный разбор Hugo v0.160.0 (фичи: CSS variable injection в css.Build, accurate Position в render hooks) и патч-релиза v0.160.1 (фиксы panic при passthrough в заголовках, утечки context markers в RenderShortcodes, multilingual root sections). Практические сценарии и чек-лист миграции.</description><content:encoded><![CDATA[<h2 class="heading" id="почему-этот-релиз-стоит-прочитать">
  Почему этот релиз стоит прочитать
  <a class="anchor" href="#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%8d%d1%82%d0%be%d1%82-%d1%80%d0%b5%d0%bb%d0%b8%d0%b7-%d1%81%d1%82%d0%be%d0%b8%d1%82-%d0%bf%d1%80%d0%be%d1%87%d0%b8%d1%82%d0%b0%d1%82%d1%8c">#</a>
</h2>
<p>8 апреля 2026 вышел <a href="https://github.com/gohugoio/hugo/releases/tag/v0.160.1">Hugo v0.160.1</a> — патч-релиз, который чинит несколько неприятных panic-сценариев из v0.160.0 (вышедшего четырьмя днями раньше). Вместе они формируют один логический шаг для проектов: 0.160.0 даёт две новые возможности, 0.160.1 закрывает баги, всплывшие сразу после релиза.</p>
<p>Если коротко: одну фичу (CSS-переменные в <code>css.Build</code>) ждали давно те, кто строит CSS через Hugo Pipes; остальные изменения — улучшения для авторов render hooks и фиксы редких краевых случаев. Никаких breaking changes, никаких депрекейтов.</p>
<p>Но фиксы в 0.160.1 — это именно тот случай, когда «пропустил минор и поймал panic в проде». Поэтому пробежать стоит.</p>
<h2 class="heading" id="главное-за-один-взгляд">
  Главное за один взгляд
  <a class="anchor" href="#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d0%be%d0%b5-%d0%b7%d0%b0-%d0%be%d0%b4%d0%b8%d0%bd-%d0%b2%d0%b7%d0%b3%d0%bb%d1%8f%d0%b4">#</a>
</h2>
<p><strong>v0.160.0 (4 апреля 2026)</strong> — фичевый минор:</p>
<ul>
<li>CSS-переменные можно инжектить в стили при сборке через <code>css.Build</code> (например, прокинуть значения из конфига).</li>
<li>Метод <code>.Position</code> теперь доступен на всех render hooks и стал точнее.</li>
<li>Несколько прицельных багфиксов (Position, escape ampersand&rsquo;ов в link URL, stray quotes в partial-декораторе script-контекста).</li>
</ul>
<p><strong>v0.160.1 (8 апреля 2026)</strong> — patch:</p>
<ul>
<li>Fix: panic при passthrough-элементах в заголовках.</li>
<li>Fix: panic при редактировании legacy template names, которые одновременно валидные пути в новой схеме.</li>
<li>Fix: <code>RenderShortcodes</code> утекал context markers при отступах.</li>
<li>Fix: standalone <code>RenderShortcodes</code> теперь снимает nested page context markers.</li>
<li>Fix: автосоздание root sections в мультиязычных сайтах.</li>
<li>Чистка: <code>cascade._target</code> → <code>cascade.target</code> (в тестах; deprecation осталось в коде).</li>
<li>README: чинены ссылки.</li>
</ul>
<p>Дальше — что это значит на практике.</p>
<h2 class="heading" id="css-переменные-в-cssbuild-">
  CSS-переменные в css.Build 🎨
  <a class="anchor" href="#css-%d0%bf%d0%b5%d1%80%d0%b5%d0%bc%d0%b5%d0%bd%d0%bd%d1%8b%d0%b5-%d0%b2-cssbuild-">#</a>
</h2>
<p>Это главная фича релиза. Если вы используете Hugo Pipes для сборки CSS (а не просто статически копируете файлы из <code>static/</code>), теперь можно прокидывать значения из Hugo-контекста прямо в CSS на этапе сборки.</p>
<p>Раньше для подобного приходилось городить пайплайн через переменные окружения, sed-замены или генерировать <code>.css</code> через Hugo template (что ломает синтаксис LSP-плагинов).</p>
<p>Теперь это выглядит ближе к декларативно:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">$opts</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">dict</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="s">&#34;vars&#34;</span><span class="w"> </span><span class="o">(</span><span class="nx">dict</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="s">&#34;primary&#34;</span><span class="w">   </span><span class="nx">site</span><span class="na">.Params.brand.primary</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="s">&#34;max-width&#34;</span><span class="w"> </span><span class="s">&#34;1200px&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="o">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">$css</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">resources</span><span class="na">.Get</span><span class="w"> </span><span class="s">&#34;css/main.css&#34;</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">css</span><span class="na">.Build</span><span class="w"> </span><span class="nx">$opts</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$css</span><span class="na">.RelPermalink</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span><span class="p">&gt;</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>В CSS можно ссылаться на эти переменные через специальный hook (<code>@import 'hugo:vars'</code> или подобный, точный синтаксис — в <a href="https://gohugo.io/functions/css/build/">документации css.Build</a>).</p>
<p>Когда полезно: сайтам с темой, где accent-цвет задаётся в <code>hugo.toml</code> и должен применяться к компонентам через CSS переменные; сборкам, где <code>max-width</code> или <code>breakpoint</code>-значения управляются из конфига; multi-tenant билдам.</p>
<p>Нашему блогу это не нужно — у нас ровно один theme и палитра жёстко зафиксирована в <code>:root</code> блоке. Но если вы строите тему-конструктор или multi-сайт — стоит изучить.</p>
<h2 class="heading" id="точная-позиция-в-render-hooks-">
  Точная позиция в render hooks 📍
  <a class="anchor" href="#%d1%82%d0%be%d1%87%d0%bd%d0%b0%d1%8f-%d0%bf%d0%be%d0%b7%d0%b8%d1%86%d0%b8%d1%8f-%d0%b2-render-hooks-">#</a>
</h2>
<p>Вторая фича — расширение <code>.Position</code> на все render hooks (image, link, heading, code-block) с одновременным улучшением точности.</p>
<p><code>.Position</code> возвращает позицию исходного Markdown-токена в файле. Для отладки и понимания «откуда пришла эта ссылка» — критично.</p>
<p>Сценарий использования:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cm">{{/* render-link.html */}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">$url</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="na">.Destination</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeURL</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="nx">urls</span><span class="na">.Parse</span><span class="w"> </span><span class="nx">$url</span><span class="o">)</span><span class="na">.IsAbs</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cm">{{/* Внутренняя ссылка — проверяем что target существует */}}</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="na">.Page.GetPage</span><span class="w"> </span><span class="o">(</span><span class="nx">string</span><span class="w"> </span><span class="nx">$url</span><span class="o">))</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">    <span class="cp">{{</span><span class="w"> </span><span class="nx">warnf</span><span class="w"> </span><span class="s">&#34;Битая внутренняя ссылка %q в %s&#34;</span><span class="w"> </span><span class="nx">$url</span><span class="w"> </span><span class="na">.Position</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$url</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span><span class="p">&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Text</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeHTML</span><span class="w"> </span><span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>В warning&rsquo;е теперь будет точное <code>файл:строка</code> исходника поста, а не нечто приблизительное. Проще ловить регрессии после рефакторингов контента.</p>
<h2 class="heading" id="багфиксы-01601-">
  Багфиксы 0.160.1 🔧
  <a class="anchor" href="#%d0%b1%d0%b0%d0%b3%d1%84%d0%b8%d0%ba%d1%81%d1%8b-01601-">#</a>
</h2>
<p>Пять патчей, каждый закрывает свой панический сценарий или утечку.</p>
<h3 class="heading" id="panic-при-passthrough-в-заголовках">
  Panic при passthrough в заголовках
  <a class="anchor" href="#panic-%d0%bf%d1%80%d0%b8-passthrough-%d0%b2-%d0%b7%d0%b0%d0%b3%d0%be%d0%bb%d0%be%d0%b2%d0%ba%d0%b0%d1%85">#</a>
</h3>
<p>Hugo Goldmark поддерживает <a href="https://gohugo.io/configuration/markup/#passthrough">passthrough</a> — режим, когда определённые символьные блоки (например, LaTeX-формулы <code>$...$</code>) пропускаются как есть и обрабатываются на клиенте через MathJax/KaTeX.</p>
<p>В 0.160.0 если passthrough-элемент попадал прямо внутрь заголовка (например, <code>## Формула $E=mc^2$</code>), Hugo падал с panic при сборке. 0.160.1 чинит этот случай: теперь рендерится корректный HTML без падения.</p>
<p>Если вы пишете научный/математический контент с inline-формулами в заголовках — обновление обязательно.</p>
<h3 class="heading" id="panic-на-legacy-template-names">
  Panic на legacy template names
  <a class="anchor" href="#panic-%d0%bd%d0%b0-legacy-template-names">#</a>
</h3>
<p>Если в проекте есть старая схема именования шаблонов (<code>_default/single.html.html</code> и подобные legacy-варианты), которая одновременно является валидным путём в новой схеме именования Hugo, при редактировании файла Hugo мог падать. 0.160.1 разводит эти кейсы.</p>
<h3 class="heading" id="rendershortcodes-и-context-markers">
  RenderShortcodes и context markers
  <a class="anchor" href="#rendershortcodes-%d0%b8-context-markers">#</a>
</h3>
<p><code>RenderShortcodes</code> — функция для обработки всех шорткодов внутри переданного контента. Она использует внутренние «context markers» для отслеживания вложенности.</p>
<p>В 0.160.0 эти маркеры могли:</p>
<ol>
<li>Утекать в финальный HTML, если контент с шорткодами был с отступами (например, внутри списка или blockquote).</li>
<li>Не очищаться при standalone-вызове <code>.RenderShortcodes</code> через <code>.Page.Content</code> после nested-рендеринга.</li>
</ol>
<p>Оба случая дают видимый «мусор» в выводе (артефакты вида <code>__hugo_ctx0__</code> и подобные). 0.160.1 чистит маркеры в обеих ситуациях.</p>
<p>Если вы используете шорткоды внутри списков или blockquote&rsquo;ов — обновляйтесь.</p>
<h3 class="heading" id="auto-creation-root-sections-в-мультиязычных-сайтах">
  Auto-creation root sections в мультиязычных сайтах
  <a class="anchor" href="#auto-creation-root-sections-%d0%b2-%d0%bc%d1%83%d0%bb%d1%8c%d1%82%d0%b8%d1%8f%d0%b7%d1%8b%d1%87%d0%bd%d1%8b%d1%85-%d1%81%d0%b0%d0%b9%d1%82%d0%b0%d1%85">#</a>
</h3>
<p>В мультиязычных сайтах Hugo автоматически создаёт «виртуальные» root section страницы для каждого языка. В 0.160.0 эта логика могла давать сбой (root section не создавалась, или создавалась только для default-языка), что ломало навигацию по неосновным языкам.</p>
<p>Если у вас один язык — пункт мимо. Если несколько — обновление лечит.</p>
<h2 class="heading" id="cascadetarget-вместо-cascade_target">
  cascade.target вместо cascade._target
  <a class="anchor" href="#cascadetarget-%d0%b2%d0%bc%d0%b5%d1%81%d1%82%d0%be-cascade_target">#</a>
</h2>
<p>Мелкий, но симптоматичный пункт: ключ <code>_target</code> в <a href="https://gohugo.io/content-management/front-matter/#cascade">cascade-конфигурации</a> переименован в <code>target</code> (без подчёркивания). Старая форма помечена как deprecated в коде, всё ещё работает.</p>
<p>Если у вас есть cascade-блоки в front matter:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">cascade</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">_target</span><span class="p">:</span><span class="w">                 </span><span class="c"># ← старая форма</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">page</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">foo</span><span class="p">:</span><span class="w"> </span><span class="l">bar</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Замените на:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">cascade</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">target</span><span class="p">:</span><span class="w">                  </span><span class="c"># ← новая форма</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">page</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">foo</span><span class="p">:</span><span class="w"> </span><span class="l">bar</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Точечная правка, занимает минуту. Запас времени до полного удаления старой формы — обычно несколько релизов.</p>
<h2 class="heading" id="чек-лист-обновления-">
  Чек-лист обновления ✅
  <a class="anchor" href="#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-">#</a>
</h2>
<ol>
<li>
<p><strong>Поднять версию Hugo до 0.160.1</strong> в Dockerfile / CI-образе или локальном binary. Если вы уже на v0.161.0+ — пропускаем, это пройденная развилка.</p>
</li>
<li>
<p><strong>Если используете passthrough</strong> для математики или специальных Markdown-блоков, и они появляются в заголовках — обновление закрывает panic. Без обновления заголовки-формулы могут быть рассадником сюрпризов.</p>
</li>
<li>
<p><strong>Если используете <code>cascade._target</code></strong> в front matter, переименовать в <code>cascade.target</code>:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep -rln <span class="s1">&#39;_target:&#39;</span> content/ <span class="p">|</span> xargs sed -i <span class="s1">&#39;&#39;</span> <span class="s1">&#39;s/_target:/target:/g&#39;</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
</li>
<li>
<p><strong>Если строите CSS через Hugo Pipes</strong>, посмотреть документацию <code>css.Build</code> с CSS variables — возможно, сможете заменить sed-замены или env-обвязку на встроенный механизм.</p>
</li>
<li>
<p><strong>Если пишете кастомные render hooks</strong>, проверить, можно ли использовать <code>.Position</code> для лучшей диагностики битых ссылок/изображений в постах.</p>
</li>
<li>
<p><strong>Стандартный прогон:</strong></p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hugo --minify --gc</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Сравнить количество warning&rsquo;ов до и после. Если новых не появилось — мигрировано чисто.</p>
</li>
</ol>
<h2 class="heading" id="итог-">
  Итог 🧠
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3-">#</a>
</h2>
<p>Hugo 0.160.x — это не громкий feature drop, а методичное улучшение инфраструктуры: новый удобный механизм для CSS-сборки, более точная диагностика в render hooks, набор фиксов под краевые случаи (passthrough в заголовках, RenderShortcodes с отступами, multilingual sections).</p>
<p>Главная причина обновляться — патч 0.160.1 закрывает несколько panic-сценариев. Если вас они никогда не цепляли, повезло; но «не цепляли» — это не «не зацепят». На статичных сайтах panic при сборке проявляется как сломанный CI и красный билд, что рассинхронизирует прод и репозиторий.</p>
<p>Если уже сидите на 0.161.0 (как мой блог сейчас), всё это вы уже получили транзитом. Если на 0.159 или раньше — есть смысл прыгать сразу на самую свежую stable, минуя промежуточные.</p>
<h2 class="heading" id="источники">
  Источники
  <a class="anchor" href="#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8">#</a>
</h2>
<ul>
<li>Релиз 0.160.0: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.160.0">gohugoio/hugo v0.160.0</a></li>
<li>Релиз 0.160.1: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.160.1">gohugoio/hugo v0.160.1</a></li>
<li>Документация <code>css.Build</code>: <a href="https://gohugo.io/functions/css/build/">gohugo.io/functions/css/build/</a></li>
<li>Документация passthrough: <a href="https://gohugo.io/configuration/markup/#passthrough">gohugo.io/configuration/markup/#passthrough</a></li>
<li>Документация cascade: <a href="https://gohugo.io/content-management/front-matter/#cascade">gohugo.io/content-management/front-matter/#cascade</a></li>
</ul>
]]></content:encoded><category>hugo</category><category>релизы</category></item><item><title>Автоматизация Hugo-блога с Codex CLI: разбор реальной рабочей сессии 🤖</title><link>https://kushnaren.co/posts/avtomatizaciya-hugo-bloga-s-codex-cli-realnyj-kejs/</link><pubDate>Thu, 05 Mar 2026 20:50:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/avtomatizaciya-hugo-bloga-s-codex-cli-realnyj-kejs/</guid><description>Показываю на реальном кейсе, как Codex CLI помогает вести Hugo-блог в production-режиме: структура задач, валидации, git-поток, исправление регрессий и устойчивый процесс публикации.</description><content:encoded><![CDATA[<p>Этот материал не «теория про ИИ». Это практический разбор конкретной рабочей сессии, где мы последовательно вели блог на Hugo через Codex CLI: писали новые статьи, чинили продовые проблемы, настраивали тему и закрепляли правила процесса.</p>
<p>Именно такой формат, на мой взгляд, лучше всего показывает ценность автоматизации: не в красивых обещаниях, а в повторяемом результате. 🔧</p>
<h2 class="heading" id="исходный-контекст">
  Исходный контекст
  <a class="anchor" href="#%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%bd%d1%8b%d0%b9-%d0%ba%d0%be%d0%bd%d1%82%d0%b5%d0%ba%d1%81%d1%82">#</a>
</h2>
<p>На старте у нас уже был рабочий блог на Hugo с темой <code>typo</code>, CI/CD через Forgejo Actions и поиском на <code>Pagefind</code>.</p>
<p>Дальше в рамках одной непрерывной сессии мы сделали:</p>
<ol>
<li>Написали и опубликовали новый пост про Hugo <code>0.157.0</code>.</li>
<li>Поймали проблему с невидимостью поста из-за времени публикации.</li>
<li>Добавили правила в <code>AGENTS.md</code>, чтобы больше не ловить future-ошибки.</li>
<li>Настроили и протестировали подсветку кода (в итоге перешли на <code>dracula</code>).</li>
<li>Привели к <code>dracula</code> цвета выдачи поиска.</li>
<li>Исправили некорректные заголовки в <code>Pagefind</code> (вместо названия поста показывалось имя сайта).</li>
<li>Структурировали <code>README.md</code> и <code>AGENTS.md</code> как рабочую документацию процесса.</li>
</ol>
<p>То есть это уже не «написать один пост», а полноценный операционный цикл контентного проекта.</p>
<h2 class="heading" id="как-выглядел-рабочий-цикл-с-codex-cli">
  Как выглядел рабочий цикл с Codex CLI
  <a class="anchor" href="#%d0%ba%d0%b0%d0%ba-%d0%b2%d1%8b%d0%b3%d0%bb%d1%8f%d0%b4%d0%b5%d0%bb-%d1%80%d0%b0%d0%b1%d0%be%d1%87%d0%b8%d0%b9-%d1%86%d0%b8%d0%ba%d0%bb-%d1%81-codex-cli">#</a>
</h2>
<p>Самый полезный паттерн из этой сессии:</p>
<h3 class="heading" id="1-чёткая-постановка-задачи">
  1. Чёткая постановка задачи
  <a class="anchor" href="#1-%d1%87%d1%91%d1%82%d0%ba%d0%b0%d1%8f-%d0%bf%d0%be%d1%81%d1%82%d0%b0%d0%bd%d0%be%d0%b2%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b8">#</a>
</h3>
<p>Каждый шаг формулировался как конкретный результат:</p>
<ul>
<li>«Напиши новую статью про релиз».</li>
<li>«Почему статья не видна? Исправь».</li>
<li>«Сделай правило, чтобы проблема не повторялась».</li>
<li>«Сделай тему поиска в dracula».</li>
</ul>
<p>Чем конкретнее цель, тем меньше «творческого шума» и выше скорость прохождения задачи.</p>
<h3 class="heading" id="2-локальная-диагностика-перед-правкой">
  2. Локальная диагностика перед правкой
  <a class="anchor" href="#2-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d1%8c%d0%bd%d0%b0%d1%8f-%d0%b4%d0%b8%d0%b0%d0%b3%d0%bd%d0%be%d1%81%d1%82%d0%b8%d0%ba%d0%b0-%d0%bf%d0%b5%d1%80%d0%b5%d0%b4-%d0%bf%d1%80%d0%b0%d0%b2%d0%ba%d0%be%d0%b9">#</a>
</h3>
<p>Перед изменением файлов Codex проходил короткий аудит:</p>
<ul>
<li>чтение текущих шаблонов/конфигов;</li>
<li>проверка фактического рендера;</li>
<li>сверка с реальным поведением сайта;</li>
<li>поиск причинно-следственной связи, а не симптомов.</li>
</ul>
<p>Пример мышления: если цвета не применились, сначала проверить порядок подключения CSS, а не сразу «добавить ещё <code>!important</code>».</p>
<h3 class="heading" id="3-точечная-правка--обязательная-валидация">
  3. Точечная правка + обязательная валидация
  <a class="anchor" href="#3-%d1%82%d0%be%d1%87%d0%b5%d1%87%d0%bd%d0%b0%d1%8f-%d0%bf%d1%80%d0%b0%d0%b2%d0%ba%d0%b0--%d0%be%d0%b1%d1%8f%d0%b7%d0%b0%d1%82%d0%b5%d0%bb%d1%8c%d0%bd%d0%b0%d1%8f-%d0%b2%d0%b0%d0%bb%d0%b8%d0%b4%d0%b0%d1%86%d0%b8%d1%8f">#</a>
</h3>
<p>После каждого изменения шёл одинаковый технический минимум:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hugo list future
</span></span><span class="line"><span class="cl">hugo --minify
</span></span><span class="line"><span class="cl">git status --short</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Это сильно снижает вероятность того, что в <code>main</code> улетит «логически верно, но технически сыро».</p>
<h3 class="heading" id="4-фиксация-в-git-маленькими-коммитами">
  4. Фиксация в git маленькими коммитами
  <a class="anchor" href="#4-%d1%84%d0%b8%d0%ba%d1%81%d0%b0%d1%86%d0%b8%d1%8f-%d0%b2-git-%d0%bc%d0%b0%d0%bb%d0%b5%d0%bd%d1%8c%d0%ba%d0%b8%d0%bc%d0%b8-%d0%ba%d0%be%d0%bc%d0%bc%d0%b8%d1%82%d0%b0%d0%bc%d0%b8">#</a>
</h3>
<p>Вместо одного гигантского коммита по итогам сессии:</p>
<ul>
<li>каждый смысловой шаг фиксировался отдельно;</li>
<li>сообщения коммитов были предметными;</li>
<li>любой этап можно быстро отследить и откатить локально при необходимости.</li>
</ul>
<p>Для блога это особенно удобно: контент, стили, шаблоны и правила не смешиваются в «одно большое изменение».</p>
<h2 class="heading" id="реальные-проблемы-которые-мы-поймали-и-закрыли">
  Реальные проблемы, которые мы поймали и закрыли
  <a class="anchor" href="#%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d1%8b-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b5-%d0%bc%d1%8b-%d0%bf%d0%be%d0%b9%d0%bc%d0%b0%d0%bb%d0%b8-%d0%b8-%d0%b7%d0%b0%d0%ba%d1%80%d1%8b%d0%bb%d0%b8">#</a>
</h2>
<h3 class="heading" id="проблема-1-пост-не-видно-на-сайте">
  Проблема 1: пост «не видно на сайте»
  <a class="anchor" href="#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-1-%d0%bf%d0%be%d1%81%d1%82-%d0%bd%d0%b5-%d0%b2%d0%b8%d0%b4%d0%bd%d0%be-%d0%bd%d0%b0-%d1%81%d0%b0%d0%b9%d1%82%d0%b5">#</a>
</h3>
<p>Симптом: статья есть в репозитории, но не отображается в списке публикаций.</p>
<p>Причина: <code>date</code> в фронтматтере оказался в будущем относительно московского времени на момент деплоя.</p>
<p>Решение:</p>
<ul>
<li>сдвиг даты в прошлое;</li>
<li>проверка <code>hugo list future</code>;</li>
<li>фиксация правила в <code>AGENTS.md</code>: новые посты ставить на 5-10 минут в прошлое и всегда проверять future-список перед коммитом.</li>
</ul>
<p>Ключевой эффект: проблема устранена один раз на уровне процесса, а не «подкручена вручную» только для одного поста.</p>
<h3 class="heading" id="проблема-2-подсветка-кода-всё-одним-цветом">
  Проблема 2: подсветка кода «всё одним цветом»
  <a class="anchor" href="#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-2-%d0%bf%d0%be%d0%b4%d1%81%d0%b2%d0%b5%d1%82%d0%ba%d0%b0-%d0%ba%d0%be%d0%b4%d0%b0-%d0%b2%d1%81%d1%91-%d0%be%d0%b4%d0%bd%d0%b8%d0%bc-%d1%86%d0%b2%d0%b5%d1%82%d0%be%d0%bc">#</a>
</h3>
<p>Симптом: HTML уже содержит <code>chroma</code>-классы, но визуально код почти монохромный.</p>
<p>Причина: базовый файл подсветки темы был близок к <code>bw</code>-палитре.</p>
<p>Решение:</p>
<ol>
<li>Включили работу через CSS-классы (<code>noClasses = false</code>).</li>
<li>Добавили собственный <code>assets/css/syntax-highlighting.css</code>.</li>
<li>Сначала проверили <code>monokai</code>, затем переключили на <code>dracula</code>.</li>
</ol>
<p>Итог: читаемая цветная подсветка с контролируемой темой в репозитории.</p>
<h3 class="heading" id="проблема-3-выдача-поиска-не-в-стиле-сайта">
  Проблема 3: выдача поиска не в стиле сайта
  <a class="anchor" href="#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-3-%d0%b2%d1%8b%d0%b4%d0%b0%d1%87%d0%b0-%d0%bf%d0%be%d0%b8%d1%81%d0%ba%d0%b0-%d0%bd%d0%b5-%d0%b2-%d1%81%d1%82%d0%b8%d0%bb%d0%b5-%d1%81%d0%b0%d0%b9%d1%82%d0%b0">#</a>
</h3>
<p>Симптом: поле и результаты поиска выпадали из общей цветовой схемы.</p>
<p>Причина: стили <code>Pagefind</code> подключались после <code>custom.css</code>, и часть переопределений не применялась.</p>
<p>Решение:</p>
<ul>
<li>сначала добавили CSS-переменные <code>Pagefind</code> в <code>custom.css</code>;</li>
<li>затем сделали гарантированное переопределение после <code>pagefind-ui.css</code> прямо в шаблоне <code>layouts/search/single.html</code>.</li>
</ul>
<p>Это важный вывод: иногда нужно исправлять не «цвета», а точку и порядок их применения.</p>
<h3 class="heading" id="проблема-4-у-результатов-поиска-заголовок-evgeny-kushnarenko">
  Проблема 4: у результатов поиска заголовок «Evgeny Kushnarenko»
  <a class="anchor" href="#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-4-%d1%83-%d1%80%d0%b5%d0%b7%d1%83%d0%bb%d1%8c%d1%82%d0%b0%d1%82%d0%be%d0%b2-%d0%bf%d0%be%d0%b8%d1%81%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b3%d0%be%d0%bb%d0%be%d0%b2%d0%be%d0%ba-evgeny-kushnarenko">#</a>
</h3>
<p>Симптом: каждая найденная строка в выдаче имела одинаковый title (имя сайта).</p>
<p>Причина: <code>Pagefind</code> подхватывал <code>h1</code> из шапки страницы.</p>
<p>Решение:</p>
<ul>
<li>вынесли локальный <code>layouts/partials/header.html</code> и пометили шапку <code>data-pagefind-ignore</code>;</li>
<li>добавили <code>data-pagefind-meta=&quot;title&quot;</code> на <code>h1</code> поста в <code>layouts/_default/single.html</code>.</li>
</ul>
<p>Итог: выдача начала показывать реальные заголовки материалов.</p>
<h2 class="heading" id="что-это-даёт-в-ежедневной-работе">
  Что это даёт в ежедневной работе
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d0%b4%d0%b0%d1%91%d1%82-%d0%b2-%d0%b5%d0%b6%d0%b5%d0%b4%d0%bd%d0%b5%d0%b2%d0%bd%d0%be%d0%b9-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b5">#</a>
</h2>
<p>После такого цикла становится заметно:</p>
<ol>
<li>Время между идеей и публикацией сокращается.</li>
<li>Ошибки переходят из категории «случайные и повторяющиеся» в «редкие и диагностируемые».</li>
<li>Репозиторий становится документацией процесса, а не просто папкой с контентом.</li>
<li>Снижается когнитивная нагрузка на автора: меньше ручной рутины, больше фокуса на смысле.</li>
</ol>
<p>На длинной дистанции это важнее, чем «сэкономить 5 минут на одной статье».</p>
<h2 class="heading" id="что-точно-не-нужно-автоматизировать">
  Что точно не нужно автоматизировать
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%82%d0%be%d1%87%d0%bd%d0%be-%d0%bd%d0%b5-%d0%bd%d1%83%d0%b6%d0%bd%d0%be-%d0%b0%d0%b2%d1%82%d0%be%d0%bc%d0%b0%d1%82%d0%b8%d0%b7%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d1%82%d1%8c">#</a>
</h2>
<p>После пары таких сессий стало ясно: не любую задачу нужно отдавать инструменту. Есть три категории, где автоматизация скорее вредит:</p>
<p><strong>Тональность и личный голос.</strong> Codex может собрать структурный черновик, но финальный тон — авторская вещь. Если читатель чувствует «это писал не человек», доверие к материалу падает мгновенно. Заменять автора на этом этапе — значит потерять то, ради чего блог вообще существует.</p>
<p><strong>Спорные технические решения.</strong> Когда в дизайне есть выбор между двумя одинаково обоснованными подходами (положить компонент в <code>layouts/_default/</code> или в <code>layouts/posts/</code>, использовать ли Hugo Pipes для CSS или статичную копию), Codex даст «средний разумный вариант». Это не плохо, но это не вы. На длинной дистанции архитектурные решения, принятые «средним способом», накапливают трение, которое в одиночку не разглядеть.</p>
<p><strong>Решения про контент.</strong> Что писать, что не писать, какие посты убирать, какие оставлять, как переименовать раздел — это редакционные решения. Они должны проходить через автора. Инструмент здесь полезен только как «помощник по реализации», когда решение уже принято.</p>
<h2 class="heading" id="размышления-после-сессии">
  Размышления после сессии
  <a class="anchor" href="#%d1%80%d0%b0%d0%b7%d0%bc%d1%8b%d1%88%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%bf%d0%be%d1%81%d0%bb%d0%b5-%d1%81%d0%b5%d1%81%d1%81%d0%b8%d0%b8">#</a>
</h2>
<p>Главный вывод после нескольких часов работы в таком режиме: ценность автоматизации не в скорости, а в <strong>предсказуемости</strong>. До регулярной работы с Codex CLI каждая публикация поста была чуть-чуть разной: где-то забыл подвинуть дату, где-то не проверил подсветку, где-то не закоммитил отдельно стилевую правку. После — процесс стал ровным, что освободило внимание для содержания.</p>
<p>Это типичная история про инфраструктуру: когда она работает плохо, она занимает 60% мысленного бюджета на каждое действие. Когда хорошо — становится незаметной, и весь бюджет уходит на смысл.</p>
<h2 class="heading" id="практический-шаблон-автоматизации-под-hugo">
  Практический шаблон автоматизации под Hugo
  <a class="anchor" href="#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b9-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd-%d0%b0%d0%b2%d1%82%d0%be%d0%bc%d0%b0%d1%82%d0%b8%d0%b7%d0%b0%d1%86%d0%b8%d0%b8-%d0%bf%d0%be%d0%b4-hugo">#</a>
</h2>
<p>Если хотите повторить подход, можно взять такой минимум:</p>
<ol>
<li>Для каждой задачи: цель в 1-2 предложениях.</li>
<li>Перед правкой: чтение текущего состояния (<code>rg</code>, <code>sed</code>, шаблоны, конфиги).</li>
<li>После правки: <code>hugo list future</code> + <code>hugo --minify</code>.</li>
<li>Фиксация: один смысл = один коммит.</li>
<li>Публикация: <code>git push</code> сразу после проверки.</li>
<li>Если баг повторяемый: правило в <code>AGENTS.md</code>, а не «устная договорённость».</li>
</ol>
<h2 class="heading" id="набор-команд-который-реально-работал-в-этой-сессии">
  Набор команд, который реально работал в этой сессии
  <a class="anchor" href="#%d0%bd%d0%b0%d0%b1%d0%be%d1%80-%d0%ba%d0%be%d0%bc%d0%b0%d0%bd%d0%b4-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b9-%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d0%be-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d0%bb-%d0%b2-%d1%8d%d1%82%d0%be%d0%b9-%d1%81%d0%b5%d1%81%d1%81%d0%b8%d0%b8">#</a>
</h2>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Проверки контента и даты</span>
</span></span><span class="line"><span class="cl">hugo list future
</span></span><span class="line"><span class="cl">hugo --minify
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Диагностика</span>
</span></span><span class="line"><span class="cl">rg -n <span class="s2">&#34;pagefind|highlight|chroma&#34;</span> -S .
</span></span><span class="line"><span class="cl">sed -n <span class="s1">&#39;1,220p&#39;</span> layouts/search/single.html
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Публикация</span>
</span></span><span class="line"><span class="cl">git add &lt;файлы&gt;
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;Осмысленный коммит по одной задаче&#34;</span>
</span></span><span class="line"><span class="cl">git push</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Никакой экзотики: сила не в сложных командах, а в дисциплине последовательности.</p>
<h2 class="heading" id="практические-выводы">
  Практические выводы
  <a class="anchor" href="#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b5-%d0%b2%d1%8b%d0%b2%d0%be%d0%b4%d1%8b">#</a>
</h2>
<p>Автоматизация блога через Codex CLI работает лучше всего, когда вы относитесь к ней как к инженерному процессу:</p>
<ol>
<li>Чёткая постановка задач.</li>
<li>Наблюдаемость и проверки после каждого шага.</li>
<li>Маленькие коммиты с понятной ответственностью.</li>
<li>Фиксация правил в репозитории.</li>
</ol>
<p>Тогда Codex становится не «генератором текста», а операционным партнёром, который помогает держать качество и темп публикаций одновременно. ✅</p>
]]></content:encoded><category>ai-инструменты</category><category>автоматизация</category><category>hugo</category><category>runbook</category></item><item><title>Hugo 0.157.0: GitInfo для модулей, timeout в GetRemote и практическая миграция 🚀</title><link>https://kushnaren.co/posts/hugo-0-157-0-gitinfo-moduli-timeouty-getremote/</link><pubDate>Thu, 05 Mar 2026 19:50:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/hugo-0-157-0-gitinfo-moduli-timeouty-getremote/</guid><description>Разбираю ключевые нововведения Hugo v0.157.0 и показываю, как безопасно обновиться: что проверить в CI, шаблонах, модулях и интеграциях с внешними API.</description><content:encoded><![CDATA[<p>25 февраля 2026 года вышел <a href="https://github.com/gohugoio/hugo/releases/tag/v0.157.0">Hugo v0.157.0</a>. На первый взгляд релиз выглядит как «ещё один минорный апдейт», но внутри есть сразу несколько изменений, которые заметно улучшают работу реальных проектов: особенно если у вас модульная архитектура, контент из нескольких репозиториев и внешние запросы в шаблонах.</p>
<p>В этой статье разберём, что именно изменилось, где может быть практический эффект, и как пройти обновление без сюрпризов. 🚀</p>
<p>Кому это релевантно сразу: командам, ведущим контент через Hugo Modules; авторам тем, использующим внешние API в шаблонах; всем, у кого билд иногда «висит» из-за медленного источника. Кому можно отложить: одиночный блог без модулей и без внешних запросов получит апгрейд почти прозрачно — обновляйтесь как часть обычной уборки технического долга.</p>
<h2 class="heading" id="почему-релиз-01570-важен">
  Почему релиз 0.157.0 важен
  <a class="anchor" href="#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%80%d0%b5%d0%bb%d0%b8%d0%b7-01570-%d0%b2%d0%b0%d0%b6%d0%b5%d0%bd">#</a>
</h2>
<p>Главная идея релиза: Hugo становится устойчивее в «боевых» сценариях, где проект не ограничивается одной папкой <code>content</code> и парой шаблонов.</p>
<p>Ключевые точки:</p>
<ol>
<li><code>Page.GitInfo</code> теперь полноценно поддерживает контент из Git-модулей (через mounts).</li>
<li>В <code>resources.GetRemote</code> появился перезапросный <code>timeout</code>, который можно задавать прямо в шаблоне.</li>
<li>Добавлена частичная поддержка AVIF/HEIF/HEIC (пока только метаданные).</li>
<li>Улучшены дефолты обработки WebP.</li>
<li>Исправлены ошибки, влияющие на меню, section pages и стабильность сборок.</li>
</ol>
<p>Если коротко: меньше «магии», больше управляемости. 🎯</p>
<h2 class="heading" id="gitinfo-для-hugo-modules-что-реально-изменилось-">
  GitInfo для Hugo Modules: что реально изменилось 🧩
  <a class="anchor" href="#gitinfo-%d0%b4%d0%bb%d1%8f-hugo-modules-%d1%87%d1%82%d0%be-%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d0%be-%d0%b8%d0%b7%d0%bc%d0%b5%d0%bd%d0%b8%d0%bb%d0%be%d1%81%d1%8c-">#</a>
</h2>
<h3 class="heading" id="проблема-до-01570">
  Проблема до 0.157.0
  <a class="anchor" href="#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-%d0%b4%d0%be-01570">#</a>
</h3>
<p>Если вы подключали контент через <code>module.imports.mounts</code> (например, общий набор статей или документацию из отдельного репозитория), <code>GitInfo</code> мог работать неполно или нестабильно для таких страниц.</p>
<p>Это мешало:</p>
<ul>
<li>корректно выводить «последнее обновление» для импортированного контента;</li>
<li>строить списки изменений по коммитам;</li>
<li>делать единый шаблон мета-блока для локального и модульного контента.</li>
</ul>
<h3 class="heading" id="что-даёт-01570">
  Что даёт 0.157.0
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d0%b4%d0%b0%d1%91%d1%82-01570">#</a>
</h3>
<p>В релизе добавлена и доработана поддержка <code>Page.GitInfo</code> для контента из Git-модулей. То есть страницы, примонтированные из модулей, теперь могут корректно отдавать данные о коммите: автора, дату, хеш, subject и т.д.</p>
<p>Это особенно полезно для:</p>
<ul>
<li>docs-порталов из нескольких репозиториев;</li>
<li>мультисайтов, где контентные команды работают независимо;</li>
<li>архитектуры «ядро темы + контент как модуль».</li>
</ul>
<h3 class="heading" id="практический-шаблон">
  Практический шаблон
  <a class="anchor" href="#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b9-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd">#</a>
</h3>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="na">.GitInfo</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;meta&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    Обновлено: <span class="cp">{{</span><span class="w"> </span><span class="na">.AuthorDate.Format</span><span class="w"> </span><span class="s">&#34;2006-01-02 15:04&#34;</span><span class="w"> </span><span class="cp">}}</span> ·
</span></span><span class="line"><span class="cl">    Автор: <span class="cp">{{</span><span class="w"> </span><span class="na">.AuthorName</span><span class="w"> </span><span class="cp">}}</span> ·
</span></span><span class="line"><span class="cl">    Коммит: <span class="p">&lt;</span><span class="nt">code</span><span class="p">&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.AbbreviatedHash</span><span class="w"> </span><span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">code</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Важный operational-момент: в CI должен быть <strong>не shallow clone</strong>, иначе Git-данные могут быть неточными.</p>
<h2 class="heading" id="resourcesgetremote-получил-timeout-контроль-вместо-зависаний-">
  <code>resources.GetRemote</code> получил <code>timeout</code>: контроль вместо зависаний ⏱️
  <a class="anchor" href="#resourcesgetremote-%d0%bf%d0%be%d0%bb%d1%83%d1%87%d0%b8%d0%bb-timeout-%d0%ba%d0%be%d0%bd%d1%82%d1%80%d0%be%d0%bb%d1%8c-%d0%b2%d0%bc%d0%b5%d1%81%d1%82%d0%be-%d0%b7%d0%b0%d0%b2%d0%b8%d1%81%d0%b0%d0%bd%d0%b8%d0%b9-">#</a>
</h2>
<h3 class="heading" id="что-болело-раньше">
  Что болело раньше
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d0%b1%d0%be%d0%bb%d0%b5%d0%bb%d0%be-%d1%80%d0%b0%d0%bd%d1%8c%d1%88%d0%b5">#</a>
</h3>
<p>Когда вы тянете внешние данные (RSS/JSON/изображения), один медленный источник мог подвесить сборку. На небольшом сайте это раздражает, а на большом CI-пайплайне превращается в регулярную проблему.</p>
<h3 class="heading" id="что-появилось-в-01570">
  Что появилось в 0.157.0
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d0%bf%d0%be%d1%8f%d0%b2%d0%b8%d0%bb%d0%be%d1%81%d1%8c-%d0%b2-01570">#</a>
</h3>
<p>Теперь в <code>resources.GetRemote</code> есть опция <code>timeout</code> (строка длительности, например <code>&quot;10s&quot;</code>). Её можно задавать для каждого запроса отдельно.</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">$url</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;https://example.org/feed.rss&#34;</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">$opts</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;timeout&#34;</span><span class="w"> </span><span class="s">&#34;10s&#34;</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="nx">try</span><span class="w"> </span><span class="o">(</span><span class="nx">resources</span><span class="na">.GetRemote</span><span class="w"> </span><span class="nx">$url</span><span class="w"> </span><span class="nx">$opts</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="na">.Err</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">    <span class="cp">{{</span><span class="w"> </span><span class="nx">warnf</span><span class="w"> </span><span class="s">&#34;Не удалось получить feed %q: %s&#34;</span><span class="w"> </span><span class="nx">$url</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="na">.Value</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">    <span class="cp">{{</span><span class="w"> </span><span class="nx">$data</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">transform</span><span class="na">.Unmarshal</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">    <span class="cm">{{/* дальнейшая работа с данными */}}</span>
</span></span><span class="line"><span class="cl">    <span class="cp">{{</span><span class="w"> </span><span class="nx">$data</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">    <span class="cp">{{</span><span class="w"> </span><span class="nx">warnf</span><span class="w"> </span><span class="s">&#34;Ресурс %q не найден&#34;</span><span class="w"> </span><span class="nx">$url</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Практический эффект:</p>
<ul>
<li>можно задавать строгий timeout для нестабильных источников;</li>
<li>сборка перестаёт «висеть» из-за одного внешнего API;</li>
<li>проще разделить критичные и некритичные источники (где-то <code>errorf</code>, где-то <code>warnf</code>).</li>
</ul>
<h2 class="heading" id="изображения-что-важно-понимать-про-avifheifheic-и-webp">
  Изображения: что важно понимать про AVIF/HEIF/HEIC и WebP
  <a class="anchor" href="#%d0%b8%d0%b7%d0%be%d0%b1%d1%80%d0%b0%d0%b6%d0%b5%d0%bd%d0%b8%d1%8f-%d1%87%d1%82%d0%be-%d0%b2%d0%b0%d0%b6%d0%bd%d0%be-%d0%bf%d0%be%d0%bd%d0%b8%d0%bc%d0%b0%d1%82%d1%8c-%d0%bf%d1%80%d0%be-avifheifheic-%d0%b8-webp">#</a>
</h2>
<p>В 0.157.0 добавили <strong>частичную</strong> поддержку AVIF/HEIF/HEIC: на текущем этапе это поддержка метаданных, а не полноценный новый pipeline обработки.</p>
<p>Что это значит на практике:</p>
<ul>
<li>Hugo лучше распознаёт такие файлы;</li>
<li>можно безопаснее работать с метаданными;</li>
<li>но не стоит ожидать полной parity с привычной обработкой JPEG/PNG/WebP прямо «из коробки».</li>
</ul>
<p>Также в релизе скорректированы настройки обработки WebP. Если вы строите агрессивный image pipeline, стоит прогнать визуальные regression-проверки на ключевых страницах.</p>
<h2 class="heading" id="дополнительные-исправления-которые-могут-всплыть-в-проде">
  Дополнительные исправления, которые могут «всплыть» в проде
  <a class="anchor" href="#%d0%b4%d0%be%d0%bf%d0%be%d0%bb%d0%bd%d0%b8%d1%82%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d0%b8%d1%81%d0%bf%d1%80%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b5-%d0%bc%d0%be%d0%b3%d1%83%d1%82-%d0%b2%d1%81%d0%bf%d0%bb%d1%8b%d1%82%d1%8c-%d0%b2-%d0%bf%d1%80%d0%be%d0%b4%d0%b5">#</a>
</h2>
<p>В релизе есть не самые заметные, но полезные улучшения:</p>
<ul>
<li>фикс <code>menu pageRef</code> в multidimensional-настройках;</li>
<li>корректировки в логике section pages;</li>
<li>улучшенные сообщения об ошибках модулей (<code>go mod download</code>);</li>
<li>пропуск пустых taxonomy-ключей/значений в конфиге.</li>
</ul>
<p>Каждый пункт по отдельности небольшой, но вместе они снижают количество «странных» инцидентов после релиза.</p>
<h2 class="heading" id="сценарий-который-раньше-ломался-контент-в-нескольких-модулях">
  Сценарий, который раньше ломался: контент в нескольких модулях
  <a class="anchor" href="#%d1%81%d1%86%d0%b5%d0%bd%d0%b0%d1%80%d0%b8%d0%b9-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b9-%d1%80%d0%b0%d0%bd%d1%8c%d1%88%d0%b5-%d0%bb%d0%be%d0%bc%d0%b0%d0%bb%d1%81%d1%8f-%d0%ba%d0%be%d0%bd%d1%82%d0%b5%d0%bd%d1%82-%d0%b2-%d0%bd%d0%b5%d1%81%d0%ba%d0%be%d0%bb%d1%8c%d0%ba%d0%b8%d1%85-%d0%bc%d0%be%d0%b4%d1%83%d0%bb%d1%8f%d1%85">#</a>
</h2>
<p>Чтобы понять ценность изменений вокруг GitInfo и section pages, представьте типичную mid-size-конфигурацию: основной репозиторий с темой, плюс два модуля с контентом — <code>docs/</code> и <code>blog/</code>, каждый в своём Git-репо. Их подмонтировали в основной сайт через <code>module.imports.mounts</code>.</p>
<p>До 0.157.0 в этой конфигурации возникали мелкие неприятности: даты в footer&rsquo;е импортированных постов отдавали поведение «default-на-momento-сборки» вместо «коммит-автор-дата»; menu иногда теряла pageRef для импортированных страниц при определённых вариантах языковой настройки; ошибки <code>go mod download</code> приходили без контекста, и приходилось вручную перебирать модули, чтобы найти проблемный.</p>
<p>В 0.157.0 каждый из этих краевых случаев починен. Если вы держите такой setup, после обновления заметна разница: меньше ручных правок шаблонов под «странности» modular-контента, прозрачнее ошибки при сетевых проблемах с одним из модулей.</p>
<h2 class="heading" id="когда-обновляться-можно-отложить">
  Когда обновляться можно отложить
  <a class="anchor" href="#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d1%8f%d1%82%d1%8c%d1%81%d1%8f-%d0%bc%d0%be%d0%b6%d0%bd%d0%be-%d0%be%d1%82%d0%bb%d0%be%d0%b6%d0%b8%d1%82%d1%8c">#</a>
</h2>
<p>Если у вас одиночный сайт без модулей, без <code>resources.GetRemote</code> в шаблонах, без AVIF/HEIF в pipeline и без активных кастомных меню с <code>pageRef</code> — этот релиз вас почти не коснётся. Обновитесь в плановом окне (вместе с пересборкой Docker-образа, например), но срочности нет.</p>
<p>Срочно обновляться стоит, если: используете <code>resources.GetRemote</code> и хотя бы раз попадали на медленный source; ведёте мультимодульный контент; пишете кастомные шаблоны с <code>.GitInfo</code> для импортированных страниц.</p>
<h2 class="heading" id="чек-лист-обновления-до-hugo-01570-">
  Чек-лист обновления до Hugo 0.157.0 ✅
  <a class="anchor" href="#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%b4%d0%be-hugo-01570-">#</a>
</h2>
<ol>
<li>Обновите бинарь/образ Hugo до <code>0.157.0</code>.</li>
<li>Если используете <code>GitInfo</code>, проверьте <code>enableGitInfo: true</code> в конфигурации.</li>
<li>Убедитесь, что CI делает глубокий клон репозитория (<code>fetch-depth: 0</code> в GitHub Actions или эквивалент).</li>
<li>Для всех <code>resources.GetRemote</code> проставьте осмысленный <code>timeout</code> и единообразный <code>try</code>-обработчик.</li>
<li>Прогоните smoke-тесты меню, section pages и страниц с импортированным модульным контентом.</li>
<li>Проверьте image pipeline (особенно WebP) на визуальные отличия.</li>
<li>Сравните длительность сборки до/после обновления.</li>
</ol>
<h2 class="heading" id="мини-план-миграции-на-30-минут">
  Мини-план миграции на 30 минут
  <a class="anchor" href="#%d0%bc%d0%b8%d0%bd%d0%b8-%d0%bf%d0%bb%d0%b0%d0%bd-%d0%bc%d0%b8%d0%b3%d1%80%d0%b0%d1%86%d0%b8%d0%b8-%d0%bd%d0%b0-30-%d0%bc%d0%b8%d0%bd%d1%83%d1%82">#</a>
</h2>
<p>Если проект уже в хорошем состоянии, переход можно сделать быстро:</p>
<ol>
<li>10 минут: поднять версию и собрать сайт локально без warning/error.</li>
<li>10 минут: проверить шаблоны с <code>.GitInfo</code> и блоки с <code>resources.GetRemote</code>.</li>
<li>10 минут: прогнать CI и открыть несколько критичных страниц руками.</li>
</ol>
<p>Обычно этого достаточно, чтобы перейти на релиз без ночных откатов.</p>
<h2 class="heading" id="итог-">
  Итог 🧠
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3-">#</a>
</h2>
<p>Hugo 0.157.0 не про «громкие фичи ради фич», а про зрелость платформы: лучшее поведение в модульных проектах, более предсказуемая работа с внешними запросами и аккуратные улучшения инфраструктурного уровня.</p>
<p>Если у вас контент разбит на модули, а сборка зависит от внешних данных, обновление стоит делать в ближайшем цикле.</p>
<h2 class="heading" id="источники">
  Источники
  <a class="anchor" href="#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8">#</a>
</h2>
<ul>
<li>Официальный релиз: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.157.0">gohugoio/hugo v0.157.0</a></li>
<li>Документация <code>Page.GitInfo</code>: <a href="https://gohugo.io/methods/page/gitinfo/">gohugo.io/methods/page/gitinfo</a></li>
<li>Документация <code>resources.GetRemote</code>: <a href="https://gohugo.io/functions/resources/getremote/">gohugo.io/functions/resources/getremote</a></li>
</ul>
]]></content:encoded><category>hugo</category><category>релизы</category></item><item><title>Hugo 0.156.0: что нового и что проверить после обновления 🚀</title><link>https://kushnaren.co/posts/hugo-0-156-0-obzor-novovvedenij/</link><pubDate>Wed, 18 Feb 2026 21:00:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/hugo-0-156-0-obzor-novovvedenij/</guid><description>Подробный разбор Hugo v0.156.0 по официальным release notes: переход на новые API hugo.Data/hugo.Sites, изменения в link render hooks, переводы site.Home, опция disableSetLocale, практический чек-лист обновления.</description><content:encoded><![CDATA[<h2 class="heading" id="зачем-вообще-читать-про-минорный-релиз">
  Зачем вообще читать про минорный релиз
  <a class="anchor" href="#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d1%87%d0%b8%d1%82%d0%b0%d1%82%d1%8c-%d0%bf%d1%80%d0%be-%d0%bc%d0%b8%d0%bd%d0%be%d1%80%d0%bd%d1%8b%d0%b9-%d1%80%d0%b5%d0%bb%d0%b8%d0%b7">#</a>
</h2>
<p>18 февраля 2026 вышел <a href="https://github.com/gohugoio/hugo/releases/tag/v0.156.0">Hugo v0.156.0</a>. Если поверхностно прочитать changelog, это очередной минор: пара новых методов, несколько депрекейтов, опция конфига. Звучит как «обновлюсь когда руки дойдут».</p>
<p>Но именно из таких минорных релизов и копится технический долг. Депрекейт API сегодня — это broken-build через год, когда вы решите перейти на крупную версию. Минута, потраченная сейчас, чтобы привести шаблоны в соответствие, экономит вечер разбирательств позже.</p>
<p>В этом посте — что реально изменилось, что заметно в шаблонах, и короткий чек-лист, который проходится за 30 минут.</p>
<h2 class="heading" id="главное-за-один-взгляд">
  Главное за один взгляд
  <a class="anchor" href="#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d0%be%d0%b5-%d0%b7%d0%b0-%d0%be%d0%b4%d0%b8%d0%bd-%d0%b2%d0%b7%d0%b3%d0%bb%d1%8f%d0%b4">#</a>
</h2>
<p>Релиз 0.156.0 содержит шесть значимых изменений:</p>
<ol>
<li>Добавлены новые API: <code>hugo.Data</code> и <code>hugo.Sites</code>.</li>
<li>Устаревшими объявлены <code>site.Data</code> и <code>site.Sites</code> (пока работают для обратной совместимости).</li>
<li>В контекст link render hooks добавлены методы <code>IsBlock</code> и <code>Ordinal</code>.</li>
<li>Добавлен новый метод <code>site.Home.Translations</code>; <code>site.Home.AllTranslations</code> помечен как deprecated.</li>
<li>Появилась опция конфигурации <code>disableSetLocale</code>.</li>
<li>Помечен как deprecated путь <code>_vendor/github.com/gohugoio/locales</code>.</li>
</ol>
<p>Дальше — по каждому пункту, что это значит на практике и что менять.</p>
<h2 class="heading" id="переезд-sitedata--hugodata-">
  Переезд site.Data → hugo.Data 🧱
  <a class="anchor" href="#%d0%bf%d0%b5%d1%80%d0%b5%d0%b5%d0%b7%d0%b4-sitedata--hugodata-">#</a>
</h2>
<p>Самое заметное смысловое изменение — переезд глобальных коллекций данных и сайтов с пространства имён <code>site.*</code> на <code>hugo.*</code>.</p>
<p>Сейчас в шаблонах вы могли писать:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">site</span><span class="na">.Data.authors</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  ...
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>После 0.156 рекомендуется:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">hugo</span><span class="na">.Data.authors</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  ...
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Версия со старым <code>site.Data</code> пока работает — ради совместимости. Но в release notes явно сказано: новые методы предпочтительнее, старые помечены как deprecated. Когда придёт следующая major-версия (Hugo любит чистить депрекейты), <code>site.Data</code> могут совсем убрать.</p>
<p>Аналогичная история с <code>site.Sites</code> → <code>hugo.Sites</code>. Используется реже, в основном на мультисайтах с подмножеством общих ресурсов.</p>
<p><strong>Что делать:</strong> массовый поиск-замена в шаблонах темы и проекта:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep -rn <span class="s1">&#39;site\.Data\|site\.Sites&#39;</span> layouts/ themes/*/layouts/ 2&gt;/dev/null</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Если хитов нет — вы никак не зависите от этого изменения. Если есть — заменить на <code>hugo.Data</code> и <code>hugo.Sites</code>.</p>
<h2 class="heading" id="render-hooks-isblock-и-ordinal-">
  Render hooks: IsBlock и Ordinal 🔗
  <a class="anchor" href="#render-hooks-isblock-%d0%b8-ordinal-">#</a>
</h2>
<p>Если у вас есть кастомные <a href="https://gohugo.io/render-hooks/links/">link render hooks</a> (файл <code>layouts/_default/_markup/render-link.html</code> или подобный), в их контексте теперь доступны два новых метода:</p>
<ul>
<li><code>IsBlock</code> — true, если ссылка стоит как самостоятельный блок (например, <code>&lt;p&gt;[link](url)&lt;/p&gt;</code> где это единственный элемент абзаца), а не inline внутри текста.</li>
<li><code>Ordinal</code> — порядковый номер ссылки в документе, начиная с 0.</li>
</ul>
<p>Зачем это нужно:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cm">{{/* render-link.html */}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.IsBlock</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cm">{{/* Самостоятельная ссылка — рендерим как карточку */}}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;link-card&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Destination</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeURL</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;link-card__num&#34;</span><span class="p">&gt;</span>№<span class="cp">{{</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="na">.Ordinal</span><span class="w"> </span><span class="nx">1</span><span class="w"> </span><span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;link-card__text&#34;</span><span class="p">&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Text</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeHTML</span><span class="w"> </span><span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cm">{{/* Inline-ссылка — обычный &lt;a&gt; */}}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Destination</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeURL</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span><span class="p">&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Text</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeHTML</span><span class="w"> </span><span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Это удобно для блогов с активным использованием ссылок: можно по-разному оформлять «отдельные ссылки на источники» и «inline-упоминания».</p>
<p><code>Ordinal</code> помогает строить нумерованные списки источников или footnote-style references без ручной разметки.</p>
<h2 class="heading" id="мультиязычность-sitehometranslations-">
  Мультиязычность: site.Home.Translations 🌐
  <a class="anchor" href="#%d0%bc%d1%83%d0%bb%d1%8c%d1%82%d0%b8%d1%8f%d0%b7%d1%8b%d1%87%d0%bd%d0%be%d1%81%d1%82%d1%8c-sitehometranslations-">#</a>
</h2>
<p>Метод <code>site.Home.AllTranslations</code> помечен как deprecated. Заменяет его более узкий <code>site.Home.Translations</code>.</p>
<p>Разница:</p>
<ul>
<li><code>AllTranslations</code> возвращает <strong>все</strong> языковые версии главной страницы, <strong>включая текущую</strong>.</li>
<li><code>Translations</code> возвращает только <strong>другие</strong> языки, без текущей версии.</li>
</ul>
<p>Старый код:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">site</span><span class="na">.Home.AllTranslations</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">ne</span><span class="w"> </span><span class="na">.Lang</span><span class="w"> </span><span class="na">$.Lang</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Permalink</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span><span class="p">&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Language.LanguageName</span><span class="w"> </span><span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>После 0.156 ту же логику можно записать прямее:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">site</span><span class="na">.Home.Translations</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Permalink</span><span class="w"> </span><span class="cp">}}</span><span class="s">&#34;</span><span class="p">&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Language.LanguageName</span><span class="w"> </span><span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Меньше кода, понятнее намерение. Если вы вели мультиязычный сайт с языковым переключателем — самое время заменить.</p>
<h2 class="heading" id="опция-disablesetlocale-">
  Опция disableSetLocale ⚙️
  <a class="anchor" href="#%d0%be%d0%bf%d1%86%d0%b8%d1%8f-disablesetlocale-">#</a>
</h2>
<p>В конфиге появилась новая опция:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="nx">disableSetLocale</span> <span class="p">=</span> <span class="kc">true</span></span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>По умолчанию Hugo пытается установить системную локаль операционной системы согласно языку текущего сайта. Это влияет на форматирование дат, чисел, сортировку строк (<code>strings.Sort</code>).</p>
<p>Иногда это поведение нежелательно. Например, в CI-контейнере может не быть нужной локали, и Hugo молча падает на дефолт. Или вы хотите, чтобы поведение было одинаковым на всех машинах сборки независимо от языка контента.</p>
<p><code>disableSetLocale = true</code> отключает эту попытку. Hugo использует значения из конфига и встроенные форматтеры без обращения к OS-уровню <code>setlocale()</code>.</p>
<p>Когда нужно: CI-сборки на минимальных образах (alpine, debian-slim), где локали могут отсутствовать; репозитории с воспроизводимой сборкой; ситуации, когда вы заметили различия в форматах между локальной сборкой и CI.</p>
<h2 class="heading" id="депрекейт-пути-_vendorgithubcomgohugoiolocales">
  Депрекейт пути _vendor/github.com/gohugoio/locales
  <a class="anchor" href="#%d0%b4%d0%b5%d0%bf%d1%80%d0%b5%d0%ba%d0%b5%d0%b9%d1%82-%d0%bf%d1%83%d1%82%d0%b8-_vendorgithubcomgohugoiolocales">#</a>
</h2>
<p>Путь <code>_vendor/github.com/gohugoio/locales</code> помечен как устаревший. Если вы его явно использовали (например, импортировали как submodule в проект), стоит начать миграцию на встроенные локали Hugo — релиз поддерживает их через стандартный механизм.</p>
<p>Большинство пользователей этот путь не трогали, поэтому пункт скорее для разработчиков тем, чем для авторов сайтов.</p>
<h2 class="heading" id="чек-лист-обновления-за-30-минут-">
  Чек-лист обновления за 30 минут ✅
  <a class="anchor" href="#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%b7%d0%b0-30-%d0%bc%d0%b8%d0%bd%d1%83%d1%82-">#</a>
</h2>
<p>Чтобы пройти миграцию без сюрпризов:</p>
<ol>
<li>
<p><strong>Поднять версию Hugo</strong> до 0.161.0 (если ещё на старой) или 0.156.0 как промежуточный шаг. Если работаете в Docker, обновить <code>ARG HUGO_VERSION</code> и пересобрать образ.</p>
</li>
<li>
<p><strong>Поиск устаревших API в шаблонах:</strong></p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep -rn <span class="s1">&#39;site\.Data\|site\.Sites\|AllTranslations&#39;</span> layouts/ themes/*/layouts/</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Если хиты есть — заменить на <code>hugo.Data</code>, <code>hugo.Sites</code>, <code>Translations</code>.</p>
</li>
<li>
<p><strong>Проверить кастомные render hooks</strong>, если они есть. Можно ли получить пользу от новых <code>IsBlock</code> и <code>Ordinal</code>?</p>
</li>
<li>
<p><strong>Решить про <code>disableSetLocale</code>.</strong> Если CI-сборки иногда дают разные форматы дат в зависимости от хоста, добавить опцию в конфиг.</p>
</li>
<li>
<p><strong>Прогнать сборку:</strong></p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hugo --minify --gc</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Не должно быть warning&rsquo;ов. Если появились — каждый из них чем-то да полезен, не игнорировать.</p>
</li>
<li>
<p><strong>Открыть несколько критичных страниц локально</strong>, проверить, что меню, переводы, render hooks работают.</p>
</li>
<li>
<p><strong>Закоммитить и задеплоить.</strong> На статичном Hugo-сайте откат — это <code>git revert</code> плюс пересборка, минут 5.</p>
</li>
</ol>
<h2 class="heading" id="итог-">
  Итог 🧠
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3-">#</a>
</h2>
<p>Hugo 0.156.0 — не «громкий релиз ради хайпа». Это пакет аккуратных архитектурных изменений: переезд на новые API, расширение возможностей для тех, кто пишет render hooks, чистка мультиязычной части.</p>
<p>Главная польза от такого релиза — возможность снять немного технического долга прямо сейчас, пока депрекейты ещё работают. Пропустишь — через год будешь разбираться, почему что-то перестало рендериться.</p>
<p>Если у вас минимальный шаблонный код (стандартная тема без кастомных hook&rsquo;ов и без мультиязычности), переход почти прозрачен. Если у вас активные render hooks или мультиязык — стоит выделить полчаса на ревизию.</p>
<p>И да: каждый минор-релиз стоит читать. Не как обязательное домашнее задание, но как короткую инвестицию в собственный код.</p>
<h2 class="heading" id="источники">
  Источники
  <a class="anchor" href="#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8">#</a>
</h2>
<ul>
<li>Официальный релиз: <a href="https://github.com/gohugoio/hugo/releases/tag/v0.156.0">gohugoio/hugo v0.156.0</a></li>
<li>Документация по новым API: <a href="https://gohugo.io/methods/hugo/data/">hugo.Data</a>, <a href="https://gohugo.io/methods/hugo/sites/">hugo.Sites</a></li>
<li>Документация по переводам: <a href="https://gohugo.io/methods/page/translations/">site.Home.Translations</a></li>
<li>Документация link render hooks: <a href="https://gohugo.io/render-hooks/links/">Render hooks: links</a></li>
</ul>
]]></content:encoded><category>hugo</category><category>релизы</category></item><item><title>Домашняя колбаса: базовый рецепт на 1 кг 🥓</title><link>https://kushnaren.co/posts/domashnyaya-kolbasa-bazovyj-recept-na-1-kg/</link><pubDate>Wed, 11 Feb 2026 12:00:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/domashnyaya-kolbasa-bazovyj-recept-na-1-kg/</guid><description>Рабочая памятка по домашней колбасе на 1 кг: соль, нитрит, специи, ледяная вода, а также технология фарш-набивка-термообработка и типичные ошибки начинающих.</description><content:encoded><![CDATA[<h2 class="heading" id="зачем-вообще-делать-колбасу-дома">
  Зачем вообще делать колбасу дома
  <a class="anchor" href="#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d0%b4%d0%b5%d0%bb%d0%b0%d1%82%d1%8c-%d0%ba%d0%be%d0%bb%d0%b1%d0%b0%d1%81%d1%83-%d0%b4%d0%be%d0%bc%d0%b0">#</a>
</h2>
<p>Магазинная колбаса — продукт, который терпит много претензий: непрозрачный состав, переизбыток фосфатов, переменное качество от партии к партии, дискомфорт от того, что «там вообще мясо или нет». Домашняя — наоборот: ты сам решаешь, какое мясо, сколько соли, какие специи, как готовить, что положить в оболочку.</p>
<p>Это не «дешевле магазинной» (по факту — нет, особенно с учётом времени). Это «честнее и вкуснее, если потратить время на освоение базовых пропорций». А пропорции — самое важное в колбасе. Если соль/специи/влага в норме, даже простой кусок мяса превращается в нормальный продукт.</p>
<p>Этот пост — рабочая памятка с пропорциями, которые я использую как отправную точку, плюс короткое объяснение, зачем там каждый ингредиент. Без претензий на «единственно верный рецепт».</p>
<h2 class="heading" id="соль-и-добавки-на-1-кг-">
  Соль и добавки на 1 кг 🧂
  <a class="anchor" href="#%d1%81%d0%be%d0%bb%d1%8c-%d0%b8-%d0%b4%d0%be%d0%b1%d0%b0%d0%b2%d0%ba%d0%b8-%d0%bd%d0%b0-1-%d0%ba%d0%b3-">#</a>
</h2>
<p>Базовый набор:</p>
<ul>
<li><strong>Соль:</strong> 18-20 г.</li>
<li><strong>Оптимальный вариант:</strong> 16 г обычной соли + 2-3 г нитритной соли.</li>
<li><strong>Сахар:</strong> 5 г (примерно 1 ч. л.).</li>
<li><strong>Чёрный перец:</strong> 1 ч. л.</li>
<li><strong>Чеснок:</strong> 2 зубчика (около 8-10 г).</li>
<li><strong>Мускатный орех:</strong> щепотка.</li>
<li><strong>Лёд или ледяная вода:</strong> 80-100 мл.</li>
</ul>
<p>Этот набор использую как отправную точку. Дальше корректирую специи под конкретное мясо и личный вкус.</p>
<h2 class="heading" id="зачем-каждое-из-этого">
  Зачем каждое из этого
  <a class="anchor" href="#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%ba%d0%b0%d0%b6%d0%b4%d0%be%d0%b5-%d0%b8%d0%b7-%d1%8d%d1%82%d0%be%d0%b3%d0%be">#</a>
</h2>
<p>Чтобы рецепт не казался магической формулой, коротко по каждому пункту:</p>
<p><strong>Соль — 18-20 г на 1 кг.</strong> Это не «по вкусу». Это пороговая концентрация: меньше — фарш плохо «связывается» (соль вытягивает миофибриллярные белки, которые удерживают воду), больше — переедено и опасно. Норма для большинства колбас 1.8-2%.</p>
<p><strong>Нитритная соль — 2-3 г.</strong> Главный вопрос. Нитрит даёт характерный розовый цвет, узнаваемый «колбасный» вкус и — главное — подавляет рост ботулинических бактерий. Без него любая колбаса с длительной выдержкой или копчением рискует превратиться в источник опасных токсинов. Не «химия, которую нужно избегать», а функциональный консервант, который существует не зря. Концентрация 2-3 г нитритной соли (а не чистого нитрита) — безопасный диапазон, давно стандартизированный.</p>
<p><strong>Сахар — 5 г.</strong> Не для сладости. Для микробиоты — она «съедает» сахар и даёт лёгкое подкисление, плюс смягчает солёность.</p>
<p><strong>Чёрный перец, чеснок, мускат.</strong> Базовая «колбасная» ароматическая основа. Каждый компонент можно менять под вкус (паприка, горчица в зёрнах, кориандр, тмин), но эта тройка — спокойная отправная точка.</p>
<p><strong>Ледяная вода — 80-100 мл.</strong> Самое неочевидное для новичков. Вода не «разбавляет», а наоборот — вместе с белками образует эмульсию, которая держит структуру колбасы при нагреве. Если воду не добавить, колбаса получится сухой и зернистой. Если добавить тёплую — белки свернутся раньше времени, эмульсия не сложится. Лёд гарантирует, что фарш во время вымешивания остаётся холодным.</p>
<h2 class="heading" id="технология-процесса">
  Технология процесса
  <a class="anchor" href="#%d1%82%d0%b5%d1%85%d0%bd%d0%be%d0%bb%d0%be%d0%b3%d0%b8%d1%8f-%d0%bf%d1%80%d0%be%d1%86%d0%b5%d1%81%d1%81%d0%b0">#</a>
</h2>
<p>Кратко, без излишних деталей, чтобы держать целое в голове:</p>
<ol>
<li><strong>Подготовка мяса.</strong> Охладить до 1-3°C (не замораживать). Срезать жёсткие плёнки и хрящи, нарезать кусками удобного размера.</li>
<li><strong>Помол.</strong> Через мясорубку с решёткой 4-6 мм. Чем мельче, тем «нежнее» структура; чем крупнее — тем «деревенская» подача.</li>
<li><strong>Внесение соли и нитрита.</strong> На холодный фарш, тщательно перемешать.</li>
<li><strong>Специи и сахар.</strong> Добавить, перемешать снова.</li>
<li><strong>Лёд/ледяная вода.</strong> Добавлять постепенно, продолжая вымешивать. Цель — получить однородную массу, в которой видна вязкость («тянется» при отрыве).</li>
<li><strong>Холодная пауза.</strong> 1-3 часа в холодильнике, чтобы соль распределилась и фарш «созрел».</li>
<li><strong>Набивка.</strong> В оболочку (натуральную или коллагеновую), без воздушных пузырей.</li>
<li><strong>Термообработка.</strong> Зависит от типа: варка при 75-80°C до внутренней температуры 70-72°C, копчение, запекание.</li>
</ol>
<p>Эта последовательность — каркас. Каждый шаг можно тонко донастраивать, но если убрать любой из них, результат заметно проседает.</p>
<h2 class="heading" id="типичные-ошибки-начинающих-">
  Типичные ошибки начинающих 🪤
  <a class="anchor" href="#%d1%82%d0%b8%d0%bf%d0%b8%d1%87%d0%bd%d1%8b%d0%b5-%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b8-%d0%bd%d0%b0%d1%87%d0%b8%d0%bd%d0%b0%d1%8e%d1%89%d0%b8%d1%85-">#</a>
</h2>
<p>Самые частые сбои, которые легко избежать, если знать заранее:</p>
<p><strong>Слишком тёплый фарш.</strong> Если на любом этапе фарш нагрелся выше 12-15°C, эмульсия не сложится. Жир «вытопится» при варке, в продукте появятся жирные карманы. Лёд обязателен.</p>
<p><strong>Переизбыток соли.</strong> «Я люблю солёное» не работает. Превысил 25 г на кг — продукт не съесть, а ребалансировать сложно: соль из готовой колбасы не уберёшь.</p>
<p><strong>Недомес.</strong> Если фарш не дошёл до состояния «вязкий, тянется» — структура развалится. Вымешивание — главное, что делает колбасу колбасой, а не «фарш в кишке».</p>
<p><strong>Воздух в оболочке.</strong> Пузыри = места, где может развиваться нежелательная микрофлора. Прокалывать иголкой при набивке — нормальная практика.</p>
<p><strong>Резкий перегрев при варке.</strong> Внутри колбасы должно быть 70-72°C, не больше. Перегрев = выплавление жира + сухость. Для контроля — кулинарный термометр; «на глаз» в этом деле подводит.</p>
<p><strong>Спешка с дегустацией.</strong> Колбасе нужно «созреть» в холодильнике хотя бы 12-24 часа после варки. Свежевыкипяченная — это не финальный вкус.</p>
<h2 class="heading" id="варианты-по-виду-мяса">
  Варианты по виду мяса
  <a class="anchor" href="#%d0%b2%d0%b0%d1%80%d0%b8%d0%b0%d0%bd%d1%82%d1%8b-%d0%bf%d0%be-%d0%b2%d0%b8%d0%b4%d1%83-%d0%bc%d1%8f%d1%81%d0%b0">#</a>
</h2>
<p>Базовый рецепт работает для свинины и для смесей. Несколько типичных вариаций:</p>
<ul>
<li><strong>Свинина (60% мякоти + 40% жирной части).</strong> Классика, сбалансированный вкус, хороший для новичков.</li>
<li><strong>Свинина + говядина (70/30).</strong> Более «мясной» характер, плотнее структура, чуть жёстче.</li>
<li><strong>Только говядина.</strong> Суше, требует больше воды и аккуратности с эмульсией; вкус интенсивнее.</li>
<li><strong>Птица (грудка + бедро).</strong> Нежнее, ниже жирность; нужно поднять долю воды и тщательнее вымешивать.</li>
</ul>
<p>Соль и нитрит — те же. Воды иногда нужно немного больше для постного мяса.</p>
<h2 class="heading" id="безопасность-">
  Безопасность ⚠️
  <a class="anchor" href="#%d0%b1%d0%b5%d0%b7%d0%be%d0%bf%d0%b0%d1%81%d0%bd%d0%be%d1%81%d1%82%d1%8c-">#</a>
</h2>
<p>Несколько моментов, о которых стоит знать обязательно:</p>
<ul>
<li><strong>Нитритная соль ≠ чистый нитрит.</strong> Это уже разбавленная смесь (обычно 0.5-0.6% NaNO₂ в обычной соли). Использовать строго по дозировке для нитритной соли, не для чистого вещества.</li>
<li><strong>Ботулизм.</strong> Главная причина существования нитрита в колбасном деле. При длительной выдержке без нитрита риск реален; не «древняя страшилка».</li>
<li><strong>Контроль температуры.</strong> Внутренняя температура готового продукта 70-72°C — это не «совет», это безопасность.</li>
<li><strong>Сроки хранения.</strong> Домашняя колбаса без консервантов хранится в холодильнике значительно меньше магазинной. Замораживание — ок, но текстура немного меняется.</li>
</ul>
<p>Если делаете колбасу впервые — стоит прочитать пару проверенных источников по технологии (например, Stanley Marianski «Home Production of Quality Meats and Sausages» или книги по колбасному делу на русском). Это не та область, где «методом тыка» — оптимальный путь обучения.</p>
<h2 class="heading" id="куда-дальше-если-втянулся">
  Куда дальше, если втянулся
  <a class="anchor" href="#%d0%ba%d1%83%d0%b4%d0%b0-%d0%b4%d0%b0%d0%bb%d1%8c%d1%88%d0%b5-%d0%b5%d1%81%d0%bb%d0%b8-%d0%b2%d1%82%d1%8f%d0%bd%d1%83%d0%bb%d1%81%d1%8f">#</a>
</h2>
<p>Если базовый цикл стал привычным, есть много направлений для развития:</p>
<ul>
<li><strong>Сухое созревание</strong> (колбаса как salami). Долгий процесс, требует контроля температуры и влажности.</li>
<li><strong>Копчение</strong> (горячее или холодное). Меняет вкус и продлевает хранение.</li>
<li><strong>Свои смеси специй.</strong> Эксперименты с паприкой, фенхелем, кориандром, перцем чили — ровно по чайной ложке, чтобы не «перегрузить».</li>
<li><strong>Натуральные оболочки.</strong> Свиная черева, говяжьи синюги, бараньи кишки — каждая дает свою текстуру.</li>
</ul>
<p>Но это уже отдельная история. Базовый рецепт хорошо работает сам по себе и закрывает большую часть домашних запросов.</p>
<h2 class="heading" id="итоги">
  Итоги
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3%d0%b8">#</a>
</h2>
<p>Колбаса — простой по сути продукт, который очень чувствителен к деталям. Соль и нитрит обеспечивают безопасность и связку, лёд держит эмульсию, специи дают характер. Если эти три блока в порядке, всё остальное — настройка под вкус.</p>
<p>Этот рецепт — мой рабочий минимум. Точные пропорции вы со временем подгоните под себя, но 18-20 г соли, 80-100 мл ледяной воды, обязательная нитритная составляющая и аккуратное вымешивание — фундамент, на котором всё остальное держится. 🍴</p>
]]></content:encoded><category>кухня</category></item><item><title>Самогоноварение как эксперимент: наш опыт с абсентом 🍸</title><link>https://kushnaren.co/posts/samogonovarenie-absent-opyt-i-vyvody/</link><pubDate>Wed, 04 Feb 2026 12:00:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/samogonovarenie-absent-opyt-i-vyvody/</guid><description>Размышления после первого опыта с домашней ректификацией: почему вообще ввязались, что узнали про процесс, чем абсент сложнее обычного дистиллята, и какие выводы из этого сделали. Без рецептов и инструкций.</description><content:encoded><![CDATA[<h2 class="heading" id="почему-вообще-в-это-ввязались">
  Почему вообще в это ввязались
  <a class="anchor" href="#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d0%b2-%d1%8d%d1%82%d0%be-%d0%b2%d0%b2%d1%8f%d0%b7%d0%b0%d0%bb%d0%b8%d1%81%d1%8c">#</a>
</h2>
<p>Мы с моим другом Андреем давно обсуждали идею «разобраться, как это работает на самом деле». Самогоноварение показалось подходящим испытанием: техническая часть, химия, дисциплина, плюс шанс сделать что-то необычное своими руками. Не для того, чтобы «гнать в промышленных масштабах», а чтобы пройти процесс целиком и понять, где спрятаны нюансы, о которых снаружи не догадаешься.</p>
<p>Так в нашей жизни появилась ректификационная колонна «Дом Спирт 2» и дерзкая цель — попробовать сделать абсент. Через несколько подходов мы получили результат, который можно назвать «похожим на абсент», и значительно больше — наблюдений о самом процессе.</p>
<h2 class="heading" id="что-такое-ректификационная-колонна-упрощённо">
  Что такое ректификационная колонна (упрощённо)
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-%d1%80%d0%b5%d0%ba%d1%82%d0%b8%d1%84%d0%b8%d0%ba%d0%b0%d1%86%d0%b8%d0%be%d0%bd%d0%bd%d0%b0%d1%8f-%d0%ba%d0%be%d0%bb%d0%be%d0%bd%d0%bd%d0%b0-%d1%83%d0%bf%d1%80%d0%be%d1%89%d1%91%d0%bd%d0%bd%d0%be">#</a>
</h2>
<p>Колонна — это вертикальная труба, в которой пары спиртосодержащей жидкости поднимаются вверх, проходят через насадку (мелкие металлические или керамические элементы), частично конденсируются и стекают обратно. Это «ректификация» — многоступенчатая дистилляция в одном устройстве.</p>
<p>Эффект: получается высокая степень очистки, спирт-ректификат может быть крепче 90%, без значительной части примесей, которые делают «обычный самогон» неприятным.</p>
<p>«Дом Спирт 2» — российская модель домашней ректификационной колонны. Она не самая мощная и не самая компактная, но достаточно «честная»: достать, освоить базовый цикл, понять, как ведёт себя пар, давление, температура — реально для технически грамотного человека за несколько вечеров.</p>
<h2 class="heading" id="первые-впечатления-техника--это-не-половина-дела-а-почти-всё">
  Первые впечатления: техника — это не половина дела, а почти всё
  <a class="anchor" href="#%d0%bf%d0%b5%d1%80%d0%b2%d1%8b%d0%b5-%d0%b2%d0%bf%d0%b5%d1%87%d0%b0%d1%82%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d1%82%d0%b5%d1%85%d0%bd%d0%b8%d0%ba%d0%b0--%d1%8d%d1%82%d0%be-%d0%bd%d0%b5-%d0%bf%d0%be%d0%bb%d0%be%d0%b2%d0%b8%d0%bd%d0%b0-%d0%b4%d0%b5%d0%bb%d0%b0-%d0%b0-%d0%bf%d0%be%d1%87%d1%82%d0%b8-%d0%b2%d1%81%d1%91">#</a>
</h2>
<p>Оказалось, что в этом хобби важна не только «железка», но и терпение. Ожидания были простыми: собрал, запустил, получил результат. Реальность — другая: время, аккуратность, много мелких нюансов и постоянное чувство, что упускаешь какую-то деталь.</p>
<p>Несколько вещей, которых мы не ожидали:</p>
<ul>
<li><strong>Длительность процесса.</strong> Полный цикл от загрузки до получения готового продукта — это много часов, иногда вся суббота.</li>
<li><strong>Чувствительность к деталям.</strong> Изменение скорости отбора, температуры, охлаждения дефлегматора — каждое влияет на результат сильнее, чем казалось из видеообзоров.</li>
<li><strong>Гигиена.</strong> «Чисто» в обычном смысле и «чисто» для процесса — разные понятия.</li>
<li><strong>Дисциплина протокола.</strong> Если делать «на глаз» — результаты непредсказуемы. Если фиксировать каждый шаг — повторяемость растёт.</li>
</ul>
<h2 class="heading" id="чем-абсент-сложнее-обычного-дистиллята">
  Чем абсент сложнее «обычного дистиллята»
  <a class="anchor" href="#%d1%87%d0%b5%d0%bc-%d0%b0%d0%b1%d1%81%d0%b5%d0%bd%d1%82-%d1%81%d0%bb%d0%be%d0%b6%d0%bd%d0%b5%d0%b5-%d0%be%d0%b1%d1%8b%d1%87%d0%bd%d0%be%d0%b3%d0%be-%d0%b4%d0%b8%d1%81%d1%82%d0%b8%d0%bb%d0%bb%d1%8f%d1%82%d0%b0">#</a>
</h2>
<p>Классический спирт-ректификат — это, грубо, очищенный этанол с водой. Абсент сложнее устроен:</p>
<ol>
<li><strong>Мацерация.</strong> Травы (полынь, анис, фенхель и менее афишированные растения) настаиваются на готовом спирте.</li>
<li><strong>Перегонка с травами.</strong> Полученный настой перегоняется заново — часть ароматических компонентов переходит в дистиллят, часть остаётся в кубе.</li>
<li><strong>Финальное окрашивание.</strong> Дополнительная мацерация на свежих травах даёт характерный зелёный или жёлтый оттенок.</li>
</ol>
<p>Каждый из этих шагов имеет свои переменные: какие травы, в какой пропорции, при какой температуре, как долго. Мелкая ошибка на любом шаге даёт ощутимое отклонение в финальном вкусе. И если «обычный самогон» прощает многое, абсент прощает мало.</p>
<p>Дополнительно — травы дают так называемые «термолабильные» компоненты, которые легко разрушаются при перегреве. Это даёт парадоксальную ситуацию: вы технически умеете гнать спирт, но при попытке сделать абсент получаете либо «горький эликсир», либо «спиртовая вода с лёгким намёком на полынь» — без середины.</p>
<h2 class="heading" id="что-получилось">
  Что получилось
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d0%bf%d0%be%d0%bb%d1%83%d1%87%d0%b8%d0%bb%d0%be%d1%81%d1%8c">#</a>
</h2>
<ul>
<li>Мы разобрались с базовой логикой процесса и перестали «бояться» оборудования.</li>
<li>Получили результат, который можно оценивать — не идеально, но уже похоже на задуманное.</li>
<li>Поняли, какие этапы требуют больше контроля, чем казалось на старте.</li>
<li>Научились читать поведение колонны: давление, температура, скорость капельного отбора.</li>
<li>Сформировали собственный «лог сессии», который помогает повторять удачные попытки.</li>
</ul>
<h2 class="heading" id="что-не-получилось">
  Что не получилось
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d0%bd%d0%b5-%d0%bf%d0%be%d0%bb%d1%83%d1%87%d0%b8%d0%bb%d0%be%d1%81%d1%8c">#</a>
</h2>
<ul>
<li><strong>Стабильность.</strong> Один и тот же подход давал разный результат от подхода к подходу.</li>
<li><strong>Предсказуемость вкуса и аромата.</strong> Это тонкая, почти ювелирная работа.</li>
<li><strong>Терпение.</strong> Иногда проще поставить эксперимент на паузу, чем пытаться «додавить» его усталостью.</li>
<li><strong>Точная имитация классического абсента.</strong> До «как в Швейцарии» далеко; пока ближе к «дегустационному образцу собственного производства».</li>
</ul>
<h2 class="heading" id="главный-вывод">
  Главный вывод
  <a class="anchor" href="#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d1%8b%d0%b9-%d0%b2%d1%8b%d0%b2%d0%be%d0%b4">#</a>
</h2>
<p>Самогоноварение — это не «про напиток», а про процесс и дисциплину. Это про внимание к деталям и честность с самим собой: если хочешь качественный результат, придётся уважать все этапы, а не только финал.</p>
<p>Любая часть, которую попытаешься «срезать», даст видимый результат: либо во вкусе, либо в стабильности, либо в безопасности. Это, наверное, и есть главная ценность таких хобби: они напоминают, что качественный результат складывается из мелочей, которые поодиночке кажутся незначительными.</p>
<h2 class="heading" id="безопасность-важное">
  Безопасность (важное)
  <a class="anchor" href="#%d0%b1%d0%b5%d0%b7%d0%be%d0%bf%d0%b0%d1%81%d0%bd%d0%be%d1%81%d1%82%d1%8c-%d0%b2%d0%b0%d0%b6%d0%bd%d0%be%d0%b5">#</a>
</h2>
<p>Несколько вещей, которые нужно проговорить отдельно:</p>
<ul>
<li><strong>Голов и хвостов нельзя пить.</strong> Первые и последние фракции содержат метанол, сивушные масла и другие соединения, потенциально опасные. Их отделяют по проверенной методике.</li>
<li><strong>Высокая концентрация спирта = высокая огнеопасность.</strong> Колонна греется, пары горючи, помещение должно быть вентилируемым.</li>
<li><strong>Лицензирование и закон.</strong> В разных странах правила производства алкоголя для личного потребления отличаются. Ответственность всегда на изготовителе.</li>
<li><strong>Дегустация.</strong> Не «глотнуть и надеяться». Малая проба, оценка по органолептике, никаких больших порций неотлежавшегося продукта.</li>
</ul>
<p>Я не даю инструкций и рецептов в этом посте сознательно. Это пост про опыт и наблюдения, а не учебник.</p>
<h2 class="heading" id="зачем-писать-об-этом">
  Зачем писать об этом
  <a class="anchor" href="#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%bf%d0%b8%d1%81%d0%b0%d1%82%d1%8c-%d0%be%d0%b1-%d1%8d%d1%82%d0%be%d0%bc">#</a>
</h2>
<p>Потому что это хороший пример живого эксперимента: мы не профессионалы, а просто люди, которым интересно разобраться. И именно в таких историях — ценность. Не в том, чтобы «повторить», а в том, чтобы увидеть путь, ошибки и выводы.</p>
<p>Любое сложное хобби работает по похожей траектории: первые попытки выглядят неуклюже, потом приходит понимание базовых механик, дальше — наблюдение за деталями, и только потом становится возможным «сознательно делать вкус», а не «случайно получать что-то близкое».</p>
<h2 class="heading" id="что-дальше">
  Что дальше
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d0%b4%d0%b0%d0%bb%d1%8c%d1%88%d0%b5">#</a>
</h2>
<p>Мы продолжаем экспериментировать. Возможно, через пару месяцев будет вторая часть — с более стабильным результатом и ясными выводами. Пока же — фиксируем этап и идём дальше.</p>
<p>Если резюмировать одной фразой: самое важное, что я понял за эти подходы — терпение в этом хобби стоит больше, чем оборудование. Хорошая колонна не сделает за тебя дисциплину. А плохая дисциплина не оправдается дорогой колонной. 🧪</p>
]]></content:encoded><category>кухня</category></item><item><title>Пишем посты с Codex: от идеи до публикации без лишней рутины ✍️</title><link>https://kushnaren.co/posts/pishem-posty-s-codex-avtomatizaciya-nashe-vse/</link><pubDate>Mon, 02 Feb 2026 12:00:00 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/pishem-posty-s-codex-avtomatizaciya-nashe-vse/</guid><description>Подробная схема работы с Codex для Hugo-блога: подготовка идеи, сборка черновика, редактура, техническая валидация, публикация и контроль качества.</description><content:encoded><![CDATA[<p>Когда блог ведётся регулярно, главная проблема часто не в том, «о чём писать», а в том, сколько мелкой рутины появляется вокруг каждой публикации: структура, фронтматтер, теги, формат даты, локальная проверка, коммит, пуш.</p>
<p>В какой-то момент я понял простую вещь: если не автоматизировать технические повторяющиеся шаги, то темп публикаций будет определяться не качеством идей, а запасом терпения. Именно здесь Codex даёт максимальную пользу. 🚀</p>
<p>Важно уточнить позицию сразу: Codex не заменяет автора и редактора. Он ускоряет производство черновика, стабилизирует техническую часть и снимает «операционную усталость», чтобы энергия оставалась на смыслы.</p>
<h2 class="heading" id="где-именно-теряется-время-при-выпуске-поста">
  Где именно теряется время при выпуске поста
  <a class="anchor" href="#%d0%b3%d0%b4%d0%b5-%d0%b8%d0%bc%d0%b5%d0%bd%d0%bd%d0%be-%d1%82%d0%b5%d1%80%d1%8f%d0%b5%d1%82%d1%81%d1%8f-%d0%b2%d1%80%d0%b5%d0%bc%d1%8f-%d0%bf%d1%80%d0%b8-%d0%b2%d1%8b%d0%bf%d1%83%d1%81%d0%ba%d0%b5-%d0%bf%d0%be%d1%81%d1%82%d0%b0">#</a>
</h2>
<p>До автоматизации у меня стабильно проседали одни и те же этапы:</p>
<ol>
<li>Переход от идеи к первому цельному черновику.</li>
<li>Ручное оформление фронтматтера без ошибок.</li>
<li>Поддержание одинакового уровня качества для <code>summary</code> и <code>description</code>.</li>
<li>Повторяемые технические действия перед публикацией.</li>
</ol>
<p>Сами шаги простые, но они фрагментируют внимание. После двух-трёх переключений между «текстом» и «технической обвязкой» уже сложнее держать мысль статьи плотной и последовательной.</p>
<h2 class="heading" id="какую-роль-я-отдаю-codex-а-какую-оставляю-себе">
  Какую роль я отдаю Codex, а какую оставляю себе
  <a class="anchor" href="#%d0%ba%d0%b0%d0%ba%d1%83%d1%8e-%d1%80%d0%be%d0%bb%d1%8c-%d1%8f-%d0%be%d1%82%d0%b4%d0%b0%d1%8e-codex-%d0%b0-%d0%ba%d0%b0%d0%ba%d1%83%d1%8e-%d0%be%d1%81%d1%82%d0%b0%d0%b2%d0%bb%d1%8f%d1%8e-%d1%81%d0%b5%d0%b1%d0%b5">#</a>
</h2>
<p>Чтобы автоматизация была полезной, роли должны быть разделены жёстко.</p>
<p><strong>За мной остаётся:</strong></p>
<ul>
<li>тезис и позиция статьи;</li>
<li>финальная логика аргументации;</li>
<li>личный опыт, выводы и тональность.</li>
</ul>
<p><strong>Codex получает:</strong></p>
<ul>
<li>сборку структурированного черновика из тезисов;</li>
<li>технически корректный фронтматтер;</li>
<li>контроль повторяющихся формальностей;</li>
<li>подготовку git-части к публикации.</li>
</ul>
<p>Такая схема позволяет не спорить с инструментом о «вкусе текста», а использовать его там, где важнее точность и скорость.</p>
<h2 class="heading" id="рабочий-конвейер-от-идеи-до-публикации">
  Рабочий конвейер: от идеи до публикации
  <a class="anchor" href="#%d1%80%d0%b0%d0%b1%d0%be%d1%87%d0%b8%d0%b9-%d0%ba%d0%be%d0%bd%d0%b2%d0%b5%d0%b9%d0%b5%d1%80-%d0%be%d1%82-%d0%b8%d0%b4%d0%b5%d0%b8-%d0%b4%d0%be-%d0%bf%d1%83%d0%b1%d0%bb%d0%b8%d0%ba%d0%b0%d1%86%d0%b8%d0%b8">#</a>
</h2>
<p>Ниже процесс, который у меня реально работает в блоге на Hugo.</p>
<h3 class="heading" id="1-формулирую-цель-поста-в-двух-фразах">
  1. Формулирую цель поста в двух фразах
  <a class="anchor" href="#1-%d1%84%d0%be%d1%80%d0%bc%d1%83%d0%bb%d0%b8%d1%80%d1%83%d1%8e-%d1%86%d0%b5%d0%bb%d1%8c-%d0%bf%d0%be%d1%81%d1%82%d0%b0-%d0%b2-%d0%b4%d0%b2%d1%83%d1%85-%d1%84%d1%80%d0%b0%d0%b7%d0%b0%d1%85">#</a>
</h3>
<p>Перед стартом фиксирую:</p>
<ul>
<li>для кого материал;</li>
<li>какое практическое действие читатель сможет сделать после прочтения.</li>
</ul>
<p>Если это не ясно на старте, статья почти всегда расползается.</p>
<h3 class="heading" id="2-даю-codex-каркас-будущего-текста">
  2. Даю Codex каркас будущего текста
  <a class="anchor" href="#2-%d0%b4%d0%b0%d1%8e-codex-%d0%ba%d0%b0%d1%80%d0%ba%d0%b0%d1%81-%d0%b1%d1%83%d0%b4%d1%83%d1%89%d0%b5%d0%b3%d0%be-%d1%82%d0%b5%d0%ba%d1%81%d1%82%d0%b0">#</a>
</h3>
<p>Минимальный вход:</p>
<ul>
<li>рабочий заголовок;</li>
<li>5-8 смысловых пунктов;</li>
<li>2-3 конкретных примера;</li>
<li>желаемая глубина (коротко / подробно / разбор).</li>
</ul>
<p>На этом этапе я не прошу «красивый текст», я прошу структуру, которую удобно усиливать.</p>
<h3 class="heading" id="3-собираю-длинный-черновик-и-сразу-проверяю-логические-провалы">
  3. Собираю длинный черновик и сразу проверяю логические провалы
  <a class="anchor" href="#3-%d1%81%d0%be%d0%b1%d0%b8%d1%80%d0%b0%d1%8e-%d0%b4%d0%bb%d0%b8%d0%bd%d0%bd%d1%8b%d0%b9-%d1%87%d0%b5%d1%80%d0%bd%d0%be%d0%b2%d0%b8%d0%ba-%d0%b8-%d1%81%d1%80%d0%b0%d0%b7%d1%83-%d0%bf%d1%80%d0%be%d0%b2%d0%b5%d1%80%d1%8f%d1%8e-%d0%bb%d0%be%d0%b3%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d0%b2%d0%b0%d0%bb%d1%8b">#</a>
</h3>
<p>Обычно я смотрю:</p>
<ul>
<li>нет ли повторов между разделами;</li>
<li>есть ли причинно-следственная связка между блоками;</li>
<li>не превращаются ли выводы в общие фразы.</li>
</ul>
<p>Если провалы есть, лучше исправить их до стилистической шлифовки.</p>
<h3 class="heading" id="4-редактирую-смысл-вручную">
  4. Редактирую смысл вручную
  <a class="anchor" href="#4-%d1%80%d0%b5%d0%b4%d0%b0%d0%ba%d1%82%d0%b8%d1%80%d1%83%d1%8e-%d1%81%d0%bc%d1%8b%d1%81%d0%bb-%d0%b2%d1%80%d1%83%d1%87%d0%bd%d1%83%d1%8e">#</a>
</h3>
<p>Это самый важный шаг. Здесь я:</p>
<ul>
<li>вырезаю «воду»;</li>
<li>добавляю реальные наблюдения;</li>
<li>уточняю формулировки, где можно понять двояко;</li>
<li>усиливаю практическую часть (чек-листы, сценарии, ограничения).</li>
</ul>
<p>Именно здесь текст перестаёт быть «генерированным» и становится авторским.</p>
<h3 class="heading" id="5-передаю-codex-техническое-оформление">
  5. Передаю Codex техническое оформление
  <a class="anchor" href="#5-%d0%bf%d0%b5%d1%80%d0%b5%d0%b4%d0%b0%d1%8e-codex-%d1%82%d0%b5%d1%85%d0%bd%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%be%d0%b5-%d0%be%d1%84%d0%be%d1%80%d0%bc%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5">#</a>
</h3>
<p>После смысловой правки Codex приводит материал к правилам блога:</p>
<ul>
<li>корректный фронтматтер;</li>
<li>осмысленные <code>summary</code> и <code>description</code> без дублирования;</li>
<li>релевантные теги;</li>
<li>структура с оглавлением и читабельными секциями;</li>
<li>уместные эмодзи для акцентов.</li>
</ul>
<h3 class="heading" id="6-финальная-техническая-валидация">
  6. Финальная техническая валидация
  <a class="anchor" href="#6-%d1%84%d0%b8%d0%bd%d0%b0%d0%bb%d1%8c%d0%bd%d0%b0%d1%8f-%d1%82%d0%b5%d1%85%d0%bd%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b0%d1%8f-%d0%b2%d0%b0%d0%bb%d0%b8%d0%b4%d0%b0%d1%86%d0%b8%d1%8f">#</a>
</h3>
<p>Перед публикацией использую обязательный минимум:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hugo list future
</span></span><span class="line"><span class="cl">hugo --minify</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Если пост попал в <code>future</code>, дата сдвигается назад (МСК, <code>+03:00</code>), затем повторная проверка.</p>
<h3 class="heading" id="7-публикация">
  7. Публикация
  <a class="anchor" href="#7-%d0%bf%d1%83%d0%b1%d0%bb%d0%b8%d0%ba%d0%b0%d1%86%d0%b8%d1%8f">#</a>
</h3>
<p>После проверки Codex помогает зафиксировать изменения:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add .
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;Осмысленный коммит на русском по теме поста&#34;</span>
</span></span><span class="line"><span class="cl">git push</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>На этом этапе уже не нужно держать в голове «не забыть бы пуш» или «как назвать коммит».</p>
<h2 class="heading" id="что-реально-улучшилось-после-внедрения">
  Что реально улучшилось после внедрения
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d0%be-%d1%83%d0%bb%d1%83%d1%87%d1%88%d0%b8%d0%bb%d0%be%d1%81%d1%8c-%d0%bf%d0%be%d1%81%d0%bb%d0%b5-%d0%b2%d0%bd%d0%b5%d0%b4%d1%80%d0%b5%d0%bd%d0%b8%d1%8f">#</a>
</h2>
<p>Я получил не «магическое ускорение», а более важную вещь: предсказуемость процесса.</p>
<p>Практические эффекты:</p>
<ul>
<li>меньше технических ошибок во фронтматтере;</li>
<li>стабильнее качество мета-описаний;</li>
<li>ниже время между идеей и публикацией;</li>
<li>меньше усталости от повторяющихся операций.</li>
</ul>
<p>Главный результат: я пишу чаще и ровнее по качеству, потому что внимание уходит в содержание, а не в микрорутину.</p>
<h2 class="heading" id="типовые-ошибки-при-работе-с-codex-и-как-их-избежать">
  Типовые ошибки при работе с Codex (и как их избежать)
  <a class="anchor" href="#%d1%82%d0%b8%d0%bf%d0%be%d0%b2%d1%8b%d0%b5-%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b8-%d0%bf%d1%80%d0%b8-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b5-%d1%81-codex-%d0%b8-%d0%ba%d0%b0%d0%ba-%d0%b8%d1%85-%d0%b8%d0%b7%d0%b1%d0%b5%d0%b6%d0%b0%d1%82%d1%8c">#</a>
</h2>
<h3 class="heading" id="ошибка-1-просить-сразу-финальный-пост">
  Ошибка 1: просить «сразу финальный пост»
  <a class="anchor" href="#%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b0-1-%d0%bf%d1%80%d0%be%d1%81%d0%b8%d1%82%d1%8c-%d1%81%d1%80%d0%b0%d0%b7%d1%83-%d1%84%d0%b8%d0%bd%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b9-%d0%bf%d0%be%d1%81%d1%82">#</a>
</h3>
<p>Так почти всегда получается текст без авторской позиции. Лучше идти через этапы: каркас → черновик → ручная редактура → техническая сборка.</p>
<h3 class="heading" id="ошибка-2-не-задавать-ограничения">
  Ошибка 2: не задавать ограничения
  <a class="anchor" href="#%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b0-2-%d0%bd%d0%b5-%d0%b7%d0%b0%d0%b4%d0%b0%d0%b2%d0%b0%d1%82%d1%8c-%d0%be%d0%b3%d1%80%d0%b0%d0%bd%d0%b8%d1%87%d0%b5%d0%bd%d0%b8%d1%8f">#</a>
</h3>
<p>Если не проговорить глубину, аудиторию и формат, модель заполняет пробелы усреднёнными формулировками. Чем точнее входные параметры, тем полезнее выход.</p>
<h3 class="heading" id="ошибка-3-пропускать-финальный-ручной-проход">
  Ошибка 3: пропускать финальный ручной проход
  <a class="anchor" href="#%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b0-3-%d0%bf%d1%80%d0%be%d0%bf%d1%83%d1%81%d0%ba%d0%b0%d1%82%d1%8c-%d1%84%d0%b8%d0%bd%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b9-%d1%80%d1%83%d1%87%d0%bd%d0%be%d0%b9-%d0%bf%d1%80%d0%be%d1%85%d0%be%d0%b4">#</a>
</h3>
<p>Без авторской правки текст может быть «правильным», но без нервов и фактуры. Читатель это считывает мгновенно.</p>
<h2 class="heading" id="мини-шаблон-запроса-который-у-меня-работает">
  Мини-шаблон запроса, который у меня работает
  <a class="anchor" href="#%d0%bc%d0%b8%d0%bd%d0%b8-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd-%d0%b7%d0%b0%d0%bf%d1%80%d0%be%d1%81%d0%b0-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b9-%d1%83-%d0%bc%d0%b5%d0%bd%d1%8f-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d0%b5%d1%82">#</a>
</h2>
<p>Я обычно даю Codex примерно такую рамку:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Нужен пост для Hugo-блога на русском.
</span></span><span class="line"><span class="cl">Формат: длинная статья, глубокая проработка, практические выводы.
</span></span><span class="line"><span class="cl">Структура: вступление, 6-8 разделов, финальный чек-лист.
</span></span><span class="line"><span class="cl">Обязательно: осмысленные summary/description, корректный фронтматтер, уместные эмодзи.
</span></span><span class="line"><span class="cl">После текста: проверить техническую часть публикации (hugo list future, hugo --minify, commit, push).</span></span></code></pre></div>
  <button class="copy-code-button">copy</button>
</div>
<p>Этого достаточно, чтобы стартовый результат был рабочим, а не «общей заготовкой».</p>
<h2 class="heading" id="когда-codex-точно-не-помогает">
  Когда Codex точно не помогает
  <a class="anchor" href="#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-codex-%d1%82%d0%be%d1%87%d0%bd%d0%be-%d0%bd%d0%b5-%d0%bf%d0%be%d0%bc%d0%be%d0%b3%d0%b0%d0%b5%d1%82">#</a>
</h2>
<p>После пары месяцев работы в этом режиме можно честно перечислить ситуации, где автоматизация скорее мешает:</p>
<p><strong>Когда тема ещё не оформилась.</strong> Если у вас в голове только смутное «хочется написать про X», Codex выдаст структуру вокруг этой смутности — формально стройную, по сути пустую. Лучше потратить полчаса на бумажный набросок, выписать 3-5 ключевых тезисов, и только потом включать инструмент. Код первым приходит туда, где есть запрос; в текстах этот запрос — авторская позиция.</p>
<p><strong>Личные посты.</strong> Эссе про опыт, наблюдение, ошибку — Codex здесь не нужен. Структуру эссе диктует не методическая разбивка на h2-разделы, а внутренняя логика рассказа. Прогон через шаблон «вступление → 6 разделов → чек-лист» убивает живость текста. Технические руководства — да; рефлексия — нет.</p>
<p><strong>Когда ответ нужно проверить руками.</strong> Если вы пишете про инструмент, который сами не запускали (например, обзор библиотеки по чужим текстам), Codex усилит риски: соберёт правдоподобный текст с фактическими ошибками. Точность здесь обеспечивает только личное прохождение сценария, до буквы команд.</p>
<h2 class="heading" id="что-меняется-в-процессе-через-3-6-месяцев">
  Что меняется в процессе через 3-6 месяцев
  <a class="anchor" href="#%d1%87%d1%82%d0%be-%d0%bc%d0%b5%d0%bd%d1%8f%d0%b5%d1%82%d1%81%d1%8f-%d0%b2-%d0%bf%d1%80%d0%be%d1%86%d0%b5%d1%81%d1%81%d0%b5-%d1%87%d0%b5%d1%80%d0%b5%d0%b7-3-6-%d0%bc%d0%b5%d1%81%d1%8f%d1%86%d0%b5%d0%b2">#</a>
</h2>
<p>Любой workflow эволюционирует. Если поначалу Codex заметно ускоряет каждую публикацию, дальше эффект становится тише и переезжает в другую плоскость:</p>
<ul>
<li>Вы начинаете писать чаще, потому что нет «трения старта».</li>
<li>Качество публикаций становится ровным — нет «слабых» постов из-за усталости от рутины.</li>
<li>Появляется привычка фиксировать правила процесса (<code>AGENTS.md</code>, шаблоны, чек-листы) сразу, а не «когда-нибудь».</li>
<li>Перестаёте откладывать посты из-за мелких технических придирок (формат даты, длина summary, теги).</li>
</ul>
<p>Это не «магия ИИ». Это просто разделение труда между «чем я уникально полезен» и «что можно поручить инструменту».</p>
<h2 class="heading" id="практические-выводы">
  Практические выводы
  <a class="anchor" href="#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b5-%d0%b2%d1%8b%d0%b2%d0%be%d0%b4%d1%8b">#</a>
</h2>
<p>Если коротко, рабочая модель такая:</p>
<ol>
<li>Смысл и позиция остаются у автора.</li>
<li>Codex берёт структуру и операционную рутину.</li>
<li>Качество растёт там, где есть дисциплина процесса, а не просто «сильный промпт». ✅</li>
</ol>
<p>Автоматизация в блоге полезна не потому, что «пишет быстрее», а потому что защищает внимание автора. А внимание, в долгую, важнее любой единичной публикации.</p>
]]></content:encoded><category>ai-инструменты</category><category>автоматизация</category><category>runbook</category></item><item><title>Здоровье, биохакинг и БАДы: как не потерять голову 💊</title><link>https://kushnaren.co/posts/zdorove-biohaking-bady/</link><pubDate>Mon, 19 Jan 2026 10:17:04 +0300</pubDate><guid isPermaLink="true">https://kushnaren.co/posts/zdorove-biohaking-bady/</guid><description>Без культа, без магии, без обещаний: разбор базовых привычек, роли анализов, принципов выбора БАДов и типичных ловушек, в которые попадает любой, кто впервые погружается в тему биохакинга.</description><content:encoded><![CDATA[<h2 class="heading" id="введение">
  Введение
  <a class="anchor" href="#%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5">#</a>
</h2>
<p>Тема здоровья легко превращается в гонку гаджетов, протоколов и баночек. Зашёл в любой телеграм-канал «про биохак», полистал — и через неделю на полке стоят семь новых БАДов, на руке трекер сна, на диване книга про митохондрии. А базовые вещи — режим сна, ежедневное движение, нормальный завтрак — продолжают проседать.</p>
<p>Я хочу держать тему в более приземлённом поле: сначала базовые привычки, потом аккуратные эксперименты, и только затем — добавки, если есть для этого основания. Этот пост — не медицинская рекомендация и не «топ-10 must-have БАДов». Это попытка нарисовать карту, по которой можно ориентироваться, не теряя голову.</p>
<h2 class="heading" id="база-без-которой-не-работает-ничего-">
  База, без которой не работает ничего 🛏
  <a class="anchor" href="#%d0%b1%d0%b0%d0%b7%d0%b0-%d0%b1%d0%b5%d0%b7-%d0%ba%d0%be%d1%82%d0%be%d1%80%d0%be%d0%b9-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d0%b5%d1%82-%d0%bd%d0%b8%d1%87%d0%b5%d0%b3%d0%be-">#</a>
</h2>
<p>Самые надёжные улучшения лежат в простых вещах, и они банальны до зевоты:</p>
<ul>
<li><strong>Сон по расписанию.</strong> Одинаковое время отбоя и подъёма (включая выходные), темнота в спальне, ограничение экранов за час до сна. Не «8 часов любой ценой», а ровные 7-8 часов в одни и те же часы.</li>
<li><strong>Движение каждый день.</strong> Прогулки, силовые, растяжка — не важно что именно, важно регулярно. Дневная норма шагов даже без зала уже даёт большой кусок пользы.</li>
<li><strong>Рацион с адекватным белком, клетчаткой и достаточной калорийностью.</strong> Не «суперфуды», не «чистое питание» в религиозном смысле, а понятный рацион без хронического дефицита.</li>
<li><strong>Управление стрессом.</strong> Простые ритуалы (вечерняя прогулка, регулярные паузы), ограничение перегруза, цифровая гигиена. Не «медитация 1 час в день», а «не работать вечером».</li>
</ul>
<p>Если эти пункты не закрыты, «биохакинг» превращается в красивую упаковку поверх системных дыр. Купить адаптоген, чтобы компенсировать хронический недосып — это не про здоровье, это про прокрастинацию здоровья.</p>
<h2 class="heading" id="биохакинг-как-дисциплина-а-не-как-шоу">
  Биохакинг как дисциплина, а не как шоу
  <a class="anchor" href="#%d0%b1%d0%b8%d0%be%d1%85%d0%b0%d0%ba%d0%b8%d0%bd%d0%b3-%d0%ba%d0%b0%d0%ba-%d0%b4%d0%b8%d1%81%d1%86%d0%b8%d0%bf%d0%bb%d0%b8%d0%bd%d0%b0-%d0%b0-%d0%bd%d0%b5-%d0%ba%d0%b0%d0%ba-%d1%88%d0%be%d1%83">#</a>
</h2>
<p>Для меня биохакинг — это не про сверхчеловеческие цели типа «жить до 120 лет», а про наблюдение и микро-настройку под собственные особенности. Три принципа, без которых тема превращается в магазин:</p>
<ol>
<li><strong>Измерять только то, что будет влиять на решения.</strong> Если данные не меняют поведение, это не данные, это шум.</li>
<li><strong>Менять по одному фактору за раз.</strong> Иначе невозможно понять, что именно сработало (и сработало ли). Ввели магний, омегу и витамин D одновременно — улучшение есть, но причина неизвестна.</li>
<li><strong>Держать горизонт не в днях, а в месяцах.</strong> Большинство значимых изменений в самочувствии копится медленно. «Через неделю стало лучше» — обычно эффект новизны, а не реальный сдвиг.</li>
</ol>
<p>Эти три правила сами по себе отсекают 80% эзотерических протоколов из соцсетей. Если автор предлагает «попробуй и сразу почувствуешь разницу» — он не про дисциплину, он про маркетинг.</p>
<h2 class="heading" id="анализы-и-данные-когда-они-действительно-нужны-">
  Анализы и данные: когда они действительно нужны 📊
  <a class="anchor" href="#%d0%b0%d0%bd%d0%b0%d0%bb%d0%b8%d0%b7%d1%8b-%d0%b8-%d0%b4%d0%b0%d0%bd%d0%bd%d1%8b%d0%b5-%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d0%be%d0%bd%d0%b8-%d0%b4%d0%b5%d0%b9%d1%81%d1%82%d0%b2%d0%b8%d1%82%d0%b5%d0%bb%d1%8c%d0%bd%d0%be-%d0%bd%d1%83%d0%b6%d0%bd%d1%8b-">#</a>
</h2>
<p>Слепая сдача десятков анализов «потому что в соседнем чате посоветовали» редко помогает. Полезнее идти от вопросов:</p>
<ul>
<li>Что именно я хочу улучшить или понять?</li>
<li>Есть ли симптомы или жалобы?</li>
<li>Что из результатов повлияет на конкретные действия?</li>
</ul>
<p>Если ответов нет — анализы становятся способом успокоиться, а не инструментом управления.</p>
<p>Несколько маркеров, которые часто имеют смысл проверять (без претензии на исчерпывающий список):</p>
<ul>
<li><strong>Витамин D (25-OH).</strong> В умеренных широтах хронический дефицит у большинства, особенно зимой.</li>
<li><strong>Ферритин.</strong> Маркер запасов железа; низкий часто объясняет «беспричинную» усталость.</li>
<li><strong>B12 и фолат.</strong> Особенно важно для тех, кто ограничивает мясо.</li>
<li><strong>Магний.</strong> Анализ малоинформативен (магний в основном внутри клеток), но если есть симптомы (судороги, аритмии) — стоит обсудить с врачом.</li>
<li><strong>ТТГ + Т4 свободный.</strong> Базовая щитовидка, влияет на энергию, вес, настроение.</li>
</ul>
<p>Это не «обязательная панель», это набор «если есть жалобы — есть смысл начать отсюда».</p>
<h2 class="heading" id="бады-инструмент-а-не-спасение">
  БАДы: инструмент, а не спасение
  <a class="anchor" href="#%d0%b1%d0%b0%d0%b4%d1%8b-%d0%b8%d0%bd%d1%81%d1%82%d1%80%d1%83%d0%bc%d0%b5%d0%bd%d1%82-%d0%b0-%d0%bd%d0%b5-%d1%81%d0%bf%d0%b0%d1%81%d0%b5%d0%bd%d0%b8%d0%b5">#</a>
</h2>
<p>Добавки могут быть уместны, когда есть <strong>дефицит</strong>, <strong>особенности питания</strong> или <strong>медицинские показания</strong>. В большинстве случаев они не перекрывают плохой сон, хаотичное питание и хронический стресс.</p>
<p>Принципы, которых я стараюсь придерживаться:</p>
<ul>
<li>Сначала проверка базовых привычек.</li>
<li>Затем чёткая цель и ожидаемый эффект.</li>
<li>И только после этого — минимальная дозировка и наблюдение за эффектом.</li>
</ul>
<p>Если БАД нельзя описать в формате «принимаю X для Y, эффект жду через Z недель» — лучше не принимать. Это правило отсеивает «модные новинки», которые завозят все блогеры разом.</p>
<h2 class="heading" id="как-выбирать-добавки-разумно">
  Как выбирать добавки разумно
  <a class="anchor" href="#%d0%ba%d0%b0%d0%ba-%d0%b2%d1%8b%d0%b1%d0%b8%d1%80%d0%b0%d1%82%d1%8c-%d0%b4%d0%be%d0%b1%d0%b0%d0%b2%d0%ba%d0%b8-%d1%80%d0%b0%d0%b7%d1%83%d0%bc%d0%bd%d0%be">#</a>
</h2>
<p>Простая схема, которая работает:</p>
<ol>
<li><strong>Определить цель.</strong> Сон, энергия, восстановление, дефицит по анализу. Что-то одно, не «всё сразу».</li>
<li><strong>Проверить безопасность и совместимость.</strong> С текущими лекарствами, состояниями, образом жизни. Это отдельная работа, не пропускать.</li>
<li><strong>Начать с одной добавки и фиксировать изменения.</strong> Дневник в три строки в день: сон, энергия, настроение. Этого достаточно.</li>
<li><strong>Отменить, если эффекта нет или он сомнителен.</strong> Через 4-8 недель, не раньше.</li>
</ol>
<p>И да: это не медицинская рекомендация. При сомнениях, симптомах или сложных состояниях — обсуждать с врачом, не с интернетом.</p>
<h2 class="heading" id="типичные-ловушки-">
  Типичные ловушки 🪤
  <a class="anchor" href="#%d1%82%d0%b8%d0%bf%d0%b8%d1%87%d0%bd%d1%8b%d0%b5-%d0%bb%d0%be%d0%b2%d1%83%d1%88%d0%ba%d0%b8-">#</a>
</h2>
<p>Самые частые ошибки, в которые попадаются почти все, кто заходит в тему:</p>
<p><strong>Покупать всё сразу.</strong> Под влиянием статьи или подкаста заказывают 5-7 добавок. В итоге не понимают, что работает, а что нет.</p>
<p><strong>Надеяться на «волшебную таблетку».</strong> Если есть проблема со сном — никакая добавка не починит то, что чинится за неделю гигиены сна.</p>
<p><strong>Забывать про доказательную базу.</strong> Многие популярные добавки имеют слабые или противоречивые исследования. «Все вокруг пьют» — не критерий.</p>
<p><strong>Игнорировать побочки.</strong> Любой БАД может давать эффект, который вы спишете на «обычный день». Дневник помогает поймать.</p>
<p><strong>Доверять ярким инфлюенсерам.</strong> Биохакинг — благодатная почва для красивых историй и плохих советов. Чем громче «у меня всё работает», тем выше вероятность маркетинга или selection bias.</p>
<h2 class="heading" id="минимальный-подход">
  Минимальный подход
  <a class="anchor" href="#%d0%bc%d0%b8%d0%bd%d0%b8%d0%bc%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b9-%d0%bf%d0%be%d0%b4%d1%85%d0%be%d0%b4">#</a>
</h2>
<p>Я склоняюсь к минимальному набору: меньше банок, больше дисциплины. Часто достаточно навести порядок в рутине, чтобы необходимость в добавках исчезла или сильно сократилась.</p>
<p>Если совсем коротко: спать в одно время, гулять каждый день, есть не «правильно», а сбалансированно, и не пить больше двух-трёх БАДов одновременно. Этого хватит большинству людей в большинстве периодов жизни.</p>
<h2 class="heading" id="итоги-">
  Итоги 🧠
  <a class="anchor" href="#%d0%b8%d1%82%d0%be%d0%b3%d0%b8-">#</a>
</h2>
<p>Здоровье — это не витрина из биохакинга, а устойчивость, которая складывается из маленьких повторяющихся решений. Добавки могут быть частью этой системы, но только после того, как база отстроена и цели ясны.</p>
<p>Биохакинг как идея полезен ровно до тех пор, пока он остаётся <strong>дисциплиной наблюдения</strong>. Как только он превращается в магазин баночек или гонку метрик — он перестаёт работать на здоровье и начинает работать на чувство «я делаю что-то важное», что сильно дешевле и проще, чем реально менять привычки.</p>
<p>Самый честный «биохакинг» — это лечь спать на час раньше. Бесплатно, без побочек, эффект через неделю.</p>
]]></content:encoded><category>здоровье</category></item></channel></rss>