Files
dev-conventions/TODO.md
T
av c04a54ffd5 todo: заведены четыре открытые темы по языку записи
- 12: `WHEN`/`AND` в блоке стыка правил — английские служебные слова там же,
  где `SHALL` отклонён как занятый OpenSpec; либо перевод, либо явная
  оговорка, что форма спецификации в этом месте намеренна
- 13: критерий «названного вреда» для ДОЛЖЕН описан в языке, но правила под
  него нет — META-6 обязывает только понижать при отсутствии проверки
- 14 и 15: одиннадцать таблиц не прочитаны на взаимоисключительность
  (подозреваемый SLOG-11), и канон не просмотрен на факты, записанные
  модальным словом
2026-07-26 13:43:45 +03:00

316 lines
25 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. Ссылка на язык — закрыто
Заменено 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 уже
говорит «чужой темы», и второе правило про то же место придётся держать
согласованным с первым.
## 12. `WHEN`/`AND` в блоке стыка правил
Блок для стыка двух правил записан английскими словами:
```
WHEN зависимость недоступна и ретраи вызова исчерпаны → ext-запись ERROR
AND тик фонового цикла упал по той же причине → доменная запись WARN
```
Рядом сказано, что `SHALL` не берётся ни в один словарь, потому что занят
OpenSpec. `WHEN` и `AND` — из того же набора и по той же причине должны бы
не браться, но взяты. Это нестыковка, а не решение.
Варианты:
- **перевести** на `КОГДА` / `И`: словарь набора один, и служебные слова
внутри канона следуют ему же;
- **оставить и объяснить**: блок стыка описывает поведение системы во
времени, а не выбор автора, — то есть это единственное место, где форма
спецификации уместна, и заимствование её синтаксиса намеренно.
Второе честнее по смыслу (субъект там действительно система), но требует
явной оговорки в `LANGUAGE.md`, иначе читается как недосмотр. Заодно решить,
подпадают ли `WHEN`/`AND` под проверку «заглавные модальные слова не
встречаются вне правил»: сейчас формально нет, потому что в словаре их нет.
## 13. Критерий «названного вреда» никого не обязывает
`LANGUAGE.md` говорит, что ДОЛЖЕН требует двух условий сразу: нарушение
причиняет названный вред (критерий BCP 14) и норма проверяема машиной
(META-6). Но правила под первое условие нет: META-6 работает только в одну
сторону — «нет машинной проверки, понижай в СЛЕДУЕТ». Обратной, «проверка
есть, а вреда нет — не повышай», не существует, и автор ничем не связан.
Решить, заводить ли META-24 под первое условие или оставить его семантикой
шкалы в описании языка. За правило: механически проверяемых мелочей больше,
чем важных вещей, и без нормы шкала размывается тем же способом, от которого
META-6 её защищает. Против: критерий «вред назван» проверяется чтением, а не
машиной, — то есть по META-6 сам он может быть только СЛЕДУЕТ.
## 14. Одиннадцать таблиц не прочитаны на взаимоисключительность
`LANGUAGE.md` объявил, что строки таблицы решений взаимоисключающи по
умолчанию, а иной порядок объявляется явно. Объявление не делает таблицы
такими: нумерованные строки есть в одиннадцати файлах канона, и ни одна
таблица под новое требование не прочитана.
Конкретный подозреваемый — SLOG-11: «по реальному действию или изменению»
против «повторяющаяся служебная, по таймеру или поллингу». Периодическая
операция, которая всё-таки меняет данные, подходит под обе строки, и уровень
из таблицы не выводится однозначно.
Работа читательская, машине не даётся; в список проверок она уже записана в
разделе «Чтением, потому что машине не даётся».
## 15. Возможность, записанная модальным словом
Четвёртая категория ISO — возможность и осуществимость — ключевого слова не
имеет: такие утверждения пишутся обычной прозой. Значит канон надо просмотреть
на обратную ошибку: где утверждение о факте («библиотеки по умолчанию отдают
именно его», «SQLite сравнивает строки побайтово») записано модальным словом
и тем самым превратилось в норму, которую никто не вводил.
Смотреть в первую очередь абзацы «Почему»: там факты и стоят, там же соблазн
усилить их модальностью выше всего.
## Из вчерашнего, не закрыто
- Вынос арх-ядра из `errors` и `logging`: закроет две хрупкие ссылки из
`web-ui` (стек) в go-слой — единственные ссылки стек → язык в каноне.
- `conv check` должен уметь отличать ссылку на удалённое правило от
упоминания дыры: сейчас `META-16` в прозе «Оформления» даёт ложное
срабатывание.