Files
dev-conventions/TOOL.md
T
av 0d335ff58d манифест набора переименован в .conventions-suite.toml
- оба манифеста теперь данные инструмента: он их читает и переписывает
  целиком, комментариев они не держат — точка в начале ставит их рядом
  со служебными файлами, а не среди содержимого репозитория
- прежнее основание из TOOL.md («в наборе файл правится при каждой новой
  теме, и прятать его незачем») отпало: правит его convy
- ссылки в README.md, CLAUDE.md, TOOL.md и TODO.md обновлены
2026-07-28 09:54:52 +03:00

16 KiB
Raw Blame History

Инструмент: 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, а не подкоманда.

«Канон» — имя этого конкретного набора, а не термин уровня; в общих формулировках употребляется «набор». «Потребитель» — слово про роль репозитория, а не про уровень.

Манифесты названы по тому, что описывают, а не по уровню ради симметрии: .conventions-suite.toml в наборе — идентичность набора, .conventions.toml в проекте — подключённые конвенции. Оба начинаются с точки, потому что оба — данные инструмента, а не документы репозитория: они читаются и переписываются целиком командами, комментариев не держат, и объяснения к ним живут в соседних файлах. Отсюда же бесплатное определение контекста: по имени рядом видно, где ты.

Прежде манифест набора назывался suite.toml и точки не имел — на том основании, что правится он руками при каждой новой теме. Основание отпало: правит его инструмент.

Раскладка команд

Глубина команды отражает частоту и адресата. Проектные команды выполняются в каждом репозитории и часто; ведение набора — в одном репозитории и редко.

convy add <тема>    подписаться и собрать
convy pull          пересобрать подписанное
convy list          что подключено и что можно взять
convy check         проверить форму того, что здесь

convy suite check   целостность набора: префиксы, темы, оси, ссылки, форма
convy suite new     новая тема: шапка, префикс, запись в манифест

Проектные команды принимают --for <компонент>. При одном компоненте флаг не нужен; при нескольких команда без него отказывает и перечисляет имена — тот же принцип, что и с контекстом: наугад не делается ничего. convy list группирует вывод по компонентам.

Граница проходит не по «проектное против наборного», а по «частое и повсеместное» против «только у автора». Поэтому check остаётся наверху: форма правила одна и та же, локальные правила проекта на X-префиксах написаны по ней же, и проверять их у себя нужно без подкоманды. Под suite уходит то, что в проекте не имеет смысла.

Три следствия для реализации:

  • контекст определяется и отказ говорится явно. .conventions.toml рядом — проект, .conventions-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.inivale syncstyles/) как образец манифеста и vendir.yml — как пример границы между «чего хочу» и «что получил».

Состояние

convy не начат. В наборе лежит conv — питоновский скрипт под прежнюю модель копий: зеркальное дерево, именованные регионы, origin_hash, команды status, diff, push. Он не истина ни в чём: модель описана в README, проверки — в LANGUAGE.md.

Логика проверок целостности написана и много раз прогнана руками, но живёт в скретчпадах сессий, а не в репозитории. При старте разработки её стоит перенести первой — это готовая спецификация в виде кода.

Переименование convconvy в обвязке (README.md, CLAUDE.md) делается одним заходом, когда инструмент будет готов.