Files
dev-conventions/TODO.md
T
av df8c58671f язык: объявлена граница правила
- область правила — от его заголовка до следующего заголовка любого уровня;
  метка открывает блок, хвост после ПОЧЕМУ — продолжение обоснования, а
  таблица после модальной метки — часть нормы
- проверка «заглавных модальных слов вне правил нет» стала реализуемой:
  прозой считается то, что лежит вне областей правил
- нормы, сидевшие в хвостах, подняты в блок нормы: заведены SLOG-25.4 и
  GERR-26.3, у GTIM-12 «базовый слой» заменён на TIME-12
2026-07-26 15:10:59 +03:00

256 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# К обсуждению
Черновик для следующего разговора: вопросы и варианты, а не принятые
решения. Закрытый вопрос отсюда удаляется — принятое решение живёт в
`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 при этом
дешевле заложить сразу, чем отпиливать потом.