# К обсуждению Черновик для следующего разговора: вопросы и варианты, а не принятые решения. Закрытый вопрос отсюда удаляется — принятое решение живёт в `README.md`, `GUIDE.md` или `LANGUAGE.md`, а не в этом файле. Две секции: сначала язык и подход, потом канон с тулингом. Пункты 1–7 пришли из внешнего ревью описания языка и проверены по файлам на месте. # Язык и подход ## 1. Примеры в LANGUAGE.md сидят на живых идентификаторах Учебные примеры используют настоящие префиксы канона с номерами, которые в каноне означают другое: | В примере `LANGUAGE.md` | В каноне на самом деле | |---|---| | `MIGR-4` проверяет `AUTOINCREMENT` в новых миграциях | MIGR-4 — «В деплое схема движется только вперёд»; про AUTOINCREMENT — MIGR-12 | | «MIGR-6. Дефолтов времени в схеме БД нет» | MIGR-6 — «ER-схема обновляется в том же изменении»; дефолты — MIGR-9 | | «KEYS-5. Разбор внешнего идентификатора на границе» | KEYS-5 — «Внешний идентификатор разбирается до обращения к базе», с другим текстом нормы | Язык держится на том, что идентификатор адресует ровно одно утверждение, и документ, определяющий язык, это нарушает. Ни одна проверка не поймает — идентификаторы существуют. Дрейф вдобавок гарантирован: канон перенумеровывался, примеры не двигались. Лечится дёшево: примеры берут префиксы на `X`, зарезервированные как раз под то, что каноном не занято. ## 2. «Тема» — несущий идентификатор без определения и реестра META-21 велит ссылаться на соседнюю конвенцию именем темы, манифест подписывается `topics = ["time", …]`, сборка собирает файл темы из слоёв — но нигде не сказано, что такое имя темы (имя файла без расширения? отдельный атрибут в шапке?) и где список тем существует. У префиксов есть реестр `prefixes.toml`, запрет переименования и запрет переиспользования. У тем нет ничего: ссылка `KEYS-5` валидируется, ссылка «конвенция `logging`» — нет, и в списке проверок её тоже нет. Переименование файла темы тихо осиротит все текстовые ссылки во всех копиях. Вопрос уже не гипотетический: пара `arch/db-identifiers.md` против `lang/go/db-schema.md` (см. вопрос 11) показывает, что имена слоёв одной темы могут не совпадать. Смежно: `extends: arch/time.md` у SLOG ломает гарантию META-24 («базовый слой отсутствовать не может») — она верна только для базы своей темы, а машинной проверке негде узнать тему, кроме имени файла. ## 3. GUIDE выведен из-под проверок ложным основанием `LANGUAGE.md` исключает обвязку из проверок формулировкой «она ключевые слова цитирует, а не употребляет». Для `GUIDE.md` это неверно: META-правила употребляют ДОЛЖЕН нормативно, а префикс META зарегистрирован в `[live]`, где прямо сказано, что правила записаны тем же языком. Три следствия. META-правила не попадают ни под одну проверку формы. В `GUIDE.md` нет строки о версии языка, то есть у META-правил формально нет ключа к толкованию. И нарушение уже есть: раздел «Оформление» содержит заглавное СЛЕДУЕТ во вводной прозе — в файле конвенции это было бы нарушением, а исключение обвязки его прячет. Честнее развести: `GUIDE.md` язык **употребляет** и проверяется как конвенция, `LANGUAGE.md` и `README.md` — цитируют. ## 4. МЕХАНИЗИРОВАНО не переживает нового подписчика META-8 запрещает удалять норму, пока механизирована не у всех, и защищает тем самым потребителей, существующих **на момент удаления**. Будущих не защищает никто. Сценарий: норма удалена, потому что у всех трёх тогдашних потребителей был линтер. Через год подключается четвёртый репозиторий, подписывается на тему — и получает правило без формулировки и без проверки: ни текста, ни линтера, восстановление только через git-историю канона. Смежное: «общий конфиг линтера или общая роль» из META-9 — сущность, которой в модели распространения (манифест, темы, слои) не существует, и непонятно, как она доезжает до потребителя. И отдельно: запись «проверяется общим правилом линтера» — это утверждение о состоянии инфраструктуры потребителей в тексте канона, то есть ровно то, что запрещает META-4. Либо это законное исключение, и тогда его надо назвать, либо конфликт. ## 5. Семантика ключевых слов в копию не едет Строка о версии языка перечисляет слова, но не их значения, а всё нетривиальное в шкале живёт только в `LANGUAGE.md`, который в репозиторий не едет: что ДОПУСКАЕТСЯ запрещает возражать на ревью, что отступление от ДОЛЖЕН требует записи, что отступление от СЛЕДУЕТ требует причины. Аналогия с BCP 14 ломается именно там, где призвана работать: RFC 2119 общедоступен и общеизвестен, «язык конвенций версии 1» — нет. Агент в репозитории-потребителе прочитает ДОПУСКАЕТСЯ как бытовое «можно» и примет возражение на ревью — ровно та потеря, ради которой слово вводилось. Для одного автора терпимо, для агентов — нет. Варианты: возить рядом с копиями короткую выжимку семантики; расширить строку о версии до двух-трёх предложений; или признать ограничение и записать его явно. ## 6. Две «механические» проверки без источника данных В списке «разбором текста» стоят два пункта, которые без дополнительного реестра нерешаемы: - **«номера не имеют пропусков вниз»** — дыры в нумерации нормальны по построению, и статическая проверка не отличит дыру от удалённого правила от опечатки в номере; - **«ссылки указывают на правила, которые ещё существуют»** — упоминание снятого номера в прозе выглядит как висячая ссылка. `GUIDE.md` завёл для себя раздел «Освободившиеся номера», но язык не требует такой таблицы от конвенций, а `prefixes.toml` хранит только префиксы. Пока реестр снятых номеров не объявлен частью языка, оба пункта принадлежат списку «чтением». ## 7. Натяжки в опоре на стандарты Три места, где источнику приписано чуть больше, чем в нём есть: - **DMN и полнота таблицы.** Политика совпадения — действительно именованное свойство DMN. Полноту стандарт не требует: индикатор полноты был в DMN 1.0 и убран в последующих версиях, её проверяют валидаторы инструментов. - **29148 и обоснование.** Rationale там — рекомендуемый атрибут требования, а не обязательный «наравне с самим требованием». Обязательный костяк стандарта — характеристики well-formed requirement, откуда честно взяты единичность и проверяемость. - **EARS.** Вывод «выигрыш дала сама обязательность шаблона, а не его конкретный вид» — экстраполяция, поданная как взятое из источника. Вывод от этого не становится неверным, но графа «что взято» описывает не содержимое EARS. Остальное в таблице проверку выдержало, включая вторую половину `MAY` из BCP 14 и списки эквивалентных словесных форм ISO Directives. ## 8. Одиннадцать таблиц не прочитаны на взаимоисключительность `LANGUAGE.md` объявил, что строки таблицы решений взаимоисключающи по умолчанию, а иной порядок объявляется явно. Объявление не делает таблицы такими: нумерованные строки есть в одиннадцати файлах канона, и ни одна таблица под новое требование не прочитана. Конкретный подозреваемый — SLOG-11: «по реальному действию или изменению» против «повторяющаяся служебная, по таймеру или поллингу». Периодическая операция, которая всё-таки меняет данные, подходит под обе строки, и уровень из таблицы не выводится однозначно. Работа читательская, машине не даётся; в список проверок она уже записана в разделе «Чтением, потому что машине не даётся». ## 9. Описание языка отдельно от набора конвенций `LANGUAGE.md` и `GUIDE.md` описывают, **как** пишутся конвенции; `conventions/` — **один конкретный** набор. Сейчас они склеены в одном репозитории, и из одного описания нельзя собрать второй набор (рабочий, доменный, чужой). Срочности нет: версия языка объявлена в самом `LANGUAGE.md` (ключ `version:`), и конвенции ссылаются на неё номером, а не путём, — то есть самодостаточность копии выноса не требует. Что осталось поводом: - из одного описания по-прежнему нельзя собрать второй набор; - тулинг валидирует правила, зашитые в его код, а не объявленную версию языка. Оба повода включаются, только когда появится второй набор. Цена — ещё одна сущность и ещё одна синхронизация; при одном наборе лечение выходит хуже болезни. # Канон, тулинг, подключение ## 10. Тулинг: две разные задачи в одном `conv` Сейчас в `conv` смешаны две категории работы, и они расходятся по всему — по частоте запуска, по тому, кто запускает, и по тому, что считается провалом. **Целостность канона.** Префиксы уникальны и не переиспользованы, шапка совпадает с реестром, у каждого правила модальность и блок ПОЧЕМУ, ссылки разрешаются, префикс чужой темы не лезет в норму (META-20), путей канона в тексте нет (META-21), строка о версии языка на месте. Запускается в каноне, при каждой правке, провал — это ошибка. Логика уже написана и много раз прогнана руками, но живёт в скретчпаде, а не в репозитории. **Установка в проект.** Манифест `.conventions.toml`, сборка файла темы из слоёв (`arch` → язык → стек), сохранение при пересборке всего, что ниже маркера, предупреждение о висячих ссылках на неподписанные темы. Запускается в репозитории-потребителе, изредка, провал — это чаще «посмотри глазами», чем «ошибка». Отчёта «канон ушёл вперёд» здесь нет: его делает `git diff` после пересборки. Что обсудить: - Разделять ли на два исполняемых файла, или хватит подкоманд с честной границей внутри. - Валидацию канона стоит ли отдать агенту скиллом вместо (или вдобавок к) скрипту: часть проверок формулируется как «прочитай и скажи, самодостаточна ли норма» — механически это не берётся, а агентом берётся. - Куда в этой раскладке ложится запаркованное предупреждение о висячих ссылках: это установка, а не целостность, но список подписок ему нужен из манифеста. Часть проверок из этого списка сейчас нереализуема по причине из вопроса 6, так что порядок такой: сначала язык, потом чекер. Перед тем как переписывать, стоит посмотреть на два готовых прототипа: дистрибуцию пакетов Vale (`.vale.ini` → `vale sync` → `styles/`) как образец манифеста и `vendir.yml` — как пример того, где проходит граница между «чего хочу» и «что получил». ## 11. Пары слоёв и темы без базы Отложено сознательно, но список стоит держать перед глазами: - `SLOG` объявляет `extends: arch/time.md` — расширение **чужой** темы. Сборщик темы `logging` на это наткнётся: базового слоя с темой `logging` нет, а `arch/time.md` он тянуть не должен. Чинится переводом в обычную ссылку «связано». Вдобавок это ломает гарантию META-24 — см. вопрос 2. - Темы без арх-слоя: `db-schema`, `errors`, `web-ui`. Собираются в файл с одной секцией — само по себе не ломается, но это и есть тот невыделенный арх-слой из известного долга. - Имена тем в паре не совпадают: `arch/db-identifiers.md` против `lang/go/db-schema.md`. При сборке по имени темы это две разные темы — проверить, что так и задумано. - В `lang/go/db-schema.md` сидит целый пласт `stack/sqlite/` (типы колонок), тоже из известного долга README. - Вынос арх-ядра из `errors` и `logging` закроет две хрупкие ссылки из `web-ui` (стек) в go-слой — единственные ссылки стек → язык в каноне. ## 12. Подключение к репозиториям Ничего ещё не подключено. Кандидаты — jellybit и pet-project-server. Понадобится: заполнить локальную часть копий тем, что сейчас в этих репозиториях записано по факту; обёртка в раннере (`inv conventions` / `task conventions`, единый интерфейс команд у трёх ansible-репозиториев); строка в `AGENTS.md` каждого потребителя про то, что файлы в `docs/conventions/` — копии. ## 13. Тулинг на Go, живущий независимо Сейчас `conv` — питоновский скрипт внутри канона, то есть тулинг и данные в одном репозитории и правятся одним движением. Мысль: вынести в отдельный Go-бинарь со своим релизным циклом, ставить через `eget` (механизм уже есть в pet-project-server). Что за этим стоит помимо вкуса: независимый бинарь физически не даёт править инструмент «заодно» с правкой конвенции, работает против **любого** канона и любого потребителя — что прямо требуется вопросом 9, — и снимает питон из зависимостей репозиториев-потребителей. Порядок обратный ожидаемому: пока вопрос 9 не сделан, инструмент всё равно работает против одного конкретного канона, и независимый релизный цикл ему нечего обслуживать. Сначала 9, потом 13. Разделение из вопроса 10 при этом дешевле заложить сразу, чем отпиливать потом.