Files
convy/README.md
T
av 23d88c4048 проектные команды и ссылки на источник
- заведён internal/source: уровни ссылаются друг на друга путём на диске
  или git-репозиторием, ревизия закрепляется хвостом #ref; клон делается
  заново и удаляется, кэша нет
- добавлены init, add, pull, list, check в проекте — манифест
  .conventions.toml, сборка копий по разу на компонент, маркер локальной
  части, READING.md рядом
- проверки формы развязаны с набором: принимают lang.Vocabulary, а язык
  копии узнаётся по строке о версии — манифеста рядом с ней нет
2026-07-27 20:42:18 +03:00

259 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# convy
CLI для управления конвенциями разработки: ведёт набор конвенций и собирает
копии в проектах.
Модель, которую инструмент реализует, описана не здесь, а в репозитории набора
(`dev-conventions`): `README.md` — устройство набора и копий, `LANGUAGE.md`
форма правила, `GUIDE.md` — правила ведения набора, `TOOL.md` — решения об этом
инструменте. При расхождении истина там.
## Термины
| Уровень | Что это |
|---|---|
| язык | как записывается правило: слова, версия, `READING.md` |
| набор | репозиторий с конвенциями, манифестом `suite.toml` и обвязкой |
| проект | репозиторий-потребитель с манифестом `.conventions.toml` |
| тема | набор правил об одном фокусе разработки; единица подписки |
| слой | один файл темы: базовый, языковой или стековый |
| компонент | адресат сборки в проекте: один язык, один стек, один вид приложения |
| префикс | четыре заглавные латинские буквы, адрес правила: `GTIM-3` |
Тему и ось слоя объявляет шапка файла, а не путь: `topic:`, `lang:`, `stack:`.
Слой без ключей оси — базовый, он попадает в копию всегда.
## Три уровня и ссылки между ними
Уровни стоят стопкой, и каждый нижний называет верхний ссылкой:
```
язык → слова, версия, короткое описание для читателя
набор → ссылается на язык: [language] version, lang, source
проект → ссылается на набор: source в .conventions.toml
```
Каждый уровень — набор файлов, а где эти файлы лежат, решает не модель, а
ссылка. Реализовано два транспорта:
| Ссылка | Что это |
|---|---|
| `../dev-conventions`, `/srv/conventions` | директория на диске, как она лежит |
| `https://git.example.org/av/conventions.git` | git-репозиторий, клонируется |
| `file:///srv/conventions#v2` | тот же репозиторий, но в закоммиченном виде |
Относительный путь считается от манифеста, который ссылку несёт. Хвост
`#ветка`, `#тег` или `#коммит` закрепляет ревизию и осмыслен только у git.
`ssh://` и `git@host:path` работают тем же клонированием, но проверены хуже.
Клон делается заново на каждый вызов и удаляется. Кэш сэкономил бы второй
клон и вернул бы вопрос, что в нём протухло, — а на «что было в прошлый раз»
отвечает git в репозитории-потребителе.
Ключ `[language] source` в наборе пока обычно пуст: описание языка живёт в
самом наборе. Когда спецификация уедет в свой репозиторий, тот же ключ её
назовёт, и больше ничего не изменится.
## Установка
Внешняя зависимость одна (`BurntSushi/toml`), сборка обычная:
```
go install git.vakhrushev.me/av/convy@latest
```
или из клона репозитория:
```
go build -o convy .
```
Способ раздачи готовых бинарей пока не выбран — вопрос открыт в `TOOL.md`.
## Команды
```
В наборе:
convy suite init завести набор: директория и манифест
convy suite add завести конвенцию: файл, тема и префикс
convy suite rule дописать правило: следующий номер, блоки по порядку
convy suite retire снять правило, конвенцию или тему — без переиспользования
convy suite list что в наборе и что возьмёт компонент
convy suite check целостность набора: префиксы, темы, оси, ссылки, форма
В проекте:
convy init подключить конвенции: источник и первый компонент
convy add <тема> подписаться и собрать
convy pull пересобрать подписанное
convy list что подключено и что ещё есть в наборе
convy check проверить форму того, что здесь
```
Контекст определяется по манифесту рядом: `suite.toml` — набор,
`.conventions.toml` — проект. Наугад не делается ничего: команда не того уровня
отказывает и подсказывает нужную.
## Два режима
Команды, которые что-то меняют, работают в двух режимах, и режим читается по
командной строке.
**Без аргументов — диалог.** Каждое поле спрашивается с подсказкой о том, что
туда кладут и почему. Это для человека.
```
$ convy suite add
The focus the rules are about: time, config, db-schema. A topic is the unit of
subscription — a consumer takes it whole. The name never changes and is never reused.
Topic: logging
It goes into the manifest and builds the table of conventions in a consumer's README.
One line about the topic: логирование: уровни, структура записи
...
```
**С флагами — автоматика.** Всё передаётся сразу, вопросов не задаётся, все
недостающие поля называются разом. Это для агентов и скриптов.
```
$ convy suite add --topic logging --about "логирование: уровни" \
--prefix SLOG --title "Логирование"
```
Без терминала пустой вызов отказывает, а не виснет в ожидании ответа, которого
некому дать.
## Проверка набора
```
$ convy suite check
conventions/lang/go/logging.md
error:5 extends points at "arch/time.md" with topic "time", while the file
carries topic "logging": the layers of one topic declare one name [spread]
suite: 13 files, 8 topics, language version 1 (ru)
errors: 1, warnings: 0
```
Строка находки — `уровень:строка что не так [семейство]`. Семейств четыре:
- **manifest** — префиксы, темы, объявленные файлы, незарегистрированные файлы;
- **form** — форма правила: нумерация, блоки, модальные слова, словарь.
Проверяется в любом файле, который язык употребляет;
- **spread** — то, что относится к отъезду к потребителю: тема в шапке, ось,
`extends`, пути канона, самодостаточность нормы. Только в конвенциях;
- **links** — разрешение ссылок вида `PREFIX-N`.
Третья часть списка проверок языка — взаимоисключительность строк таблицы,
покрытие области действия, отвечает ли обоснование на «что сломается» — разбором
текста не даётся и остаётся работой читателя.
Коды возврата: `0` — чисто, `1` — есть ошибки, `2` — команда набрана неверно или
не в том контексте.
## Подключение в проект
```
$ convy init --source ../dev-conventions --component backend \
--dir backend/docs/conventions --lang go --stack sqlite
$ convy add time
$ convy add logging --for backend
```
`init` проверяет источник до того, как писать манифест: набор, до которого
никто не доберётся, проходит любую проверку и никому не помогает. Получается
`.conventions.toml`:
```toml
source = "../dev-conventions"
[components.backend]
dir = "backend/docs/conventions"
lang = ["go"]
stack = ["sqlite"]
topics = ["logging", "time"]
```
Компонент пишется всегда, даже когда он один; при нескольких команда без
`--for` не угадывает, а перечисляет имена. `stack` — список: `sqlite` и
`postgres` действуют вместе, это разные таблицы одного сервиса. Два языка не
действуют вместе никогда — ради этого компонент и заведён.
Копия плоская, файл на тему. Первый слой — сам документ; каждый следующий
становится его разделом, и заголовки внутри опускаются на уровень: слой
реализует и сужает базу, а не стоит рядом с ней. Строка о версии языка
остаётся одна.
```markdown
---
origin: time
---
# Время
...
### TIME-1. Единый формат — RFC 3339, UTC
...
## Время: реализация на Go
...
#### GTIM-1. «Сейчас» берётся у слоя хранилища
...
<!-- conv:local -->
```
Всё ниже `<!-- conv:local -->` принадлежит репозиторию и переживает `pull`;
всё выше перезаписывается. Рядом с копиями кладётся `READING.md` — он
приезжает с уровня языка. `README.md` в той же директории принадлежит проекту
и не трогается, а файл, у которого убрали `origin:`, перестаёт быть копией:
`pull` его не перезапишет и скажет почему.
## Проверка проекта
`convy check` до набора не дотягивается и сети не требует: форма правила одна
и та же, локальные правила проекта на `X`-префиксах записаны по ней же.
Проверяются шапка `origin:`, маркер локальной части, нумерация по каждому
префиксу, блоки правила, словарь — и то, чего в наборе не бывает:
```
$ convy check
docs/conventions/time.md
error:22 rule XTIM-1 is a rule of the repository standing above the
marker: a reassembly would wipe it [spread]
project: 2 files in 1 component
errors: 1, warnings: 0
```
Язык копии определяется по строке о версии, которую она несёт: манифест рядом
с ней не лежит, и больше сказать некому.
## Ступени и словарь
Слова, которыми записаны модальность и метки, — свойство версии языка и
естественного языка набора, а не самого набора. Инструмент знает их сам, и
`suite.toml` их не дублирует: достаточно `[language] version` и `lang`.
Поэтому ступень называется категорией, а не словом:
```
--modality requirement | prohibition | recommendation | not-recommended | permission
```
`prohibition` в русском наборе превращается в `**НЕ ДОЛЖЕН.**`, в английском —
в `**MUST NOT.**`. Вызывающему не нужно знать, на каком языке записан набор.
По той же причине `convy` умеет написать строку о версии языка, и созданный им
файл проходит `suite check` без единой правки.
## Чего инструмент не делает
- не сливает трёхсторонне и не разрешает конфликты: правка выше маркера
локальной части теряется, и это заявленное поведение;
- не ведёт лок-файл: копии закоммичены, ответ на «что было в прошлый раз» даёт
git;
- не хранит список подписчиков: подписка — свойство проекта;
- не переносит правки из проекта в набор: операция ручная и редкая;
- не перенумеровывает правила: номер — идентификатор, а не позиция;
- не кэширует источник: клон делается заново и удаляется;
- не знает нескольких наборов сразу: `source` в проекте один.