- вопрос 3 закрыт: модель записана в README и GUIDE, отмечены три места, где итог разошёлся с прежними заготовками — нет лока, нет маркеров секций, локальные префиксы не объявляются, а резервируются буквой `X` - вопрос 4 переформулирован: регионы не переносятся в единую секцию, а удаляются из канона — наполнять их в каноне нечем - вопрос 7 заменён разбором: транспорт переизобретался, оси и сборка аналога не имеют, Vale отклонён; в 1, 9 и 10 добавлены следствия
261 lines
21 KiB
Markdown
261 lines
21 KiB
Markdown
# К обсуждению
|
||
|
||
Черновик для следующего разговора: вопросы и варианты, а не принятые
|
||
решения.
|
||
|
||
## 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` в прозе «Оформления» даёт ложное
|
||
срабатывание.
|