- компонент — область репозитория, где выбранные слои действуют одновременно; сборка идёт по разу на компонент, у каждого своя директория копий, подписка и локальная часть, секции [components.<имя>] в манифесте - плоский набор описан как низкий конец модели, а не отдельный режим: тема с одним слоем собирается копированием, ключи оси и lang/stack не пишутся - в TODO заведён вопрос о реестре значений осей и судьбе extends:
199 lines
16 KiB
Markdown
199 lines
16 KiB
Markdown
# Инструмент: 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`) делается
|
||
одним заходом, когда инструмент будет готов.
|