- шкала обязательности объявлена инвариантом, а набор ключевых слов — параметром естественного языка набора: для английского готовый словарь даёт BCP 14, для прочих берут перевод стандарта или делают свой; отклонены синонимы ступеней и `SHALL`, занятый OpenSpec - применены шесть дельт: нормативно только заглавное написание (RFC 8174), ДОЛЖЕН требует названного вреда и машинной проверки сразу, ДОПУСКАЕТСЯ адресовано рецензенту, МЕХАНИЗИРОВАНО выведено из шкалы в отметку рядом с модальностью (ISO/IEC/IEEE 29148), у таблиц объявлены политика совпадения и полнота (DMN) - «Форма записи — LANGUAGE.md» в двенадцати конвенциях заменена строкой о версии языка по образцу boilerplate BCP 14: пути канона в копии не существует, а словарь и правило заглавных строка несёт сама
20 KiB
К обсуждению
Черновик для следующего разговора: вопросы и варианты, а не принятые решения.
1. Ссылка на язык — закрыто
Заменено boilerplate-строкой по образцу BCP 14: каждая конвенция называет
ключевые слова, версию языка и правило заглавных, но не путь. Строка стоит
отдельным абзацем после вводной прозы во всех двенадцати файлах, «Форма
записи — LANGUAGE.md» удалена. Точный текст — в LANGUAGE.md, раздел
«Ссылка на язык из конвенции».
Побочно: строка перечисляет модальные слова заглавными, то есть сама нарушает проверку «заглавные модальные слова не встречаются вне правил». Исключение записано в список проверок — так же, как оно устроено в BCP 14, где boilerplate тоже содержит ключевые слова.
2. Тулинг: две разные задачи в одном conv
Сейчас в conv смешаны две категории работы, и они расходятся по всему —
по частоте запуска, по тому, кто запускает, и по тому, что считается
провалом.
Целостность канона. Префиксы уникальны и не переиспользованы, шапка совпадает с реестром, номера без дыр вниз, у каждого правила модальность и «Почему», ссылки разрешаются, чужой префикс не лезет в норму (META-20), путей канона в тексте нет (META-21). Запускается в каноне, при каждой правке, провал — это ошибка. Логика уже написана и много раз прогнана руками, но живёт в скретчпаде, а не в репозитории.
Установка в проект. Манифест .conventions.toml, сборка файла темы из
секций (arch → языки → стеки → local), сохранение локальной секции при
пересборке, отчёт «канон ушёл вперёд», предупреждение о висячих ссылках на
неподписанные темы. Запускается в репозитории-потребителе, изредка, провал —
это чаще «посмотри глазами», чем «ошибка».
Что обсудить:
- Разделять ли на два исполняемых файла, или хватит подкоманд с честной границей внутри.
- Валидацию канона стоит ли отдать агенту скиллом вместо (или вдобавок к) скрипту: часть проверок формулируется как «прочитай и скажи, самодостаточна ли норма» — механически это не берётся, а агентом берётся.
- Куда в этой раскладке ложится запаркованное предупреждение о висячих ссылках: это установка, а не целостность, но список подписок ему нужен из манифеста.
3. Модель сборки — записана, закрыто
Записана в README.md (разделы «Копия в репозитории» и «Манифест») и в
GUIDE.md (META-22, META-23). Плоская раскладка, файл на тему, один маркер
<!-- conv:local -->, манифест из источника и списка тем, направление
строго одностороннее.
Против прежних заготовок разошлось в трёх местах:
- лока нет — «что было в прошлый раз» знает git, копии закоммичены, и
автоматического обновления не существует; заодно из шапки копии ушли
origin_hashиsynced, осталось одноorigin:; - маркеров секций нет — раз обновление перезаписывает всё выше локального маркера, разметка слоёв тулингу не нужна; слои идут обычными заголовками;
- локальные префиксы не объявляются в манифесте — за репозиториями
зарезервирована буква
X, столкновение невозможно по построению.
Остаётся переписать conv: сейчас в нём зеркальное дерево, именованные
регионы, origin_hash и команды status/diff/push. Это вопрос 2.
4. Именованные регионы — удалить из канона
Решение принято и записано: регионов нет, есть один маркер
<!-- conv:local -->, который ставит сборщик в копии. Значит регионы не
переносятся в единую секцию, а удаляются: в каноне их нечем наполнять,
локальное принадлежит копии.
Остаётся механическая работа — 31 регион в 12 файлах:
7 связано 7 отступления 7 механизировано
1 эталон / эталоны / словарь / секреты / секретные-поля
1 проверки / поля / модель-владельца / маппинг / границы
Вопрос про связано снят: META-17 переписан, канонический раздел «Связано»
остаётся в тексте и содержит только ссылки, верные у всех, а репо-специфичные
уходят под маркер.
5. Пары слоёв и темы без базы
Отложено сознательно, но список стоит держать перед глазами:
SLOGобъявляетextends: arch/time.md— расширение чужой темы. Сборщик темыloggingна это наткнётся: базового слоя с темойloggingнет, аarch/time.mdон тянуть не должен. Чинится переводом в обычную ссылку «связано».- Темы без арх-слоя:
db-schema,errors,web-ui. Собираются в файл с одной секцией — само по себе не ломается, но это и есть тот невыделенный арх-слой из известного долга. - Имена тем в паре не совпадают:
arch/db-identifiers.mdпротивlang/go/db-schema.md. При сборке по имени темы это две разные темы — проверить, что так и задумано. - В
lang/go/db-schema.mdсидит целый пластstack/sqlite/(типы колонок), тоже из известного долга README.
6. Подключение к репозиториям
Ничего ещё не подключено. Кандидаты — jellybit и pet-project-server.
Понадобится: заполнить локальные секции тем, что сейчас в этих репозиториях
записано по факту; обёртка в раннере (inv conventions / task conventions,
единый интерфейс команд у трёх ansible-репозиториев); строка в AGENTS.md
каждого потребителя про то, что файлы в docs/conventions/ — копии.
Открытый кусок с прошлого раза: как копия ссылается на канон, не ломая
самодостаточность. Абсолютный путь ~/projects/private/dev-conventions
в закоммиченном файле не годится — репозиторий перестаёт быть
самодостаточным и получает хардкод путей. Пересекается с вопросом 1.
7. Не переизобретено ли это — разобрано
Проверка перед вложением в тулинг сделана. По слоям:
- Язык записи — совпал со стандартами почти во всём: шкала и правило
«нормативно только заглавное» — BCP 14, обязательное обоснование и
единичность нормы — ISO/IEC/IEEE 29148, категории — ISO/IEC Directives
Part 2, таблицы решений — DMN, стабильные идентификаторы — semgrep/ESLint.
Шесть точечных дельт применены, опора на источники записана в
LANGUAGE.mdразделом «Опора на стандарты». - Оси и сборка — аналога не нашлось. Ближайшие соседи (каскад
extendsв ESLint, слои стилей Vale) дают праву слоя отменять базу; наш запрет строже, и это содержательная часть. Вкладываться сюда. - Транспорт — переизобретался, но задача у нас другая, чем у copier и
cruft. У них трёхсторонний merge потому, что шаблон порождает код, который
правят везде: расхождение неограниченно. У нас расхождение ограничено и
живёт под маркером, поэтому исключение из перезаписи лучше merge —
детерминированно и без конфликтных маркеров. Из vendir/cruft взят разбор
«манифест против лока», из ansible
blockinfile— идея размечать управляемый кусок, а не локальный. Лок при этом не взят: его роль играет git. - Раздача агентам — не наша задача, остаёмся на
AGENTS.md/CLAUDE.mdплюс скиллы.
Vale отклонён. Из восьми проверок в LANGUAGE.md он честно закрывает
две-три (модальные слова вне правил, запрещённые формулировки); остальные
пять структурные — «у каждого ### ПРЕФИКС-N есть модальность и Почему»,
«номера без дыр вниз», «префикс совпадает с реестром». Это разбор документа,
а не проверка токенов в области. Ради двух-трёх проверок тянуть ещё один
бинарь и директорию стилей при одном пользователе не стоит.
Что стоит посмотреть перед переписыванием conv (вопрос 2): дистрибуцию
пакетов Vale (.vale.ini → vale sync → styles/) как готовый прототип
манифеста, и vendir.yml — как пример того, где проходит граница между
«чего хочу» и «что получил».
8. Мультиязычность ключевых слов — закрыто по механике
Вопрос был поставлен как «нужен ли английский набор параллельно русскому». Ответ оказался другой формы: словарь — параметр естественного языка набора, а не часть языка конвенций. Шкала из пяти ступеней инвариантна, слова под неё подбираются: для английского готовый словарь даёт BCP 14, для любого другого языка слова берут из перевода стандарта или переводят сами.
Отсюда следствия, снимающие исходный вопрос:
- параллельных наборов не бывает. Словарь один на канон: два словаря дают две формы записи одного требования и удваивают каждую проверку;
- версия языка при смене словаря не меняется — версия принадлежит шкале и правилам формы, а не буквам. Прежняя оценка «английский набор = версия 2» неверна;
SHALLне берётся ни в одном словаре, потому что занято OpenSpec; для английского это выбор в пользуMUSTиз BCP 14, а неshallиз ISO;- отметка о механизации стандартом не даётся ни в одном языке и подбирается
так же, как остальные слова (
МЕХАНИЗИРОВАНО/MECHANIZED).
Открытым остаётся только прикладное: понадобится ли этому канону английская версия вообще. Механика для неё уже описана, заводить заранее нечего.
9. Описание языка отдельно от набора конвенций
LANGUAGE.md и GUIDE.md описывают, как пишутся конвенции;
conventions/ — один конкретный набор. Сейчас они склеены в одном
репозитории, и из одного описания нельзя собрать второй набор (рабочий,
доменный, чужой).
Что это даёт, если разнести:
- канон объявляет, какой версии языка следует, а тулинг валидирует набор против объявленного описания, а не против зашитых в код правил;
- таблица модальных слов (вопрос 8) живёт в описании и одна на все наборы;
- вопрос 1 (
LANGUAGE.mdне переживает сборку) меняет форму: собранный файл ссылается не на путь, а на язык с версией.
Цена — ещё одна сущность и ещё одна синхронизация. Проверить, не выйдет ли лечение хуже болезни при одном пользователе.
Срочность снята: версия появилась у LANGUAGE.md (ключ version: в шапке),
и boilerplate-строка из вопроса 1 уже ссылается на язык номером, а не путём —
не дожидаясь выноса. Отдельный репозиторий остаётся вопросом второго набора
конвенций, а не вопросом самодостаточности копии.
Что осталось поводом: из одного описания по-прежнему нельзя собрать второй набор, и тулинг валидирует правила, зашитые в код, а не объявленную версию языка. Оба повода включаются, только когда появится второй набор.
10. Тулинг на Go, живущий независимо
Сейчас conv — питоновский скрипт внутри канона, то есть тулинг и данные в
одном репозитории и правятся одним движением. Мысль: вынести в отдельный
Go-бинарь со своим релизным циклом, ставить через eget (механизм уже есть
в pet-project-server).
Что за этим стоит помимо вкуса: независимый бинарь физически не даёт править инструмент «заодно» с правкой конвенции, работает против любого канона и любого потребителя — что прямо требуется вопросом 9, — и снимает питон из зависимостей репозиториев-потребителей.
Связано с вопросом 2: если тулинг всё равно переписывается, разделение «целостность канона / установка в проект» дешевле заложить сразу, чем отпиливать потом.
Порядок при этом обратный написанному: пока вопрос 9 не сделан, инструмент всё равно работает против одного конкретного канона, и независимый релизный цикл ему нечего обслуживать. Сначала 9, потом 10.
11. Ссылки на родительский слой своей темы
Из lang/ и stack/ ссылаться на идентификаторы своего же арх-слоя
безопасно: при сборке они оказываются секциями одного файла, и ссылка
никуда не ведёт — правило рядом. Ограничение META-20 писалось именно про
чужую тему, так что формально это уже разрешено.
Формулировка проверки исправлена: в LANGUAGE.md теперь «префикс чужой
темы не встречается в абзаце с модальностью», и там же сказано, что
префикс своего базового слоя допустим. Ложного срабатывания на GTIM →
TIME-3 больше нет.
Осталось решить одно: добавлять ли в META-20 явную строку ДОПУСКАЕТСЯ про родительский слой. За — правило, которое читают строже, чем оно есть, заставляет авторов дублировать текст без нужды. Против — норма META-20 уже говорит «чужой темы», и второе правило про то же место придётся держать согласованным с первым.
Из вчерашнего, не закрыто
- Вынос арх-ядра из
errorsиlogging: закроет две хрупкие ссылки изweb-ui(стек) в go-слой — единственные ссылки стек → язык в каноне. conv checkдолжен уметь отличать ссылку на удалённое правило от упоминания дыры: сейчасMETA-16в прозе «Оформления» даёт ложное срабатывание.