манифесты стали данными, заведён convy sync

- убраны комментарии из suite.toml и .conventions.toml: файл, который
  машина переписывает, комментарий через круг не проносит; объяснения
  ушли в README рядом, который suite init теперь заводит
- удалена текстовая правка манифеста целиком — 520 строк ручного
  лексера TOML вместе со всем классом ошибок порчи данных
- запись идёт из структур энкодером; ключ, которого инструмент не
  знает, запись останавливает, а не теряется молча
- convy sync сверяет манифест и подводит под него раскладку файлов:
  чего не хватает — собирает, что осиротело — удаляет, копию с
  локальной частью не трогает никогда
This commit is contained in:
av
2026-07-28 09:45:10 +03:00
parent b6b0976c19
commit 92bd1f463d
18 changed files with 851 additions and 1055 deletions
+24 -18
View File
@@ -34,7 +34,7 @@ CLI для управления конвенциями. Модель здесь
```
internal/lang словарь: реестр «версия языка × естественный язык»
internal/source ссылки между уровнями: путь на диске, git-репозиторий
internal/manifest suite.toml и .conventions.toml — чтение и текстовая правка
internal/manifest suite.toml и .conventions.toml — чтение и запись
internal/doc разбор документа: шапка, области правил, блоки
internal/suite сборка набора в память, отбор слоёв под компонент
internal/project сборка копий в проекте: разделы, маркер, READING.md
@@ -59,11 +59,14 @@ internal/cli команды, диалог, два режима
- **Что инструмент пишет, инструмент принимает.** Набор, созданный `suite init`,
`add` и `rule`, обязан проходить `suite check` без правок. Это проверяет
`checkClean` в `internal/cli`; ломать инвариант нельзя.
- **Манифест правится текстом, а не энкодером.** В `suite.toml` комментариев
больше, чем данных. `manifest.AddEntry` вставляет запись, подстраиваясь под
порядок таблицы: отсортированную по алфавиту держит отсортированной,
упорядоченную вручную дополняет в конец. Комментарий, отделённый пустой
строкой, принадлежит таблице **ниже** себя.
- **Манифест — данные.** Оба манифеста декодируются в структуры и пишутся
обратно энкодером целиком. Комментариев в них нет: файл, который машина
переписывает, комментарий через круг не проносит, и вид, что проносит, стоит
этого комментария в день, когда никто не смотрит. Объяснения — в соседних
файлах, которых ни одна команда не касается.
- **Непонятый ключ останавливает запись.** Раз запись идёт из структур, ключ,
которого в них нет, при сохранении исчез бы. `manifest.save` отказывается,
называя ключ: это единственный исход, который его не теряет и не прячет.
- **Разбор опирается на разметку, а не на суждение.** Область правила — от
заголовка до следующего заголовка любого уровня. Метка открывает блок только
первой в абзаце и полужирным. Огороженные блоки кода исключаются везде;
@@ -71,11 +74,6 @@ internal/cli команды, диалог, два режима
пути канона. Маркер локальной части — то же самое: `doc.LocalMarker` один на
весь инструмент, `doc.Marker()` пропускает огороженные блоки, потому что
конвенция о ведении копий этот маркер цитирует.
- **Манифест читается так, как он записан.** Решётка внутри строки не открывает
комментарий, скобка внутри комментария не закрывает массив, имя внутри
комментария не подписка. Регуляркой по сырым строкам это не берётся —
`splitComment` и `scanCode` в `internal/manifest`. Превращение комментария в
данные — единственная ошибка, из которой нет дороги назад.
- **Уровень называется ссылкой, а не путём.** Проект ссылается на набор, набор
на язык; `source.Ref` разбирает ссылку, `source.Open` отдаёт директорию,
которую можно читать. Транспортов два, но `Kind` — перечисление, а не булево:
@@ -124,9 +122,16 @@ internal/cli команды, диалог, два режима
Проверка гоняется на каждой правке и в сеть ходить не должна. Пропуск
объявляется строкой в выводе: молча не выполненная проверка читается ровно
как пройденная.
- **Всё, что попадает в манифест, проходит через `manifest.Quote`.** Обратный
слэш в пути — обычный случай, на котором инструмент перестаёт читать файл,
который сам записал.
- **Комментариев в манифестах не будет.** Пробовали держать их текстовой
правкой — вышло четыре случая порчи данных подряд: комментарий с кавычками
становился подпиской, скобка в комментарии обрезала массив. Формат с
сохранением комментариев при записи (`go-toml-edit`, YAML через `yaml.Node`)
отвергнут как усложнение под задачу, которой нет: манифест машинный.
- **`sync` — о наборе файлов, `pull`о содержимом.** `pull` берёт текст всех
подписок заново, и оставленный им дифф и есть смысл запуска. `sync` сверяет
манифест и подводит под него раскладку: чего не хватает — собирает, что
осиротело — удаляет. Копию с непустой локальной частью не удаляет никогда и
завершается с ошибкой, пока она лежит.
- **Позиционный аргумент отсекается явно.** `flag` прекращает разбор на первом
не-флаге, поэтому лишний аргумент не просто лежит без дела — он прячет все
флаги после себя. `noStrayArgs` в командах без позиционных, ручное снятие
@@ -203,11 +208,12 @@ Commits и `Co-Authored-By` не используются.
## Состояние
Наборная сторона: `init`, `add`, `rule`, `retire`, `list`, `check`.
Проектная: `init`, `add`, `pull`, `list`, `check`. Обе стороны закончены по
тому, что намечено в `TOOL.md`.
Проектная: `init`, `add`, `pull`, `sync`, `list`, `check`. Обе стороны
закончены по тому, что намечено в `TOOL.md`; `sync` в `TOOL.md` не значится и
заведён сверх него.
Отбор слоёв под компонент — один на обе стороны: `suite.Assemble`. `suite list`
показывает, что взял бы компонент, `convy pull` то же самое пишет в файл;
разъехаться они не должны.
показывает, что взял бы компонент, `convy pull` и `convy sync` то же самое
пишут в файл; разъехаться они не должны.
Линтеров и CI нет.