# Язык конвенций Как записываются правила в этом каноне. Документ описывает форму, а не содержание: что такое правило, чем оно отличается от прозы вокруг и как на него сослаться. Форма подсмотрена у OpenSpec, но взято оттуда не всё — см. «Чего мы не берём». ## Зачем формализовать Не ради строгости. Три конкретные вещи, которые без адресуемых правил не работают: - **Механизация.** Регион `механизировано` должен говорить «правило `MIGR-4` проверяет `archrules`», а не «`AUTOINCREMENT` в новых миграциях — `archrules`»: во втором случае читатель сам догадывается, к какому утверждению это относится, и догадывается по-разному. - **Отступления.** «У нас не так» бесполезно, пока не сказано, что именно «не так». Со ссылкой на правило отступления становятся счётными: видно, сколько правил конвенции репозиторий реально не соблюдает. - **Промоут находки.** Путь «находка → конвенция → правило линтера → удаление прозы» требует ручки, за которую берут конкретное правило. Общий знаменатель — **идентификатор**. Модальные слова и таблицы полезны, но вторичны. ## Единица — правило ```markdown ### KEYS-5. Разбор внешнего идентификатора на границе **ДОЛЖЕН.** Идентификатор, пришедший снаружи, проходит разбор до запроса к базе. **Почему.** Разбор валидирует формат и нормализует регистр. Сравнение строк в базе побайтовое, поэтому без нормализации запрос молча не находит существующую запись — отладка такого случая стоит дороже, чем сам разбор. ``` Четыре обязательные части: **идентификатор**, **заголовок**, **модальность с нормой**, **почему**. Норма — одна фраза; если в неё не влезает, это два правила. ## Правило без «почему» не принимается Это жёсткое требование к форме, а не пожелание. Причины: - **«Почему» — единственный способ понять, когда правило перестало действовать.** Норма стареет молча; обоснование стареет заметно. Когда причина отпала, видно, что правило пора убрать, а не соблюдать по инерции. - **Правило без обоснования не переживает спор.** Через год ни автор, ни агент не восстановят мотив, и правило будет либо отменено первым же возражением, либо соблюдено там, где вредит. - **Формулировка «почему» — проверка на то, что это вообще правило.** Если причина не формулируется, перед нами привычка или вкусовщина; ей место в черновиках, а не в конвенции. «Почему» отвечает на «что сломается, если сделать иначе», а не пересказывает норму другими словами. «Потому что так принято» — не обоснование. ## Модальные слова Пишутся капсом — это ключевые слова, а не обычный текст. | Слово | Значение | Отступление | |---|---|---| | **ДОЛЖЕН** | нарушение считается ошибкой | только с записью в регион `отступления` | | **НЕ ДОЛЖЕН** | запрет | то же | | **СЛЕДУЕТ** | сильная рекомендация; новый код пишем так | допустимо, причину записываем | | **НЕ СЛЕДУЕТ** | обратное к СЛЕДУЕТ | то же | | **ДОПУСКАЕТСЯ** | явное разрешение | не требуется — правило ничего не запрещает | **ДОПУСКАЕТСЯ** нужно не для симметрии: оно снимает вопрос «а так можно?» там, где соседнее правило звучит строго и его легко перечитать шире, чем задумано. Модальность живёт на **правиле**, а не на файле. Прежний файловый статус (`status: рекомендуемая` / `обязательная` в шапке) отменён: он неизбежно врал, потому что один файл смешивает жёсткие требования с советами. В шапке остаются только `prefix`, `extends` и служебные ключи копии. Мы **не используем SHALL и прочие английские ключевые слова**. Они заняты спецификациями (OpenSpec), и общий словарь стирал бы границу «конвенция — не capability». Разный словарь эту границу держит бесплатно. ## Идентификаторы - Формат — `<ПРЕФИКС>-<номер>`: `KEYS-5`, `SLOG-27`. Префикс принадлежит файлу, нумерация внутри файла сквозная и начинается с единицы. - Строка таблицы, если на неё нужно ссылаться отдельно, — `KEYS-5.1`, `KEYS-5.2`. - **Идентификатор глобален.** Префикс уникален по всему канону, поэтому путь файла в ссылке не нужен: `KEYS-5` адресует правило одинаково изнутри файла, из соседней конвенции и из чужого репозитория. В собранной копии слои разных осей лежат в одном документе, так что ссылка на базовый слой из языкового вообще никуда не ведёт — правило рядом. - **Идентификаторы стабильны и не переиспользуются.** Удалённое правило оставляет дыру в нумерации; занимать её новым нельзя — иначе ссылка из чужого репозитория начнёт указывать на другое утверждение. То же относится к префиксам: выбывшие хранит `prefixes.toml`. - Префикс **выбирается под файл, а не выводится по формуле**: он нужен, чтобы по нему искать, а не чтобы его разбирать. Выводимый префикс вдобавок привязал бы идентификатор к таксономии, которую канон перестраивает, и упёрся бы в потолок из числа букв алфавита. - Перенос правила в другой файл — смысловое изменение, а не переименование: новый файл означает новый префикс и новую нумерацию. Переезд самого файла между осями идентификаторы не трогает. Порядок правил в файле выбирается по читаемости, не по номерам: номер — это идентификатор, а не позиция. ## Правило, чья норма уехала в линтер Когда правило механизировано у всех потребителей, его норма из канона удаляется, а обоснование — нет. Остаётся **правило без модальности**, и чтобы оно не выглядело недописанным, место нормы занимает отметка: ```markdown ### MIGR-6. Дефолтов времени в схеме БД нет **МЕХАНИЗИРОВАНО.** Проверяется общим правилом линтера; формулировка удалена, потому что дублировала работающую проверку. **Почему.** Дефолт превращает забытую вставку в тихо работающий код… ``` - Идентификатор и заголовок сохраняются: ссылки из репозиториев продолжают указывать на то же утверждение. - «Почему» остаётся навсегда — линтер сообщает, что нарушено, но не сообщает, зачем правило существует, и без обоснования нельзя понять, когда проверку пора отменять. - **МЕХАНИЗИРОВАНО** — не шестое модальное слово: оно не задаёт обязательность, а сообщает, что обязательность теперь обеспечена машиной. В остальном такое правило равно ДОЛЖЕН. Факт «механизировано у всех» устанавливается вручную: канон по построению не знает списка подписчиков, и обойти репозитории перед удалением нормы — часть работы, а не то, что можно проверить автоматически. ## Таблицы вместо сценариев Часть правил **классифицирует ситуации**: какой уровень лога, какая категория директории, что делать с невалидным вводом в зависимости от его источника. Для них каноническая форма — таблица «ситуация → вердикт», строки которой при необходимости нумеруются. Таблица плотнее прозы и не даёт пропустить ветку: пустая клетка видна, а неупомянутый случай в абзаце — нет. ## Чего мы не берём из OpenSpec **GIVEN/WHEN/THEN.** У спецификации субъект — система, и её поведение разворачивается во времени: состояние, событие, исход. У конвенции субъект — автор кода, и разворачивать нечего: есть ситуация выбора и вердикт. Это таблица, а не траектория. **SHALL.** См. выше про словарь. **Сценарии как общая форма.** Прозаический сценарий остаётся точечным инструментом — для **стыка правил**, когда два правила вместе дают неочевидный результат: ``` WHEN зависимость недоступна и ретраи вызова исчерпаны → ext-запись ERROR AND тик фонового цикла упал по той же причине → доменная запись WARN ``` Такой блок ставится после обоих правил и ссылается на их номера. Если стыков нет — сценариев в файле нет. ## Что правилом не является Модальные слова в этих частях **не употребляются** — иначе перестанет быть понятно, что адресуемо, а что нет: - **Область действия** — на что конвенция распространяется во времени («новые таблицы; существующие не переписываются»). Это рамка для всех правил файла, а не правило. - **Связано** — ссылки на смежные конвенции, ADR, код. - **Локальные регионы** — содержимое принадлежит репозиторию. - Вводная проза, объясняющая предмет конвенции. ## Как на правила ссылаются копии В репозитории: ```markdown MIGR-2, MIGR-4 — `internal/archrules` (проверяются в новых миграциях). MIGR-6 — не соблюдается в легаси-таблицах `show_history`, `queue`: составные ключи там появились до конвенции, переписывание требует миграции данных. ``` Отсюда видно и то, чего раньше не было видно: конвенция из восьми правил, из которых два механизированы и одно не соблюдается. ## Что стоит проверять машиной Сейчас не реализовано; список — на будущее для `conv`: - префикс в шапке файла совпадает с реестром, состоит из четырёх заглавных латинских букв и не значится в списке выбывших; - заголовки правил файла используют только его собственный префикс; - номера уникальны внутри файла и не имеют пропусков вниз (новое правило берёт следующий свободный, а не первый освободившийся); - у каждого `### <ПРЕФИКС>-` есть модальное слово (или отметка МЕХАНИЗИРОВАНО) и блок «Почему»; - ссылки вида `<ПРЕФИКС>-` — хоть в тексте канона, хоть в локальных регионах копии — указывают на правила, которые ещё существуют; - чужой префикс не встречается в абзаце с модальностью (META-20); - путь файла канона не встречается в тексте конвенции (META-21); - модальные слова не встречаются вне правил. ## Порядок перевода Все конвенции канона записаны на этом языке. Новая конвенция пишется на нём сразу; смешение форм в каноне больше не предполагается.