Files
dev-conventions/TODO.md
T
av fea0285619 todo: закрыт вопрос про модель сборки, разобраны прототипы
- вопрос 3 закрыт: модель записана в README и GUIDE, отмечены три места,
  где итог разошёлся с прежними заготовками — нет лока, нет маркеров секций,
  локальные префиксы не объявляются, а резервируются буквой `X`
- вопрос 4 переформулирован: регионы не переносятся в единую секцию, а
  удаляются из канона — наполнять их в каноне нечем
- вопрос 7 заменён разбором: транспорт переизобретался, оси и сборка аналога
  не имеют, Vale отклонён; в 1, 9 и 10 добавлены следствия
2026-07-26 12:55:16 +03:00

21 KiB
Raw Blame History

К обсуждению

Черновик для следующего разговора: вопросы и варианты, а не принятые решения.

1. Ссылка на LANGUAGE.md не переживает сборку

Все двенадцать конвенций во вводной прозе пишут «Форма записи — LANGUAGE.md». Обвязка в репозиторий не едет (conv синхронизирует только conventions/), поэтому в собранной копии эта ссылка указывает в никуда.

Это тот же класс дефекта, который вчера закрыло META-21 для ссылок между темами: путь, верный в каноне, молча умирает при сборке. Разница в том, что META-21 предлагает заменить путь на имя темы — а здесь заменять не на что, целевого документа в репозитории просто нет.

Варианты, которые видно сейчас:

  • Убрать упоминание из тел конвенций. Язык записи — забота того, кто пишет канон, а не того, кто читает конвенцию в своём репозитории. Читателю достаточно самого текста: модальные слова и «Почему» самоописательны. Дешевле всего, но копия теряет указание, по каким правилам её править.
  • Возить обвязку вместе с конвенциями. Тогда LANGUAGE.md и, возможно, GUIDE.md появляются в docs/conventions/ как ещё одни синхронизируемые файлы. Честно, но противоречит нынешнему разделению «канон — конвенции, корень — обвязка» и добавляет в репозиторий текст, который агенту при чтении конвенции не нужен.
  • Вкладывать короткую преамбулу в собранный файл. Сборщик пишет в шапку три-четыре строки: что такое модальное слово, что «Почему» обязательно, где лежит полный документ. Самодостаточно и не тащит весь язык, но преамбула дублируется в каждом файле темы.

Появился четвёртый вариант, и он выглядит лучше трёх: boilerplate-строка как в RFC 8174. Каждая конвенция несёт одну фразу — «ключевые слова … толкуются как описано в „Языке конвенций“ версии 1 — тогда и только тогда, когда написаны заглавными». Пути нет, документа рядом нет, а норму исполнить можно: строка сама несёт и словарь, и правило капса. Разбирается в заходе про язык записи, вместе с версией — она же цепляет вопрос 9.

Сопутствующее: GUIDE.md в телах конвенций не упоминается ни разу — проверено, так что вопрос только про LANGUAGE.md.

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. Не переизобретено ли это — разобрано

Проверка перед вложением в тулинг сделана. По слоям:

  • Язык записи — велосипед, но собранный из проверенных деталей: словарь совпадает с RFC 2119/8174 вплоть до правила «нормативен только капс», обязательное «Почему» — дисциплина из requirements engineering (ISO/IEC/IEEE 29148), стабильные идентификаторы — из semgrep/ESLint. Менять архитектуру нечего, остались шесть точечных дельт — отдельным заходом.
  • Оси и сборка — аналога не нашлось. Ближайшие соседи (каскад extends в ESLint, слои стилей Vale) дают праву слоя отменять базу; наш запрет строже, и это содержательная часть. Вкладываться сюда.
  • Транспорт — переизобретался, но задача у нас другая, чем у copier и cruft. У них трёхсторонний merge потому, что шаблон порождает код, который правят везде: расхождение неограниченно. У нас расхождение ограничено и живёт под маркером, поэтому исключение из перезаписи лучше merge — детерминированно и без конфликтных маркеров. Из vendir/cruft взят разбор «манифест против лока», из ansible blockinfile — идея размечать управляемый кусок, а не локальный. Лок при этом не взят: его роль играет git.
  • Раздача агентам — не наша задача, остаёмся на AGENTS.md/CLAUDE.md плюс скиллы.

Vale отклонён. Из восьми проверок в LANGUAGE.md он честно закрывает две-три (модальные слова вне правил, запрещённые формулировки); остальные пять структурные — «у каждого ### ПРЕФИКС-N есть модальность и Почему», «номера без дыр вниз», «префикс совпадает с реестром». Это разбор документа, а не проверка токенов в области. Ради двух-трёх проверок тянуть ещё один бинарь и директорию стилей при одном пользователе не стоит.

Что стоит посмотреть перед переписыванием conv (вопрос 2): дистрибуцию пакетов Vale (.vale.inivale syncstyles/) как готовый прототип манифеста, и vendir.yml — как пример того, где проходит граница между «чего хочу» и «что получил».

8. Мультиязычность ключевых слов

Модальные слова сейчас русские, и это осознанно: разный словарь держит границу «конвенция — не спецификация» бесплатно. Вопрос — нужен ли английский набор параллельно.

За: канон может однажды понадобиться на английском; агенты натренированы на MUST/SHOULD плотнее, чем на ДОЛЖЕН/СЛЕДУЕТ. Против: два набора — это два способа записать одно, и проверка «модальные слова не встречаются вне правил» усложняется вдвое.

Если делать, то таблица ключевых слов должна принадлежать описанию языка, а не каждой конвенции — то есть вопрос завязан на следующий.

9. Описание языка отдельно от набора конвенций

LANGUAGE.md и GUIDE.md описывают, как пишутся конвенции; conventions/один конкретный набор. Сейчас они склеены в одном репозитории, и из одного описания нельзя собрать второй набор (рабочий, доменный, чужой).

Что это даёт, если разнести:

  • канон объявляет, какой версии языка следует, а тулинг валидирует набор против объявленного описания, а не против зашитых в код правил;
  • таблица модальных слов (вопрос 8) живёт в описании и одна на все наборы;
  • вопрос 1 (LANGUAGE.md не переживает сборку) меняет форму: собранный файл ссылается не на путь, а на язык с версией.

Цена — ещё одна сущность и ещё одна синхронизация. Проверить, не выйдет ли лечение хуже болезни при одном пользователе.

Оценка выросла: у выноса появилась вторая, независимая причина. Boilerplate- строка из вопроса 1 обязана назвать язык с версией — иначе она врёт при первом же изменении словаря. Версия предполагает документ, у которого версия бывает, то есть отдельный от набора. Так IETF и решил ровно эту задачу: RFC 2119 — самостоятельный документ, на который спецификации ссылаются номером, а не путём.

10. Тулинг на Go, живущий независимо

Сейчас conv — питоновский скрипт внутри канона, то есть тулинг и данные в одном репозитории и правятся одним движением. Мысль: вынести в отдельный Go-бинарь со своим релизным циклом, ставить через eget (механизм уже есть в pet-project-server).

Что за этим стоит помимо вкуса: независимый бинарь физически не даёт править инструмент «заодно» с правкой конвенции, работает против любого канона и любого потребителя — что прямо требуется вопросом 9, — и снимает питон из зависимостей репозиториев-потребителей.

Связано с вопросом 2: если тулинг всё равно переписывается, разделение «целостность канона / установка в проект» дешевле заложить сразу, чем отпиливать потом.

Порядок при этом обратный написанному: пока вопрос 9 не сделан, инструмент всё равно работает против одного конкретного канона, и независимый релизный цикл ему нечего обслуживать. Сначала 9, потом 10.

11. Ссылки на родительский слой своей темы

Из lang/ и stack/ ссылаться на идентификаторы своего же арх-слоя безопасно: при сборке они оказываются секциями одного файла, и ссылка никуда не ведёт — правило рядом. Ограничение META-20 писалось именно про чужую тему, так что формально это уже разрешено.

Но стоит проговорить явно, потому что сейчас читается уже как запрет:

  • в LANGUAGE.md проверка сформулирована как «чужой префикс не встречается в абзаце с модальностью» — по букве это ловит и GTIMTIME-3, то есть ложно срабатывает на ровно том случае, который разрешён. Должно быть «префикс чужой темы»;
  • обсудить, не добавить ли в META-20 явную строку ДОПУСКАЕТСЯ про родительский слой: правило, которое читают как более строгое, чем оно есть, заставляет авторов дублировать текст без нужды.

Из вчерашнего, не закрыто

  • Вынос арх-ядра из errors и logging: закроет две хрупкие ссылки из web-ui (стек) в go-слой — единственные ссылки стек → язык в каноне.
  • conv check должен уметь отличать ссылку на удалённое правило от упоминания дыры: сейчас META-16 в прозе «Оформления» даёт ложное срабатывание.