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

261 lines
21 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.
# К обсуждению
Черновик для следующего разговора: вопросы и варианты, а не принятые
решения.
## 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.ini``vale sync``styles/`) как готовый прототип
манифеста, и `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` проверка сформулирована как «**чужой префикс** не
встречается в абзаце с модальностью» — по букве это ловит и `GTIM`
`TIME-3`, то есть ложно срабатывает на ровно том случае, который
разрешён. Должно быть «префикс **чужой темы**»;
- обсудить, не добавить ли в META-20 явную строку **ДОПУСКАЕТСЯ** про
родительский слой: правило, которое читают как более строгое, чем оно
есть, заставляет авторов дублировать текст без нужды.
## Из вчерашнего, не закрыто
- Вынос арх-ядра из `errors` и `logging`: закроет две хрупкие ссылки из
`web-ui` (стек) в go-слой — единственные ссылки стек → язык в каноне.
- `conv check` должен уметь отличать ссылку на удалённое правило от
упоминания дыры: сейчас `META-16` в прозе «Оформления» даёт ложное
срабатывание.