Files
dev-conventions/TODO.md
T
av c1cb240540 темы: определение, объявление в шапке и манифест набора
- тема — набор правил об одном фокусе разработки, имя латиницей (нижний
  kebab-case рекомендуется, годится любой идентификатор, пригодный для имени
  файла); определение в LANGUAGE.md и README.md
- заведены META-28 и META-29: тема объявляется в шапке (`topic:`), стоит в
  манифесте набора и не переиспользуется; `topic:` добавлен во все 12 файлов
- prefixes.toml и topics.toml слиты в manifest.toml — манифест набора против
  манифеста подключения `.conventions.toml`, разделы topics/prefixes с live
  и retired
2026-07-26 15:27:55 +03:00

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