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