diff --git a/CLAUDE.md b/CLAUDE.md index 5d0bb2f..2222cde 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,6 +114,6 @@ code in this repository. проверка. - Ни один репозиторий-потребитель ещё не подключён: копий с шапкой `origin:` в природе нет, все локальные регионы канона пусты. -- `TODO.md` не коммитится. Это площадка для обсуждения на будущее, а не - принятые решения; при работе над обвязкой его стоит прочесть, но истина о - текущем устройстве — `README.md`. +- `TODO.md` — площадка для обсуждения на будущее, а не принятые решения; при + работе над обвязкой его стоит прочесть, но истина о текущем устройстве — + `README.md`. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..e4e1c65 --- /dev/null +++ b/TODO.md @@ -0,0 +1,240 @@ +# К обсуждению + +Черновик для следующего разговора: вопросы и варианты, а не принятые +решения. + +## 1. Ссылка на `LANGUAGE.md` не переживает сборку + +Все двенадцать конвенций во вводной прозе пишут «Форма записи — +`LANGUAGE.md`». Обвязка в репозиторий не едет (`conv` синхронизирует только +`conventions/`), поэтому в собранной копии эта ссылка указывает в никуда. + +Это тот же класс дефекта, который вчера закрыло META-21 для ссылок между +темами: путь, верный в каноне, молча умирает при сборке. Разница в том, что +META-21 предлагает заменить путь на имя темы — а здесь заменять не на что, +целевого документа в репозитории просто нет. + +Варианты, которые видно сейчас: + +- **Убрать упоминание из тел конвенций.** Язык записи — забота того, кто + пишет канон, а не того, кто читает конвенцию в своём репозитории. Читателю + достаточно самого текста: модальные слова и «Почему» самоописательны. + Дешевле всего, но копия теряет указание, по каким правилам её править. +- **Возить обвязку вместе с конвенциями.** Тогда `LANGUAGE.md` и, возможно, + `GUIDE.md` появляются в `docs/conventions/` как ещё одни синхронизируемые + файлы. Честно, но противоречит нынешнему разделению «канон — конвенции, + корень — обвязка» и добавляет в репозиторий текст, который агенту при + чтении конвенции не нужен. +- **Вкладывать короткую преамбулу в собранный файл.** Сборщик пишет в шапку + три-четыре строки: что такое модальное слово, что «Почему» обязательно, + где лежит полный документ. Самодостаточно и не тащит весь язык, но + преамбула дублируется в каждом файле темы. + +Сопутствующее: `GUIDE.md` в телах конвенций не упоминается ни разу — +проверено, так что вопрос только про `LANGUAGE.md`. + +## 2. Тулинг: две разные задачи в одном `conv` + +Сейчас в `conv` смешаны две категории работы, и они расходятся по всему — +по частоте запуска, по тому, кто запускает, и по тому, что считается +провалом. + +**Целостность канона.** Префиксы уникальны и не переиспользованы, шапка +совпадает с реестром, номера без дыр вниз, у каждого правила модальность и +«Почему», ссылки разрешаются, чужой префикс не лезет в норму (META-20), +путей канона в тексте нет (META-21). Запускается в каноне, при каждой +правке, провал — это ошибка. Логика уже написана и много раз прогнана +руками, но живёт в скретчпаде, а не в репозитории. + +**Установка в проект.** Манифест `.conventions.toml`, сборка файла темы из +секций (arch → языки → стеки → local), сохранение локальной секции при +пересборке, отчёт «канон ушёл вперёд», предупреждение о висячих ссылках на +неподписанные темы. Запускается в репозитории-потребителе, изредка, провал — +это чаще «посмотри глазами», чем «ошибка». + +Что обсудить: + +- Разделять ли на два исполняемых файла, или хватит подкоманд с честной + границей внутри. +- Валидацию канона стоит ли отдать агенту скиллом вместо (или вдобавок к) + скрипту: часть проверок формулируется как «прочитай и скажи, самодостаточна + ли норма» — механически это не берётся, а агентом берётся. +- Куда в этой раскладке ложится запаркованное предупреждение о висячих + ссылках: это установка, а не целостность, но список подписок ему нужен из + манифеста. + +## 3. Согласованная модель сборки нигде не записана + +Самое срочное. Договорённости про плоскую раскладку живут только в +переписке, а репозиторий описывает прежнюю модель — и противоречит новой в +нескольких местах сразу. + +Что решено, но не зафиксировано: + +- копия плоская, файл на тему: `docs/conventions/config.md`, а не дерево + `arch/` + `lang/`; +- файл темы собирается из секций `arch → языки → стеки → local` с + машиночитаемыми маркерами `` и ``; +- языки и стеки образуют разреженную матрицу; файл темы собирает её строку, + многоязычная тема держит несколько языковых секций в одном файле; +- выбор описывается манифестом `.conventions.toml` в корне + репозитория-потребителя; путь к канону там **не** хранится; +- направление строго одностороннее: канон → код. Правка канона делается + руками в каноне, потом пересборка; +- локальные префиксы правил репозитория объявляются в манифесте и не + пересекаются с реестром канона. + +Что этому прямо противоречит в репозитории сейчас: + +- `README.md` → «Раскладка в репозитории» показывает зеркальное дерево + `docs/conventions/arch/db-identifiers.md`; +- `README.md` → «Команды» и «Контракт с агентом» описывают `conv push` и + `conv push --new` как штатный путь; при односторонней модели транспорт + назад исчезает, остаётся только детект «копия правлена вне локальной + секции»; +- `conv` содержит `cmd_push` со всей обвязкой (`--new`, `--force`). + +## 4. Именованные регионы → одна локальная секция + +Решено заменить регионы `` на одну локальную секцию в +конце собранного файла: отступление ссылается на идентификатор правила +(`DIRS-5`), а не стоит рядом с ним. Это то, что делает +сравнение копии с каноном одним хешем и выкидывает из `conv` перенос +регионов по именам. + +Сделать перенос ещё предстоит: в каноне сейчас **31 регион в 12 файлах**. + +``` +7 связано 7 отступления 7 механизировано +1 эталон / эталоны / словарь / секреты / секретные-поля +1 проверки / поля / модель-владельца / маппинг / границы +``` + +Отдельно решить, что делать с `связано`: сейчас это репо-специфичная часть +раздела «Связано» (META-17), и при единой локальной секции она переезжает +туда же — надо проверить, что 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. Не переизобретено ли это + +Проверить перед тем, как вкладываться в тулинг. Ближайшие кандидаты, куда +смотреть: + +- **copier / cruft** — шаблон проекта с последующим `update`: ровно та же + задача «стянуть обновление апстрима, не затерев локальные правки», с + ответом через три-way merge вместо наших регионов. Стоит понять, почему у + них merge, а у нас исключение из сравнения. +- **vendir** — вендоринг чужого содержимого с лок-файлом; ближе к нашей + модели «канон это лавка». +- **Vale** — линтер прозы с правилами в файлах: значительная часть проверок + целостности канона (модальные слова вне правил, запрещённые формулировки) + выражается его языком. +- **RFC 2119 / 8174** — канонический источник модальных слов; наш словарь + фактически его перевод, полезно сверить границы значений. +- **EARS** — шаблоны требований (ubiquitous / event-driven / state-driven); + соседняя формализация того же, что мы решили таблицами. +- **Наборы правил для агентов** — `AGENTS.md`, cursor rules, скиллы: задачу + «раздать читаемые агентом договорённости по репозиториям» сейчас решают + несколько продуктов, и там уже могли устояться форматы. + +## 8. Мультиязычность ключевых слов + +Модальные слова сейчас русские, и это осознанно: разный словарь держит +границу «конвенция — не спецификация» бесплатно. Вопрос — нужен ли +английский набор параллельно. + +За: канон может однажды понадобиться на английском; агенты натренированы на +MUST/SHOULD плотнее, чем на ДОЛЖЕН/СЛЕДУЕТ. Против: два набора — это два +способа записать одно, и проверка «модальные слова не встречаются вне +правил» усложняется вдвое. + +Если делать, то таблица ключевых слов должна принадлежать **описанию +языка**, а не каждой конвенции — то есть вопрос завязан на следующий. + +## 9. Описание языка отдельно от набора конвенций + +`LANGUAGE.md` и `GUIDE.md` описывают, **как** пишутся конвенции; +`conventions/` — **один конкретный** набор. Сейчас они склеены в одном +репозитории, и из одного описания нельзя собрать второй набор (рабочий, +доменный, чужой). + +Что это даёт, если разнести: + +- канон объявляет, какой версии языка следует, а тулинг валидирует набор + **против объявленного описания**, а не против зашитых в код правил; +- таблица модальных слов (вопрос 8) живёт в описании и одна на все наборы; +- вопрос 1 (`LANGUAGE.md` не переживает сборку) меняет форму: собранный файл + ссылается не на путь, а на язык с версией. + +Цена — ещё одна сущность и ещё одна синхронизация. Проверить, не выйдет ли +лечение хуже болезни при одном пользователе. + +## 10. Тулинг на Go, живущий независимо + +Сейчас `conv` — питоновский скрипт внутри канона, то есть тулинг и данные в +одном репозитории и правятся одним движением. Мысль: вынести в отдельный +Go-бинарь со своим релизным циклом, ставить через `eget` (механизм уже есть +в pet-project-server). + +Что за этим стоит помимо вкуса: независимый бинарь физически не даёт править +инструмент «заодно» с правкой конвенции, работает против **любого** канона и +любого потребителя — что прямо требуется вопросом 9, — и снимает питон из +зависимостей репозиториев-потребителей. + +Связано с вопросом 2: если тулинг всё равно переписывается, разделение +«целостность канона / установка в проект» дешевле заложить сразу, чем +отпиливать потом. + +## 11. Ссылки на родительский слой своей темы + +Из `lang/` и `stack/` ссылаться на идентификаторы своего же арх-слоя +безопасно: при сборке они оказываются секциями одного файла, и ссылка +никуда не ведёт — правило рядом. Ограничение META-20 писалось именно про +**чужую тему**, так что формально это уже разрешено. + +Но стоит проговорить явно, потому что сейчас читается уже как запрет: + +- в `LANGUAGE.md` проверка сформулирована как «**чужой префикс** не + встречается в абзаце с модальностью» — по букве это ловит и `GTIM` → + `TIME-3`, то есть ложно срабатывает на ровно том случае, который + разрешён. Должно быть «префикс **чужой темы**»; +- обсудить, не добавить ли в META-20 явную строку **ДОПУСКАЕТСЯ** про + родительский слой: правило, которое читают как более строгое, чем оно + есть, заставляет авторов дублировать текст без нужды. + +## Из вчерашнего, не закрыто + +- Вынос арх-ядра из `errors` и `logging`: закроет две хрупкие ссылки из + `web-ui` (стек) в go-слой — единственные ссылки стек → язык в каноне. +- `conv check` должен уметь отличать ссылку на удалённое правило от + упоминания дыры: сейчас `META-16` в прозе «Оформления» даёт ложное + срабатывание.