- README описывает термины, команды, два режима и вывод проверки; модель не пересказывается — истина остаётся в репозитории набора - CLAUDE.md фиксирует распределение языков, инварианты кода и решения, которые уже приняты и не пересматриваются без просьбы - отдельным разделом перечислены известные остатки, чтобы их не искали заново
10 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Язык
Общение по репозиторию — на русском. Внутри репозитория язык распределён так, и это распределение проверяется глазами при ревью:
- английский — комментарии в коде, имена, тексты ошибок, вывод CLI, сообщения и имена тестов;
- русский —
README.md,CLAUDE.md, сообщения коммитов; - язык проверяемого набора — содержимое тестовых фикстур и литералы
словарей в
internal/lang. Это данные под проверкой, а не текст инструмента, и переводить их нельзя: сломается то, что они проверяют.
Слова словаря законно попадают внутрь английских сообщений, потому что
подставляются из набора: rule GTIM-1 has no ПОЧЕМУ block. Инструмент говорит
по-английски и цитирует то, чем записан проверяемый набор.
Что это
CLI для управления конвенциями. Модель здесь не описывается: она живёт в
репозитории набора dev-conventions — README.md (набор и копии),
LANGUAGE.md (форма правила и список проверок), GUIDE.md (ведение набора,
префикс META), TOOL.md (решения об инструменте). При расхождении истина там,
а не в коде и не здесь.
Пользовательская сторона — в README.md. Ниже — то, что нужно знать, правя код.
Раскладка
internal/lang словарь: реестр «версия языка × естественный язык»
internal/manifest suite.toml — чтение и текстовая правка
internal/doc разбор документа: шапка, области правил, блоки
internal/suite сборка набора в память, отбор слоёв под компонент
internal/check проверки: manifest, form, spread, links
internal/cli команды, диалог, два режима
Зависимость одна — BurntSushi/toml. Вторую не заводить: так решено в
TOOL.md, и Undecoded() этого парсера бесплатно ловит опечатки в ключах
манифеста.
Инварианты кода
- В коде нет ни одной темы, ни одного префикса, ни одного пути набора. Всё
это приходит из манифеста. Список проверяемых файлов берётся из
[prefixes.live], а не из дерева директорий: это и есть граница «язык употребляет» против «язык цитирует». - Словарь зашит в бинарь по паре «версия языка × естественный язык», а не
объявляется в манифесте. Когда спецификация языка уедет в отдельный
репозиторий и там появятся спеки словарей, источником станут они —
подменяется
registry, типVocabularyи проверки поверх него не трогаются. - Что инструмент пишет, инструмент принимает. Набор, созданный
suite init,addиrule, обязан проходитьsuite checkбез правок. Это проверяетcheckCleanвinternal/cli; ломать инвариант нельзя. - Манифест правится текстом, а не энкодером. В
suite.tomlкомментариев больше, чем данных.manifest.AddEntryвставляет запись, подстраиваясь под порядок таблицы: отсортированную по алфавиту держит отсортированной, упорядоченную вручную дополняет в конец. Комментарий, отделённый пустой строкой, принадлежит таблице ниже себя. - Разбор опирается на разметку, а не на суждение. Область правила — от заголовка до следующего заголовка любого уровня. Метка открывает блок только первой в абзаце и полужирным. Огороженные блоки кода исключаются везде; инлайн-код вырезается там, где ищутся ссылки, и не вырезается там, где ищутся пути канона.
Решения, которые уже приняты
Их не пересматривают без явной просьбы — каждое обсуждалось и стоило времени.
- Порядок правил в файле — по читаемости, а не по номерам. Номер стабилен и не переиспользуется, поэтому «порядок по номерам» означал бы «порядок по времени написания» навсегда. Проверяется сплошность нумерации, а не возрастание.
- Ссылка на чужую тему вне нормы разрешена (META-20): обоснование, потерявшее адресата, деградирует честно. Внутри своей темы ссылаться можно только в базовый слой, и это проверяется во всём документе, а не только в норме: гарантированно присутствует в копии один базовый слой.
- Заголовок правила — объявление, а не ссылка. При разборе ссылок строки заголовков пропускаются.
- Лок-файла,
push, отчёта о расхождении и перенумерации не будет. Модель отвергает каждое явно.
Проверки
Семейства повторяют деление из LANGUAGE.md, раздел «Что стоит проверять
машиной», и это деление держится в коде: form — в любом файле, который язык
употребляет; spread — только в конвенциях, потому что эти проверки о том, что
документ уезжает к потребителю. Третья часть списка (взаимоисключительность
строк таблицы, покрытие области действия, отвечает ли обоснование на «что
сломается») разбором текста не даётся и в коде отсутствует намеренно.
Новая проверка заводится вместе с двумя тестами: что она срабатывает и что она
молчит там, где не должна. Второй важнее: проверка, краснеющая на исправном
файле, выключается целиком. Ложные срабатывания собраны в
TestNoFalsePositives.
Перед тем как заводить проверку, стоит прогнать её замысел по живому канону
(dev-conventions): если она покраснеет на исправном наборе, замысел неверен.
Известные остатки
- Набор без документа самоуправления, в котором конвенция потеряла
topic, проскочит: признаков «этот документ один» и «у него нет ключей слоя» не хватает. Закрывается маркером в манифесте — правка формата, не сделана. - Проверка пути канона считает путём любой токен
*.md, который резолвится в файл набора; упоминаниеREADME.mdв конвенции она пометит ошибочно. На текущем каноне не срабатывает. - Машиночитаемого вывода находок (
--json) нет.
Тесты
go test ./... всё
go test ./internal/check/ -v проверки, по одному подтесту на случай
gofmt -l . && go vet ./... перед коммитом
Тесты фикстурные: набор пишется во временную директорию и прогоняется целиком.
internal/cli проверяет обе моды, включая диалог — интерактивный режим иначе не
покрыть, из шелла он требует терминала.
Коммиты
Русский, строчная буква, без точки в конце, прошедшее время или страдательный
залог. Изредка область через двоеточие (suite check:). Тело — маркированный
список на 2–4 пункта с переносом по ~76 колонок, объясняет почему. Conventional
Commits и Co-Authored-By не используются.
Состояние
Наборная сторона закончена: init, add, rule, retire, list, check.
Проектные команды (add, pull, list, check без suite) не начаты; отбор
слоёв под компонент для них уже написан — suite.Assemble, — и переписывать его
в сборщике не нужно.
Линтеров и CI нет.