Функциональная спецификация · раздел админки № 4
Раздел «SEO» операторской админки: блочный редактор статей с ИИ-помощником и SEO-баллом, публикация на домен своего продукта в один клик, аналитика каждой статьи из четырёх источников.
ED-3, на который ссылаются задачи и тесты), §22–23 — граничные случаи и качество, §24–27 — план, вопросы и границы. Тест полноты: разработчик может строить, а QA — писать тест-план, не задавая вопросов вне этого документа.01
Проблема. У продуктов «По душе» (события) и «По любви» (знакомства) нет канала бесплатного стабильного притока пользователей из поиска: нет блога, публичные страницы не оптимизированы, а команда не обладает SEO-экспертизой, чтобы писать находимые статьи вручную. Платный трафик дорожает; органический поиск — единственный канал, который со временем дешевеет.
Решение. Инструмент внутри своей админки, который берёт SEO-экспертизу на себя: менеджер сосредоточен на смысле, система — на находимости, а ИИ ускоряет производство при обязательном человеческом контроле.
| Метрика | Роль | Определение | Цель |
|---|---|---|---|
| Конверсии из статей | главная | Клики «скачать приложение» + клики по событиям/анкетам из статей (cta_click + feed_item_click, §18) | стабильный недельный поток новых пользователей из статей; целевая цифра — открытый вопрос O-3 (§26) |
| Визиты из поиска | вспомогательная | Просмотры статей с источником Яндекс/Google | рост месяц к месяцу после 3-го месяца (индексация инерционна) |
| Доля статей «в топе» | вспомогательная | Статьи со средней позицией ≤ 10 хотя бы по одному запросу | ≥ 30% опубликованных к 6-му месяцу |
| Здоровье контента | гигиеническая | Средний SEO-балл опубликованных; доля «дочитал» | балл ≥ 80; дочитываемость ≥ 25% |
Правило приоритизации для любых споров: выигрывает то, что быстрее ведёт к конверсиям из статей. Красивая фича редактора проигрывает скучной работе над индексацией, если вторая даёт больше читателей.
02
03
Пишет хорошо, в SEO — ноль. Не знает слов «canonical» и «метатег» и не должна узнать. Ей нужны: понятная цель («доведи балл до 80»), подсказки императивами («сократи заголовок до 60 знаков»), ИИ, который делает черновую работу. Роль в админке: Content.
Настраивает: авторов, базу знаний, витринные анкеты, бюджеты ИИ. Смотрит сводную аналитику и отвечает за то, чтобы блог не попал под фильтры. Роль: Admin.
Пришёл из Яндекса с конкретным вопросом («куда сходить в москве на выходных»). Ждёт быстрый ответ в первом экране, скан-структуру, живые примеры. Идеальный исход — клик по событию или «скачать приложение».
Профессионала с Ahrefs и Key Collector в команде нет и не предполагается — инструмент не обязан удовлетворять его привычки (это non-goal, §27).
04
/blog/kuda-shodit-v-moskve; Яндекс получает IndexNow-пинг. Общее время: ~1,5 часа вместо дня.Как S1, но текст Марина пишет сама с нуля; ИИ-панель свёрнута. Все подсказки, балл и публикация работают одинаково — ИИ строго опционален (плашки «вычитайте» нет).
Через месяц во вкладке «Аналитика» у статьи подсказка: «запрос "куда сходить в москве бесплатно" — позиция 11, близко к топу». Марина открывает статью из карточки аналитики, добавляет раздел про бесплатные события, жмёт «Опубликовать изменения» — дата обновления меняется, поисковики видят свежесть. Через две недели по графику видно рост кликов после отметки публикации.
Стажёр случайно удалил половину опубликованной статьи и опубликовал. Марина открывает «⋯ → Версии», сравнивает с предыдущей, жмёт «Восстановить» и публикует. В аудит-логе видно, кто и что сделал. Потерь нет.
Пока Марина редактирует, Олег открывает ту же статью — видит плашку «Сейчас редактирует Марина» и режим «только чтение». Ему нужно срочно: жмёт «Всё равно редактировать» — блокировка переходит к нему, у Марины появляется предупреждение «Редактирование перехватил Олег — ваши несохранённые правки: скопировать». Никто молча не потерял работу. (Механика — EC-9, §22.)
05
| Вопрос | Решение | Комментарий |
|---|---|---|
| Тематика | Оба приложения | Поле «продукт» обязательно: events | dating; определяет домен, контекст ИИ, CTA, тип ленты. |
| Домены | У каждого продукта свой | События — новый домен (купить); дейтинг — не решено (O-2). Движок один. |
| Язык и поиск | Русский, упор на Яндекс | Google — вторым номером. |
| Кто публикует | Любой менеджер | Доступ к разделу = право публиковать (роли Admin, Content). |
| ИИ | OpenRouter | Все 4 режима; ИИ не публикует сам; ИИ-черновик требует вычитки. |
| База знаний ИИ | Документы + живые данные | Курируемые доки + свежие цифры каталога событий. |
| Редактор | Блочный (Notion-like) | Контент — структура блоков, не сырой HTML. |
| Лента в статье | Да, опционально | Живые события или витринные анкеты; клики — в аналитике. |
| Аналитика | Гибрид, 4 источника | Свой счётчик + Метрика (+Вебвизор) + Вебмастер + GSC. |
| Инструменты | Вордстат · балл · читабельность | Анализ конкурентов из выдачи — исключён полностью. |
| Жизненный цикл | Версии + расписание | Снапшот при публикации, откат, отложенная публикация. |
| Авторство | Авторы-персоны | Справочник; страница автора на сайте. |
| Цель статьи | Приложения + веб | Обе конверсии считаются; главная метрика проекта — §1. |
| Главная метрика | Установки из статей | Гриль-2: успех = конверсии, не голый трафик. |
| Конфликт правок | Мягкая блокировка | Гриль-2: «редактирует Х» + перехват с предупреждением (EC-9). |
06
| Продукт | Домен | Статус | Что живёт |
|---|---|---|---|
| «По душе» (события) | <домен-событий>.ru | купить — блокер этапа 0 (O-1) | витрина событий + блог /blog/… |
| «По любви» (дейтинг) | не выбран (кандидат plbvi.ru) | открытый вопрос O-2 | блог /blog/…; движок тот же |
Принцип: один движок — много доменов. Блог — одно приложение с серверным рендером, развёрнутое на обоих доменах; настройка SITE_PRODUCT решает, чьи статьи домен отдаёт. Смена/добавление домена — конфигурация, не разработка. На каждый домен — свой комплект внешних привязок: счётчик Метрики, сайт в Вебмастере, ресурс GSC, sitemap, robots.txt, ключ IndexNow. Менеджер этого не видит: он выбирает продукт статьи, остальное система делает по нужному домену.
Серверный рендер обязателен (Яндекс рендерит JavaScript нестабильно). Кеш страниц 5–10 минут; после публикации кеш конкретной страницы сбрасывается принудительно.
07
Список + редактор (§9–15).
Дашборд + карточки статей (§19).
Документы для ИИ (§20).
Авторы-персоны (§20).
Реестр верхнеуровневых требований. Каждое — атомарное и проверяемое; детали — в указанном разделе. Приоритеты SHOULD/COULD подтверждаются владельцем (вопрос O-5).
| ID | Система должна… | Приоритет | Детали |
|---|---|---|---|
| LS-1 | показывать список статей с фильтрами, сортировкой и действиями | MUST | §9 |
| ED-1 | давать блочный редактор с автосохранением ≤ 5 с после паузы ввода | MUST | §10 |
| ED-2 | поддерживать все типы блоков из §10, включая CTA и карточку события | MUST | §10 |
| ED-3 | ограничивать одновременное редактирование мягкой блокировкой с перехватом | MUST | §22 EC-9 |
| MT-1 | вести title/description с пиксельным предпросмотром сниппета и автогенерацией | MUST | §11 |
| MT-2 | генерировать всю техническую разметку страницы без участия менеджера | MUST | §11 |
| SC-1 | считать SEO-балл 0–100 по 20 проверкам §12 живо при редактировании; истина — на сервере | MUST | §12 |
| KW-1 | давать подбор запросов из Вордстата с кешем 7 дней | MUST | §13 |
| AI-1 | генерировать статью целиком: бриф → план → черновик, с флагом «нужна вычитка» | MUST | §14 |
| AI-2 | давать ИИ-чат с контекстом статьи и diff-подтверждением правок | MUST | §14 |
| AI-3 | давать правку выделенного текста (перефразировать/сократить/расширить/упростить) | SHOULD | §14 |
| AI-4 | генерировать картинки по описанию | SHOULD | §14 |
| AI-5 | не давать ИИ публиковать и блокировать публикацию невычитанного ИИ-черновика | MUST | §14, §16 |
| FD-1 | поддерживать блок «Лента событий» с живым фильтром и учётом кликов | MUST | §15 |
| FD-2 | поддерживать блок «Лента анкет» только из витринных анкет с согласием | COULD | §15 |
| PB-1 | публиковать статью конвейером §16 (версия → страница → sitemap → IndexNow) | MUST | §16 |
| PB-2 | вести версии с просмотром, сравнением и откатом | MUST | §16 |
| PB-3 | поддерживать отложенную публикацию (точность ≤ 1 мин) | SHOULD | §16 |
| PB-4 | хранить историю slug и вечно отдавать 301 со старых адресов | MUST | §16 |
| BL-1 | отдавать страницы блога серверным рендером со всеми страницами §17 | MUST | §17 |
| BL-2 | держать Core Web Vitals статей в зоне «хорошо» | MUST | §23 |
| TR-1 | считать события чтения собственным анонимным счётчиком | MUST | §18 |
| AN-1 | собирать метрики из Метрики/Вебмастера/GSC раз в сутки и показывать вкладку §19 | MUST (этап 2) | §19 |
| KB-1 | вести базу знаний и авторов-персон с влиянием на ИИ и страницы | MUST | §20 |
| AC-1 | ограничивать раздел ролями и писать действия в аудит-лог | MUST | §21 |
08
| Поле | Тип | Описание |
|---|---|---|
id | UUID | Идентификатор. |
product | enum | events | dating. Обязательно. Определяет домен. После первой публикации не меняется (EC-12). |
status | enum | draft → scheduled → published → unpublished; из unpublished можно вернуть в published. |
slug | строка, уник. в продукте | Адрес /blog/<slug>; правила §11. |
slug_history | список строк | Прежние slug — вечные 301 (PB-4). |
h1 | строка ≤ 120 | Видимый заголовок, единственный H1 страницы. |
seo_title / seo_description | строки ≤ 120 / ≤ 300 | Для выдачи; правила §11. |
cover + cover_alt | картинка (S3) + строка | Обложка ≥ 1200×630 = og:image; лимит загрузки 10 МБ (EC-6). |
lead | текст ≤ 400 | Лид-абзац; Яндекс часто берёт сниппет отсюда. |
blocks | JSON | Массив блоков §10. Сырой HTML не хранится. |
author | FK → Author | Обязателен для публикации. |
keywords | JSON | 1 primary + до 10 secondary, с частотностями и датой снятия. |
tags | список строк | Рубрики блога. |
needs_review | bool | Авто-true при ≥ 50% текста от ИИ; публикация заблокирована до снятия человеком (AI-5). |
seo_score | 0–100 | Последний серверный расчёт (SC-1). |
editing_lock | сотрудник + heartbeat | Мягкая блокировка (EC-9); снимается при выходе или молчании 2 мин. |
scheduled_at / published_at / updated_at | datetime | Расписание / первая публикация / последняя публикация изменений. |
created_by | сотрудник | Для аудита; на сайте не показывается. |
| Сущность | Поля | Назначение |
|---|---|---|
| ArticleVersion | article, №, снапшот публикуемых полей, кто/когда | Создаётся при каждой публикации; просмотр, сравнение, откат (PB-2). |
| Author | slug, имя, фото, био ≤ 600, регалии, is_active | Автор-персона; страница /blog/authors/<slug>. Удаление запрещено — только деактивация (EC-13). |
| KnowledgeDoc | заголовок, markdown, продукт, is_active, updated_at | Активные — целиком в контекст ИИ (§20). |
| ArticleChat | article, сообщения, расход токенов | История ИИ-чата статьи. |
| BlogHit | article, тип, анонимный session_id, класс источника, время, payload | Сырые события счётчика; хранение 180 дней (§18). |
| ArticleMetricDaily | article, дата, источник (own/metrika/webmaster/gsc), метрики JSON | Суточные агрегаты; хранятся вечно; вся вкладка §19 строится на них. |
| ShowcaseProfile | имя, возраст, город, фото, «о себе» ≤ 200, согласие (дата/основание), is_active | Витринная анкета для ленты дейтинга; только ручные записи с согласием (FD-2). |
09 · LS
Таблица в стандартном стиле админки (панель, sticky-шапка, пагинация по 20).
| Столбец | Содержимое | Сортировка |
|---|---|---|
| Статья | мини-обложка + H1 + slug серым | по алфавиту |
| Продукт | бейдж «События» / «Знакомства» | — |
| Статус | Черновик · Запланирована (со временем) · Опубликована · Снята; + метка «нужна вычитка» | да |
| SEO-балл | число: 0–49 красный · 50–79 жёлтый · 80–100 зелёный | да |
| Автор | автор-персона | — |
| Опубликована / обновлена | даты | да (по умолчанию новые сверху) |
| Просмотры 30 дн | число (появляется на этапе 2) | да |
Фильтры: статус, продукт, автор, рубрика, «нужна вычитка», поиск по названию/slug. Действия строки: в редактор · в аналитику · открыть на сайте · дублировать (копия-черновик) · снять с публикации. Удаление — только черновиков, с подтверждением; опубликованные не удаляются — снимаются (история и метрики сохраняются). Состояния экрана: пусто — крупные «Начать с нуля / Попросить ИИ»; загрузка — скелетон; ошибка — тост + «Повторить» без падения оболочки.
10 · ED
Шапка: статус · индикатор сохранения · «Предпросмотр» (страница как на сайте, в новой вкладке, с noindex) · «Опубликовать» / «Опубликовать изменения» · меню «⋯»: версии, запланировать, снять с публикации, дублировать. Плашка мягкой блокировки — если статью уже редактируют (S5, EC-9).
| Блок | Настройки | Рендер |
|---|---|---|
| Абзац | жирный, курсив, ссылка (внешние — rel="noopener", опция nofollow) | <p> |
| Подзаголовок | H2 / H3 (H1 в блоках запрещён) | <h2>/<h3> с якорем |
| Картинка | файл ≤ 10 МБ (сжатие в WebP, ресайзы), alt обязателен, подпись, ширина: колонка/широкая | <figure> c width/height, lazy-load ниже первого экрана |
| Список | маркированный / нумерованный | <ul>/<ol> |
| Цитата | текст + источник | <blockquote> |
| Врезка | совет / важно / факт | стилизованный <aside> |
| Вопрос-ответ | пары «вопрос → ответ» | секция FAQ (H2 + пары H3/абзац); schema-разметку FAQ не ставим (§27) |
| Кнопка приложения (CTA) | «скачать приложение» (сторы по продукту) / произвольная ссылка + текст | кнопки; клик → cta_click |
| Карточка события | поиск по каталогу → событие | живая карточка; архивное событие скрывается (EC-4) |
| Лента | §15 | живая подборка |
| Разделитель | — | <hr> |
Вставка из Word/Google Docs очищается до поддерживаемых блоков. «Содержание» генерируется автоматически из H2 при их числе ≥ 3.
11 · MT
<title>) — MT-1-2 с предупреждением (EC-7).<title>Куда сходить в Москве: 15 идей на выходные — По душе</title>
<meta name="description" content="Собрали 15 проверенных мест…">
<link rel="canonical" href="https://ДОМЕН/blog/kuda-shodit-v-moskve"> ← срезает UTM
<meta property="og:type" content="article"> + og:title/description/url/site_name
<meta property="og:image" content="…1200×630…">
<meta name="twitter:card" content="summary_large_image">
<script type="application/ld+json"> ← для Google; Яндексу хватает HTML+OG
{ "@type":"Article", "headline":h1, "image":[16:9, 4:3, 1:1],
"author":{"@type":"Person","name":автор,"url":стр.автора},
"datePublished":…, "dateModified":…,
"publisher":{"@type":"Organization","name":бренд,"logo":…} }
</script>
<script type="application/ld+json">{ "@type":"BreadcrumbList", … }</script>
Плюс: видимые даты «Опубликовано / Обновлено», блок автора, noindex на черновиках и предпросмотрах.
12 · SC
Считается кодом (без ИИ) при каждом сохранении, живо в редакторе; истина — серверный пересчёт при публикации (SC-1). Зоны: 0–49 · 50–79 · 80–100. Публикация возможна с любым баллом; при < 50 — предупреждение (EC-10). Текст перед проверками лемматизируется («Москва/в Москве/москвичи» — одно слово).
| # | Группа / проверка | Условие «зачёт» | Вес |
|---|---|---|---|
| 1 | Мета · Title | заполнен, ≤ 580 px, лемма главного запроса в первых 4 словах | 8 |
| 2 | Description | 120–160 знаков, содержит главный запрос, не дублирует title | 7 |
| 3 | Slug | латиница, ≤ 6 слов, содержит транслит ключа | 3 |
| 4 | Обложка | задана, ≥ 1200×630, alt заполнен | 5 |
| 5 | Структура · H1 | 20–90 знаков, содержит главный запрос | 4 |
| 6 | Иерархия | H3 не раньше первого H2, без пропусков уровней | 4 |
| 7 | Подзаголовки | при > 800 слов — H2 каждые ≤ 350 слов | 4 |
| 8 | Лид-абзац | заполнен, ≤ 400 знаков, содержит леммы запроса, без «воды» | 5 |
| 9 | Прямой ответ | для H2-вопросов первый абзац секции — самодостаточный ответ (ставка на ИИ-ответы поисковиков) | 5 |
| 10 | Контент · Объём | ≥ 600 слов; 300–599 — половина | 5 |
| 11 | Главный запрос | леммы в title, H1 и первых 100 словах | 8 |
| 12 | Доп. запросы | ≥ 60% secondary в H2 или тексте | 6 |
| 13 | Без переспама | ни одна значимая лемма > 3% слов (анти-фильтр Яндекса) | 4 |
| 14 | Читабельность | предложений > 20 слов ≤ 25%; абзацы ≤ 4 предложений; подсветка канцелярита с заменами | 7 |
| 15 | Медиа · Alt | у 100% картинок | 4 |
| 16 | Иллюстрированность | ≥ 1 визуальный блок на 500 слов | 4 |
| 17 | Ссылки · Внутренние | 2–5 на наши статьи/события; редактор предлагает кандидатов | 6 |
| 18 | Внешние источники | при цифрах/фактах — ≥ 1 ссылка на первоисточник | 4 |
| 19 | Прочее · Автор | выбран | 4 |
| 20 | CTA | ≥ 1 CTA-блок (кнопка/карточка/лента) | 3 |
| Итого | 100 | ||
Веса — серверная конфигурация, меняются без релиза. Каждый пункт в панели раскрывается в объяснение простым языком: что сделать и почему это важно.
13 · KW
Кеш обязателен: квота API ~1000 обращений/сутки; ответы кешируются 7 дней (запрос+регион). Частотности прикреплённых запросов обновляются раз в месяц фоном. Поведение при исчерпании квоты — EC-2.
14 · AI
Вызовы — через сервер (ключ OpenRouter в браузер не попадает), ответы стримятся. Три модельные настройки: писательская (длинные тексты), быстрая (title/description/мелкие правки), картиночная; меняются в конфигурации без релиза.
Статичная часть кешируется на стороне модели (prompt caching) — повторные обращения кратно дешевле. Проверки балла, читабельность и разметка работают без ИИ — мгновенно и бесплатно; ИИ включается только там, где нужен текст.
needs_review; «Опубликовать» недоступна до нажатия «Вычитано» (фиксируется в аудите). Месячный бюджет — настройка с индикатором: 80% — предупреждение, 100% — генерация выключена (чат и правки на быстрой модели — до 120%). Расход виден по каждой статье. Поведение при недоступности ИИ — EC-1.15 · FD
| Настройка | Варианты | По умолчанию |
|---|---|---|
| Источник | фильтр каталога: город, категории, период (ближайшие N дней), только бесплатные | город из темы, 14 дней |
| Ручной режим | конкретные события списком (id) | выкл |
| Сортировка | ближайшие / популярные | ближайшие |
| Количество | 3–9 | 6 |
| Вид | сетка карточек / компактный список | сетка |
| Заголовок блока | свой текст | «Ближайшие события» |
Карточка: обложка, название, дата ближайшего сеанса, место, цена. Клик → страница события на том же домене. Прошедшие выпадают сами; пустая лента не рендерится совсем (EC-5). Первичный рендер серверный (индексируется, даёт свежесть), на клиенте — тихая актуализация.
Источник — только витринные анкеты (ShowcaseProfile): ручной набор с зафиксированным согласием (или редакционные персоны). Карточка: фото, имя, возраст, город, строка «о себе»; клик — умная ссылка на установку/открытие приложения (стор по устройству). Публичных страниц анкет не существует.
Аналитика ленты: feed_view (блок доехал до экрана) и feed_item_click (id и позиция карточки) — свой счётчик; в карточке статьи — CTR ленты и топ карточек; клики входят в главную метрику конверсий (§1).
16 · PB
needs_review · пустой текст. Предупреждения (пускают после подтверждения): балл < 50 · нет прикреплённых запросов.published; выставляются даты./blog/<slug> на домене продукта; доступна сразу со всей разметкой §11.updated_at обновляется, published_at неизменен.scheduled; фоновая задача раз в минуту публикует созревшие; «Отменить» → черновик. Время в прошлом — EC-14.17 · BL
| Страница | Адрес | Содержимое |
|---|---|---|
| Главная блога | /blog | карточки статей (обложка, H1, лид, автор, дата, время чтения), пагинация по 12, рубрики. |
| Статья | /blog/<slug> | обложка → H1 → автор+даты+время чтения → лид → «Содержание» (H2 ≥ 3) → блоки → блок автора → «Читайте также» (3 по рубрикам, автоматически). |
| Рубрика | /blog/t/<tag> | статьи рубрики; мета по шаблону. |
| Автор | /blog/authors/<slug> | фото, био, регалии, статьи. Обязательна для E-E-A-T. |
| Карта сайта | /sitemap.xml | статьи + рубрики + авторы этого домена, lastmod. |
| RSS | /blog/rss.xml | последние 20 — доп. канал быстрой индексации. |
| robots.txt | /robots.txt | разрешает всё + ссылка на sitemap. На публичных доменах не должно быть следов стенда: ни X-Robots-Tag: noindex, ни basic auth. |
18 · TR
Скрипт < 2 КБ на страницах статей. Анонимный: случайный session_id на вкладку, без кук идентификации и PII. Публичная ручка без авторизации; боты отсекаются по User-Agent и отсутствию взаимодействий. Зачем при Метрике: мгновенные цифры в админке, независимость, вечное хранение агрегатов.
| Событие | Когда | Полезная нагрузка |
|---|---|---|
view | открытие статьи | класс источника: yandex / google / другой поиск / соцсеть / прямой / внутренний (по referer, URL не хранится) |
scroll | 25 / 50 / 75 / 90% | порог |
read | 90% скролла и ≥ 45 с | «дочитал» — главная метрика качества |
time | уход со страницы | активные секунды |
cta_click | клик CTA | id блока, назначение (стор/веб) |
feed_view / feed_item_click | лента на экране / клик карточки | id блока; id и позиция карточки |
Сырые BlogHit — 180 дней; ночная свёртка в ArticleMetricDaily (source=own) — вечно. «Дочитал», «клик CTA», «клик ленты» дублируются в Метрику как цели (сегменты + Вебвизор по дочитавшим).
19 · AN
| Метрика | Определение | Источник |
|---|---|---|
| Просмотры | события view | счётчик |
| Из поиска | доля view с источником yandex/google | счётчик |
| Дочитываемость | read / view | счётчик |
| Время на статье | медиана активных секунд | счётчик (сверка — Метрика) |
| Показы / клики / CTR выдачи | показы в результатах поиска и клики | Вебмастер + GSC, раздельно |
| Позиция | средняя по запросам за период, отдельно Я и G | Вебмастер + GSC |
| Конверсия | (cta_click + feed_item_click) / view — главная метрика проекта | счётчик |
| Поведение-детали | глубина, отказы, карта кликов, записи сессий | Метрика (переход по ссылке с фильтром статьи) |
Внешние API опрашиваются ночью за вчера → ArticleMetricDaily. Вкладка открывается мгновенно из нашей базы; у внешних данных подпись «данные за <дата>». История не теряется (GSC сам хранит лишь 16 мес).
Тайлы: конверсии из статей (главная, с динамикой) · визиты из поиска · опубликовано статей · средняя дочитываемость · средний SEO-балл. Ниже: топ-5 статей по конверсиям и топ-10 запросов (Я+G).
Столбцы: статья · продукт · опубликована · просмотры (спарклайн) · % из поиска · дочитываемость · позиция Я/G · конверсия. Сортировка по всем; фильтры: продукт, автор, рубрика, период.
20 · KB
База знаний: список документов (заголовок, продукт, активен, обновлён) + markdown-редактор. Стартовый набор: «Продукт "По душе"» · «Продукт "По любви"» · «Аудитория» · «Тон и стиль» (пишем так / не так) · «Факты и цифры» (с датами) · «Запретные темы» · «Стиль иллюстраций». Деактивация выводит из контекста ИИ немедленно. Индикатор суммарного объёма активных документов; при превышении лимита — предупреждение, какие ужать.
Авторы: имя, slug, фото (квадрат ≥ 400 px), био ≤ 600, регалии, активен. Используются в выборе автора, блоке автора, странице автора, разметке Article. Деактивированный скрыт из выбора; его статьи и страница работают (EC-13). Удаление запрещено.
21 · AC
| Сервис | Что берём / шлём | Ограничения | Частота |
|---|---|---|---|
| OpenRouter | тексты (2 модели) + картинки; prompt caching | месячный бюджет; учёт по статьям | по запросу |
| Вордстат API | topRequests, dynamics, regions | ~1000/сутки, OAuth | по запросу + кеш 7 дн |
| Метрика | Reporting API; Management API (автосоздание целей); ссылки в Вебвизор | ~5000/сутки; счётчик на домен | ночь, за вчера |
| Вебмастер | мониторинг запросов; IndexNow; переобход | сайт на домен; подтверждение прав | ночь; IndexNow — при публикации |
| GSC | searchanalytics.query | ресурс на домен; задержка ~2 дня | ночь |
| S3 (существующий) | обложки, картинки, фото авторов; WebP-ресайзы | файл ≤ 10 МБ | при загрузке |
seo.write — роли Admin и Content; остальным раздел не виден. Витринные анкеты — только Admin.22 · EC
| ID | Ситуация | Поведение системы | Что видит менеджер |
|---|---|---|---|
| EC-1 | OpenRouter недоступен / таймаут | редактор полностью работает; ИИ-кнопки неактивны; начатая генерация сохраняет готовые секции | «ИИ-помощник временно недоступен. Всё остальное работает. Повторить» |
| EC-2 | квота Вордстата исчерпана | отдаём кеш с датой снятия; живые запросы — завтра | «Показаны данные от 21 августа — лимит подбора на сегодня исчерпан» |
| EC-3 | IndexNow-пинг упал | публикация не блокируется; ретрай фоном до 24 ч | ничего (журнал — в аудите) |
| EC-4 | событие из карточки ушло в архив | карточка не рендерится на сайте | в редакторе карточка помечена «событие прошло — замените» |
| EC-5 | лента пуста по фильтру | блок не рендерится целиком | в редакторе предупреждение «сейчас лента пуста — расширьте фильтр» |
| EC-6 | картинка > 10 МБ или не картинка | отказ загрузки | «Файл больше 10 МБ. Сожмите или выберите другой» |
| EC-7 | slug занят | суффикс -2 | «Такой адрес занят статьёй "…" — сохранили как …-2» |
| EC-8 | обрыв связи при наборе | правки копятся в браузере; отправка при восстановлении; конфликт версий — диалог сравнения | индикатор «Нет связи — правки сохраняются локально» |
| EC-9 | двое редактируют (ED-3) | мягкая блокировка: у второго read-only + «Всё равно редактировать» (перехват); heartbeat 30 с, молчание 2 мин снимает блок | «Сейчас редактирует Марина» / у первого: «Редактирование перехватил Олег — скопировать несохранённые правки» |
| EC-10 | публикация с баллом < 50 | подтверждение с перечнем главных проблем | «Балл 42. Главное: title длиннее нормы, нет внутренних ссылок. Опубликовать всё равно?» |
| EC-11 | смена slug опубликованной | вечный 301 со старого адреса | «Адрес изменится. Старый будет перенаправлять автоматически. Продолжить?» |
| EC-12 | смена продукта после публикации | запрещена (статья «переехала» бы между доменами) | поле неактивно: «Продукт нельзя менять после публикации — создайте копию» |
| EC-13 | автор деактивирован | его статьи и страница работают; выбрать его для новых нельзя | в списке авторов помечен «неактивен» |
| EC-14 | время расписания в прошлом | отказ сохранения расписания | «Выберите время в будущем» |
| EC-15 | Метрика/Вебмастер/GSC не ответили ночью | показываются последние собранные данные; ретрай следующей ночью | в аналитике подпись «данные Метрики за 25 августа» |
| EC-16 | бюджет ИИ исчерпан | генерация выключена; чат/правки на быстрой модели до 120% | «Месячный бюджет ИИ израсходован. Генерация включится 1 сентября. Админ может поднять лимит» |
23 · NF
| ID | Область | Требование (проверяемое) |
|---|---|---|
| NF-1 | Скорость блога | Core Web Vitals статей в «хорошо» на 75-м перцентиле: LCP < 2,5 с · INP < 200 мс · CLS < 0,1. Обложка WebP/AVIF + fetchpriority; размеры у всех картинок и лент; JS на странице — только счётчик, Метрика, гидрация ленты. |
| NF-2 | Скорость админки | Список статей ≤ 1 с при 1000 статей; пересчёт балла в редакторе ≤ 300 мс; автосохранение не блокирует набор. |
| NF-3 | Надёжность | Публикация атомарна: либо все шаги 2–5 конвейера §16, либо статус не меняется и менеджеру показана ошибка. Недоступность любого внешнего API не ломает ни редактор, ни публикацию (EC-1, EC-3, EC-15). |
| NF-4 | Сохранность | Потеря текста недопустима: автосохранение ≤ 5 с, локальный буфер при обрыве (EC-8), версии при публикации, мягкая блокировка (EC-9). |
| NF-5 | Безопасность | Ключи интеграций — только на сервере. Публичная ручка счётчика — троттлинг по IP и объёму. Счётчик не собирает PII (NF-6). Админ-API — существующий fail-closed RBAC. |
| NF-6 | Приватность | На публичных страницах нет данных реальных пользователей (правило §15). Счётчик — анонимный, referer хранится только классом источника. |
| NF-7 | Совместимость | Админка: последние версии Chrome, Firefox, Safari, Edge. Блог: те же + мобильные браузеры (mobile-first — обязательное условие индексации). |
| NF-8 | Локализация | Только русский; модель не блокирует добавление языков позже (§27). |
24
feed_item_click с id и позицией.25
Идёт параллельно разработке, а не перед ней: фоновый воркер, OpenRouter и Вордстат нужны рано; домен, Метрика + Вебмастер + GSC + IndexNow — только к го-лайву (последний шаг этапа 1). Разработка и приёмка — на закрытом стенде.
→ К моменту готовности кода — публичный адрес и все ключи; ничто не ждёт друг друга.
MUST: список и редактор (§9–10), метаданные (§11), балл (§12), Вордстат (§13), ИИ-генерация и чат (§14), лента событий (§15), конвейер публикации и версии (§16), блог (§17), счётчик пишет с первого дня (§18), база знаний и авторы (§20), RBAC (§21). SHOULD (режутся первыми при сдвиге сроков): правка выделенного, расписание, ИИ-картинки. COULD: лента анкет.
→ Первая статья опубликована; данные копятся с первого дня.
Ночные сборщики трёх API; вкладка «Аналитика» целиком (§19); подсказки «близко к топу».
→ По каждой статье видно, приносит ли она пользователей и что усилить.
26
| ID | Вопрос | Владелец | Блокирует |
|---|---|---|---|
| O-1 | Имя и покупка домена событий | команда | только го-лайв (открытие индексации и кабинеты); разработке не мешает |
| O-2 | Домен дейтинга: plbvi.ru или новый | команда | публикацию dating-статей (писать можно сразу) |
| O-3 | Целевая цифра главной метрики (конверсий/мес к 6-му месяцу) | владелец продукта | ничего; нужна для оценки успеха |
| O-4 | Масштаб: авторов и статей в месяц (сейчас — допущение Д-1) | владелец продукта | бюджет ИИ; при > 40/мес — пересмотр защиты R-1 |
| O-5 | Подтвердить приоритеты SHOULD/COULD (§25): расписание, ИИ-картинки, правка выделенного, лента анкет | владелец продукта | порядок работ этапа 1 |
| O-6 | Аккаунт OpenRouter и способ оплаты | команда | ИИ-функции |
| O-7 | Юр. оформление согласий витринных анкет | команда + Admin | ленту анкет (FD-2) |
Все вопросы должны быть закрыты (и решения внесены в эту спеку) до начала соответствующих работ; O-1, O-6 — до старта этапа 0/1.
27
28
seo в PRODUCTS (admin-panel/assets/core.js) и adminpanel/rbac.py; секции в SECTIONS, файлы assets/sections/seo*.js, регистрация в admin.js; эталон — sections/telegram.js. Инварианты: секция → SECTION_PRODUCT (тест), endpoint → ENDPOINT_RULES (иначе 403).seo/. Образец публикуемой сущности — EventCollection + views/collections.py + collections/[slug].vue.web-events (Nuxt SSR), routeRules '/blog/**': swr 300 + инвалидация при публикации; один билд, продукт домена — env SITE_PRODUCT; для дейтинга — второй деплой того же приложения.X-Robots-Tag и basic auth. Стенд остаётся закрытым.rundramatiq. Задачи: публикация по расписанию (минутная), ночные сборщики, свёртка BlogHit, IndexNow-ретраи, часовой срез данных для ИИ, месячный рефреш частотностей.ENDPOINT_RULES, запись — seo.write)GET/POST /seo/articles список / создание
GET/PATCH /seo/articles/{id} чтение / автосохранение
POST /seo/articles/{id}/publish | unpublish | schedule | review-done
POST /seo/articles/{id}/lock | unlock мягкая блокировка (heartbeat)
GET /seo/articles/{id}/versions POST …/versions/{n}/restore
POST /seo/articles/{id}/score серверный пересчёт
GET /seo/keywords?q=®ion= Вордстат (кеш)
POST /seo/ai/chat | generate | edit | image (SSE-стриминг)
GET/POST/PATCH /seo/authors, /seo/kb, /seo/showcase-profiles
GET /seo/analytics/summary | articles | articles/{id}
GET /api/v1/blog/articles?page=&tag= список (SSR)
GET /api/v1/blog/articles/{slug} статья (404 / 410 / 301 по slug_history)
GET /api/v1/blog/feed/{block_id} данные ленты
POST /api/v1/blog/track счётчик (батч, троттлинг по IP)
OPENROUTER_API_KEY SEO_AI_MODEL_WRITER / _FAST / _IMAGE
SEO_AI_MONTHLY_BUDGET_RUB WORDSTAT_OAUTH_TOKEN
METRIKA_TOKEN METRIKA_COUNTER_EVENTS / _DATING
WEBMASTER_TOKEN GSC_CREDENTIALS_JSON
INDEXNOW_KEY_EVENTS / _DATING BLOG_DOMAIN_EVENTS / _DATING
SITE_PRODUCT (events | dating — на каждом деплое витрины)
29
| Версия | Дата | Что изменилось |
|---|---|---|
| v1 | 27.08.2026 | Первый гриль (15 решений), базовая спецификация: 4 вкладки, ИИ, публикация, аналитика, этапы. |
| v2 | 27.08.2026 | Мультидоменная публикация; блок «Лента»; анализ конкурентов исключён; детализация: модель данных, правила title/description, 20 проверок балла, конвейер публикации, API. |
| v3 | 27.08.2026 | Реструктуризация по канону функциональных спецификаций (гриль-2 + исследование best practices): проблема и метрики успеха (главная — конверсии из статей), допущения, персоны, сценарии S1–S5, реестр нумерованных требований с приоритетами MUST/SHOULD/COULD, мягкая блокировка редактирования, таблица граничных случаев EC-1…EC-16 с текстами ошибок, НФТ NF-1…NF-8, критерии приёмки A-1…A-8 и DoD этапов, открытые вопросы с владельцами, статус документа. |
| v3.1 | 28.08.2026 | Правка арифметики §12: веса двадцати проверок давали в сумме 97 при заявленных «Итого 100». Три очка добавлены обложке (4→5: она же og:image и блокер публикации), иллюстрированности (3→4) и внешним источникам (3→4) — самым лёгким весам относительно их влияния. Условия проверок не менялись. |
| v3.2 | 28.08.2026 | Добавлен §30 «Состояние реализации»: что из спеки уже живёт в коде (§8, §12, §21 целиком; §9, §10 — на уровне API), где реализация сознательно отошла от буквы и почему, что нужно пересобрать на стенде. Требования не менялись. |
30
feature/seo-w1-backend (три коммита, не запушены). Контракт админ-API как он построен — backend/docs/SEO_ADMIN_API.md; решения, которые не надо «чинить» обратно, — docs/internal/14-seo-section.md.| Раздел | Состояние | Что именно |
|---|---|---|
| §8 Модель данных | сделано | Десять таблиц, две миграции. У статьи две копии: колонки — черновик, published — снимок показываемых полей (иначе правка опубликованной уезжала на сайт сама). |
| §9 Список статей | API готов | Семь фильтров, сортировки, пагинация, дублирование, удаление только черновика. Экрана нет — это S-2. |
| §10 Редактор | API готов | Автосейв по полям, валидатор блоков (11 типов, санитайзер инлайна), мягкая блокировка с перехватом, загрузка картинок. Самого редактора нет — S-3/S-4. |
| §11 Метаданные | частично | Поля, правила адреса (транслит, стоп-слова, суффикс, вечные 301) и пиксельная ширина заголовка есть. Разметка страницы (og:*, JSON-LD) — за витриной, F-2. |
| §12 SEO-балл | сделано | Все двадцать проверок, чек-лист в ответе автосейва, явный пересчёт, пороги для индикаторов панели. Статья 3127 слов — 9 мс при бюджете 300. |
| §13 Вордстат | нет | Запросы прикрепляются руками в keywords; проверки №1, 11, 12 их уже читают. Клиент и кеш — B-5. |
| §14 ИИ-помощник | нет | B-6. Таблицы под чат и расход заведены. |
| §15 Лента | частично | Блок есть в схеме и валидаторе, считается визуальным и CTA-блоком. Наполнение и рендер — B-8/F-3. |
| §16 Публикация | нет | B-7. Снимок публикуемых полей уже собирается моделью, конвейеру остаётся его положить. |
| §17 Публичный блог | нет | B-8 + F-1…F-4. |
| §18 Счётчик | нет | Таблицы есть, ручка и свёртка — B-9. |
| §19 Аналитика | нет | Этап 2, после го-лайва. |
| §20 База знаний, авторы | частично | Таблицы есть, автор выбирается в статье и проверяется баллом. CRUD справочников — B-10. |
| §21 RBAC и аудит | сделано | Продукт seo, четыре секции, действие seo.write; каждая ручка в ENDPOINT_RULES, иначе 403. Автосейв и биение замка в журнал не пишут — иначе тысячи строк в сутки на автора. |
| §22 Граничные случаи | частично | Закрыты EC-6, EC-7, EC-9, EC-11, EC-12, EC-13. Остальные ждут своих задач (ИИ, Вордстат, расписание, аналитика). |
Менеджер заводит статью и получает адрес из заголовка → набирает текст блоками, статья сохраняется сама, второй редактор видит плашку блокировки → загружает обложку, сервер сжимает её в WebP и сам читает размеры из файла → балл и чек-лист обновляются тем же ответом, что и сохранение. Проверено сквозным прогоном по HTTP; на стенде раздел ещё не разворачивался.
pillow, pymorphy3 со словарём и razdel. Без морфологии балл продолжает считаться, но по словоформам, а не по леммам — то есть занижен; об этом теперь предупреждает системная проверка Django на старте.Каждое отклонение помечено Δ в SEO_ADMIN_API.md и объяснено там же. Коротко:
srcset) загрузка не делает — класть их некуда: схема блока закрытая, а в cover_id помещается один ключ. Ширины появятся вместе с рендером витрины.