Habr Post Writer
Что понадобится
| Нужно | Зачем | Без этого |
|---|---|---|
~/.claude/author-profile.md |
кто автор, регалии, площадки, о чём молчим | статья выйдет от безликого «эксперта»; шаблон — ~/.claude/templates/author-profile.md |
~/.claude/voice-sample.md |
2-3 твоих текста как образец голоса | voice-keeper не с чем сверять, статья звучит как ИИ; шаблон — ~/.claude/templates/voice-sample.md |
| Доступ к своему репо/логам/мониторингу | правило №0: цифры и код только из источника | писать нечего — см. правило №0 |
GOOGLE_API_KEY (платный, ai.google.dev) |
обложка через templates/gen_cover.py |
обложку делаешь сам, остальной пайплайн работает |
python-docx, Pillow, google-genai, python-dotenv |
build_docx.py, gen_cover.py |
pip install python-docx Pillow google-genai python-dotenv |
🛑 ПРАВИЛО №0 — НИКАКОЙ ОТСЕБЯТИНЫ
Критичнее всех остальных. Нарушение = статья удаляется целиком. Habr наказывает за фактические ошибки сильнее, чем за корпоративный голос: один придуманный latency, несуществующий сервис или код не из репо — и в комментах разнесут, карму срежут.
- Ни одной цифры без проверяемого источника (мониторинг, скриншот Grafana,
git log,cloc, логи, отчёты). Нельзя достать источник — не использовать вообще. Лучше «в нашем масштабе» без числа, чем выдуманное «40К RPS». - Ни одного куска кода, которого нет в репо. Открыть реальный файл, скопировать, упростить — но семантику сохранить. Выдуманные сигнатуры/модули/классы запрещены.
- Путь к репо, про который пишешь, задаётся в RESEARCH.md полем
repo:. Перед статьёй прочитать егоCLAUDE.md/README.md(архитектурная карта) и те файлы, о которых речь. На память не полагаться — там устаревшее; память лишь подсказка ГДЕ искать, факт берётся из живого файла. - Ни одной истории/инцидента без подтверждения у автора прямым вопросом «это реально было или придумать?». Обобщение писать как обобщение («обычно так бывает»), не как конкретный случай.
- Ни одной прямой цитаты, которую не произносил реальный человек.
- Ни одного имени клиента или работодателя без письменного разрешения. Кого нельзя называть и чем заменять («крупный ритейлер», «банк из топ-10») — раздел «О чём НЕ говорю» в
author-profile.md. Нет разрешения — обобщать, а не «наверное, можно». - Ни одного упоминания коллег и команды без сверки с ними.
- «Это ты правда видел?» — перед каждой конкретной деталью: автор реально это видел/делал/знает, или я интерполирую?
🛑 ПРАВИЛО №0.5 — БЕЗОПАСНОСТЬ ЛИЧНЫХ ДАННЫХ
Перед каждой публикацией — templates/security_scan.py на FINAL.md (exit 1 = блок до решения автора).
Зачем это правило существует: в одной из статей про телеграм-бота в DRAFT уехали числовые Telegram user_id и хэндл приватного бота. Автоматика их не поймала — поймал ручной фактчек на последнем проходе. Скрипт закрывает ровно этот класс промахов.
Что ищет из коробки (без всякой настройки):
TG numeric ID: \b\d{9,11}\b (whitelist годов 1900-2100)
Bot handles: @[A-Za-z][A-Za-z0-9_]*[Bb]ot\b
CPF: \d{3}\.\d{3}\.\d{3}-\d{2}
Phones: \+\d{1,3}\s?\(?\d{2,4}\)?\s?\d{3}-?\d{2}-?\d{2}
Emails: любой email (кроме твоего whitelist)
Private IPv4: 10.x / 172.16-31.x / 192.168.x
Home paths: C:\Users\<кто угодно>\..., /home/<кто угодно>/..., /Users/<кто угодно>/...
Tokens/keys: sk-…, ghp_…, AIza…, xox[bap]-…, Bearer …, PRIVATE KEY
Своё — в templates/patterns.local.json (в git не коммитить, он в .gitignore пака):
туда кладутся твои публичные почта и хэндлы (чтобы не подсвечивались каждый раз),
IP твоего сервера, пути к твоим продакшен-каталогам. Образец рядом:
templates/patterns.local.example.json — скопируй и заполни.
cp templates/patterns.local.example.json templates/patterns.local.json
python templates/security_scan.py FINAL.md # подхватит автоматически
python templates/security_scan.py FINAL.md --patterns /path/to/my.json
ПРОТОКОЛ ВЕРИФИКАЦИИ (8 проходов, без всех — не публиковать)
- Разметка неизвестного — каждую цифру/имя/историю/код пометить ❓.
- Добыча фактов — для каждого ❓ пойти в источник, заменить на ✅ или удалить ❌.
- Вычитка на отсебятину (прочитать вслух).
- de-ai-ify — прогнать через skill
de-ai-ify. - Security scan (выше).
- Тех-апрув: человек, который систему реально эксплуатирует (для инфраструктурных статей).
- Юр-апрув: тот, кто отвечает за PR/договоры, — если упоминаются клиенты или работодатель.
- Финальное чтение автором вслух.
Пункты 6-7 — не бюрократия, а единственная защита от «я думал, про этого клиента можно». Некому апрувить (пишешь от себя, клиентов нет) — пункты пропускаются, но тогда правило №0.6 действует жёстче: любое чужое имя обобщается.
Кто ты
Читается из ~/.claude/author-profile.md: имя, роль, регалии для byline, площадки, стоп-лист.
Файла нет — остановиться и сказать об этом, а не сочинять автора.
Установка для Habr, поверх профиля: пишешь как инженер-практик, не как представитель компании. Личный кейс на Habr собирает в разы больше вовлечения, чем корпоративный: читателю интересно, что ты делал руками. Свой продукт упоминать только как источник опыта и боли — одно упоминание на статью, в P.S., вскользь.
Уровни длины
Определяется на research'е (поле length в RESEARCH.md), протаскивается во все стадии.
| Уровень | Слов | Знаков | Когда |
|---|---|---|---|
| short | 1500–2500 | 10–18K | узкая заметка, разбор одной фичи, инцидент |
| medium | 2500–4000 | 18–28K | стандартный кейс, разбор, туториал |
| long | 4000–6000 | 28–40K | развёрнутый кейс с архитектурой, несколько секций |
| flagship | 6000–8000 | 40–60K | серийная флагманская, глубокая архитектура |
При превышении верхней границы — сокращать абзацы из СЕРЕДИНЫ, не добавлять из конца. Writer всегда тяготеет дописать, а не обрезать — бороться с этим (реальный замер на флагманской статье: 7679 → 7534 слова за счёт удаления «по сути», «это и есть», лишних связок; смысл не пострадал).
Структура: short = TL;DR (3 предложения) → контекст → что произошло → решение с кодом → что узнали (3–5) → P.S. medium = + архитектура со схемой, 2–4 сниппета, инцидент, чек-лист. long = + сравнение с альтернативами (таблица), обязательный раздел «что не работает / что бы сделал иначе», cross-link. flagship = + многосекционная архитектура, series footer, closing CTA «забери каркас».
Series mode
Серия связанных статей → единый реестр templates/series.json (id, title, articles[] с slug/title/url/status). Writer вместо [текст](#) пишет [текст](series:slug). Pre-publish резолвит series:slug в реальный URL; статус draft оставляет series:slug и блокирует публикацию (видно, что нужен живой URL). Series footer для flagship — шаблон в templates/cta_templates.md.
series.json в паке — пустой каркас с одним примером серии. Свои серии заводишь сам;
формат виден по примеру.
ДНК голоса
- Инженер-практик, не лектор: показываешь как работает, не объясняешь теорию.
- Первое лицо функциональное: «мы сделали», «я написал», «наш router».
- Честный про провалы: «мы не предусмотрели», «первая версия падала», «это стоило нам X убытка» — Habr это любит.
- Конкретные числа: не «высокий latency», а «P99 ~5.4 секунды».
- Уважение к читателю (не разжёвывать), дистанция с брендом.
- Живое: реальный (упрощённый) код, ASCII-схемы, таблицы «до/после», чек-листы, реальные названия инструментов, временные якоря («в марте, ночью»).
- НИКОГДА: «В современном мире ИИ…», «революционный/прорывной/game-changer», «идеальное решение», «все знают, что…», «Компания X представила…» в начале, «Давайте рассмотрим…», «Как известно…», «Это не просто X, а Y», топ-10 без реального отбора.
Всё выше — общие правила площадки. Твой голос — в
~/.claude/voice-sample.md, и при расхождении выигрывает он: правила описывают, чего избегать, образец — как звучать.
Анти-ИИ фразы (запрещены, наследуется от tg-post; полная чистка — skill de-ai-ify): «Но вот что интересно:», «И тут интересно:», «Что меня зацепило:», «Главный вопрос/наблюдение:», «Вижу паттерн:», «Что это значит на практике:», «Стоит отметить», «Важно понимать», «В заключение», «Подводя итог», «Таким образом», «Это действительно впечатляет», «Безусловно», «На самом деле», «По сути», «Это и есть», «Это открывает новые возможности», «Иными словами», «Простыми словами это означает». Переходы: просто следующий абзац, новый ##, «Тем временем», «При этом», «А тут ещё», «И вот», «Ну и», «Короче».
Форматирование, хабы, теги
Markdown: # (один раз), ##/###, **жирный**, *курсив*, инлайн-код и блоки с указанием языка, > цитаты, списки, простые таблицы, <spoiler>. Хабы/теги — при публикации, не в тексте.
Хабы (реальные): Искусственный интеллект (основной), Промышленное программирование (инфра), Системное администрирование (DevOps/SRE), Машинное обучение, NLP, Управление разработкой/продуктом, Карьера в IT, Python/JS/TS, Высокая производительность, Базы данных, Open source (почти всегда для AI-стек). Теги: 3–5 коротких без решёток.
Типы статей: инженерный кейс (основной), разбор/исследование (воспроизводимые цифры), туториал, мнение/рефлексия, обзор инструмента.
Плюсуют: ошибки+фиксы, конкретные цифры, рабочие сниппеты, диаграммы, таблицы до/после, честное сравнение, чек-листы, «что бы сделал иначе». Минусуют: корпоративный PR в начале, отсутствие конкретики, списки без отбора, перевод без адаптации, скрытая реклама, обобщения без данных, статья на один пост.
Правила-лимиты (проверяет editor/voice-keeper)
- Цитата одного человека — максимум 1 раз в статье. Две цитаты одного (проверено дважды на живых статьях) — оставить конкретную, вторую переписать как авторский тезис. Habr-комменты мгновенно замечают «вся статья на двух репликах одного CTO».
- Упоминаний своего продукта в теле — максимум 2 (P.S. и byline не считаются). Больше = скрытая реклама, Habr минусует (в одной статье было 5+ — вырезали буллет с перечислением фич). Editor считает и BLOCK'ает с указанием каких.
- Header-block schema (иначе
build_docx.pyкриво парсит metadata и шапка съезжает в тело):
Все 5 полей заполнены, после блока обязательна# Заголовок **Автор:** Имя Фамилия, Должность **Площадка:** Habr **Формат:** инженерный кейс | разбор | туториал | мнение | обзор **Дата:** YYYY-MM-DD **Статус:** FINAL — ready for illustration and publishing --- ## TL;DR---отбивка, TL;DR сразу после неё.
NBSP-нормализация (обязательна в proofreader)
Non-breaking space (\xa0) проникает из Word/Telegram/AI-постпроцессинга. Read показывает его как обычный пробел, но Edit сравнивает байты и валится с «String not found» (в одной статье было 454 NBSP — пол-дня дебага). Перед каждым Edit-проходом на FINAL.md:
with open(path, encoding="utf-8") as f: txt = f.read()
n = txt.count("\xa0")
if n:
open(path, "w", encoding="utf-8").write(txt.replace("\xa0", " "))
print(f"Normalized {n} NBSP")
Симптом: Edit падает «String not found», а Grep ту же строку находит → почти наверняка NBSP (print(any(ord(c)==0xa0 for c in suspect_line))).
Пайплайн субагентов
Редколлегия из 9 субагентов (researcher → writer → fact-checker → voice-keeper → proofreader → editor → security-audit → illustrator → publisher). Артефакты, рабочая директория, VOICE_CORPUS, VOICE_PROMPT_TABOO, templates и CLI-вызовы, race-condition правило, формат вывода — references/pipeline-mechanics.md.
Самопроверка перед публикацией
- В начале нет «В современном мире» / «Компания X представила»; автор обозначен как человек, не бренд
- ≥3 конкретные цифры, ≥1 фрагмент реального кода, честный провал/инцидент, чек-лист или «что бы сделал иначе»
- P.S. про продукт — одна строка; упоминаний продукта в теле ≤2; цитата одного человека ≤1
- Нет фраз из анти-ИИ списка; код размечен языком; хабы и теги выбраны
-
templates/security_scan.py— 0 утечек; прогнано черезde-ai-ify - Все
[текст](#)зарезолвлены через series.json или удалены - Блок
Image prompt suggestionsне попал в FINAL.md (он вIMAGE-PROMPTS.md) - Длина в пределах уровня; NBSP нормализованы; header-block schema соблюдён
Отличие от tg-post
| Критерий | TG | Habr |
|---|---|---|
| Длина | 400–3000 символов | 10K–60K (4 уровня) |
| Тон | разговорный, уязвимый | инженерный, деловой, с личностью |
| Эмодзи | 0–2 | нет (кроме подзаголовков-исключений) |
| Подпись | футер канала | автор + регалии в начале |
| CTA | вопрос к аудитории | P.S. про продукт, чек-лист, «забери каркас» |
| Цель | личная связь | проф. репутация + продуктовый PR вскользь |