заведён TOOL.md — стартовая точка для convy

- имя, термины suite/project, раскладка команд: проектные наверху, ведение
  набора под подкомандой suite, check остаётся общим
- собрано в одном месте то, что инструмент делает и чего не делает: сборка
  копии, три группы проверок, независимость от конкретного набора, отказ от
  слияния, лока и обратного транспорта
- два тулинговых вопроса вынесены из TODO в раздел «Открытые вопросы»;
  вопросы про инструмент там больше не живут
This commit is contained in:
av
2026-07-26 17:00:00 +03:00
parent c96566d4b4
commit e8fdc98557
2 changed files with 182 additions and 63 deletions
+8 -63
View File
@@ -2,9 +2,12 @@
Черновик для следующего разговора: вопросы и варианты, а не принятые
решения. Закрытый вопрос отсюда удаляется — принятое решение живёт в
`README.md`, `GUIDE.md` или `LANGUAGE.md`, а не в этом файле.
`README.md`, `GUIDE.md`, `LANGUAGE.md` или `TOOL.md`, а не в этом файле.
Две секции: сначала язык и подход, потом канон с тулингом.
Вопросы про инструмент здесь не живут — они собраны в `TOOL.md`, раздел
«Открытые вопросы».
Две секции: сначала язык и подход, потом сам набор и подключение.
# Язык и подход
@@ -64,50 +67,9 @@ API, а норму при этом нельзя поправить, не зад
сущность и ещё одна синхронизация; при одном наборе лечение выходит хуже
болезни.
# Канон, тулинг, подключение
# Канон и подключение
## 4. Тулинг: две разные задачи в одном `conv`
Сейчас в `conv` смешаны две категории работы, и они расходятся по всему —
по частоте запуска, по тому, кто запускает, и по тому, что считается
провалом.
**Целостность канона.** Префиксы уникальны и не переиспользованы, шапка
совпадает с манифестом, у каждого правила модальность и блок ПОЧЕМУ, ссылки
разрешаются, префикс чужой темы не лезет в норму (META-20), путей канона в
тексте нет (META-21), строка о версии языка на месте. Запускается в каноне,
при каждой правке, провал — это ошибка. Логика уже написана и много раз
прогнана руками, но живёт в скретчпаде, а не в репозитории.
**Установка в проект.** Манифест `.conventions.toml`, сборка файла темы из
слоёв (`arch` → язык → стек), сохранение при пересборке всего, что ниже
маркера, `READING.md` рядом с копиями, предупреждение о висячих ссылках на
неподписанные темы. Запускается
в репозитории-потребителе, изредка, провал — это чаще «посмотри глазами»,
чем «ошибка». Отчёта «канон ушёл вперёд» здесь нет: его делает `git diff`
после пересборки.
Что обсудить:
- Разделять ли на два исполняемых файла, или хватит подкоманд с честной
границей внутри.
- Валидацию канона стоит ли отдать агенту скиллом вместо (или вдобавок к)
скрипту: часть проверок формулируется как «прочитай и скажи, самодостаточна
ли норма» — механически это не берётся, а агентом берётся.
- Куда в этой раскладке ложится запаркованное предупреждение о висячих
ссылках: это установка, а не целостность, но список подписок ему нужен из
манифеста.
Список проверок теперь реализуем целиком: граница правила определена,
нумерация сплошная, снятое правило остаётся заглушкой — данных со стороны
языка проверке хватает.
Перед тем как переписывать, стоит посмотреть на два готовых прототипа:
дистрибуцию пакетов Vale (`.vale.ini``vale sync``styles/`) как образец
манифеста и `vendir.yml` — как пример того, где проходит граница между «чего
хочу» и «что получил».
## 5. Пары слоёв и темы без базы
## 4. Пары слоёв и темы без базы
Отложено сознательно, но список стоит держать перед глазами:
@@ -128,7 +90,7 @@ API, а норму при этом нельзя поправить, не зад
- Вынос арх-ядра из `errors` и `logging` закроет две хрупкие ссылки из
`web-ui` (стек) в go-слой — единственные ссылки стек → язык в каноне.
## 6. Подключение к репозиториям
## 5. Подключение к репозиториям
Ничего ещё не подключено. Кандидаты — jellybit и pet-project-server.
Понадобится: заполнить локальную часть копий тем, что сейчас в этих
@@ -136,20 +98,3 @@ API, а норму при этом нельзя поправить, не зад
`task conventions`, единый интерфейс команд у трёх ansible-репозиториев);
строка в `AGENTS.md` каждого потребителя про то, что файлы в
`docs/conventions/` — копии.
## 7. Тулинг на Go, живущий независимо
Сейчас `conv` — питоновский скрипт внутри канона, то есть тулинг и данные в
одном репозитории и правятся одним движением. Мысль: вынести в отдельный
Go-бинарь со своим релизным циклом, ставить через `eget` (механизм уже есть
в pet-project-server).
Что за этим стоит помимо вкуса: независимый бинарь физически не даёт править
инструмент «заодно» с правкой конвенции, работает против **любого** канона и
любого потребителя — что прямо требуется вопросом 3, — и снимает питон из
зависимостей репозиториев-потребителей.
Порядок обратный ожидаемому: пока вопрос 3 не сделан, инструмент всё равно
работает против одного конкретного канона, и независимый релизный цикл ему
нечего обслуживать. Сначала 3, потом 7. Разделение из вопроса 4 при этом
дешевле заложить сразу, чем отпиливать потом.
+174
View File
@@ -0,0 +1,174 @@
# Инструмент: convy
Стартовая точка для разработки. Здесь — принятые решения об инструменте и
открытые вопросы к нему; модель, которую он реализует, описана в
[README.md](README.md), а форма правил — в [LANGUAGE.md](LANGUAGE.md). При
расхождении истина там, а не здесь.
## Что это
`convy` — CLI для управления конвенциями: собирает копии в проектах и
проверяет целостность набора. Пишется на Go, живёт в отдельном репозитории,
ставится готовым бинарём.
Имя выбрано за то, что держит корень предметной области и звучит как маленький
помощник, а уменьшительное `-y` обещает малость — что правда: инструмент
копирует файлы, собирает тему из слоёв и проверяет форму, не более. Рядом
существует английское слово `convey`, отличающееся одной буквой и подходящее
по смыслу, — опечатку в текстах стоит проверять отдельно.
Отдельный репозиторий нужен по двум причинам. Инструмент и данные в одном
репозитории правятся одним движением, и это мешает: лежащий отдельно бинарь
физически не даёт починить инструмент «заодно» с правкой конвенции. Вторая —
независимый бинарь работает против любого набора и любого проекта, а не против
одного конкретного канона.
## Термины
| Уровень | Английский | Русский | Что там лежит |
|---|---|---|---|
| набор | `suite` | набор | `conventions/`, манифест набора, обвязка |
| проект | `project` | проект | `docs/conventions/`, манифест подключения, копии |
`package`, `bundle`, `library` не берём: они тащат багаж менеджеров
зависимостей — версии, разрешение, лок, — которого в модели нет. `set` не
годится в CLI: в позиции подкоманды читается глаголом.
«Канон» — имя этого конкретного набора, а не термин уровня; в общих
формулировках употребляется «набор». «Потребитель» — слово про роль
репозитория, а не про уровень.
## Раскладка команд
Глубина команды отражает частоту и адресата. Проектные команды выполняются в
каждом репозитории и часто; ведение набора — в одном репозитории и редко.
```
convy add <тема> подписаться и собрать
convy pull пересобрать подписанное
convy list что подключено и что можно взять
convy check проверить форму того, что здесь
convy suite check целостность набора: префиксы, темы, ссылки, форма
convy suite new новая тема: шапка, префикс, запись в манифест
```
Граница проходит не по «проектное против наборного», а по «частое и
повсеместное» против «только у автора». Поэтому `check` остаётся наверху:
форма правила одна и та же, локальные правила проекта на `X`-префиксах
написаны по ней же, и проверять их у себя нужно без подкоманды. Под `suite`
уходит то, что в проекте не имеет смысла.
Три следствия для реализации:
- **контекст определяется и отказ говорится явно.** `.conventions.toml` рядом
— проект, `manifest.toml` — набор. Проектная команда, набранная в наборе, не
делает ничего наугад: она отказывает и подсказывает наборный аналог;
- **помощь группируется заголовками** «В проекте» и «В наборе»: в плоском
списке уровни не видны;
- **синонимов нет.** `convy project pull` рядом с `convy pull` не заводим: два
способа сказать одно — то, от чего модель избавлялась в остальных местах.
В проектах вызов идёт через обёртку раннера (`inv conventions -- pull`,
`task conventions -- pull`), которая пробрасывает аргументы. Обёртка уже
называет предмет, поэтому короткая проектная команда здесь тоже выигрывает.
## Две задачи, которые нельзя смешивать
Они расходятся по частоте, по адресату и по тому, что считается провалом.
**Целостность набора.** Префиксы уникальны и не переиспользованы, шапка
совпадает с манифестом, тема объявлена и зарегистрирована, у каждого правила
модальность с нормой и обоснование либо заглушка, нумерация сплошная, ссылки
разрешаются, путей набора в тексте конвенции нет, строка о версии языка на
месте. Запускается в наборе при каждой правке; провал — ошибка.
**Установка в проект.** Манифест подключения, сборка файла темы из слоёв,
сохранение локальной части, `READING.md` рядом с копиями. Запускается в
проекте изредка; провал чаще означает «посмотри глазами», чем «ошибка».
Отчёта «набор ушёл вперёд» нет: его делает `git diff` после пересборки.
## Что делает установка
Подробности — в README, раздел «Копия в репозитории». Коротко, что важно для
реализации:
- копия плоская, **один файл на тему**; слои идут секциями в порядке
`arch` → язык → стек, выбор слоёв — по `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`** в текстах — вместе с остальными проверками формы
или отдельной мелочью.
- **Имена манифестов.** `suite.toml` в наборе против `.conventions.toml` в
проекте — каждый файл называет свой уровень. Сейчас `manifest.toml`;
решение вкусовое, но принимать его до первого потребителя дешевле.
- **Прототипы, на которые стоит посмотреть** до того, как писать: дистрибуция
пакетов Vale (`.vale.ini``vale sync``styles/`) как образец манифеста и
`vendir.yml` — как пример границы между «чего хочу» и «что получил».
## Состояние
`convy` не начат. В наборе лежит `conv` — питоновский скрипт под прежнюю
модель копий: зеркальное дерево, именованные регионы, `origin_hash`, команды
`status`, `diff`, `push`. Он не истина ни в чём: модель описана в README,
проверки — в `LANGUAGE.md`.
Логика проверок целостности написана и много раз прогнана руками, но живёт в
скретчпадах сессий, а не в репозитории. При старте разработки её стоит
перенести первой — это готовая спецификация в виде кода.
Переименование `conv``convy` в обвязке (`README.md`, `CLAUDE.md`) делается
одним заходом, когда инструмент будет готов.