From e8fdc985575ff9f37a6d67dac39cf6ed6ba79244 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 26 Jul 2026 17:00:00 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B7=D0=B0=D0=B2=D0=B5=D0=B4=D1=91=D0=BD=20TO?= =?UTF-8?q?OL.md=20=E2=80=94=20=D1=81=D1=82=D0=B0=D1=80=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=82=D0=BE=D1=87=D0=BA=D0=B0=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20convy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - имя, термины suite/project, раскладка команд: проектные наверху, ведение набора под подкомандой suite, check остаётся общим - собрано в одном месте то, что инструмент делает и чего не делает: сборка копии, три группы проверок, независимость от конкретного набора, отказ от слияния, лока и обратного транспорта - два тулинговых вопроса вынесены из TODO в раздел «Открытые вопросы»; вопросы про инструмент там больше не живут --- TODO.md | 71 +++-------------------- TOOL.md | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 63 deletions(-) create mode 100644 TOOL.md diff --git a/TODO.md b/TODO.md index 2e78895..fb539f9 100644 --- a/TODO.md +++ b/TODO.md @@ -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 при этом -дешевле заложить сразу, чем отпиливать потом. diff --git a/TOOL.md b/TOOL.md new file mode 100644 index 0000000..60637df --- /dev/null +++ b/TOOL.md @@ -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:` с именем темы; отпечатков и дат нет; +- всё ниже маркера `` переживает пересборку, всё выше + перезаписывается; маркер ставит сборщик; +- `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`) делается +одним заходом, когда инструмент будет готов.