Files
dev-conventions/TOOL.md
T
av 9c86d9f2de компоненты как адресат сборки и плоский набор
- компонент — область репозитория, где выбранные слои действуют
  одновременно; сборка идёт по разу на компонент, у каждого своя директория
  копий, подписка и локальная часть, секции [components.<имя>] в манифесте
- плоский набор описан как низкий конец модели, а не отдельный режим: тема с
  одним слоем собирается копированием, ключи оси и lang/stack не пишутся
- в TODO заведён вопрос о реестре значений осей и судьбе extends:
2026-07-26 22:01:39 +03:00

199 lines
16 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.
# Инструмент: convy
Стартовая точка для разработки. Здесь — принятые решения об инструменте и
открытые вопросы к нему; модель, которую он реализует, описана в
[README.md](README.md), а форма правил — в [LANGUAGE.md](LANGUAGE.md). При
расхождении истина там, а не здесь.
## Что это
`convy` — CLI для управления конвенциями: собирает копии в проектах и
проверяет целостность набора. Пишется на Go, живёт в отдельном репозитории,
ставится готовым бинарём.
Имя выбрано за то, что держит корень предметной области и звучит как маленький
помощник, а уменьшительное `-y` обещает малость — что правда: инструмент
копирует файлы, собирает тему из слоёв и проверяет форму, не более. Рядом
существует английское слово `convey`, отличающееся одной буквой и подходящее
по смыслу, — опечатку в текстах стоит проверять отдельно.
Отдельный репозиторий нужен по двум причинам. Инструмент и данные в одном
репозитории правятся одним движением, и это мешает: лежащий отдельно бинарь
физически не даёт починить инструмент «заодно» с правкой конвенции. Вторая —
независимый бинарь работает против любого набора и любого проекта, а не против
одного конкретного канона.
## Термины
| Уровень | Английский | Русский | Что там лежит |
|---|---|---|---|
| набор | `suite` | набор | `conventions/`, манифест набора, обвязка |
| проект | `project` | проект | манифест подключения, компоненты |
| компонент | `component` | компонент | директория копий: один язык, один стек, один вид приложения |
`package`, `bundle`, `library` не берём: они тащат багаж менеджеров
зависимостей — версии, разрешение, лок, — которого в модели нет. `set` не
годится в CLI: в позиции подкоманды читается глаголом.
Компонент — адресат сборки: подписка принадлежит проекту, а собранный файл
читает тот, кто правит конкретный код. Определение — область, где все
выбранные слои действуют одновременно (`sqlite` и `postgres` — да, go и
javascript — никогда). Уровнем CLI компонент не становится: это аргумент
`--for`, а не подкоманда.
«Канон» — имя этого конкретного набора, а не термин уровня; в общих
формулировках употребляется «набор». «Потребитель» — слово про роль
репозитория, а не про уровень.
Манифесты названы по тому, что описывают, а не по уровню ради симметрии:
`suite.toml` в наборе — идентичность набора, `.conventions.toml` в проекте —
подключённые конвенции. Точка в проекте отделяет служебное от содержимого
проекта; в наборе файл правится при каждой новой теме, и прятать его незачем.
Отсюда же бесплатное определение контекста: по имени рядом видно, где ты.
## Раскладка команд
Глубина команды отражает частоту и адресата. Проектные команды выполняются в
каждом репозитории и часто; ведение набора — в одном репозитории и редко.
```
convy add <тема> подписаться и собрать
convy pull пересобрать подписанное
convy list что подключено и что можно взять
convy check проверить форму того, что здесь
convy suite check целостность набора: префиксы, темы, оси, ссылки, форма
convy suite new новая тема: шапка, префикс, запись в манифест
```
Проектные команды принимают `--for <компонент>`. При одном компоненте флаг не
нужен; при нескольких команда без него отказывает и перечисляет имена — тот
же принцип, что и с контекстом: наугад не делается ничего. `convy list`
группирует вывод по компонентам.
Граница проходит не по «проектное против наборного», а по «частое и
повсеместное» против «только у автора». Поэтому `check` остаётся наверху:
форма правила одна и та же, локальные правила проекта на `X`-префиксах
написаны по ней же, и проверять их у себя нужно без подкоманды. Под `suite`
уходит то, что в проекте не имеет смысла.
Три следствия для реализации:
- **контекст определяется и отказ говорится явно.** `.conventions.toml` рядом
— проект, `suite.toml` — набор. Проектная команда, набранная в наборе, не
делает ничего наугад: она отказывает и подсказывает наборный аналог;
- **помощь группируется заголовками** «В проекте» и «В наборе»: в плоском
списке уровни не видны;
- **синонимов нет.** `convy project pull` рядом с `convy pull` не заводим: два
способа сказать одно — то, от чего модель избавлялась в остальных местах.
В проектах вызов идёт через обёртку раннера (`inv conventions -- pull`,
`task conventions -- pull`), которая пробрасывает аргументы. Обёртка уже
называет предмет, поэтому короткая проектная команда здесь тоже выигрывает.
## Две задачи, которые нельзя смешивать
Они расходятся по частоте, по адресату и по тому, что считается провалом.
**Целостность набора.** Префиксы уникальны и не переиспользованы, шапка
совпадает с манифестом, тема объявлена и зарегистрирована, ось объявлена
ключами и у темы не больше одного базового слоя, у каждого правила
модальность с нормой и обоснование либо заглушка, нумерация сплошная, ссылки
разрешаются, путей набора в тексте конвенции нет, строка о версии языка на
месте. Запускается в наборе при каждой правке; провал — ошибка.
**Установка в проект.** Манифест подключения, сборка файла темы из слоёв на
каждый компонент, сохранение локальной части, `READING.md` рядом с копиями.
Запускается в проекте изредка; провал чаще означает «посмотри глазами», чем
«ошибка». Отчёта «набор ушёл вперёд» нет: его делает `git diff` после
пересборки.
## Что делает установка
Подробности — в README, раздел «Копия в репозитории». Коротко, что важно для
реализации:
- сборка идёт **по разу на компонент**, в директорию `dir` из его секции;
директории компонентов обязаны различаться — иначе копии столкнутся
именами, и это ошибка манифеста, а не повод переименовывать файлы;
- копия плоская, **один файл на тему**; слои идут секциями в порядке
база → язык → стек, выбор слоёв — по `lang` и `stack` компонента, сверяемым
с ключами оси в шапке слоя (META-38), а не с путём файла в наборе; слой без
ключей оси — базовый и попадает в копию всегда;
- набор может быть плоским: у темы один слой, ключей оси нет, `lang` и
`stack` в компоненте отсутствуют. Отдельной ветки в коде это не требует —
сборка из одного слоя есть копирование;
- шапка копии — только `origin:` с именем темы; отпечатков и дат нет;
- всё ниже маркера `<!-- conv:local -->` переживает пересборку, всё выше
перезаписывается; маркер ставит сборщик;
- `README.md` в директории принадлежит проекту и не трогается; `READING.md`
принадлежит набору и перезаписывается целиком;
- транспорта обратно нет: ни `push`, ни отчёта о расхождении, ни лок-файла.
## Что делает проверка
Список проверок — в `LANGUAGE.md`, раздел «Что стоит проверять машиной». Он
уже разделён на три части, и это деление прямо ложится в код:
- **форма правила** — разбором текста, в любом файле, который язык
употребляет: конвенции и `GUIDE.md`;
- **распространение** — разбором текста, только в файлах конвенций: тема в
шапке, префикс чужой темы в норме, пути набора, `X` у локальных правил;
- **чтением** — то, что машине не даётся: взаимоисключительность строк
таблицы, покрытие области действия, самодостаточность нормы, обоснование
отвечает на «что сломается», примеры не расширяют норму.
Третья часть — не работа `convy`. Её выполняет агент, и по ней стоит завести
скилл, а не пытаться выразить регуляркой.
## Независимость от набора
Инструмент не зашивает правила языка в код: версия и документы объявлены в
манифесте набора, секция `[language]`. Пока версия одна, это выглядит
избыточным — но именно здесь разница между «инструмент для этого канона» и
«инструмент для любого набора».
Отсюда же: никаких упоминаний конкретных тем, префиксов и путей в коде.
## Чего инструмент не делает
- не сливает трёхсторонне и не разрешает конфликты: правка выше маркера
теряется, и это заявленное поведение;
- не ведёт лок-файл: копии закоммичены, автоматического обновления нет,
ответ на «что было в прошлый раз» даёт git;
- не хранит список подписчиков: подписка — свойство проекта;
- не переносит правки из проекта в набор: это ручная и редкая операция.
## Открытые вопросы
- **Один бинарь или два.** Пока — один с подкомандой `suite`. Разделение
дешевле заложить сразу, чем отпиливать потом: если задачи разъедутся, у них
уже будут разные точки входа.
- **Установка.** `eget` умеет GitHub-релизы, а git у меня свой
(`git.vakhrushev.me`) — проверить, тянет ли `eget` релизы Gitea, иначе
остаётся прямой URL или `go install`.
- **TOML.** В stdlib парсера нет, значит одна внешняя зависимость
(`BurntSushi/toml` или `pelletier/go-toml`). Выбрать и больше зависимостей
не заводить.
- **Предупреждение о висячих ссылках** на неподписанные темы: это установка,
а не целостность, но список подписок ему нужен из манифеста подключения.
- **Проверка на `convey`** в текстах — вместе с остальными проверками формы
или отдельной мелочью.
- **Прототипы, на которые стоит посмотреть** до того, как писать: дистрибуция
пакетов Vale (`.vale.ini``vale sync``styles/`) как образец манифеста и
`vendir.yml` — как пример границы между «чего хочу» и «что получил».
## Состояние
`convy` не начат. В наборе лежит `conv` — питоновский скрипт под прежнюю
модель копий: зеркальное дерево, именованные регионы, `origin_hash`, команды
`status`, `diff`, `push`. Он не истина ни в чём: модель описана в README,
проверки — в `LANGUAGE.md`.
Логика проверок целостности написана и много раз прогнана руками, но живёт в
скретчпадах сессий, а не в репозитории. При старте разработки её стоит
перенести первой — это готовая спецификация в виде кода.
Переименование `conv``convy` в обвязке (`README.md`, `CLAUDE.md`) делается
одним заходом, когда инструмент будет готов.