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