diff --git a/GUIDE.md b/GUIDE.md index d6f521d..cb052c6 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -1,3 +1,7 @@ +--- +prefix: META +--- + # Как мы ведём конвенции Конвенция описывает повторяющийся выбор: как называть директории, как @@ -29,11 +33,10 @@ ## Оформление Имя файла — kebab-case по теме: `app-directories.md`. Правилом это не -записано: обоснование сводится к «чтобы адрес правила -(`stack/ansible/app-directories.md R4`) писался одним способом», а -проверить нарушение всё равно проще глазом, чем сформулировать норму. Номер -R16, под которым это правило существовало, оставлен свободным и не -переиспользуется. +записано: обоснование сводится к «чтобы имя файла в реестре префиксов +писалось одним способом», а проверить нарушение всё равно проще глазом, чем +сформулировать норму. Номер META-16, под которым это правило существовало, +оставлен свободным и не переиспользуется. ## Канон и копии @@ -51,7 +54,7 @@ R16, под которым это правило существовало, ос ## Правила -### R1. Одна конвенция — один файл +### META-1. Одна конвенция — один файл **ДОЛЖЕН.** Файл описывает ровно один повторяющийся выбор. @@ -61,7 +64,7 @@ R16, под которым это правило существовало, ос позже дорого: путь файла — часть адреса правила, и после разреза внешние ссылки указывают не туда. -### R2. Конвенция заводится, когда решение принимается третий раз +### META-2. Конвенция заводится, когда решение принимается третий раз **СЛЕДУЕТ.** Поводом служит одно и то же решение, принятое третий раз и каждый раз чуть по-другому. @@ -72,7 +75,7 @@ R16, под которым это правило существовало, ос спорный, читателю нужен вместе с мотивом — это ADR, где мотив и есть содержание записи. -### R3. Новая конвенция пишется там, где заболело +### META-3. Новая конвенция пишется там, где заболело **СЛЕДУЕТ.** Первые шаги пути «находка → конвенция → правило линтера → удаление прозы» делаются в репозитории, где случилась находка; в канон @@ -83,7 +86,7 @@ R16, под которым это правило существовало, ос платят за это все потребители сразу. Формулировка, обкатанная на одном репозитории, приезжает в канон уже с известной границей. -### R4. В тексте конвенции нет утверждений о состоянии репозитория +### META-4. В тексте конвенции нет утверждений о состоянии репозитория **НЕ ДОЛЖЕН.** Норма пишется в настоящем предписывающем времени, без описаний того, как сейчас устроен конкретный репозиторий. @@ -94,7 +97,7 @@ R16, под которым это правило существовало, ос с первого дня. «Так сделано у нас» — содержимое региона отступлений, где оно и локально, и проверяемо. -### R5. Расхождение кода с правилом — отступление, а не повод переписать правило +### META-5. Расхождение кода с правилом — отступление, а не повод переписать правило **ДОЛЖЕН.** Правило правится, только когда неверно по существу: содержит фактическую ошибку, внутреннее противоречие или условие применимости, @@ -105,7 +108,7 @@ R16, под которым это правило существовало, ос переписывает его снова. Направление «конвенция → код» держится ровно тем, что факт не считается аргументом. -### R6. `ДОЛЖЕН` без механической проверки либо механизируется, либо понижается +### META-6. `ДОЛЖЕН` без механической проверки либо механизируется, либо понижается **ДОЛЖЕН.** Правило, нарушение которого объявлено ошибкой, получает машинную проверку или переводится в СЛЕДУЕТ. @@ -119,18 +122,18 @@ R16, под которым это правило существовало, ос (вкус формулировки, выбор границы, суждение о ситуации), не может быть ДОЛЖЕН — его модальность СЛЕДУЕТ по построению, а не по слабости. -### R7. Факт механизации фиксируется в локальном регионе со ссылкой на номер +### META-7. Факт механизации фиксируется в локальном регионе со ссылкой на правило -**ДОЛЖЕН.** Регион `механизировано` называет номер правила и конкретную -проверку. +**ДОЛЖЕН.** Регион `механизировано` называет идентификатор правила и +конкретную проверку. **Почему.** Механизация — состояние конкретного репозитория, канон о ней не знает, а без записи следующий автор либо заведёт вторую проверку того же, -либо будет вычитывать глазами уже проверенное машиной. Без номера правила +либо будет вычитывать глазами уже проверенное машиной. Без идентификатора читатель догадывается сам, к какому утверждению относится проверка, — и догадывается по-разному. -### R8. Формулировка не удаляется из канона, пока механизирована не у всех +### META-8. Формулировка не удаляется из канона, пока механизирована не у всех **НЕ ДОЛЖЕН.** Норма остаётся в каноне, пока хотя бы у одного потребителя машинной проверки нет. @@ -145,7 +148,7 @@ R16, под которым это правило существовало, ос удалению нормы, а не то, что можно проверить машиной (см. `LANGUAGE.md`, состояние МЕХАНИЗИРОВАНО). -### R9. Общая механизация разрешает удалить норму из канона +### META-9. Общая механизация разрешает удалить норму из канона **ДОПУСКАЕТСЯ.** Правило, уехавшее в общий конфиг линтера или в общую роль, удаляется из канона одним `push`. @@ -153,10 +156,10 @@ R16, под которым это правило существовало, ос **Почему.** Формулировка, дублирующая работающую у всех проверку, размазывает внимание: файл на несколько сотен строк заставляет человека и агента добросовестно вычитывать тривиальное именование и не доходить до -формы решения. Явное разрешение нужно, чтобы R8 не читался как запрет +формы решения. Явное разрешение нужно, чтобы META-8 не читался как запрет удалять вообще. -### R10. Обоснование не удаляется никогда +### META-10. Обоснование не удаляется никогда **НЕ ДОЛЖЕН.** Блок «Почему» остаётся и после того, как норма уехала в линтер. @@ -165,7 +168,7 @@ R16, под которым это правило существовало, ос существует. Без обоснования не видно, когда причина отпала, — проверка продолжает работать по инерции, и возразить ей нечем, кроме как отключив. -### R11. У трудноизменяемого слоя область действия пишется явно +### META-11. У трудноизменяемого слоя область действия пишется явно **ДОЛЖЕН.** Конвенция о схеме БД, формате хранения или раскладке директорий называет, к чему применяется: к новым таблицам и миграциям, а не к @@ -177,7 +180,7 @@ R16, под которым это правило существовало, ос состоянию, и оба исхода плохи — миграция живых данных ради опрятности либо молчаливый вывод, что конвенция не соблюдается совсем. -### R12. Механизируется граница изменения, а не состояние +### META-12. Механизируется граница изменения, а не состояние **ДОЛЖЕН.** Проверка запрещает нарушение в новых миграциях, а не в уже существующей схеме. @@ -187,7 +190,7 @@ R16, под которым это правило существовало, ос новое, ради чего заводилась. Проверка границы оставляет старое в покое и делает новую ошибку невозможной. -### R13. Список отступлений трудноизменяемого слоя — постоянный +### META-13. Список отступлений трудноизменяемого слоя — постоянный **ДОЛЖЕН.** Перечисленные старые таблицы и раскладки живут как есть, а не как задачи на дочистку. @@ -197,25 +200,25 @@ R16, под которым это правило существовало, ос тем, что список перестают вести, — и пропадает единственное место, где видно, где именно правило не действует. -### R14. Отступления перечисляются поимённо, со ссылкой на номера правил +### META-14. Отступления перечисляются поимённо, со ссылкой на правила **ДОЛЖЕН.** В локальном регионе перечислены отступления, которые уже есть в -коде, с номером правила и причиной. +коде, с идентификатором правила и причиной. **Почему.** Иначе репозиторий выглядит соблюдающим конвенцию, а проверить это можно только чтением всего кода. Со ссылками отступления счётны: видно, сколько правил конвенции репозиторий реально не соблюдает. Пустой регион при этом почти всегда означает не отсутствие отступлений, а то, что их не искали. -### R15. Запись в регионе отступлений разбирается по масштабу +### META-15. Запись в регионе отступлений разбирается по масштабу **ДОЛЖЕН.** Судьба записи зависит от того, что в ней сказано: | № | Что записано | Куда идёт | |---|---|---| -| R15.1 | правилу не следуем в перечисленных местах, причина названа | остаётся отступлением | -| R15.2 | правило не применяется в репозитории целиком | чинится условие применимости — в каноне | -| R15.3 | конвенция репозиторию не нужна | копия удаляется, подписки нет | +| META-15.1 | правилу не следуем в перечисленных местах, причина названа | остаётся отступлением | +| META-15.2 | правило не применяется в репозитории целиком | чинится условие применимости — в каноне | +| META-15.3 | конвенция репозиторию не нужна | копия удаляется, подписки нет | **Почему.** Отступление описывает исключение, и по нему видно, какая часть правила нарушена. Запись «мы это правило вообще не применяем» такой @@ -223,7 +226,7 @@ R16, под которым это правило существовало, ос правила в каноне, которую чинят один раз для всех, или лишнюю подписку, где файл просто не нужен. Оставленная отступлением, она прячет обе. -### R17. Репо-специфичная часть «Связано» — в локальном регионе +### META-17. Репо-специфичная часть «Связано» — в локальном регионе **ДОЛЖЕН.** Раздел «Связано» стоит в конце файла; канонические ссылки — в общем тексте, ссылки на ADR, код и файлы конкретного репозитория — в @@ -233,7 +236,7 @@ R16, под которым это правило существовало, ос чужой файл у них битая с первого дня. Регион исключён из сравнения, поэтому та же ссылка внутри него никого не задевает и не даёт вечного шума в `diff`. -### R18. README директории перечисляет конвенции с однострочным описанием +### META-18. README директории перечисляет конвенции с однострочным описанием **СЛЕДУЕТ.** Одна плоская таблица: файл и строка о том, про что он. @@ -241,15 +244,16 @@ R16, под которым это правило существовало, ос «какая из них про мой случай» решается открыванием каждой. Ценой в десяток файлов это означает, что не открывают ни одной. -### R19. Короткие инварианты дублируются в точку входа агента +### META-19. Короткие инварианты дублируются в точку входа агента **ДОЛЖЕН.** В `AGENTS.md` / `CLAUDE.md` едет одна строка на правило с его -номером; детали остаются в конвенции. +идентификатором; детали остаются в конвенции. **Почему.** Сама по себе конвенция агенту не видна: он дойдёт до неё, только если его туда отправили, — а безусловно он читает точку входа. Строка с -номером служит и напоминанием, и адресом, по которому за подробностями -идут; перенос деталей туда же вернул бы задачу поддержки двух текстов. +идентификатором служит и напоминанием, и адресом, по которому за +подробностями идут; перенос деталей туда же вернул бы задачу поддержки двух +текстов. diff --git a/LANGUAGE.md b/LANGUAGE.md index b00e119..48eeec0 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -12,8 +12,8 @@ Не ради строгости. Три конкретные вещи, которые без адресуемых правил не работают: -- **Механизация.** Регион `механизировано` должен говорить «правило R4 - проверяет `archrules`», а не «`AUTOINCREMENT` в новых миграциях — +- **Механизация.** Регион `механизировано` должен говорить «правило + `MIGR-4` проверяет `archrules`», а не «`AUTOINCREMENT` в новых миграциях — `archrules`»: во втором случае читатель сам догадывается, к какому утверждению это относится, и догадывается по-разному. - **Отступления.** «У нас не так» бесполезно, пока не сказано, что именно @@ -28,7 +28,7 @@ ## Единица — правило ```markdown -### R5. Разбор внешнего идентификатора на границе +### KEYS-5. Разбор внешнего идентификатора на границе **ДОЛЖЕН.** Идентификатор, пришедший снаружи, проходит разбор до запроса к базе. @@ -38,8 +38,8 @@ существующую запись — отладка такого случая стоит дороже, чем сам разбор. ``` -Четыре обязательные части: **номер**, **заголовок**, **модальность с -нормой**, **почему**. Норма — одна фраза; если в неё не влезает, это два +Четыре обязательные части: **идентификатор**, **заголовок**, **модальность +с нормой**, **почему**. Норма — одна фраза; если в неё не влезает, это два правила. ## Правило без «почему» не принимается @@ -79,7 +79,7 @@ Модальность живёт на **правиле**, а не на файле. Прежний файловый статус (`status: рекомендуемая` / `обязательная` в шапке) отменён: он неизбежно врал, потому что один файл смешивает жёсткие требования с советами. В шапке -остаются только `extends` и служебные ключи копии. +остаются только `prefix`, `extends` и служебные ключи копии. Мы **не используем SHALL и прочие английские ключевые слова**. Они заняты спецификациями (OpenSpec), и общий словарь стирал бы границу «конвенция — @@ -87,13 +87,26 @@ ## Идентификаторы -- Формат — `R<номер>`, сквозная нумерация внутри файла, начиная с `R1`. -- Строка таблицы, если на неё нужно ссылаться отдельно, — `R5.1`, `R5.2`. -- **Номера стабильны и не переиспользуются.** Удалённое правило оставляет - дыру в нумерации; занимать её новым правилом нельзя — иначе ссылка из - чужого репозитория начнёт указывать на другое утверждение. -- Глобальный адрес — путь файла плюс номер: `arch/db-identifiers.md R5`. - В пределах одного файла достаточно `R5`. +- Формат — `<ПРЕФИКС>-<номер>`: `KEYS-5`, `SLOG-27`. Префикс принадлежит + файлу, нумерация внутри файла сквозная и начинается с единицы. +- Строка таблицы, если на неё нужно ссылаться отдельно, — `KEYS-5.1`, + `KEYS-5.2`. +- **Идентификатор глобален.** Префикс уникален по всему канону, поэтому + путь файла в ссылке не нужен: `KEYS-5` адресует правило одинаково изнутри + файла, из соседней конвенции и из чужого репозитория. В собранной копии + слои разных осей лежат в одном документе, так что ссылка на базовый слой + из языкового вообще никуда не ведёт — правило рядом. +- **Идентификаторы стабильны и не переиспользуются.** Удалённое правило + оставляет дыру в нумерации; занимать её новым нельзя — иначе ссылка из + чужого репозитория начнёт указывать на другое утверждение. То же + относится к префиксам: выбывшие хранит `conventions/prefixes.toml`. +- Префикс **выбирается под файл, а не выводится по формуле**: он нужен, + чтобы по нему искать, а не чтобы его разбирать. Выводимый префикс вдобавок + привязал бы идентификатор к таксономии, которую канон перестраивает, и + упёрся бы в потолок из числа букв алфавита. +- Перенос правила в другой файл — смысловое изменение, а не переименование: + новый файл означает новый префикс и новую нумерацию. Переезд самого файла + между осями идентификаторы не трогает. Порядок правил в файле выбирается по читаемости, не по номерам: номер — это идентификатор, а не позиция. @@ -105,7 +118,7 @@ чтобы оно не выглядело недописанным, место нормы занимает отметка: ```markdown -### R6. Дефолтов времени в схеме БД нет +### MIGR-6. Дефолтов времени в схеме БД нет **МЕХАНИЗИРОВАНО.** Проверяется общим правилом линтера; формулировка удалена, потому что дублировала работающую проверку. @@ -113,7 +126,7 @@ **Почему.** Дефолт превращает забытую вставку в тихо работающий код… ``` -- Номер и заголовок сохраняются: ссылки из репозиториев продолжают +- Идентификатор и заголовок сохраняются: ссылки из репозиториев продолжают указывать на то же утверждение. - «Почему» остаётся навсегда — линтер сообщает, что нарушено, но не сообщает, зачем правило существует, и без обоснования нельзя понять, @@ -175,11 +188,11 @@ AND тик фонового цикла упал по той же причине ```markdown -R2, R4 — `internal/archrules` (проверяются в новых миграциях). +MIGR-2, MIGR-4 — `internal/archrules` (проверяются в новых миграциях). -R6 — не соблюдается в легаси-таблицах `show_history`, `queue`: составные +MIGR-6 — не соблюдается в легаси-таблицах `show_history`, `queue`: составные ключи там появились до конвенции, переписывание требует миграции данных. ``` @@ -191,12 +204,15 @@ R6 — не соблюдается в легаси-таблицах `show_histor Сейчас не реализовано; список — на будущее для `conv`: +- префикс в шапке файла совпадает с реестром, состоит из четырёх заглавных + латинских букв и не значится в списке выбывших; +- заголовки правил файла используют только его собственный префикс; - номера уникальны внутри файла и не имеют пропусков вниз (новое правило берёт следующий свободный, а не первый освободившийся); -- у каждого `### R` есть модальное слово (или отметка МЕХАНИЗИРОВАНО) и - блок «Почему»; -- ссылки вида `R` в локальных регионах копии указывают на правила, - которые в каноне ещё существуют; +- у каждого `### <ПРЕФИКС>-` есть модальное слово (или отметка + МЕХАНИЗИРОВАНО) и блок «Почему»; +- ссылки вида `<ПРЕФИКС>-` — хоть в тексте канона, хоть в локальных + регионах копии — указывают на правила, которые ещё существуют; - модальные слова не встречаются вне правил. ## Порядок перевода diff --git a/README.md b/README.md index 340f25d..e148303 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,11 @@ conventions/ stack/<стек>/ привязка к инструменту, хранилищу, транспорту ``` -Пути правил даются относительно `conventions/`: адрес -`arch/db-identifiers.md R5`, а не `conventions/arch/…` — так же, как они -лягут в `docs/conventions/` репозитория. +Пути **файлов** даются относительно `conventions/`: `arch/db-identifiers.md`, +а не `conventions/arch/…` — так же, как они лягут в `docs/conventions/` +репозитория. На **правила** ссылаются идентификатором без пути: `KEYS-5`. +Префикс уникален по всему канону (реестр — `conventions/prefixes.toml`), +поэтому идентификатор не зависит от того, на какой оси файл лежит сегодня. Тест — по тому, замена чего убивает правило: @@ -77,9 +79,23 @@ conventions/ пласт `stack/sqlite/` (типы колонок). Это не принцип, а незавершённая работа. +## Префиксы + +Каждый файл канона объявляет в шапке свой префикс правил: + +```yaml +prefix: KEYS +``` + +Четыре заглавные латинские буквы, уникальные по всему канону; реестр — +[`conventions/prefixes.toml`](conventions/prefixes.toml). Префикс выбирается +под файл, а не выводится по формуле, и не переиспользуется никогда. Правила +адресуются идентификатором `KEYS-5` — без пути к файлу. Подробности формы — +`LANGUAGE.md`. + ## Расширение -Файл в `lang/` или `stack/` может объявить в шапке: +Файл в `lang/` или `stack/` может объявить в шапке ещё и базу: ```yaml extends: arch/db-identifiers.md @@ -109,7 +125,7 @@ local: нет # или: чем и почему разошлись `origin_hash` — контент-отпечаток, а не git-SHA. Он позволяет отличать «канон обновился» от «изменено локально»; без него `status` умеет только «differs», а такой отчёт быстро перестают читать. Прочие ключи шапки -(`status`, `extends`) — часть документа: они сравниваются наравне с телом. +(`prefix`, `extends`) — часть документа: они сравниваются наравне с телом. **Локальные регионы** — куски, принадлежащие репозиторию по определению. Из сравнения исключаются, поэтому вечного шума в `diff` не дают: