проектные команды и ссылки на источник

- заведён internal/source: уровни ссылаются друг на друга путём на диске
  или git-репозиторием, ревизия закрепляется хвостом #ref; клон делается
  заново и удаляется, кэша нет
- добавлены init, add, pull, list, check в проекте — манифест
  .conventions.toml, сборка копий по разу на компонент, маркер локальной
  части, READING.md рядом
- проверки формы развязаны с набором: принимают lang.Vocabulary, а язык
  копии узнаётся по строке о версии — манифеста рядом с ней нет
This commit is contained in:
av
2026-07-27 20:42:18 +03:00
parent 4615de6e86
commit 23d88c4048
27 changed files with 3009 additions and 57 deletions
+55 -6
View File
@@ -33,9 +33,11 @@ CLI для управления конвенциями. Модель здесь
```
internal/lang словарь: реестр «версия языка × естественный язык»
internal/manifest suite.toml — чтение и текстовая правка
internal/source ссылки между уровнями: путь на диске, git-репозиторий
internal/manifest suite.toml и .conventions.toml — чтение и текстовая правка
internal/doc разбор документа: шапка, области правил, блоки
internal/suite сборка набора в память, отбор слоёв под компонент
internal/project сборка копий в проекте: разделы, маркер, READING.md
internal/check проверки: manifest, form, spread, links
internal/cli команды, диалог, два режима
```
@@ -67,6 +69,15 @@ internal/cli команды, диалог, два режима
первой в абзаце и полужирным. Огороженные блоки кода исключаются везде;
инлайн-код вырезается там, где ищутся ссылки, и не вырезается там, где ищутся
пути канона.
- **Уровень называется ссылкой, а не путём.** Проект ссылается на набор, набор
на язык; `source.Ref` разбирает ссылку, `source.Open` отдаёт директорию,
которую можно читать. Транспортов два, но `Kind` — перечисление, а не булево:
третий (rclone, дерево по https) ожидается, и отказ обязан сначала сказать,
чем ссылку сочли, и только потом — что не так.
- **Проверки формы не знают про набор.** `checkRules`, `checkVersionLine`,
`checkModalsOutside` и прочие принимают `lang.Vocabulary`, а не `*suite.Suite`
— иначе `convy check` в проекте пришлось бы писать заново. Копия несёт язык
строкой о версии, и `lang.Recognize` читает его оттуда: манифеста рядом нет.
## Решения, которые уже приняты
@@ -84,6 +95,26 @@ internal/cli команды, диалог, два режима
заголовков пропускаются.
- **Лок-файла, `push`, отчёта о расхождении и перенумерации не будет.** Модель
отвергает каждое явно.
- **Кэша источника нет.** Клон делается заново и удаляется вместе с `Tree`.
Кэш экономит второй клон и возвращает вопрос, что в нём протухло; на «что
было в прошлый раз» отвечает git потребителя. Если станет дорого, кэш
прячется за `source.Tree` и наружу не виден.
- **`file://` — это git, а не директория.** Простой путь уже означает «эта
директория, как она лежит», вместе с грязным рабочим деревом; `file://`
означает «тот же репозиторий в закоммиченном виде». Ради этой разницы оба
написания и существуют — и ради неё же git-транспорт тестируется без сети.
- **Слой ниже первого становится разделом.** Заголовки опускаются на уровень,
строка о версии языка выбрасывается у всех, кроме первого. Расширение
реализует и сужает базу, а не стоит рядом с ней, — поэтому правила базы в
копии на `###`, а правила языкового слоя на `####`. Проверка копий уровень
заголовка правила не требует, лестницу заголовков — требует.
- **`convy check` до набора не дотягивается.** Форма правила одна и та же,
локальные правила на `X` записаны по ней же, а проверять своё нужно без сети
и без знания, откуда копии приехали.
- **Целостность набора проверяется локально.** Когда `[language] source`
заполнен, `suite check` не тянет описание языка и пропускает проверки
документов о языке (META-30 в том числе), проверяя вместо этого саму ссылку.
Проверка гоняется на каждой правке и в сеть ходить не должна.
## Проверки
@@ -111,6 +142,16 @@ internal/cli команды, диалог, два режима
файл набора; упоминание `README.md` в конвенции она пометит ошибочно. На
текущем каноне не срабатывает.
- Машиночитаемого вывода находок (`--json`) нет.
- Предупреждения о висячей ссылке на неподписанную тему нет: `convy check` до
набора не дотягивается и разрешает только ссылки на префиксы самого файла.
Открытый вопрос из `TOOL.md`; закрывается отдельной командой, а не этой.
- Набор в поддиректории git-репозитория не адресуется: `#рев` есть, `//путь`
нет. Появится вместе с первым набором, который так лежит.
- Источник у проекта один. Модель нескольких допускает; форма `source = "..."`
расширяется до `[sources.имя]` не ломая существующие манифесты.
- `convy add` пишет подписку после сборки — если сборка прошла, а запись в
манифест упала, копия останется неучтённой. Обратный порядок хуже: подписка
без файла отправляет следующий `pull` искать то, чего не делали.
## Тесты
@@ -122,7 +163,12 @@ gofmt -l . && go vet ./... перед коммитом
Тесты фикстурные: набор пишется во временную директорию и прогоняется целиком.
`internal/cli` проверяет обе моды, включая диалог — интерактивный режим иначе не
покрыть, из шелла он требует терминала.
покрыть, из шелла он требует терминала. Проектные тесты строят набор теми же
командами и подключают его в проект: `subscribable` в `project_test.go`
`retirable` плюс `READING.md`, без которого копиям нечего везти рядом.
Git-транспорт проверяется на локальном репозитории через `file://` и
пропускается, если `git` не найден. Сети тесты не требуют.
## Коммиты
@@ -133,9 +179,12 @@ Commits и `Co-Authored-By` не используются.
## Состояние
Наборная сторона закончена: `init`, `add`, `rule`, `retire`, `list`, `check`.
Проектные команды (`add`, `pull`, `list`, `check` без `suite`) не начаты; отбор
слоёв под компонент для них уже написан — `suite.Assemble`, — и переписывать его
в сборщике не нужно.
Наборная сторона: `init`, `add`, `rule`, `retire`, `list`, `check`.
Проектная: `init`, `add`, `pull`, `list`, `check`. Обе стороны закончены по
тому, что намечено в `TOOL.md`.
Отбор слоёв под компонент — один на обе стороны: `suite.Assemble`. `suite list`
показывает, что взял бы компонент, `convy pull` то же самое пишет в файл;
разъехаться они не должны.
Линтеров и CI нет.