Тема
Реестр критериев ядра — возможности, инварианты, гипотезы
Статус: рабочий реестр к ТЗ ядра (создан 14.07.2026). Пара к ТЗ ядра + API: ТЗ отвечает «как устроено», этот файл — «что ядро обязано закрывать и как это проверить». Каждая строка = проверяемый критерий. Колонка «Где в ТЗ» связывает с разделом ТЗ/спекой; «Статус»: ✅ закрыто в ТЗ · 🧪 гипотеза (требует подтверждения практикой) · ⬜ пробел (в ТЗ не отражено — кандидат на следующую ревизию). Новый критерий добавляется сюда тем же PR, что и правка ТЗ; пробелы закрываются ревизией контрактов (по образцу ревизии 14.07.2026).
1. Продуктовые критерии (зачем ядро существует)
| # | Критерий | Проверка | Где в ТЗ | Статус |
|---|---|---|---|---|
| 1.1 | Контент-менеджер клиента собирает и публикует страницу из блоков без обращения в студию | наблюдение на первом клиенте (метрика README) | core: компоненты, build-order волна 3 | 🧪 |
| 1.2 | Новый тип контента (вакансии, проекты) заводится в админке без кода | демо «вакансии» на playground | core: типы контента | ✅ |
| 1.3 | Новый корпоративный сайт поднимается из skeleton + профиль установки за часы, не дни | хронометраж пилота | roadmap фаза 1 | 🧪 |
| 1.4 | Обновление клиентского сайта — минуты и обратимо | cms:upgrade с бэкапом/health/откатом на пилоте | update-center | 🧪 |
| 1.5 | Сайт без единого модуля (голое ядро) — полноценный корпоративный сайт: страницы, формы, SEO, меню | playground-профиль «минимум» | core: состав | ✅ |
| 1.6 | Headless-режим: публичный фронт может быть отдельным приложением поверх /api/v1 | JSON-схема форм, дерево меню, страницы по slug — без Blade | core: API | ✅ |
| 1.7 | Один инсталл обслуживает парк: мультисайт/мультитенант подключаемы без переделки ядра | контрактные тесты измерений в обоих режимах | core: контекст | ✅ |
2. Инварианты надёжности (нарушение = инцидент)
| # | Критерий | Проверка | Где в ТЗ | Статус |
|---|---|---|---|---|
| 2.1 | Сбой любого блока/виджета/слушателя/фильтра не роняет страницу — fallback, не 500 | контрактный тест barrier-рендера | core: крайние случаи | ✅ |
| 2.2 | Выключение/сбой любого модуля не роняет публичный сайт | тест деградации в cms-testing | standard §0 | ✅ |
| 2.3 | 5xx допустим только при отказе самого ядра; деградация провайдеров — 200 + meta.degraded | тесты по каждому provides-потребителю | core: API-конвенции | ✅ |
| 2.4 | Параллельное редактирование не теряет данные молча — optimistic lock, 409, диалог | тест 409 + UX-проверка диалога | core: крайние случаи | ✅ |
| 2.5 | Обновление обратимо: бэкап → maintenance → migrate → health → откат при красном | прогон пайплайна на playground | update-center | ✅ |
| 2.6 | Рестор бэкапа + cms:postupgrade возвращает работоспособный сайт (индексы/кеши пересозданы) | периодический рестор-тест | core: эксплуатация | ✅ |
| 2.7 | Событийная шина at-least-once: слушатели ядра идемпотентны, повтор — no-op | тест двойной доставки | core: крайние случаи | ✅ |
| 2.8 | Частичный отказ инфраструктуры (Redis недоступен) — сайт живёт без кеша, деградация скорости, не 500 | chaos-тест на playground | core: крайние случаи, core: ревизия №2 п. 1 | ✅ |
| 2.9 | Переполнение диска (логи, медиа) детектится health-чеком до падения БД | health-чек free-space с порогом | core: эксплуатация, core: ревизия №2 п. 2 (базовый чек — ядро, расширенные — cms/health) | ✅ |
3. Контент и блоки
| # | Критерий | Проверка | Где в ТЗ | Статус |
|---|---|---|---|---|
| 3.1 | Черновик → preview (подписанный токен) → публикация → откат на любую ревизию | feature-тесты ревизий | core: страницы | ✅ |
| 3.2 | Схема блока версионируется (_v), данные мигрируют лениво + батчем | cms:blocks:migrate идемпотентен | block-contract | ✅ |
| 3.3 | Редактор «средней свободы»: клиент не может сломать дизайн (нет стилей на блоке) | Q9; ревью темы | decisions Q9 | ✅ |
| 3.4 | Контент переносим между инсталляциями (dev → prod): экспорт с hash-сверкой схем | cms:export/cms:import манифест | content-model: перенос | ✅ |
| 3.5 | Медиа: конверсии, webp/avif, «где используется», защита от удаления используемого | тест блокировки удаления | core: медиа | ✅ |
| 3.6 | Ревизии не растут бесконечно — политика хранения N последних | настройка + джоба ретеншна | core: эксплуатация | ✅ |
| 3.7 | Совместное редактирование в реальном времени (два редактора видят курсоры друг друга) | — | — | 🧪 не берём в 1.0: 409-диалог достаточен, пересмотр по жалобам клиентов |
| 3.8 | Планируемая публикация ревизии (в дату) — зона модуля scheduled-publishing, ядро даёт только событие/API публикации | тест модуля | modules/scheduled-publishing | ✅ |
| 3.9 | AI-ассистенция контента (генерация текста блока, alt) — вне ядра, кандидат cms/ai-content | — | open-questions: кандидаты | 🧪 |
4. API и интеграции
| # | Критерий | Проверка | Где в ТЗ | Статус |
|---|---|---|---|---|
| 4.1 | Единый конверт {data, meta} / {error: {type, code, …}}, машиночитаемые code | контрактный тест конверта | api-contract | ✅ |
| 4.2 | Keyset-пагинация везде; COUNT(*) — только по запросу | тест ?with_count | api-contract | ✅ |
| 4.3 | Idempotency-Key на мутациях с внешним эффектом; replay → тот же ответ | тест повтора | api-contract | ✅ |
| 4.4 | Депрекация: заголовок + OpenAPI, удаление только в мажоре | линтер OpenAPI-диффа | core: API-конвенции | ✅ |
| 4.5 | OpenAPI-автоспека покрывает 100% эндпоинтов (включая модули) | CI-гейт | standard §14 | ✅ |
| 4.6 | Persistent-роуты переживают выключение модуля (RFC 8058) | тест выключения | core: API-конвенции | ✅ |
| 4.7 | Runtime-типы контента видны интегратору: JSON-схема полей типа по API | GET /api/v1/content-types/{type}/schema | core: API типов, core: ревизия №2 п. 3 | ✅ |
| 4.8 | Batch-вызовы / bulk-канал JSONL для массовых операций | фаза 3, cms/import/cms/export | api-lessons §8 | 🧪 |
| 4.9 | GraphQL — не берём, пока нет потребителя | — | build-order: не делать раньше | ✅ |
5. Производительность и масштаб
| # | Критерий | Проверка | Где в ТЗ | Статус |
|---|---|---|---|---|
| 5.1 | Page-cache hit = 0 запросов к БД; miss ≤ 15 | контрактный тест бюджета | core: производительность | ✅ |
| 5.2 | Настройки на горячем пути = 0 запросов (кеш групп) | контрактный тест | three-axes: настройки | ✅ |
| 5.3 | Инвалидация только тегами по событиям; Cache::flush() запрещён | линтер + ревью | performance | ✅ |
| 5.4 | Персонализация не убивает кеш: 2 штатных режима (блочный/полностраничный с лимитом кардинальности) | тест утечки кеша между сегментами | core: персонализация | ✅ |
| 5.5 | Голое ядро на VPS 2 vCPU/4 ГБ держит корпоративный трафик (p95 < 300 мс без кеша) | нагрузочный прогон на playground | — | 🧪 цифры зафиксировать после Pulse на пилоте |
| 5.6 | Octane-совместимость кода ядра (без глобального состояния между запросами) | смок под Octane | build-order: не раньше времени | 🧪 |
| 5.7 | 100+ блоков на странице рендерятся в бюджете (кеш областей/фрагментов) | синтетический тест | core: крайние случаи | ✅ |
| 5.8 | Медленный внешний вызов не блокирует рендер (очереди; синхронно ≤ 2 с + fallback) | стандарт §9, ревью | standard §9 | ✅ |
6. Безопасность и ПДн
| # | Критерий | Проверка | Где в ТЗ | Статус |
|---|---|---|---|---|
| 6.1 | Все 5 границ входа фильтруются стандартизированными обработчиками ядра | security-гейты волн | core: безопасность | ✅ |
| 6.2 | Rich-text — двойной барьер; {!! !!} только studio; ReDoS-валидатор regex | линтер + тесты | security | ✅ |
| 6.3 | Секреты никогда не в БД/логах/телеметрии; settings-store их отвергает схемой | тест схемы настроек, env-линтер | core: безопасность | ✅ |
| 6.4 | «Выгрузить всё по субъекту» и «забыть» — по всем модулям одной командой | cms:privacy:export/forget --dry-run | core: PrivacyRegistry | ✅ |
| 6.5 | Смена пароля инвалидирует сессии и токены | тест UserPasswordChanged | core: безопасность | ✅ |
| 6.6 | Брутфорс/перебор гасятся rate-limit + attack-monitor (обязателен в managed) | playground-тест волны 6 | build-order волна 6 | ✅ |
| 6.7 | RBAC покрывает парк-сценарии: клиентские роли ≠ studio; опасное — только studio | матрицы ролей в ТЗ модулей | core: права | ✅ |
| 6.8 | Загружаемые файлы сканируются (ClamAV/аналог) до публикации | контракт upload-scanner: карантин pending до вердикта | core: безопасность, core: ревизия №2 п. 4; модуль-реализация — кандидат каталога | ✅ |
| 6.9 | CSP-заголовки и SRI для админки и публичной части — дефолт темы/ядра | смок заголовков; report-only → enforce | core: безопасность, core: ревизия №2 п. 5 (фильтр security.csp) | ✅ |
| 6.10 | Аудит действий админов (кто что менял) — модуль cms/audit, но события ядра достаточны для него | покрытие событий | modules/audit | ✅ |
7. Расширяемость и экосистема модулей
| # | Критерий | Проверка | Где в ТЗ | Статус |
|---|---|---|---|---|
| 7.1 | Модуль расширяет всё через 5 каналов + реестры; правка ядра не требуется никогда | инвариант §0 стандарта, ревью | data-exchange | ✅ |
| 7.2 | Контракты в cms/core-contracts semver-стабильны; breaking — только мажор с депрекацией | CI-дифф контрактов | standard §3 | ✅ |
| 7.3 | Новый provides-контракт вводится только ревизией ядра (реестр закрыт) | ревью манифестов | standard §2 | ✅ |
| 7.4 | /local-слой: точечные правки конкретного сайта без форка ядра/модулей | демо-override на playground | extensibility | ✅ |
| 7.5 | Платформа знает о модуле всё из манифеста (декларативность §0.3) | cms:doctor, cms:map | standard §2 | ✅ |
| 7.6 | Тема — тоже контракт: каскад резолва, theme.json → Tailwind, галерея блоков | смена темы из админки на playground | theme-contract | ✅ |
| 7.7 | Сторонний разработчик может написать модуль по докам, не заглядывая в код ядра | пробный модуль по docs/module.md + стандарту | dx-claude-code | 🧪 фаза 5 |
| 7.8 | Capability/permission-слой для будущего маркетплейса заложен (модуль объявляет, что ему нужно) | манифест permissions | security: маркетплейс | ✅ |
8. Эксплуатация парка и DX студии
| # | Критерий | Проверка | Где в ТЗ | Статус |
|---|---|---|---|---|
| 8.1 | cms:doctor --json диагностирует типовые поломки без чтения кода | сценарии рассогласования волны 6 | core: компоненты | ✅ |
| 8.2 | Машинная карта системы (cms:map) — модули, версии, роуты, хуки — для агента и флит-дашборда | GET /admin/system/map | core: API | ✅ |
| 8.3 | Health-агрегат ядра+модулей за ≤ 2 с, пригоден для Caddy/мониторинга | тест таймаутов чеков | core: производительность | ✅ |
| 8.4 | Телеметрия парка: версии, health, p95, hit-rate — суточный пинг (прозрачный для клиента) | fleet-dashboard фаза 4 | update-center | ✅ |
| 8.5 | Claude Code пишет 100% кода модуля по ТЗ + донору: петля DX воспроизводима | факт полигона (волны 0–24 собраны агентом) | dx-claude-code | ✅ |
| 8.6 | Генераторы cms:make:* дают скелет модуля, проходящий composer test из коробки | смок генератора | build-order волна 0 | ✅ |
| 8.7 | Время диагностики инцидента на клиентском сайте ≤ 15 мин (ранбук + doctor + health) | пост-инцидентные разборы | core: эксплуатация | 🧪 |
| 8.8 | Staging-копия клиентского сайта поднимается одной командой (для проверки обновлений) | прогон cms:backup:clone-to-staging на playground | modules/backup, core: ревизия №2 п. 6 | ✅ |
9. Гипотезы принятые (проверяются пилотом, могут изменить контракты)
| # | Гипотеза | Как проверяем | Риск, если неверна |
|---|---|---|---|
| 9.1 | JSONB+GIN достаточно для типов контента до ~10⁵ записей — EAV не нужен | нагрузочный тест выборок по полям | пересмотр хранения (дорогой мажор) — content-model |
| 9.2 | «Средней свободы» редактора хватает клиентам — запросов на стили на блоке не будет массово | обратная связь первых 3 клиентов | давление в сторону Tilda-режима (Q9 пересмотр) |
| 9.3 | Одна кодовая база ядра тянет и корпсайт, и (позже) e-commerce без форка | пилот фазы 3 на реальном магазине | расщепление на профили ядра |
| 9.4 | Path-repos + Satis + cms:upgrade достаточно для дистрибуции — свой транспорт не нужен | эксплуатация парка фазы 2 | возврат к идее агента обновлений |
| 9.5 | Blade + острова закрывают 95% фронтенд-потребностей корпсайтов (React-темы не нужны) | первые клиентские темы | ревизия frontend-architecture |
| 9.6 | Bус-фактор снят документацией: новый исполнитель (человек/агент) входит в проект за день | онбординг-тест на свежем агенте | усиление docs/module.md-гейтов |
10. Граница ядра: что в ядре, что в модуле
Тест на включение в ядро — все четыре «да» (иначе — модуль):
- Нужно каждому корпоративному сайту с первого дня (не «многим», а каждому).
- Нельзя доустановить позже без переделки данных/контрактов (ревизии, измерения locale/city/site, движок полей — ретрофит дороже включения).
- На это опираются контракты: минимум два модуля из разных доменов используют способность через реестр/сервис ядра.
- Не тянет внешних зависимостей (API третьих сторон, тяжёлые бинарники, платные сервисы) — внешнее всегда за provides-контрактом в модуле.
Обратные признаки (любой — почти наверняка модуль): нужен не всем · внешний API/тариф · свой жизненный цикл данных (журналы с ретеншном, подписчики) · доменная логика (коммерция, маркетинг) · заменяемая реализация одной способности (поиск, капча, оплата).
Графовые критерии (проверяемые по манифестам — граф зависимостей): ядро не может требовать модуль — понадобилась стрелка ядро → модуль, значит способность спроектирована неверно (в ядре остаётся контракт, реализация уезжает в модуль); потребители из ≥ 2 разных доменов — аргумент за контракт в ядре, потребители одного домена — это хаб домена (commerce-orders, integrations-bus), а не ядро; у хаба с in-degree ≥ 5 дисциплина изменений — как у ядра (deprecation-окно, мажор с миграцией потребителей), но в ядро он от этого не переезжает.
Разбор пограничной зоны (паттерн везде один: ядро владеет точкой расширения и минимальной реализацией, модуль — расширенной/заменяемой):
| Способность | В ядре | В модуле | Мотив |
|---|---|---|---|
| Поиск | PG tsvector + GET /search | cms/search (Meilisearch/Typesense) через search-provider | базовый поиск нужен всем; движок — тяжёлая зависимость |
| Редиректы | RedirectService + таблица + админ-CRUD | — (модули только регистрируют) | «изменён URL → 301» — целостность SEO каждого сайта |
| Sitemap/robots | генерация + SitemapRegistry | модули регистрируют свои URL | контракт, на который опираются ≥5 модулей |
| ПДн | PrivacyRegistry + cms:privacy:* + consent_at форм | cms/consents (реестр согласий, версии текстов, отчёты РКН) | каскад «забыть» обязан работать и без модуля |
| Здоровье | /system/health агрегат + таймауты | cms/health (расширенные чеки, алерты, эскалация) | эндпоинт нужен мониторингу с первого дня |
| Медиа | медиатека + конверсии + hook скана загрузок | cms/storage-s3 (storage-backend), антивирус-модуль (контракт upload-scanner введён ревизией №2) | локальный диск — каждому; S3/ClamAV — внешнее |
отправка через .env-транспорт (уведомления ядра) | cms/transport-providers (фейловер, suppression), cms/email-templates (версии, редактор) | письмо о лиде должно уйти и на голом ядре | |
| Мультиязычность | измерение locale в данных/кеше/RequestContext, i18n строк админки | cms/multilang (переводы контента, hreflang, fallback-цепочки UI) | ретрофит измерения невозможен; UI переводов нужен не всем |
| Мультисайт/город | измерения site_id/city_id (nullable) везде | cms/multisite, cms/multicity (резолв, справочники, домены) | то же правило измерений |
| Защита форм | CSRF/honeypot/rate-limit/consent_at | cms/antispam (капчи через captcha-provider, скоринг), cms/antifraud | без базовой защиты нельзя выпускать ни один сайт |
| Аудит | канонические события (*Saved/Deleted, auth) | cms/audit (журнал, hash-цепочка, ретеншн, экспорт дел) | журнал = свой жизненный цикл данных |
| Обновления | реестр модулей, версии, cms:doctor/cms:map, машина состояний | cms/updates (пайплайн cms:upgrade, Satis-клиент, UI) | знание «что установлено» — ядро; процесс — модуль |
| Планировщик | ScheduleRegistrar | cms/scheduled-publishing (отложенные ревизии) | регистрация cron — ось ядра; отложенный контент — не всем |
| Персонализация | флаг personalized, сегментация ключа кеша | cms/feature-flags, cms/ab-testing | механизм кеша нельзя отдать модулям (5.4) |
| Кабинет посетителя | аутентификация, роли, /auth/* | cms/cabinet-b2c и далее | вход нужен админке всегда; кабинет — доменная надстройка |
Решённые «да, в ядре» из брейншторма остаются в силе: типы контента (Q5), ревизии (Q7), конструктор форм (Q8), виджеты как отдельная сущность (Q4) — решения. Кандидаты на новые контракты ядра копятся здесь со статусом ⬜ и вводятся только ревизией. Первый пакет (2.8, 2.9, 4.7, 6.8, 6.9, 8.8) закрыт ревизией №2 от 14.07.2026; очередь пуста.
Правила ведения реестра
- Новая возможность ядра → сначала строка здесь (со статусом ⬜/🧪), затем ревизия контрактов в ТЗ ядра, затем реализация. Не наоборот.
- Смена статуса 🧪→✅ — только со ссылкой на факт (тест, прогон, метрика пилота).
- Пробелы ⬜ копятся здесь и закрываются пакетно очередной ревизией контрактов (не «сразу в код»): первый пакет закрыт ревизией №2 14.07.2026, текущих ⬜ нет.
- Критерий, потерявший актуальность, не удаляется — зачёркивается с причиной (история решений дороже чистоты списка).