Compare commits

...
11 Commits
Author SHA1 Message Date
av 59a1c23f55 язык: заведён блок ПРИМЕРЫ
- пятая, необязательная часть правила: код парой «плохо → хорошо» после
  обоснования; метка добавлена в словарь (EXAMPLES) и в строку о версии
  языка во всех тринадцати файлах
- сказано, чем примеры не являются: требований в блоке нет, дословным
  сниппетом он не служит, при расхождении с нормой правят пример
- READING.md обновлён по META-30, в машинные проверки добавлен порядок
  блоков, в читательские — что примеры норму не расширяют
2026-07-26 16:09:58 +03:00
av 1d19e0357b язык: усиления источников названы своими
- три клетки «что взято» правились по факту: DMN даёт политику совпадения,
  но не требует полноты; в 29148 обоснование — рекомендуемый атрибут; вывод
  про обязательность шаблона в EARS не сформулирован
- заведён раздел «Где источник усилен»: полнота таблиц, обязательность
  обоснования и вывод из EARS предъявлены как наши решения с доводами
- поправлены два места, где та же натяжка повторялась прозой: «Таблицы
  решений» и «Обоснование обязательно»
2026-07-26 16:03:47 +03:00
av 682fa075bb снятое правило остаётся заглушкой, нумерация сплошная
- META-31 и META-32: номера идут без пропусков, ссылка обязана разрешаться;
  обе проверки стали механическими — данных со стороны языка им хватает
- заведена метка СНЯТО: заголовок и номер снятого правила сохраняются, норму
  с обоснованием заменяет блок с датой и причиной, отдельный реестр снятых
  номеров не нужен
- META-9, META-16 и META-26 переписаны из таблицы «Освободившиеся номера» в
  заглушки; три висячие ссылки, тянувшиеся с утра, закрылись
2026-07-26 15:59:22 +03:00
av 170c06c1da язык: короткое описание для читателя копии едет в репозиторий
- заведён READING.md: словарь со значениями, форма правила и её граница,
  ссылки, локальная часть — без разделов о ведении набора и без META-ссылок,
  примеры на X-префиксах
- сборщик кладёт его в docs/conventions/ и перезаписывает целиком; в манифест
  набора добавлена секция [language] с версией и двумя документами
- META-30: правка словаря или состава частей правила доходит до READING.md,
  иначе потребитель толкует ДОПУСКАЕТСЯ по прежней версии
2026-07-26 15:51:54 +03:00
av 67d51db212 механизация больше не разрешает удалять норму
- META-9 снят: удаление оставляло подписчика, пришедшего после, без нормы и
  без проверки, а «механизировано у всех» канону не проверить — списка
  подписчиков у него нет по построению
- META-8 переписан в запрет: норма остаётся в правиле, чем бы её ни
  проверяли; линтер сообщает, что нарушено, но не что требуется (META-6)
- МЕХАНИЗИРОВАНО объявлена свойством репозитория: в тексте конвенции отметки
  нет, её место — запись о механизации ниже маркера (META-7)
2026-07-26 15:41:52 +03:00
av fe61ecd6c5 guide: язык употребляется, значит и проверяется как в конвенции
- проверки разведены по роли слова: то, что язык употребляет (конвенции и
  GUIDE.md), проверяется; то, что цитирует (LANGUAGE.md, README.md), — нет
- список машинных проверок разбит на форму правила и распространение:
  вторая группа (тема в шапке, пути канона, чужие префиксы, локальные X)
  касается только того, что едет к потребителю
- в GUIDE.md добавлена строка о версии языка и убрано заглавное СЛЕДУЕТ из
  вводной прозы «Оформления» — единственное нарушение, которое исключение
  прятало
2026-07-26 15:34:49 +03:00
av c1cb240540 темы: определение, объявление в шапке и манифест набора
- тема — набор правил об одном фокусе разработки, имя латиницей (нижний
  kebab-case рекомендуется, годится любой идентификатор, пригодный для имени
  файла); определение в LANGUAGE.md и README.md
- заведены META-28 и META-29: тема объявляется в шапке (`topic:`), стоит в
  манифесте набора и не переиспользуется; `topic:` добавлен во все 12 файлов
- prefixes.toml и topics.toml слиты в manifest.toml — манифест набора против
  манифеста подключения `.conventions.toml`, разделы topics/prefixes с live
  и retired
2026-07-26 15:27:55 +03:00
av d5118336cb язык: примеры переведены на вымышленные X-правила
- живые идентификаторы KEYS-5, SLOG-8.1, SLOG-27, MIGR-2/4/6 в примерах
  заменены на XKEY, XMIG, XLOG: номера канона означали не то, что в примере,
  и расходились дальше при каждой перенумерации
- сказано явно, что описание языка ни на один набор конвенций не опирается;
  ссылка на обоснования канона в разделе про ПОЧЕМУ заменена на внешние
  практики
2026-07-26 15:15:30 +03:00
av df8c58671f язык: объявлена граница правила
- область правила — от его заголовка до следующего заголовка любого уровня;
  метка открывает блок, хвост после ПОЧЕМУ — продолжение обоснования, а
  таблица после модальной метки — часть нормы
- проверка «заглавных модальных слов вне правил нет» стала реализуемой:
  прозой считается то, что лежит вне областей правил
- нормы, сидевшие в хвостах, подняты в блок нормы: заведены SLOG-25.4 и
  GERR-26.3, у GTIM-12 «базовый слой» заменён на TIME-12
2026-07-26 15:10:59 +03:00
av 4943bf1dd2 второе условие ДОЛЖЕН ослаблено до воспроизводимости вердикта
- META-6 переписан: ступень требует не машинной проверки, а того, чтобы
  двое проверяющих по тексту правила выносили один вердикт; проверяющий по
  умолчанию — читатель, человек или агент
- заведён META-27: машинная проверка желательна везде, где пишется, но
  ступени не задаёт — иначе канон стоит в СЛЕДУЕТ до появления скриптов;
  обоснование META-25 пересобрано на новом условии
- LANGUAGE.md и CLAUDE.md приведены к той же формулировке, вопрос 1 из
  TODO закрыт, остальные перенумерованы
2026-07-26 15:01:51 +03:00
av 7b2869d3a4 todo: заведена секция «Язык и подход» по итогам ревью
- девять находок ревью описания языка записаны вопросами 1–9: противоречие в
  условиях ДОЛЖЕН, неопределённая граница правила, примеры на живых
  идентификаторах, «тема» без реестра, ложное исключение GUIDE из проверок,
  МЕХАНИЗИРОВАНО без защиты нового подписчика, семантика слов вне копии,
  проверки без источника данных, натяжки в опоре на стандарты
- вопросы поделены на две секции: язык с подходом идёт первым, канон с
  тулингом вторым; «Мелкое» слито в вопрос 8, куда относилось по смыслу
- нумерация сплошная 1–15, внутренние ссылки переписаны под неё; в вопрос 12
  добавлена зависимость чекера от вопросов 2 и 8
2026-07-26 14:49:41 +03:00
20 changed files with 959 additions and 334 deletions
+62 -26
View File
@@ -9,8 +9,9 @@ code in this repository.
Канон конвенций разработки для личных проектов. Сами конвенции лежат в Канон конвенций разработки для личных проектов. Сами конвенции лежат в
`conventions/{arch,lang/<язык>,stack/<стек>}/`; обвязка канона (`README.md`, `conventions/{arch,lang/<язык>,stack/<стек>}/`; обвязка канона (`README.md`,
`LANGUAGE.md`, `GUIDE.md`, `prefixes.toml`, `conv`) живёт в корне и в `LANGUAGE.md`, `GUIDE.md`, `READING.md`, `manifest.toml`, `conv`) живёт в
репозитории-потребители не едет. корне. К потребителю из неё едет только `READING.md` — короткое описание языка
для читателя копий.
Ниже — короткие инварианты с идентификаторами; детали и обоснования в Ниже — короткие инварианты с идентификаторами; детали и обоснования в
`LANGUAGE.md` (форма записи) и `GUIDE.md` (процесс, префикс META). `LANGUAGE.md` (форма записи) и `GUIDE.md` (процесс, префикс META).
@@ -20,7 +21,15 @@ code in this repository.
- Четыре обязательные части: `### <ПРЕФИКС>-<N>. Заголовок`, абзац - Четыре обязательные части: `### <ПРЕФИКС>-<N>. Заголовок`, абзац
`**МОДАЛЬНОСТЬ.** норма`, абзац `**ПОЧЕМУ.** …`. Правило без обоснования не `**МОДАЛЬНОСТЬ.** норма`, абзац `**ПОЧЕМУ.** …`. Правило без обоснования не
принимается. принимается.
- `**ПРИМЕРЫ.**` — необязательный пятый блок после обоснования: код парой
«плохо → хорошо». Иллюстрация нормы, а не спецификация — требований в блоке
нет, дословным сниппетом он не является, при расхождении действует норма.
- Норма — одна фраза; если в неё не влезает, это два правила. - Норма — одна фраза; если в неё не влезает, это два правила.
- Область правила — от его заголовка до следующего заголовка любого уровня;
метка открывает блок, блок длится до следующей метки или до конца области.
Абзацы после `**ПОЧЕМУ.**` — продолжение обоснования: требований в них не
живёт, требование ставят в блок нормы. Таблица и список после модальной
метки — часть нормы.
- Шкала инвариантна, словарь — параметр языка набора. Канон русский, значит - Шкала инвариантна, словарь — параметр языка набора. Канон русский, значит
слова: **ДОЛЖЕН**, **НЕ ДОЛЖЕН**, **СЛЕДУЕТ**, **НЕ СЛЕДУЕТ**, слова: **ДОЛЖЕН**, **НЕ ДОЛЖЕН**, **СЛЕДУЕТ**, **НЕ СЛЕДУЕТ**,
**ДОПУСКАЕТСЯ**. Словарь один на канон, синонимов на ступень нет. **ДОПУСКАЕТСЯ**. Словарь один на канон, синонимов на ступень нет.
@@ -28,14 +37,20 @@ code in this repository.
- Нормативно только заглавное написание (правило RFC 8174): строчное - Нормативно только заглавное написание (правило RFC 8174): строчное
«должен» в прозе нормой не является. «должен» в прозе нормой не является.
- ДОЛЖЕН требует двух условий сразу: назван вред от нарушения (META-25) и - ДОЛЖЕН требует двух условий сразу: назван вред от нарушения (META-25) и
норма проверяема машиной (META-6). Проверяемость сама по себе до ДОЛЖЕН не вердикт о нарушении воспроизводим (META-6). Воспроизводимость сама по себе
повышает — иначе шкала наполняется проверяемыми мелочами. до ДОЛЖЕН не повышает — иначе шкала наполняется проверяемыми мелочами.
- ДОПУСКАЕТСЯ адресовано рецензенту: помеченный им выбор на ревью не - ДОПУСКАЕТСЯ адресовано рецензенту: помеченный им выбор на ревью не
обсуждается. обсуждается.
- **МЕХАНИЗИРОВАНО** — не модальность, а способ проверки: отметка стоит - **МЕХАНИЗИРОВАНО** — не модальность, а способ проверки, и свойство
рядом с модальным словом (`**ДОЛЖЕН. МЕХАНИЗИРОВАНО.**`), а не вместо него. репозитория, а не канона: в тексте конвенции отметки нет, она стоит при
- Метки правила — **ПОЧЕМУ** и **МЕХАНИЗИРОВАНО** — тоже словарь набора и записи о механизации в локальной части копии (META-7).
перечислены в строке о версии языка наравне с модальными словами. - META-8: норма не удаляется из канона никогда, чем бы её ни проверяли.
Механизация её не заменяет и не сокращает.
- Метки правила — **ПОЧЕМУ**, **ПРИМЕРЫ**, **МЕХАНИЗИРОВАНО** и **СНЯТО**
тоже словарь набора и перечислены в строке о версии языка наравне с
модальными словами.
- META-30: правка словаря или состава частей правила доходит до `READING.md`
документа, который едет к потребителю. Словари двух описаний совпадают.
- Заглавные модальные слова не употребляются вне правил: ни в «Область - Заглавные модальные слова не употребляются вне правил: ни в «Область
действия», ни в «Связано», ни в локальной части копии, ни во вводной прозе. действия», ни в «Связано», ни в локальной части копии, ни во вводной прозе.
Исключение — строка о версии языка, которая их перечисляет. Исключение — строка о версии языка, которая их перечисляет.
@@ -55,18 +70,32 @@ code in this repository.
действия. действия.
- Модальность принадлежит правилу, а не файлу: `status:` в шапке отменён. - Модальность принадлежит правилу, а не файлу: `status:` в шапке отменён.
## Идентификаторы и префиксы ## Идентификаторы: тема и префикс
- Тема — набор правил об одном фокусе разработки и единица подписки. Имя —
латиницей, рекомендуется нижний kebab-case, годится любой идентификатор,
пригодный для имени файла.
- META-28: тема объявлена в шапке (`topic: time`) и стоит в манифесте набора
(`manifest.toml`, секция `[topics.live]`). Слои одной темы несут одно имя —
по нему собираются в один файл, как бы ни назывались их файлы; имя файла
повторяет тему из удобства.
- META-29: имя темы не переиспользуется, снятое уходит в `[topics.retired]`
с причиной и датой. Оно живёт в `origin:` копий и в подписках манифестов.
- Формат `<ПРЕФИКС>-<номер>`, нумерация сквозная внутри файла. Порядок правил - Формат `<ПРЕФИКС>-<номер>`, нумерация сквозная внутри файла. Порядок правил
в файле — по читаемости: номер это идентификатор, а не позиция. в файле — по читаемости: номер это идентификатор, а не позиция.
- Идентификаторы не переиспользуются. Удалённое правило оставляет дыру, новое - Идентификаторы не переиспользуются: новое правило берёт номер, следующий за
берёт следующий свободный номер, а не первый освободившийся. наибольшим.
- META-31: нумерация в файле сплошная. Снятое правило не исчезает, а остаётся
заглушкой: заголовок с номером плюс блок `**СНЯТО <дата>.**` с причиной
вместо нормы и ПОЧЕМУ. Реестра снятых номеров нет — файл сам себе реестр.
- META-32: ссылок на несуществующие правила нет; неразрешённый идентификатор —
всегда ошибка, а не «правило, наверное, сняли».
- Новый файл конвенции — новый префикс: четыре заглавные латинские буквы, - Новый файл конвенции — новый префикс: четыре заглавные латинские буквы,
уникальные по всему канону, выбираются под файл, а не выводятся по формуле. уникальные по всему канону, выбираются под файл, а не выводятся по формуле.
Объявляется в шапке (`prefix: KEYS`) и регистрируется в `prefixes.toml`, Объявляется в шапке (`prefix: KEYS`) и регистрируется в манифесте набора,
секция `[live]`, путём от корня репозитория. секция `[prefixes.live]`, путём от корня репозитория.
- Удаление или разделение файла: префикс уходит в `[retired]` с причиной и - Удаление или разделение файла: префикс уходит в `[prefixes.retired]` с
датой, а не освобождается. причиной и датой, а не освобождается.
- Префиксы на букву `X` канон не занимает: они зарезервированы за локальными - Префиксы на букву `X` канон не занимает: они зарезервированы за локальными
правилами репозиториев-потребителей. правилами репозиториев-потребителей.
- Перенос правила в другой файл — смысловое изменение: новый префикс и новый - Перенос правила в другой файл — смысловое изменение: новый префикс и новый
@@ -93,11 +122,13 @@ code in this repository.
- META-5: расхождение кода с правилом — отступление, а не повод переписать - META-5: расхождение кода с правилом — отступление, а не повод переписать
правило. Направление всегда конвенция → код; факт «в приложении уже иначе» правило. Направление всегда конвенция → код; факт «в приложении уже иначе»
не является аргументом. не является аргументом.
- META-6: ДОЛЖЕН без механической проверки либо механизируется, либо - META-6: ДОЛЖЕН требует воспроизводимого вердикта — двое проверяющих по
понижается в СЛЕДУЕТ. Правило, непроверяемое машиной в принципе (вкус тексту правила отвечают одинаково. Правило, вердикт которого зависит от
формулировки, суждение о ситуации), — СЛЕДУЕТ по построению. суждения (вкус формулировки, уместность в конкретном месте), — СЛЕДУЕТ по
построению. META-27: машинная проверка желательна, но ступени не задаёт;
проверяющий по умолчанию — читатель правила, человек или агент.
- META-10: блок ПОЧЕМУ не удаляется никогда, в том числе после того, как - META-10: блок ПОЧЕМУ не удаляется никогда, в том числе после того, как
норма уехала в линтер. правило стало проверяться линтером.
- META-1: один файл — ровно один повторяющийся выбор. META-2: конвенция - META-1: один файл — ровно один повторяющийся выбор. META-2: конвенция
заводится, когда решение принимается третий раз. заводится, когда решение принимается третий раз.
- Репозиторного в каноне нет вовсе: механизация, отступления и ссылки на код - Репозиторного в каноне нет вовсе: механизация, отступления и ссылки на код
@@ -114,13 +145,12 @@ code in this repository.
## Оформление файла ## Оформление файла
Шапка `prefix:` (плюс `extends:`) → `# Тема` → вводная проза → отдельным Шапка `topic:` и `prefix:` (плюс `extends:`) → `# Тема` → вводная проза →
абзацем строка о версии языка (её точный текст — в `LANGUAGE.md`, раздел отдельным абзацем строка о версии языка (её точный текст — в `LANGUAGE.md`,
«Ссылка на язык из конвенции») → `## Область действия` (обязателен для раздел «Ссылка на язык из конвенции») → `## Область действия` (обязателен для
трудноизменяемых слоёв — META-11) → правила → `## Связано`, если трудноизменяемых слоёв — META-11) → правила → `## Связано`, если канонические
канонические ссылки есть (META-17; пустого раздела не заводят). Имя файла ссылки есть (META-17; пустого раздела не заводят). Имя файла повторяет имя
kebab-case по теме. Проза темы. Проза переносится по ~76 колонок; таблицы и блоки кода не переносятся.
переносится по ~76 колонок; таблицы и блоки кода не переносятся.
## Ревью формы ## Ревью формы
@@ -128,6 +158,12 @@ kebab-case по теме. Проза
проверять машиной». Ни одна из проверок не реализована, поэтому при ревью их проверять машиной». Ни одна из проверок не реализована, поэтому при ревью их
выполняют чтением. выполняют чтением.
Проверяется всё, что язык **употребляет**: конвенции и `GUIDE.md` (он несёт
правила META и строку о версии языка). `LANGUAGE.md` и `README.md` язык
цитируют — ключевые слова в них предмет описания, а не норма. Проверки
распространения (тема в шапке, самодостаточность нормы, отсутствие путей
канона) касаются только конвенций: обвязка к потребителю не едет.
## Коммиты ## Коммиты
Русский, строчная буква, без точки в конце, прошедшее время или страдательный Русский, строчная буква, без точки в конце, прошедшее время или страдательный
+150 -58
View File
@@ -12,6 +12,13 @@ prefix: META
[LANGUAGE.md](LANGUAGE.md). Здесь — про то, зачем конвенции заводятся, где [LANGUAGE.md](LANGUAGE.md). Здесь — про то, зачем конвенции заводятся, где
живут и как соотносятся с соседними видами документов. живут и как соотносятся с соседними видами документов.
Правила ниже записаны тем же языком, что и сами конвенции, и проверяются так
же.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
## Область действия ## Область действия
Правила ниже адресованы автору конвенции — тому, кто заводит новый файл или Правила ниже адресованы автору конвенции — тому, кто заводит новый файл или
@@ -32,11 +39,11 @@ prefix: META
## Оформление ## Оформление
Имя файла — kebab-case по теме: `app-directories.md`. Правилом это не Имя файла повторяет имя темы: `app-directories.md`. Правилом это не
записано, и это случай META-25: регуляркой имя проверяется тривиально, но записано, и это случай META-25: регуляркой имя проверяется тривиально, но
обоснование сводится к «чтобы имя файла в реестре префиксов писалось одним вреда от нарушения нет — тема объявлена в шапке (META-28), и сборка идёт по
способом» — вреда от нарушения нет, значит и высшей модальности нет, а на объявленному имени, а не по имени файла. Значит, и высшей модальности нет, а
СЛЕДУЕТ такое правило не окупает строчку. ступенью ниже такое правило не окупает строчку.
## Канон и копии ## Канон и копии
@@ -68,6 +75,70 @@ prefix: META
дорого: перенос правила в другой файл — это новый префикс и новая дорого: перенос правила в другой файл — это новый префикс и новая
нумерация, поэтому после разреза все внешние ссылки обходят руками. нумерация, поэтому после разреза все внешние ссылки обходят руками.
### META-28. Тема объявляется в шапке файла и стоит в манифесте набора
**ДОЛЖЕН.** Шапка несёт ключ `topic:` с именем темы, и это имя стоит в
манифесте набора.
**ПОЧЕМУ.** Тема — единица подписки и единица сборки: потребитель перечисляет
темы в своём манифесте, а сборщик складывает в один файл все слои темы. Пока
имя выводится из имени файла, у сборщика нет способа узнать, что два слоя,
названные по-разному, — один документ; переименование файла при этом молча
заводит новую тему, подписка перестаёт находить прежнюю, а ссылки «конвенция
`logging`» в копиях остаются висеть. Объявленное имя вдобавок сверяется с
манифестом — выведенное сверять не с чем.
### META-29. Имя темы не переиспользуется
**НЕ ДОЛЖЕН.** Снятое или переименованное имя темы другой теме не выдаётся:
оно уходит в раздел выбывших манифеста с причиной и датой.
**ПОЧЕМУ.** Имя темы живёт в чужих репозиториях — в шапке `origin:` каждой
копии, в подписке потребителя, в тексте ссылок. Выданное второй теме, оно
начинает указывать на другой набор правил, и обнаруживается это не на сборке,
а по содержанию: файл обновится штатно, изменится текст. Та же дисциплина и по
той же причине действует для префиксов правил.
### META-30. Правка словаря или формы правила доходит до документа для читателя
**ДОЛЖЕН.** Изменение ключевых слов, их значений или состава частей правила
вносится и в короткое описание языка, которое едет в копию.
**ПОЧЕМУ.** Полное описание языка остаётся у автора набора, а по правилам код
проверяет читатель копии — человек или агент в чужом репозитории, у которого
из двух документов есть только короткий. Разошедшись, он начинает толковать
слова по прежней версии: ДОПУСКАЕТСЯ читается как бытовое «можно», отступление
от ДОЛЖЕН перестаёт требовать записи — то есть отказывает ровно то, ради чего
слова вводились, и молча. Проверить расхождение дёшево: словари в двух
документах либо совпадают, либо нет.
### META-31. Нумерация правил в файле сплошная
**ДОЛЖЕН.** Номера идут от единицы до наибольшего без пропусков: снятое
правило остаётся на месте заглушкой с меткой СНЯТО, а не исчезает.
**ПОЧЕМУ.** Дыра в нумерации неотличима от опечатки в номере и от правила,
которое забыли дописать, — проверка, увидев пропуск, не может сказать, ошибка
это или норма, поэтому либо молчит всегда, либо краснеет на живом файле.
Заглушка отвечает на тот же вопрос текстом: номер занят, правило снято
тогда-то и по такой-то причине. Переиспользовать номер по-прежнему нельзя —
ссылка из чужого репозитория обязана указывать на то же утверждение, — но и
отдельный
реестр снятых номеров не нужен: он был бы вторым источником правды рядом с
файлом, который и так всё сказал.
### META-32. Ссылка ведёт на правило, которое существует
**НЕ ДОЛЖЕН.** Идентификатор в тексте не указывает на правило, которого в
наборе нет.
**ПОЧЕМУ.** Неразрешимая ссылка означает одно из двух: опечатку в номере или
след переноса правила в другой файл. Читатель — тем более в чужом
репозитории — не различит эти случаи и решит, что правила больше нет, хотя оно
могло переехать. С заглушками (META-31) проверка становится однозначной:
идентификатор либо ведёт к правилу, либо к объяснению, почему его сняли, а
третьего исхода нет — и любой неразрешённый идентификатор точно ошибка.
### META-2. Конвенция заводится, когда решение принимается третий раз ### META-2. Конвенция заводится, когда решение принимается третий раз
**СЛЕДУЕТ.** Поводом служит одно и то же решение, принятое третий раз и **СЛЕДУЕТ.** Поводом служит одно и то же решение, принятое третий раз и
@@ -81,9 +152,9 @@ prefix: META
### META-3. Новая конвенция пишется там, где заболело ### META-3. Новая конвенция пишется там, где заболело
**СЛЕДУЕТ.** Первые шаги пути «находка → конвенция → правило линтера **СЛЕДУЕТ.** Первые шаги пути «находка → конвенция → правило линтера»
удаление прозы» делаются в репозитории, где случилась находка; в канон делаются в репозитории, где случилась находка; в канон продвигается общая
продвигается общая часть. часть.
**ПОЧЕМУ.** Правило, написанное сразу в общем виде, не проверено ни одним **ПОЧЕМУ.** Правило, написанное сразу в общем виде, не проверено ни одним
применением, и условие применимости у него придумано, а не найдено, — применением, и условие применимости у него придумано, а не найдено, —
@@ -157,33 +228,52 @@ prefix: META
переписывает его снова. Направление «конвенция → код» держится ровно тем, переписывает его снова. Направление «конвенция → код» держится ровно тем,
что факт не считается аргументом. что факт не считается аргументом.
### META-6. `ДОЛЖЕН` без механической проверки либо механизируется, либо понижается ### META-6. Высшая модальность требует воспроизводимого вердикта
**ДОЛЖЕН.** Правило, нарушение которого объявлено ошибкой, получает **ДОЛЖЕН.** Правило со ступенью ДОЛЖЕН или НЕ ДОЛЖЕН формулируется так, что
машинную проверку или переводится в СЛЕДУЕТ. двое проверяющих по одному его тексту выносят один и тот же вердикт.
**ПОЧЕМУ.** Без проверки правило держится на внимании: нарушения копятся **ПОЧЕМУ.** Проверяют конвенцию в первую очередь агент и человек — они читают
молча и всплывают выборочно — на том ревью, куда дошли руки. Для СЛЕДУЕТ текст правила и по нему смотрят код. Проверка, стало быть, есть у каждого
это честно, а ДОЛЖЕН при этом обещает то, чего не делает, и через несколько правила с первого дня, и её инструмент — формулировка, а не скрипт. Отсюда
таких случаев обесценивает остальные ДОЛЖЕН в файле. цена невоспроизводимой нормы: вердикт зависит от того, кто читал, нарушения
всплывают выборочно, а отступление нечем записать — неизвестно, нарушено ли.
Для СЛЕДУЕТ это честно, там суждение и есть содержание правила; ДОЛЖЕН в
таком виде обещает то, чего не делает, и через несколько случаев обесценивает
остальные ДОЛЖЕН в файле.
Отсюда следствие: правило, машинная проверка которого невозможна в принципе Отсюда следствие: правило, вердикт которого зависит от суждения по построению
(вкус формулировки, выбор границы, суждение о ситуации), не может быть (вкус формулировки, выбор границы, уместность в конкретном месте), не может
ДОЛЖЕН — его модальность СЛЕДУЕТ по построению, а не по слабости. быть ДОЛЖЕН — его модальность СЛЕДУЕТ по природе нормы, а не по слабости.
### META-25. Высшая модальность выбирается, только когда назван вред ### META-25. Высшая модальность выбирается, только когда назван вред
**СЛЕДУЕТ.** Правило получает ДОЛЖЕН или НЕ ДОЛЖЕН, если в обосновании **СЛЕДУЕТ.** Правило получает ДОЛЖЕН или НЕ ДОЛЖЕН, если в обосновании
сказано, что́ ломается при нарушении. сказано, что́ ломается при нарушении.
**ПОЧЕМУ.** Машинная проверка — условие необходимое (META-6), но не **ПОЧЕМУ.** Воспроизводимость вердикта — условие необходимое (META-6), но не
достаточное: проверяемых мелочей больше, чем важных вещей, и без второго достаточное: воспроизводимо проверяемых мелочей больше, чем важных вещей, и
условия единственным фильтром остаётся удобство проверки. Шкала наполняется без второго условия единственным фильтром остаётся удобство проверки. Шкала
опрятностью, читатель перестаёт отличать «уронит прод» от «неаккуратно» — и наполняется опрятностью, читатель перестаёт отличать «уронит прод» от
обесцениваются все ДОЛЖЕН в файле, ровно то, от чего META-6 защищает с «неаккуратно» — и обесцениваются все ДОЛЖЕН в файле, ровно то, от чего META-6
другой стороны. Само это правило машиной не проверяется: «вред назван» защищает с другой стороны. Собственная ступень этого правила — СЛЕДУЕТ:
устанавливается чтением, поэтому его собственная модальность по META-6 — форма обоснования ничем не ограничена, поэтому «вред назван» вердикта не
СЛЕДУЕТ. даёт — один читатель увидит названный вред там, где другой увидит объяснение
мотива.
### META-27. Механизация правила желательна, но ступени не задаёт
**СЛЕДУЕТ.** Правило со ступенью ДОЛЖЕН получает машинную проверку, когда
такую проверку можно написать.
**ПОЧЕМУ.** Линтер не забывает, ничего не стоит на каждом прогоне и краснеет
до ревью, а не на нём: там, где проверка пишется, она дешевле самого
внимательного чтения, и путь «находка → конвенция → проверка» кончается ею.
Норму она при этом не заменяет и не отменяет (META-8). Условием ступени
механизация не является:
проверяющий по умолчанию — читатель правила (META-6), а если требовать
скрипт, весь канон стоит в СЛЕДУЕТ до того дня, когда скрипты появятся.
Ступень говорит о важности нормы, а не о состоянии инструментов.
### META-7. Факт механизации фиксируется в копии со ссылкой на правило ### META-7. Факт механизации фиксируется в копии со ссылкой на правило
@@ -196,36 +286,25 @@ prefix: META
читатель догадывается сам, к какому утверждению относится проверка, — и читатель догадывается сам, к какому утверждению относится проверка, — и
догадывается по-разному. догадывается по-разному.
### META-8. Формулировка не удаляется из канона, пока механизирована не у всех ### META-8. Норма из канона не удаляется, чем бы она ни проверялась
**НЕ ДОЛЖЕН.** Норма остаётся в каноне, пока хотя бы у одного потребителя **НЕ ДОЛЖЕН.** Формулировка нормы остаётся в правиле независимо от того, кто и
машинной проверки нет. чем её проверяет.
**ПОЧЕМУ.** У кого линтера нет, тот после удаления остаётся без правила **ПОЧЕМУ.** Проверяющий по умолчанию — читатель правила (META-6), и удаление
вообще: ни проверки, ни текста. Механизация у одного потребителя ничего не нормы забирает у него ровно то, чем он проверяет: линтер сообщает, что
говорит о прочих, поэтому удалять по факту «у нас уже проверяется» — нарушено, но не сообщает, что требуется. Условие «механизировано у всех»
значит чинить свой файл за чужой счёт. спасти не может: оно измеряется в день удаления, а подписчики появляются
после. Репозиторий, подключившийся через год, получил бы правило без нормы и
Списка подписчиков канон по построению не знает, поэтому факт «механизировано без проверки — заголовок с обоснованием и никакого способа узнать, что́ именно
у всех» устанавливается обходом репозиториев вручную — это часть работы по предписано, кроме git-истории канона, до которой он не дойдёт. Списка
удалению нормы, а не то, что можно проверить машиной (см. `LANGUAGE.md`, подписчиков у канона к тому же нет по построению, так что «у всех» ему всё
состояние МЕХАНИЗИРОВАНО). равно не проверить.
### META-9. Общая механизация разрешает удалить норму из канона
**ДОПУСКАЕТСЯ.** Правило, уехавшее в общий конфиг линтера или в общую роль,
удаляется из канона.
**ПОЧЕМУ.** Формулировка, дублирующая работающую у всех проверку,
размазывает внимание: файл на несколько сотен строк заставляет человека и
агента добросовестно вычитывать тривиальное именование и не доходить до
формы решения. Явное разрешение нужно, чтобы META-8 не читался как запрет
удалять вообще.
### META-10. Обоснование не удаляется никогда ### META-10. Обоснование не удаляется никогда
**НЕ ДОЛЖЕН.** Блок ПОЧЕМУ остаётся и после того, как норма уехала в **НЕ ДОЛЖЕН.** Блок ПОЧЕМУ остаётся и после того, как правило стало
линтер. проверяться линтером.
**ПОЧЕМУ.** Линтер сообщает, что нарушено, но не сообщает, зачем правило **ПОЧЕМУ.** Линтер сообщает, что нарушено, но не сообщает, зачем правило
существует. Без обоснования не видно, когда причина отпала, — проверка существует. Без обоснования не видно, когда причина отпала, — проверка
@@ -338,13 +417,26 @@ prefix: META
подробностями идут; перенос деталей туда же вернул бы задачу поддержки двух подробностями идут; перенос деталей туда же вернул бы задачу поддержки двух
текстов. текстов.
## Освободившиеся номера ## Снятые правила
Номер удалённого правила не переиспользуется, поэтому дыры в нумерации — Снятое правило остаётся здесь заглушкой: номер занят навсегда, ссылка на него
норма. Здесь перечислено, что́ под ними было: без этого упоминание номера в ведёт к объяснению, а нумерация в файле остаётся сплошной (META-31).
прозе не отличить от ссылки на исчезнувшее правило.
| Номер | Что было | Почему снято | ### META-9. Общая механизация разрешала удалить норму из канона
|---|---|---|
| META-16 | имя файла — kebab-case | вреда от нарушения нет (META-25); осталось прозой в «Оформлении» | **СНЯТО 2026-07-26.** Удаление нормы оставляло подписчика, пришедшего позже,
| META-26 | запрет слов обязательства в обосновании | правило о заглавных уже делает строчное слово ненормативным, а форму обоснования рамками не ограничивают | без текста и без проверки, а условие «механизировано у всех» набору не
проверить: списка подписчиков у него нет. Взамен — META-8, запрет удалять
норму вообще.
### META-16. Имя файла — kebab-case
**СНЯТО 2026-07-26.** Вреда от нарушения нет, а значит нет и высшей
модальности (META-25): сборка идёт по имени темы из шапки, а не по имени
файла. Осталось прозой в разделе «Оформление».
### META-26. Запрет слов обязательства в обосновании
**СНЯТО 2026-07-26.** Правило о заглавных уже делает строчное «обязан»
ненормативным, поэтому запрет ничего не добавлял, а форму обоснования рамками
не ограничивают.
+292 -83
View File
@@ -12,6 +12,18 @@ version: 1
пополниться, и текст, написанный по предыдущей версии, должен читаться по пополниться, и текст, написанный по предыдущей версии, должен читаться по
той, по которой написан. той, по которой написан.
Документ адресован автору набора и в репозиторий-потребитель не едет. К
читателю копии едет короткое `READING.md`: словарь со значениями, форма
правила и её граница, ссылки, локальная часть — без разделов о ведении набора.
Словарь в двух документах обязан совпадать (META-30), и это единственное
место, где между ними возможен дрейф.
Описание языка ни на один набор конвенций не опирается, поэтому все примеры
здесь — вымышленные правила с префиксами на `X` (`XKEY`, `XMIG`, `XLOG`).
Такие префиксы канон не занимает никогда, в манифесте набора их нет — значит
пример не спутать с настоящим правилом, а перенумерация конвенций описание
языка не задевает.
## Опора на стандарты ## Опора на стандарты
Язык не выводится из вкуса автора. Каждое решение о форме взято из Язык не выводится из вкуса автора. Каждое решение о форме взято из
@@ -22,9 +34,9 @@ version: 1
|---|---|---| |---|---|---|
| **BCP 14** (RFC 2119 + RFC 8174) | шкала модальности; правило «нормативно только заглавное»; сдержанность в высшей модальности; англоязычный словарь как готовый | синонимы `REQUIRED`, `RECOMMENDED`, `OPTIONAL`; `SHALL` как форма требования | | **BCP 14** (RFC 2119 + RFC 8174) | шкала модальности; правило «нормативно только заглавное»; сдержанность в высшей модальности; англоязычный словарь как готовый | синонимы `REQUIRED`, `RECOMMENDED`, `OPTIONAL`; `SHALL` как форма требования |
| **ISO/IEC Directives, Part 2** | разделение «требование — рекомендация — разрешение — возможность»; запрет модальных глаголов в утверждениях о факте | списки равнозначных словесных форм («is to», «is required to», «only … is permitted») | | **ISO/IEC Directives, Part 2** | разделение «требование — рекомендация — разрешение — возможность»; запрет модальных глаголов в утверждениях о факте | списки равнозначных словесных форм («is to», «is required to», «only … is permitted») |
| **ISO/IEC/IEEE 29148** | обоснование как обязательный атрибут; единичность нормы; проверяемость; метод верификации отдельным атрибутом | остальной аппарат требований: приоритеты, источники, матрицы трассируемости | | **ISO/IEC/IEEE 29148** | характеристики хорошего требования — единичность и проверяемость; обоснование и метод верификации как отдельные атрибуты требования | остальной аппарат требований: приоритеты, источники, матрицы трассируемости |
| **DMN** | таблица решений с объявленной политикой совпадения и требованием полноты | исполняемая семантика и всё, что предполагает движок решений | | **DMN** | таблица решений с объявленной политикой совпадения | исполняемая семантика и всё, что предполагает движок решений |
| **EARS** | вывод о том, что выигрыш даёт жёсткий шаблон, а не его конкретный вид; паттерн «нежелательное поведение» — в виде таблицы | шаблоны как форма записи правила: `WHEN`, `WHILE`, `WHERE` | | **EARS** | паттерн «нежелательное поведение» — в виде таблицы | шаблоны как форма записи правила: `WHEN`, `WHILE`, `WHERE` |
| **OpenSpec** | четырёхчастная форма правила; адресуемость правила идентификатором; форма «условие → следствие» для стыка правил | `SHALL`; `GIVEN/WHEN/THEN` как общая форма записи | | **OpenSpec** | четырёхчастная форма правила; адресуемость правила идентификатором; форма «условие → следствие» для стыка правил | `SHALL`; `GIVEN/WHEN/THEN` как общая форма записи |
Три отклонения стоят объяснения, потому что выглядят как произвол. Три отклонения стоят объяснения, потому что выглядят как произвол.
@@ -44,27 +56,50 @@ version: 1
система, и её поведение разворачивается во времени: состояние, событие, система, и её поведение разворачивается во времени: состояние, событие,
исход. У конвенции субъект — автор кода, и разворачивать нечего: есть исход. У конвенции субъект — автор кода, и разворачивать нечего: есть
ситуация выбора и вердикт. Это таблица, а не траектория. Тем же рассуждением ситуация выбора и вердикт. Это таблица, а не траектория. Тем же рассуждением
отклонены шаблоны EARS как форма записи правила, а взят из EARS другой отклонены шаблоны EARS как форма записи правила, а взято из EARS другое
результат: измеримый выигрыш дала там сама обязательность шаблона, а не его сообщённое снижение числа дефектов после введения шаблонов. Вывод, что дело в
конкретная форма. самой обязательности формы, а не в её конкретном виде, наш; он ниже, среди
усилений.
Исключение — стык правил, где субъект действительно система: там форма Исключение — стык правил, где субъект действительно система: там форма
«условие → следствие» берётся сознательно, вместе со служебными словами под «условие → следствие» берётся сознательно, вместе со служебными словами под
неё. Это единственное место, и оно описано в «Таблицах решений». неё. Это единственное место, и оно описано в «Таблицах решений».
## Где источник усилен
Три решения идут дальше источника, и это наши решения, а не его требования.
Названы они отдельно, чтобы довод не подменялся ссылкой: спорить с ними нужно
по существу, а не со стандартом.
- **Обоснование обязательно.** В 29148 rationale — из списка рекомендуемых
атрибутов требования; обязательный костяк там другой, это характеристики
самого требования. Здесь правило без блока ПОЧЕМУ не принимается, потому что
конвенция живёт годами и переживает автора: норма без причины через год либо
отменяется первым возражением, либо соблюдается там, где вредит.
- **Полнота таблицы решений.** DMN даёт политику совпадения как именованный
атрибут, а полноты не требует: индикатор полноты был в первой версии
спецификации и из последующих убран, полноту проверяют валидаторы
инструментов. Здесь она требуется, потому что таблицу и заводят ради
видимости пропуска: неперечисленный случай в прозе не виден, а пустая
клетка видна.
- **Вывод про обязательность шаблона.** В EARS сообщается о снижении числа
дефектов в требованиях после введения шаблонов. Вывод, что выигрыш даёт сама
обязательность формы, а не её конкретный вид, — наш: он объясняет, почему мы
берём из EARS результат, но не берём сами шаблоны.
## Что даёт формализация ## Что даёт формализация
Адресуемое правило — не украшение формы, а условие работы трёх механизмов: Адресуемое правило — не украшение формы, а условие работы трёх механизмов:
- **Механизация.** Запись о ней должна говорить «правило `MIGR-4` проверяет - **Механизация.** Запись о ней должна говорить «правило `XMIG-4` проверяет
`archrules`», а не «`AUTOINCREMENT` в новых миграциях — `archrules`»: во `archrules`», а не «`AUTOINCREMENT` в новых миграциях — `archrules`»: во
втором случае читатель сам догадывается, к какому утверждению это втором случае читатель сам догадывается, к какому утверждению это
относится, и догадывается по-разному. относится, и догадывается по-разному.
- **Отступления.** «У нас не так» бесполезно, пока не сказано, что именно - **Отступления.** «У нас не так» бесполезно, пока не сказано, что именно
«не так». Со ссылкой на правило отступления становятся счётными: видно, «не так». Со ссылкой на правило отступления становятся счётными: видно,
сколько правил конвенции репозиторий реально не соблюдает. сколько правил конвенции репозиторий реально не соблюдает.
- **Промоут находки.** Путь «находка → конвенция → правило линтера - **Промоут находки.** Путь «находка → конвенция → правило линтера» требует
удаление прозы» требует ручки, за которую берут конкретное правило. ручки, за которую берут конкретное правило.
Общий знаменатель — **идентификатор**. Модальные слова и таблицы полезны, Общий знаменатель — **идентификатор**. Модальные слова и таблицы полезны,
но вторичны. но вторичны.
@@ -72,7 +107,7 @@ version: 1
## Единица — правило ## Единица — правило
```markdown ```markdown
### KEYS-5. Разбор внешнего идентификатора на границе ### XKEY-5. Разбор внешнего идентификатора на границе
**ДОЛЖЕН.** Идентификатор, пришедший снаружи, проходит разбор до запроса **ДОЛЖЕН.** Идентификатор, пришедший снаружи, проходит разбор до запроса
к базе. к базе.
@@ -83,20 +118,112 @@ version: 1
``` ```
Четыре обязательные части: **идентификатор**, **заголовок**, **модальность Четыре обязательные части: **идентификатор**, **заголовок**, **модальность
с нормой**, **обоснование под меткой ПОЧЕМУ**. Норма — одна фраза; если в неё с нормой**, **обоснование под меткой ПОЧЕМУ**. Пятый блок, ПРИМЕРЫ,
не влезает, это два правила. Требование единичности взято из ISO/IEC/IEEE необязателен — о нём ниже. Норма — одна фраза; если в неё не влезает, это два
29148: составная норма не проверяема целиком, и нарушение одной её половины правила. Требование единичности взято из ISO/IEC/IEEE 29148: составная норма
нечем адресовать. не проверяема целиком, и нарушение одной её половины нечем адресовать.
Обе метки правила — модальное слово и ПОЧЕМУ — пишутся заглавными и Метки правила — модальное слово, ПОЧЕМУ, ПРИМЕРЫ — пишутся заглавными и
принадлежат словарю набора: скелет правила читается одинаково в любом языке, принадлежат словарю набора: скелет правила читается одинаково в любом языке,
на который канон переведён. на который канон переведён.
**Правило кончается перед следующим заголовком.** Область правила — от его
заголовка до следующего заголовка любого уровня. Внутри области текст
принадлежит последнему открытому блоку: метка блок открывает, и блок длится
до следующей метки или до конца области.
```markdown
### XKEY-3. Заголовок правила
**ДОЛЖЕН.** Норма одной фразой.
| № | ситуация | вердикт | ← блок нормы: таблица уточняет её
**ПОЧЕМУ.** Причина.
Продолжение причины, пример, ← блок обоснования продолжается
ссылка на внешнюю практику.
### XKEY-4. Следующее правило ← здесь область кончилась
```
Отсюда три следствия:
- **Хвост после ПОЧЕМУ — обоснование** до следующей метки или до конца
области, а не безымянная часть правила и не проза вокруг. Требований в нём
не живёт: то, что подлежит исполнению, стоит в блоке нормы, где у него есть
модальность и адрес. Требование, оставленное
в хвосте, требованием не является — сослаться на него нельзя и отступление
от него записать нельзя.
- **Таблица и список после модальной метки — часть нормы.** Правило,
классифицирующее ситуации, ровно так и записывается («Таблицы решений»), а
вердикт из такой таблицы адресуется номером строки.
- **Проза — это то, что лежит вне областей правил.** Тем самым проверка
«заглавных модальных слов вне правил нет» становится реализуемой: границу
считает разметка, а не читательское суждение о том, где правило кончилось.
Заглавное модальное слово внутри области правила законно, когда это
упоминание ступени в обосновании («для СЛЕДУЕТ это честно»). Метку от
упоминания отличает положение: метка стоит первой в своём абзаце, полужирным
и с точкой.
## Примеры к правилу
Пятый блок правила — необязательный, под меткой ПРИМЕРЫ. В нём код,
показывающий норму в деле, обычно парой «плохо → хорошо». Стоит он после
обоснования: сначала требование, потом причина, потом иллюстрация.
````markdown
### XKEY-5. Внешний идентификатор разбирается до обращения к базе
**ДОЛЖЕН.** Значение, пришедшее снаружи, проходит разбор и нормализацию
раньше, чем по нему делается запрос.
**ПОЧЕМУ.** Синтаксически невалидное значение не может соответствовать
записи, поэтому поход в базу за ним — заведомо холостой.
**ПРИМЕРЫ.**
Плохо — строка уходит в запрос как пришла:
```go
row := db.QueryRow("select … where id = ?", r.PathValue("id"))
```
Хорошо — разбор на границе, запроса при неудаче нет:
```go
id, err := ident.Parse(r.PathValue("id"))
if err != nil {
return notFound(w)
}
row := db.QueryRow("select … where id = ?", id)
```
````
**Пример иллюстрирует норму, а не задаёт её.** Три следствия, ради которых
это сказано:
- **требований в блоке нет.** Всё, что подлежит исполнению, стоит в блоке
нормы; деталь примера — имя переменной, конкретная функция, форма ответа —
требованием не становится. Разошёлся пример с нормой — действует норма, а
пример правят;
- **это не готовый сниппет.** Код в примере сокращён до того, что показывает
правило: обработка ошибок, контекст, импорты в нём условны, и копировать его
дословно не нужно;
- **пример стареет быстрее нормы.** Он привязан к сегодняшнему API, поэтому
расхождение примера с текущим кодом — повод поправить пример, а не отменять
правило.
Блок необязателен: он окупается там, где норму словами описать дороже, чем
показать, — форма вызова, структура записи в логе, раскладка файла. У правила
про выбор границы или про уровень лога иллюстрировать нечего.
## Обоснование обязательно ## Обоснование обязательно
Правило без блока ПОЧЕМУ не принимается. Это требование к форме, а не Правило без блока ПОЧЕМУ не принимается. Это требование к форме, а не
пожелание; в 29148 обоснование — атрибут требования наравне с самим пожелание. В 29148 обоснование — отдельный атрибут требования, но из
требованием, и по тем же причинам: рекомендуемых; здесь оно обязательно, и вот почему:
- **Обоснование — единственный способ увидеть, что правило устарело.** - **Обоснование — единственный способ увидеть, что правило устарело.**
Норма стареет молча; причина стареет заметно. Когда причина отпала, видно, Норма стареет молча; причина стареет заметно. Когда причина отпала, видно,
@@ -114,9 +241,9 @@ version: 1
Форма обоснования при этом ничем не ограничена: рамки здесь только Форма обоснования при этом ничем не ограничена: рамки здесь только
смысловые. Абзац может быть длинным, вести рассуждение, приводить пример, смысловые. Абзац может быть длинным, вести рассуждение, приводить пример,
ссылаться на внешние практики, стандарты и чужие проекты — канон это уже ссылаться на стандарты, внешние практики и чужие проекты — на устройство
делает («адаптация OpenTelemetry», «как по умолчанию в zap и zerolog», OpenTelemetry, на умолчания библиотек логирования, на процедуру миграции из
двенадцатишаговая процедура SQLite). Запрещённых слов и обязательной документации СУБД. Запрещённых слов и обязательной
структуры у обоснования нет, и заводить их не нужно: обязательность несёт структуры у обоснования нет, и заводить их не нужно: обязательность несёт
норма, а обоснование её объясняет — путаницу между этими двумя ролями норма, а обоснование её объясняет — путаницу между этими двумя ролями
исключает правило о заглавных. исключает правило о заглавных.
@@ -162,18 +289,19 @@ Directives, Part 2, по одной форме записи на ступень,
1. нарушение причиняет названный вред, а не расходится со вкусом — META-25, 1. нарушение причиняет названный вред, а не расходится со вкусом — META-25,
он же критерий BCP 14, где высшая модальность резервируется под то, что он же критерий BCP 14, где высшая модальность резервируется под то, что
действительно ломается, и не употребляется для навязывания метода; действительно ломается, и не употребляется для навязывания метода;
2. норма проверяема машиной — META-6, иначе обязательность держится на 2. вердикт о нарушении воспроизводим — META-6: по тексту правила двое
внимании и обещает то, чего не делает. проверяющих приходят к одному ответу, иначе обязательность держится на
том, кто читал.
Не выполнено первое — правилу место в СЛЕДУЕТ или нигде. Не выполнено Не выполнено первое — правилу место в СЛЕДУЕТ или нигде. Не выполнено
второе — в СЛЕДУЕТ. Проверяемость сама по себе не повышает правило до второе — в СЛЕДУЕТ. Воспроизводимость сама по себе не повышает правило до
ДОЛЖЕН: механически проверяемых мелочей больше, чем важных вещей, и ДОЛЖЕН: проверяемых мелочей больше, чем важных вещей, и безразборное
безразборное повышение обесценивает шкалу быстрее, чем её отсутствие. повышение обесценивает шкалу быстрее, чем её отсутствие.
Модальность живёт на **правиле**, а не на файле. Файловый статус Модальность живёт на **правиле**, а не на файле. Файловый статус
(`status: рекомендуемая` / `обязательная` в шапке) не используется: он (`status: рекомендуемая` / `обязательная` в шапке) не используется: он
неизбежно врёт, потому что один файл смешивает жёсткие требования с неизбежно врёт, потому что один файл смешивает жёсткие требования с
советами. В шапке остаются только `prefix` и `extends`. советами. В шапке остаются только `topic`, `prefix` и `extends`.
## Словарь другого языка ## Словарь другого языка
@@ -192,14 +320,18 @@ Directives, Part 2, по одной форме записи на ступень,
| рекомендация против | НЕ СЛЕДУЕТ | SHOULD NOT | | рекомендация против | НЕ СЛЕДУЕТ | SHOULD NOT |
| разрешение | ДОПУСКАЕТСЯ | MAY | | разрешение | ДОПУСКАЕТСЯ | MAY |
**Метки правила.** Обязательности не задают, а размечают его части. **Метки.** Обязательности не задают, а размечают: ПОЧЕМУ — обоснование,
Стандартом не даются ни в одном языке: в BCP 14 таких понятий нет, слова ПРИМЕРЫ — иллюстрации к норме, МЕХАНИЗИРОВАНО — запись о проверке в копии,
подбираются под язык так же, как остальные. СНЯТО — заглушку на месте убранного правила. Стандартом не даются ни в одном
языке: в BCP 14 таких понятий нет, слова подбираются под язык так же, как
остальные.
| Метка | Русский | Английский | | Метка | Русский | Английский |
|---|---|---| |---|---|---|
| обоснование | ПОЧЕМУ | WHY | | обоснование | ПОЧЕМУ | WHY |
| иллюстрации | ПРИМЕРЫ | EXAMPLES |
| способ проверки | МЕХАНИЗИРОВАНО | MECHANIZED | | способ проверки | МЕХАНИЗИРОВАНО | MECHANIZED |
| снятое правило | СНЯТО | RETIRED |
**Служебные слова сценарного блока** — `КОГДА`, `ТОГДА`, `И`, `ИЛИ`; таблица **Служебные слова сценарного блока** — `КОГДА`, `ТОГДА`, `И`, `ИЛИ`; таблица
и объяснение в разделе «Таблицы решений». и объяснение в разделе «Таблицы решений».
@@ -226,15 +358,15 @@ Directives, Part 2, по одной форме записи на ступень,
Каждая конвенция называет язык одной строкой во вводной прозе: Каждая конвенция называет язык одной строкой во вводной прозе:
> Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки > Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
> ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — > ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
> тогда и только тогда, когда написаны заглавными. > конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
Слова в строке — из словаря того языка, на котором написан набор. Для Слова в строке — из словаря того языка, на котором написан набор. Для
англоязычного набора та же строка выглядит так: англоязычного набора та же строка выглядит так:
> The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY and the marks WHY > The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY and the marks WHY,
> and MECHANIZED are to be interpreted as described in the conventions > EXAMPLES, MECHANIZED and RETIRED are to be interpreted as described in the
> language, version 1, and only when written in capitals. > conventions language, version 1, and only when written in capitals.
Форма скопирована у BCP 14, где та же задача решается тем же способом: Форма скопирована у BCP 14, где та же задача решается тем же способом:
спецификация не прикладывает к себе словарь и не указывает путь к нему, а спецификация не прикладывает к себе словарь и не указывает путь к нему, а
@@ -249,40 +381,55 @@ Directives, Part 2, по одной форме записи на ступень,
правило обязательно, а о том, чем эта обязательность обеспечена. В 29148 это правило обязательно, а о том, чем эта обязательность обеспечена. В 29148 это
два разных атрибута требования, и здесь тоже два. два разных атрибута требования, и здесь тоже два.
Когда правило механизировано у всех потребителей, его норма из канона **Проверяющий по умолчанию — читатель правила**, человек или агент. Канон
удаляется, а модальность и обоснование остаются: пишется прежде всего под агента: он читает конвенцию и по ней смотрит код,
то есть проверка есть у каждого правила с первого дня, и её инструмент —
формулировка нормы. Поэтому вторым условием ДОЛЖЕН стоит воспроизводимость
вердикта (META-6), а не наличие скрипта: ступень говорит о важности нормы и о
том, сколько внимания она получает при проверке, а не о состоянии
инструментов. Линтер сильнее чтения — он не забывает, ничего не стоит на
каждом прогоне и краснеет до ревью, — поэтому механизация желательна везде,
где проверка пишется (META-27), и остаётся концом пути «находка → конвенция →
проверка». Но обязательным условием высшей ступени она не является: иначе весь
канон стоял бы в СЛЕДУЕТ до появления скриптов, которых пока нет ни одного.
**Механизация нормы не заменяет и не сокращает.** Норма остаётся в правиле
навсегда — как и обоснование (META-8, META-10), — сколько бы проверок её ни
подпирало. Причин три:
- **линтер сообщает, что нарушено, но не сообщает, что требуется.** Без нормы
правило нечем исполнить и не с чем сверить вердикт проверки, а проверяющий
по умолчанию читает именно норму;
- **подписчики появляются позже.** Репозиторий, подключившийся через год,
получил бы правило без нормы и без линтера — ни текста, ни проверки;
- **«механизировано у всех» набору не проверить:** списка подписчиков у него
нет по построению.
**Отметка — свойство репозитория, а не набора.** Механизирована норма или нет,
зависит от того, чей это репозиторий, поэтому в тексте конвенции отметки нет:
её место — запись о механизации в локальной части копии, со ссылкой на
идентификатор правила (META-7).
```markdown ```markdown
### MIGR-6. Дефолтов времени в схеме БД нет <!-- conv:local -->
**ДОЛЖЕН. МЕХАНИЗИРОВАНО.** Проверяется общим правилом линтера; XMIG-2, XMIG-4 — МЕХАНИЗИРОВАНО: `internal/archrules`, в новых миграциях.
формулировка удалена, потому что дублировала работающую проверку.
**ПОЧЕМУ.** Дефолт превращает забытую вставку в тихо работающий код…
``` ```
- Идентификатор и заголовок сохраняются: ссылки из репозиториев продолжают Так у правила остаются оба атрибута сразу: обязательность — в норме, которая
указывать на то же утверждение. приезжает из набора и одинакова у всех, способ проверки — в записи, которая
- Модальность сохраняется, поэтому «все ли ДОЛЖЕН механизированы» принадлежит репозиторию и у каждого своя.
остаётся вычислимым вопросом, а не предметом чтения всего канона.
- Обоснование остаётся навсегда — линтер сообщает, что нарушено, но не
сообщает, зачем правило существует, и без обоснования нельзя понять,
когда проверку пора отменять.
Факт «механизировано у всех» устанавливается вручную: канон по построению
не знает списка подписчиков, и обойти репозитории перед удалением нормы —
часть работы, а не то, что можно проверить автоматически.
## Таблицы решений ## Таблицы решений
Часть правил **классифицирует ситуации**: какой уровень лога, какая Часть правил **классифицирует ситуации**: какой уровень лога, какая
категория директории, что делать с невалидным вводом в зависимости от его категория директории, что делать с невалидным вводом в зависимости от его
источника. Каноническая форма для них — таблица «ситуация → вердикт», строки источника. Каноническая форма для них — таблица «ситуация → вердикт», строки
которой нумеруются как подпункты правила (`SLOG-8.1`). которой нумеруются как подпункты правила (`XLOG-8.1`).
Таблица плотнее прозы и не даёт пропустить ветку: пустая клетка видна, а Таблица плотнее прозы и не даёт пропустить ветку: пустая клетка видна, а
неупомянутый случай в абзаце — нет. Два свойства такой таблицы взяты из DMN, неупомянутый случай в абзаце — нет. От такой таблицы требуются два свойства —
где они называются и проверяются: первое названо в DMN, второе мы добавили сами («Где источник усилен»):
- **Политика совпадения.** По умолчанию строки взаимоисключающи: любой - **Политика совпадения.** По умолчанию строки взаимоисключающи: любой
ситуации соответствует ровно одна. Если это не так, таблица объявляет ситуации соответствует ровно одна. Если это не так, таблица объявляет
@@ -326,19 +473,30 @@ Directives, Part 2, по одной форме записи на ступень,
## Идентификаторы ## Идентификаторы
- Формат — `<ПРЕФИКС>-<номер>`: `KEYS-5`, `SLOG-27`. Префикс принадлежит Идентификаторов в языке два: **правило** адресуется префиксом с номером,
**конвенция целиком** — именем темы. Ссылаться путём к файлу нельзя ни на то,
ни на другое.
**Правило.**
- Формат — `<ПРЕФИКС>-<номер>`: `XKEY-5`, `XLOG-27`. Префикс принадлежит
файлу, нумерация внутри файла сквозная и начинается с единицы. файлу, нумерация внутри файла сквозная и начинается с единицы.
- Строка таблицы, если на неё нужно ссылаться отдельно, — `KEYS-5.1`, - Строка таблицы, если на неё нужно ссылаться отдельно, — `XKEY-5.1`,
`KEYS-5.2`. `XKEY-5.2`.
- **Идентификатор глобален.** Префикс уникален по всему канону, поэтому - **Идентификатор глобален.** Префикс уникален по всему канону, поэтому
путь файла в ссылке не нужен: `KEYS-5` адресует правило одинаково изнутри путь файла в ссылке не нужен: `XKEY-5` адресует правило одинаково изнутри
файла, из соседней конвенции и из чужого репозитория. В собранной копии файла, из соседней конвенции и из чужого репозитория. В собранной копии
слои разных осей лежат в одном документе, так что ссылка на базовый слой слои разных осей лежат в одном документе, так что ссылка на базовый слой
из языкового вообще никуда не ведёт — правило рядом. из языкового вообще никуда не ведёт — правило рядом.
- **Идентификаторы стабильны и не переиспользуются.** Удалённое правило - **Идентификаторы стабильны и не переиспользуются.** Занять номер снятого
оставляет дыру в нумерации; занимать её новым нельзя — иначе ссылка из правила новым нельзя — иначе ссылка из чужого репозитория начнёт указывать
чужого репозитория начнёт указывать на другое утверждение. То же на другое утверждение. То же относится к префиксам: выбывшие хранит манифест
относится к префиксам: выбывшие хранит `prefixes.toml`. набора.
- **Снятое правило остаётся заглушкой.** Заголовок и номер сохраняются, норму
с обоснованием заменяет блок СНЯТО с датой и причиной. Поэтому нумерация в
файле сплошная, а любая ссылка разрешается — либо в правило, либо в
объяснение, почему его сняли (META-31, META-32). Отдельного реестра снятых
номеров нет: он был бы вторым источником правды рядом с файлом.
- Префикс **выбирается под файл, а не выводится по формуле**: он нужен, - Префикс **выбирается под файл, а не выводится по формуле**: он нужен,
чтобы по нему искать, а не чтобы его разбирать. Выводимый префикс вдобавок чтобы по нему искать, а не чтобы его разбирать. Выводимый префикс вдобавок
привязал бы идентификатор к таксономии, которую канон перестраивает, и привязал бы идентификатор к таксономии, которую канон перестраивает, и
@@ -352,6 +510,25 @@ Directives, Part 2, по одной форме записи на ступень,
Порядок правил в файле выбирается по читаемости, не по номерам: номер — это Порядок правил в файле выбирается по читаемости, не по номерам: номер — это
идентификатор, а не позиция. идентификатор, а не позиция.
**Тема.**
- **Тема — набор правил об одном фокусе разработки:** время, конфигурация,
схема БД. Она же — единица подписки: потребитель берёт тему целиком, а не
отдельные правила.
- Имя темы записывается латиницей. Рекомендуется нижний kebab-case
(`db-identifiers`), но годится любой идентификатор, пригодный для имени
файла: имя попадает и в файловую систему потребителя, и в его манифест.
- **Тему объявляет файл, а не файловая система.** Имя стоит в шапке
(`topic:`) и зарегистрировано в манифесте набора. Слои одной темы несут
одно и то же имя — по нему они и собираются в один документ, как бы ни
назывались их файлы.
- **Имя темы не переиспользуется** — как и префикс: оно живёт в шапке
`origin:` каждой копии, в подписке манифеста и в тексте ссылок, поэтому
снятое имя уходит в раздел выбывших манифеста, а не достаётся другой теме.
- Ссылка на конвенцию — имя темы (конвенция `logging`), ссылка на правило —
идентификатор (`XLOG-27`). Путь файла не употребляется ни там, ни там: в
собранной копии путей канона не существует.
## Что правилом не является ## Что правилом не является
Заглавные модальные слова в этих частях **не употребляются** — иначе Заглавные модальные слова в этих частях **не употребляются** — иначе
@@ -364,6 +541,10 @@ Directives, Part 2, по одной форме записи на ступень,
- **Локальная часть копии** — содержимое принадлежит репозиторию. - **Локальная часть копии** — содержимое принадлежит репозиторию.
- Вводная проза, объясняющая предмет конвенции. - Вводная проза, объясняющая предмет конвенции.
Все четыре части лежат вне областей правил: до первого заголовка правила или
после заголовка, которым область закрылась. Хвост обоснования сюда не
относится — он внутри правила, и модальные слова в нём законны как упоминания.
## Как на правила ссылаются копии ## Как на правила ссылаются копии
Ниже маркера локальной части, в репозитории: Ниже маркера локальной части, в репозитории:
@@ -371,10 +552,9 @@ Directives, Part 2, по одной форме записи на ступень,
```markdown ```markdown
<!-- conv:local --> <!-- conv:local -->
MIGR-2, MIGR-4 механизированы — `internal/archrules` (проверяются в новых XMIG-2, XMIG-4 — МЕХАНИЗИРОВАНО: `internal/archrules`, в новых миграциях.
миграциях).
MIGR-6 не соблюдается в легаси-таблицах `show_history`, `queue`: составные XMIG-6 не соблюдается в легаси-таблицах `show_history`, `queue`: составные
ключи там появились до конвенции, переписывание требует миграции данных. ключи там появились до конвенции, переписывание требует миграции данных.
``` ```
@@ -383,38 +563,67 @@ MIGR-6 не соблюдается в легаси-таблицах `show_histor
## Что стоит проверять машиной ## Что стоит проверять машиной
Проверки применяются к файлам конвенций; обвязка канона в них не входит — Проверке подлежит всё, что язык **употребляет**: файлы конвенций и документ,
она ключевые слова цитирует, а не употребляет. Ни одна пока не реализована, которым канон сам себя ведёт (в этом каноне — `GUIDE.md`, префикс META). Файл,
поэтому при ревью их выполняют чтением. который язык **цитирует**, — это описание вроде текущего: ключевые слова стоят
в нём как предмет разговора, а не как норма, и проверки к нему не применяются.
Различает не расположение файла, а роль слова в нём.
Разбором текста: Ни одна проверка пока не реализована, поэтому при ревью их выполняют чтением.
**Форма правила** — разбором текста, в любом файле, который язык употребляет:
- модальные и служебные слова принадлежат объявленному словарю канона, а не - модальные и служебные слова принадлежат объявленному словарю канона, а не
смеси словарей; смеси словарей;
- префикс в шапке файла совпадает с реестром, состоит из четырёх заглавных - префикс в шапке файла совпадает с манифестом набора, состоит из четырёх
латинских букв, не начинается на `X` и не значится в списке выбывших; заглавных латинских букв, не начинается на `X` и не значится в списке
выбывших;
- заголовки правил файла используют только его собственный префикс; - заголовки правил файла используют только его собственный префикс;
- номера уникальны внутри файла и не имеют пропусков вниз (новое правило - нумерация внутри файла сплошная: от единицы до наибольшего номера без
берёт следующий свободный, а не первый освободившийся); пропусков, номера не повторяются, новое правило берёт следующий за
- у каждого `### <ПРЕФИКС>-<n>` есть модальное слово и блок ПОЧЕМУ; наибольшим (META-31);
отметка МЕХАНИЗИРОВАНО стоит рядом с модальностью, а не вместо неё; - у каждого `### <ПРЕФИКС>-<n>` есть либо модальное слово с нормой и блок
ПОЧЕМУ — ни норма, ни обоснование не удаляются никогда (META-8, META-10), —
либо блок СНЯТО с датой и причиной;
- блок ПРИМЕРЫ, если он есть, стоит после обоснования и не открывает правило:
порядок блоков — норма, ПОЧЕМУ, ПРИМЕРЫ;
- вводная проза содержит строку о версии языка; - вводная проза содержит строку о версии языка;
- ссылки вида `<ПРЕФИКС>-<n>` — хоть в тексте канона, хоть в локальной части - ссылки вида `<ПРЕФИКС>-<n>` — хоть в тексте набора, хоть в локальной части
копии — указывают на правила, которые ещё существуют; копии — разрешаются: неразрешённый идентификатор всегда ошибка (META-32);
- заглавные модальные слова не встречаются вне областей правил (область —
от заголовка правила до следующего заголовка) — кроме строки о версии
языка, которая их перечисляет по назначению;
- модальная метка стоит первой в своём абзаце: заглавное слово в середине
фразы — упоминание ступени, а не вторая норма правила.
**Распространение** — разбором текста, только в файлах конвенций: эти проверки
о том, что документ уезжает к потребителю, а документ, которым канон ведёт
себя, не уезжает никуда.
- шапка файла несёт имя темы, и это имя стоит в манифесте набора — среди
живых, а не среди выбывших;
- имя темы, названное в ссылке или в подписке потребителя, тоже разрешается по
манифесту: ссылка на снятую тему не проходит молча;
- префиксы локальных правил копии начинаются на `X`; - префиксы локальных правил копии начинаются на `X`;
- заглавные модальные слова не встречаются вне правил — кроме строки о - отметки МЕХАНИЗИРОВАНО в тексте конвенции нет: её место — запись о
версии языка, которая их перечисляет по назначению; механизации в локальной части копии (META-7);
- словарь в коротком описании языка совпадает с этим: те же ступени, те же
метки, те же значения (META-30);
- префикс **чужой темы** не встречается в абзаце с модальностью (META-20); - префикс **чужой темы** не встречается в абзаце с модальностью (META-20);
префикс арх-слоя своей темы там допустим (META-24), префикс другого языка префикс арх-слоя своей темы там допустим (META-24), префикс другого языка
или стека — нет; или стека — нет;
- путь файла канона не встречается в тексте конвенции (META-21). - путь файла канона не встречается в тексте конвенции (META-21).
Чтением, потому что машине не даётся: **Чтением**, потому что машине не даётся:
- строки таблицы взаимоисключающи либо политика совпадения объявлена; - строки таблицы взаимоисключающи либо политика совпадения объявлена;
- перечисленные в таблице случаи покрывают область действия; - перечисленные в таблице случаи покрывают область действия;
- норма исполнима без обращения к другим файлам; - норма исполнима без обращения к другим файлам (в файлах конвенций: они
- обоснование отвечает на «что сломается», а не пересказывает норму. уезжают по одной, а обвязка ссылается на соседей свободно);
- обоснование отвечает на «что сломается», а не пересказывает норму;
- хвост обоснования не вводит требований, которых нет в блоке нормы;
- примеры иллюстрируют норму и не расширяют её: деталь примера не читается
как требование.
## Версия языка ## Версия языка
+134
View File
@@ -0,0 +1,134 @@
---
version: 1
---
# Как читать конвенцию
Файлы рядом с этим — конвенции: правила о том, как в этом репозитории пишут
код. Здесь сказано, как они записаны: что означают заглавные слова, из чего
состоит правило и как на него сослаться. Читается один раз, дальше нужен как
справка.
Файл кладёт сюда сборщик конвенций и перезаписывает целиком при каждом
обновлении. Правки в нём не живут.
## Ключевые слова
Заглавное слово в начале абзаца задаёт обязательность правила.
| Слово | Что означает | Если делаем иначе |
|---|---|---|
| **ДОЛЖЕН** | нарушение считается ошибкой | только с записью в отступления |
| **НЕ ДОЛЖЕН** | запрет, та же строгость | то же |
| **СЛЕДУЕТ** | сильная рекомендация: новый код пишем так | допустимо, причину записываем |
| **НЕ СЛЕДУЕТ** | обратное к СЛЕДУЕТ | то же |
| **ДОПУСКАЕТСЯ** | выбор за автором кода | ничего не требуется — правило не запрещает |
Две вещи, которые легко прочитать неверно:
- **ДОПУСКАЕТСЯ — не бытовое «можно».** У слова есть вторая половина: выбор,
помеченный им, на ревью не обсуждается. Возражение «сделай иначе» против
такого выбора не принимается — иначе разрешение ничего не значило бы.
- **Отступление от ДОЛЖЕН — не запрет на отступление.** Нарушать можно, но
тогда об этом появляется запись: какое правило, где именно, почему. Разница
между ДОЛЖЕН и СЛЕДУЕТ — в том, чем платят за отклонение, а не в том,
возможно ли оно.
Ещё четыре метки заглавными: **ПОЧЕМУ** открывает обоснование правила,
**ПРИМЕРЫ** — код, показывающий норму в деле, **МЕХАНИЗИРОВАНО** стоит при
записи о том, что правило проверяет линтер или скрипт, **СНЯТО** — на месте
правила, которое убрали.
Заглушка со СНЯТО занимает место убранного правила вместе с его номером:
так нумерация остаётся сплошной, а ссылка на снятое правило приводит к
объяснению, а не в пустоту. Требований в такой заглушке нет.
```markdown
### XLOG-4. Уровень записи выбирался по громкости отказа
**СНЯТО 2026-05-14.** Заменено на XLOG-8: громкость каждый оценивал
по-своему, и шкала расползалась.
```
**Нормативно только заглавное написание.** Строчное «должен» в прозе — обычная
речь, а не норма; спорить с ней как с правилом не нужно.
## Из чего состоит правило
Правило в примере вымышленное: префиксы на `X` общий набор не занимает
никогда, поэтому пример нельзя спутать с настоящим правилом.
```markdown
### XLOG-8. Уровень выбирается по адресату
**ДОЛЖЕН.** Уровень отвечает на вопрос «кому сообщение», а не «насколько
громко сломалось».
| № | Уровень | Кому и когда |
|---|---|---|
| XLOG-8.1 | `DEBUG` | разработчику при отладке; в проде выключен |
| XLOG-8.2 | `INFO` | владельцу, аудит постфактум |
**ПОЧЕМУ.** Адресат — единственный признак, по которому разные авторы в
разных местах кода выберут уровень одинаково…
```
- **Идентификатор и заголовок.** `XLOG-8` — адрес правила: по нему на правило
ссылаются, им помечают отступления и механизацию.
- **Модальность с нормой.** Собственно требование, одной фразой. Таблица или
список сразу за модальным словом — часть нормы: она уточняет вердикт, и на
её строку ссылаются номером (`XLOG-8.2`).
- **ПОЧЕМУ.** Зачем правило существует и что сломается, если сделать иначе.
Обоснование ничего не требует — по нему решают, применимо ли правило к
случаю, и видно, когда причина отпала.
- **ПРИМЕРЫ** — необязательный последний блок: код, обычно парой «плохо →
хорошо». Иллюстрация, а не спецификация: деталь примера требованием не
становится, дословно копировать его не нужно, а если пример разошёлся с
нормой — действует норма.
**Правило кончается перед следующим заголовком.** Абзацы после ПОЧЕМУ — это
продолжение обоснования: примеры, разбор границ, ссылки на внешние практики.
Требований в них нет; всё, что подлежит исполнению, стоит в блоке нормы.
## Как ссылаться
- На **правило** — идентификатором: `XLOG-27`. Путь к файлу не нужен,
идентификатор уникален.
- На **конвенцию целиком** — именем темы: конвенция `logging`. Имя темы стоит
в шапке файла (`origin:`).
- Строка таблицы адресуется номером с точкой: `XLOG-8.2`.
## Что ниже маркера
```markdown
<!-- conv:local -->
```
Всё выше маркера приезжает из общего набора и перезаписывается при
обновлении. Всё ниже принадлежит этому репозиторию и обновление переживает.
Там живёт:
- **отступления** — какое правило не соблюдается, где и почему: «`XMIG-6` не
соблюдается в `queue`: составные ключи там появились до конвенции»;
- **механизация** — кто проверяет правило машинно: «`XMIG-4`
МЕХАНИЗИРОВАНО: `internal/archrules`»;
- **разрешение условий**, которые правило оставило открытыми;
- **свои правила** — по той же форме, но с префиксом на `X` (`XLOG-1`).
Префиксы на `X` общий набор не занимает никогда, так что столкнуться они не
могут.
Правка выше маркера живёт до первого обновления и исчезает молча. Если
исправить нужно приехавший текст — либо правку переносят в общий набор, либо
файл перестаёт быть копией: из шапки убирают `origin:`.
## Чего в конвенции не бывает
- **Утверждений о том, как сейчас устроен этот репозиторий.** Правило пишется
в предписывающем времени; «у нас пока не так» — это отступление, и его
место ниже маркера.
- **Заглавных ключевых слов вне правил.** Во вводной прозе, в разделах
«Область действия» и «Связано» их нет, поэтому искать там требования не
нужно.
Язык записи — версия 1. Полное описание живёт в наборе конвенций, у автора;
здесь ровно то, что нужно читателю.
+88 -23
View File
@@ -12,13 +12,15 @@
| `README.md` | устройство канона, оси, сборка копий, жизненный цикл | | `README.md` | устройство канона, оси, сборка копий, жизненный цикл |
| [LANGUAGE.md](LANGUAGE.md) | язык записи правил: идентификаторы, модальность, обоснование | | [LANGUAGE.md](LANGUAGE.md) | язык записи правил: идентификаторы, модальность, обоснование |
| [GUIDE.md](GUIDE.md) | как ведут конвенции: когда заводить, механизация, отступления | | [GUIDE.md](GUIDE.md) | как ведут конвенции: когда заводить, механизация, отступления |
| `prefixes.toml` | реестр префиксов правил | | [READING.md](READING.md) | как читать конвенцию: то, что едет к потребителю |
| [manifest.toml](manifest.toml) | манифест набора: язык, темы, префиксы правил |
| `conv` | сборка копий | | `conv` | сборка копий |
Обвязка живёт только в каноне и в репозитории не оказывается — в копию едет К потребителю едет содержимое `conventions/` и один файл обвязки —
лишь содержимое `conventions/`. Самодостаточность копии это не нарушает: `READING.md`; остальная обвязка остаётся в каноне. Самодостаточность копии это
конвенция называет язык записи одной строкой с номером версии и не ссылается не нарушает: конвенция называет язык записи одной
на путь (`LANGUAGE.md`, раздел «Ссылка на язык из конвенции»). строкой с номером версии и не ссылается на путь (`LANGUAGE.md`, раздел «Ссылка
на язык из конвенции»).
Правило то же, что у ролей: **деплоится и читается только то, что лежит в Правило то же, что у ролей: **деплоится и читается только то, что лежит в
git репозитория**. Канон никем не подключается на лету. git репозитория**. Канон никем не подключается на лету.
@@ -55,8 +57,9 @@ conventions/
тему. Пути файлов даются относительно `conventions/` тему. Пути файлов даются относительно `conventions/`
(`arch/db-identifiers.md`) и адресуют исходник канона, а не место в копии. (`arch/db-identifiers.md`) и адресуют исходник канона, а не место в копии.
На **правила** ссылаются идентификатором без пути: `KEYS-5`. Префикс На **правила** ссылаются идентификатором без пути: `KEYS-5`. Префикс
уникален по всему канону (реестр — `prefixes.toml`), поэтому идентификатор уникален по всему канону (он перечислен в манифесте набора), поэтому
не зависит ни от оси, ни от того, как собран файл у потребителя. идентификатор не зависит ни от оси, ни от того, как собран файл у
потребителя.
Тест — по тому, замена чего убивает правило: Тест — по тому, замена чего убивает правило:
@@ -81,6 +84,31 @@ conventions/
пласт `stack/sqlite/` (типы колонок). Это не принцип, а незавершённая пласт `stack/sqlite/` (типы колонок). Это не принцип, а незавершённая
работа. работа.
## Темы
**Тема — набор правил об одном фокусе разработки:** время, конфигурация,
схема БД. Она же единица подписки и единица сборки: потребитель берёт тему
целиком, а сборщик складывает в один файл все её слои.
Имя темы записывается латиницей; рекомендуется нижний kebab-case, но годится
любой идентификатор, пригодный для имени файла — имя попадает и в файловую
систему потребителя, и в его манифест. Файл конвенции объявляет тему в шапке:
```yaml
topic: db-identifiers
prefix: KEYS
```
Слои одной темы несут одно и то же имя — по нему они и собираются в один
документ, как бы ни назывались их файлы. Имя файла повторяет тему из
удобства, но истина — в шапке.
Темы перечислены в манифесте набора — [`manifest.toml`](manifest.toml),
секция `[topics.live]`: имя и однострочное описание. Имя темы не
переиспользуется по той же причине, что и префикс: оно живёт в чужих
репозиториях — в шапке `origin:` каждой копии, в подписке манифеста, в тексте
ссылок, — и выданное второй теме начинает указывать на другой набор правил.
## Префиксы ## Префиксы
Каждый файл канона объявляет в шапке свой префикс правил: Каждый файл канона объявляет в шапке свой префикс правил:
@@ -89,10 +117,11 @@ conventions/
prefix: KEYS prefix: KEYS
``` ```
Четыре заглавные латинские буквы, уникальные по всему канону; реестр — Четыре заглавные латинские буквы, уникальные по всему канону; перечислены в
[`prefixes.toml`](prefixes.toml). Префикс выбирается под файл, а не выводится манифесте набора, секция `[prefixes.live]`. Префикс выбирается под файл, а не
по формуле, и не переиспользуется никогда. Правила адресуются идентификатором выводится по формуле, и не переиспользуется никогда. Правила адресуются
`KEYS-5` — без пути к файлу. Подробности формы — `LANGUAGE.md`. идентификатором `KEYS-5` — без пути к файлу. Подробности формы —
`LANGUAGE.md`.
Буква `X` в начале префикса зарезервирована за репозиториями: канон её не Буква `X` в начале префикса зарезервирована за репозиториями: канон её не
занимает никогда, а локальные правила потребителя берут префиксы только на занимает никогда, а локальные правила потребителя берут префиксы только на
@@ -124,6 +153,7 @@ extends: arch/db-identifiers.md
``` ```
docs/conventions/ docs/conventions/
README.md собственный, не собирается README.md собственный, не собирается
READING.md как читать конвенцию — приезжает из канона
time.md arch/time.md + lang/go/time.md time.md arch/time.md + lang/go/time.md
db-identifiers.md arch/db-identifiers.md + lang/go/db-identifiers.md db-identifiers.md arch/db-identifiers.md + lang/go/db-identifiers.md
app-directories.md arch/… + stack/ansible/… app-directories.md arch/… + stack/ansible/…
@@ -132,6 +162,10 @@ docs/conventions/
Так конвенция остаётся самодостаточным документом: тот, кто проверяет по ней Так конвенция остаётся самодостаточным документом: тот, кто проверяет по ней
код — человек или агент, — читает один файл и не собирает тему из трёх мест. код — человек или агент, — читает один файл и не собирает тему из трёх мест.
Имена в директории делятся на три вида: `README.md` принадлежит репозиторию и
сборщик его не трогает, `READING.md` принадлежит канону и перезаписывается
целиком, остальные файлы — копии тем с шапкой `origin:` и локальной частью.
**Шапка копии** ставится при сборке и в каноне не хранится: **Шапка копии** ставится при сборке и в каноне не хранится:
```yaml ```yaml
@@ -140,10 +174,11 @@ origin: time
--- ---
``` ```
Больше в шапке ничего нет. Отпечатка канона и даты синхронизации в ней не В `origin:` стоит имя темы — то же, что в манифесте набора и в шапках
хранится: обновление перезаписывает файл в рабочем дереве, и что именно `topic:` слоёв, из которых файл собран. Больше в шапке ничего нет: отпечатка
изменилось, показывает `git diff` до коммита. Второй механизм сравнения канона и даты синхронизации в ней не хранится, потому что обновление
рядом с git не нужен. перезаписывает файл в рабочем дереве, и что именно изменилось, показывает
`git diff` до коммита. Второй механизм сравнения рядом с git не нужен.
**Маркер локальной части** — единственная машинно значимая разметка внутри **Маркер локальной части** — единственная машинно значимая разметка внутри
файла: файла:
@@ -151,7 +186,7 @@ origin: time
```markdown ```markdown
<!-- conv:local --> <!-- conv:local -->
MIGR-2, MIGR-4 механизированы — `internal/archrules`. MIGR-2, MIGR-4 — МЕХАНИЗИРОВАНО: `internal/archrules`.
MIGR-6 не соблюдается в `show_history`, `queue`: составные ключи там MIGR-6 не соблюдается в `show_history`, `queue`: составные ключи там
появились до конвенции, миграция данных не окупается. появились до конвенции, миграция данных не окупается.
``` ```
@@ -170,10 +205,36 @@ MIGR-6 не соблюдается в `show_history`, `queue`: составны
репозитория. Файл, оставивший шапку, при следующем обновлении потеряет репозитория. Файл, оставивший шапку, при следующем обновлении потеряет
всё, что выше маркера. всё, что выше маркера.
## Манифест ## Язык записи едет вместе с копиями
Откуда взяты копии и где брать обновления — `.conventions.toml` в корне Конвенция называет язык одной строкой с номером версии и без пути — строка
репозитория-потребителя: работает и сама по себе. Но семантика заглавных слов живёт в описании языка, а
описание в репозиторий-потребитель раньше не попадало: агент, читающий копию,
принимал ДОПУСКАЕТСЯ за бытовое «можно» и терял ровно то, ради чего слово
введено.
Поэтому в `docs/conventions/` сборщик кладёт `READING.md` — короткое описание
для читателя правил: словарь со значениями, правило заглавных, из чего состоит
правило и где его граница, как ссылаться, что живёт ниже маркера. Полное
[LANGUAGE.md](LANGUAGE.md) остаётся в каноне: три его раздела адресованы
автору набора и ссылаются на правила `GUIDE.md`, которых у потребителя нет.
Два документа — один словарь, и это единственное место, где возможен дрейф.
Правка ключевых слов или состава частей правила обязана дойти до `READING.md`
(META-30), а сверить их дёшево: таблицы либо совпадают, либо нет.
## Два манифеста
Манифестов в модели два, и они отвечают на разные вопросы:
| Файл | Где лежит | Что описывает |
|---|---|---|
| `manifest.toml` | в наборе | сам набор: темы и префиксы правил |
| `.conventions.toml` | в проекте | подключение: откуда копии, какие темы, язык, стек |
Манифест набора — единственное место, где перечислены оба идентификатора
канона; правила у них общие, поэтому и файл один. Манифест подключения
отвечает, откуда взяты копии и где брать обновления:
```toml ```toml
source = "ssh://git@git.vakhrushev.me:2222/av/dev-conventions.git" source = "ssh://git@git.vakhrushev.me:2222/av/dev-conventions.git"
@@ -185,8 +246,10 @@ topics = ["time", "config", "db-identifiers"]
``` ```
`lang` и `stack` выбирают строку разреженной матрицы: файл темы собирает `lang` и `stack` выбирают строку разреженной матрицы: файл темы собирает
только те слои, которые репозиторию подходят. `topics` — подписка; списка только те слои, которые репозиторию подходят. `topics` — подписка, именами из
подписчиков у канона по-прежнему нет, список тем есть только у потребителя. манифеста набора; списка подписчиков у канона по-прежнему нет, список подписок
есть
только у потребителя.
Как именно инструмент добирается до канона — путь на диске, git, HTTP — Как именно инструмент добирается до канона — путь на диске, git, HTTP —
дело инструмента, а не модели. Манифест отвечает на два вопроса: откуда это дело инструмента, а не модели. Манифест отвечает на два вопроса: откуда это
@@ -213,6 +276,7 @@ topics = ["time", "config", "db-identifiers"]
conv list # какие темы есть в каноне conv list # какие темы есть в каноне
conv add time # добавить тему в манифест и собрать файл conv add time # добавить тему в манифест и собрать файл
conv pull # пересобрать всё, что перечислено в манифесте conv pull # пересобрать всё, что перечислено в манифесте
# (и обновить READING.md рядом с копиями)
``` ```
Отчёт о том, что изменилось, отдельной командой не выдаётся: после `pull` Отчёт о том, что изменилось, отдельной командой не выдаётся: после `pull`
@@ -250,6 +314,7 @@ conv pull # пересобрать всё, что переч
Модель выше — согласованная, а не реализованная. `conv` пока собран под Модель выше — согласованная, а не реализованная. `conv` пока собран под
прежнюю: зеркальное дерево копий вместо плоского, именованные регионы прежнюю: зеркальное дерево копий вместо плоского, именованные регионы
`<!-- local:имя -->` вместо одного маркера, `origin_hash` в шапке и команды `<!-- local:имя -->` вместо одного маркера, `origin_hash` в шапке и команды
`status`, `diff`, `push`. Сами конвенции уже приведены к новой модели — `status`, `diff`, `push`; `READING.md` рядом с копиями он тоже пока не
именованных регионов в каноне нет. Ни один репозиторий-потребитель не кладёт. Сами конвенции уже приведены к новой модели — именованных регионов в
каноне нет. Ни один репозиторий-потребитель не
подключён, поэтому переход никого не ломает. подключён, поэтому переход никого не ломает.
+65 -58
View File
@@ -4,23 +4,67 @@
решения. Закрытый вопрос отсюда удаляется — принятое решение живёт в решения. Закрытый вопрос отсюда удаляется — принятое решение живёт в
`README.md`, `GUIDE.md` или `LANGUAGE.md`, а не в этом файле. `README.md`, `GUIDE.md` или `LANGUAGE.md`, а не в этом файле.
## 1. Тулинг: две разные задачи в одном `conv` Две секции: сначала язык и подход, потом канон с тулингом.
# Язык и подход
## 1. Одиннадцать таблиц не прочитаны на взаимоисключительность
`LANGUAGE.md` объявил, что строки таблицы решений взаимоисключающи по
умолчанию, а иной порядок объявляется явно. Объявление не делает таблицы
такими: нумерованные строки есть в одиннадцати файлах канона, и ни одна
таблица под новое требование не прочитана.
Конкретный подозреваемый — SLOG-11: «по реальному действию или изменению»
против «повторяющаяся служебная, по таймеру или поллингу». Периодическая
операция, которая всё-таки меняет данные, подходит под обе строки, и уровень
из таблицы не выводится однозначно.
Работа читательская, машине не даётся; в список проверок она уже записана в
разделе «Чтением, потому что машине не даётся».
## 2. Описание языка отдельно от набора конвенций
`LANGUAGE.md` и `GUIDE.md` описывают, **как** пишутся конвенции;
`conventions/`**один конкретный** набор. Сейчас они склеены в одном
репозитории, и из одного описания нельзя собрать второй набор (рабочий,
доменный, чужой).
Срочности нет: версия языка объявлена в самом `LANGUAGE.md` (ключ
`version:`), и конвенции ссылаются на неё номером, а не путём, — то есть
самодостаточность копии выноса не требует. Примеры в `LANGUAGE.md` вдобавок
переведены на вымышленные `X`-правила, так что на конкретный набор описание
языка больше не ссылается вовсе.
Что осталось поводом:
- из одного описания по-прежнему нельзя собрать второй набор;
- тулинг валидирует правила, зашитые в его код, а не объявленную версию
языка.
Оба повода включаются, только когда появится второй набор. Цена — ещё одна
сущность и ещё одна синхронизация; при одном наборе лечение выходит хуже
болезни.
# Канон, тулинг, подключение
## 3. Тулинг: две разные задачи в одном `conv`
Сейчас в `conv` смешаны две категории работы, и они расходятся по всему — Сейчас в `conv` смешаны две категории работы, и они расходятся по всему —
по частоте запуска, по тому, кто запускает, и по тому, что считается по частоте запуска, по тому, кто запускает, и по тому, что считается
провалом. провалом.
**Целостность канона.** Префиксы уникальны и не переиспользованы, шапка **Целостность канона.** Префиксы уникальны и не переиспользованы, шапка
совпадает с реестром, номера без дыр вниз, у каждого правила модальность и совпадает с манифестом, у каждого правила модальность и блок ПОЧЕМУ, ссылки
«Почему», ссылки разрешаются, префикс чужой темы не лезет в норму (META-20), разрешаются, префикс чужой темы не лезет в норму (META-20), путей канона в
путей канона в тексте нет (META-21), строка о версии языка на месте. тексте нет (META-21), строка о версии языка на месте. Запускается в каноне,
Запускается в каноне, при каждой правке, провал — это ошибка. Логика уже при каждой правке, провал — это ошибка. Логика уже написана и много раз
написана и много раз прогнана руками, но живёт в скретчпаде, а не в прогнана руками, но живёт в скретчпаде, а не в репозитории.
репозитории.
**Установка в проект.** Манифест `.conventions.toml`, сборка файла темы из **Установка в проект.** Манифест `.conventions.toml`, сборка файла темы из
слоёв (`arch` → язык → стек), сохранение при пересборке всего, что ниже слоёв (`arch` → язык → стек), сохранение при пересборке всего, что ниже
маркера, предупреждение о висячих ссылках на неподписанные темы. Запускается маркера, `READING.md` рядом с копиями, предупреждение о висячих ссылках на
неподписанные темы. Запускается
в репозитории-потребителе, изредка, провал — это чаще «посмотри глазами», в репозитории-потребителе, изредка, провал — это чаще «посмотри глазами»,
чем «ошибка». Отчёта «канон ушёл вперёд» здесь нет: его делает `git diff` чем «ошибка». Отчёта «канон ушёл вперёд» здесь нет: его делает `git diff`
после пересборки. после пересборки.
@@ -36,19 +80,25 @@
ссылках: это установка, а не целостность, но список подписок ему нужен из ссылках: это установка, а не целостность, но список подписок ему нужен из
манифеста. манифеста.
Список проверок теперь реализуем целиком: граница правила определена,
нумерация сплошная, снятое правило остаётся заглушкой — данных со стороны
языка проверке хватает.
Перед тем как переписывать, стоит посмотреть на два готовых прототипа: Перед тем как переписывать, стоит посмотреть на два готовых прототипа:
дистрибуцию пакетов Vale (`.vale.ini``vale sync``styles/`) как образец дистрибуцию пакетов Vale (`.vale.ini``vale sync``styles/`) как образец
манифеста и `vendir.yml` — как пример того, где проходит граница между «чего манифеста и `vendir.yml` — как пример того, где проходит граница между «чего
хочу» и «что получил». хочу» и «что получил».
## 2. Пары слоёв и темы без базы ## 4. Пары слоёв и темы без базы
Отложено сознательно, но список стоит держать перед глазами: Отложено сознательно, но список стоит держать перед глазами:
- `SLOG` объявляет `extends: arch/time.md` — расширение **чужой** темы. - `SLOG` объявляет `extends: arch/time.md` — расширение **чужой** темы.
Сборщик темы `logging` на это наткнётся: базового слоя с темой `logging` Сборщик темы `logging` на это наткнётся: базового слоя с темой `logging`
нет, а `arch/time.md` он тянуть не должен. Чинится переводом в обычную нет, а `arch/time.md` он тянуть не должен. Чинится переводом в обычную
ссылку «связано». ссылку «связано». Вдобавок это ломает гарантию META-24: она верна только
для базы своей темы, а `arch/time.md` объявляет тему `time`, не `logging`.
С объявленной темой расхождение стало проверяемым машинно.
- Темы без арх-слоя: `db-schema`, `errors`, `web-ui`. Собираются в файл с - Темы без арх-слоя: `db-schema`, `errors`, `web-ui`. Собираются в файл с
одной секцией — само по себе не ломается, но это и есть тот невыделенный одной секцией — само по себе не ломается, но это и есть тот невыделенный
арх-слой из известного долга. арх-слой из известного долга.
@@ -60,7 +110,7 @@
- Вынос арх-ядра из `errors` и `logging` закроет две хрупкие ссылки из - Вынос арх-ядра из `errors` и `logging` закроет две хрупкие ссылки из
`web-ui` (стек) в go-слой — единственные ссылки стек → язык в каноне. `web-ui` (стек) в go-слой — единственные ссылки стек → язык в каноне.
## 3. Подключение к репозиториям ## 5. Подключение к репозиториям
Ничего ещё не подключено. Кандидаты — jellybit и pet-project-server. Ничего ещё не подключено. Кандидаты — jellybit и pet-project-server.
Понадобится: заполнить локальную часть копий тем, что сейчас в этих Понадобится: заполнить локальную часть копий тем, что сейчас в этих
@@ -69,28 +119,7 @@
строка в `AGENTS.md` каждого потребителя про то, что файлы в строка в `AGENTS.md` каждого потребителя про то, что файлы в
`docs/conventions/` — копии. `docs/conventions/` — копии.
## 4. Описание языка отдельно от набора конвенций ## 6. Тулинг на Go, живущий независимо
`LANGUAGE.md` и `GUIDE.md` описывают, **как** пишутся конвенции;
`conventions/`**один конкретный** набор. Сейчас они склеены в одном
репозитории, и из одного описания нельзя собрать второй набор (рабочий,
доменный, чужой).
Срочности нет: версия языка объявлена в самом `LANGUAGE.md` (ключ
`version:`), и конвенции ссылаются на неё номером, а не путём, — то есть
самодостаточность копии выноса не требует.
Что осталось поводом:
- из одного описания по-прежнему нельзя собрать второй набор;
- тулинг валидирует правила, зашитые в его код, а не объявленную версию
языка.
Оба повода включаются, только когда появится второй набор. Цена — ещё одна
сущность и ещё одна синхронизация; при одном наборе лечение выходит хуже
болезни.
## 5. Тулинг на Go, живущий независимо
Сейчас `conv` — питоновский скрипт внутри канона, то есть тулинг и данные в Сейчас `conv` — питоновский скрипт внутри канона, то есть тулинг и данные в
одном репозитории и правятся одним движением. Мысль: вынести в отдельный одном репозитории и правятся одним движением. Мысль: вынести в отдельный
@@ -99,32 +128,10 @@ Go-бинарь со своим релизным циклом, ставить ч
Что за этим стоит помимо вкуса: независимый бинарь физически не даёт править Что за этим стоит помимо вкуса: независимый бинарь физически не даёт править
инструмент «заодно» с правкой конвенции, работает против **любого** канона и инструмент «заодно» с правкой конвенции, работает против **любого** канона и
любого потребителя — что прямо требуется вопросом 4, — и снимает питон из любого потребителя — что прямо требуется вопросом 2, — и снимает питон из
зависимостей репозиториев-потребителей. зависимостей репозиториев-потребителей.
Порядок обратный ожидаемому: пока вопрос 4 не сделан, инструмент всё равно Порядок обратный ожидаемому: пока вопрос 2 не сделан, инструмент всё равно
работает против одного конкретного канона, и независимый релизный цикл ему работает против одного конкретного канона, и независимый релизный цикл ему
нечего обслуживать. Сначала 4, потом 5. Разделение из вопроса 1 при этом нечего обслуживать. Сначала 2, потом 6. Разделение из вопроса 3 при этом
дешевле заложить сразу, чем отпиливать потом. дешевле заложить сразу, чем отпиливать потом.
## 6. Одиннадцать таблиц не прочитаны на взаимоисключительность
`LANGUAGE.md` объявил, что строки таблицы решений взаимоисключающи по
умолчанию, а иной порядок объявляется явно. Объявление не делает таблицы
такими: нумерованные строки есть в одиннадцати файлах канона, и ни одна
таблица под новое требование не прочитана.
Конкретный подозреваемый — SLOG-11: «по реальному действию или изменению»
против «повторяющаяся служебная, по таймеру или поллингу». Периодическая
операция, которая всё-таки меняет данные, подходит под обе строки, и уровень
из таблицы не выводится однозначно.
Работа читательская, машине не даётся; в список проверок она уже записана в
разделе «Чтением, потому что машине не даётся».
## Мелкое, не закрыто
- `conv check` и отличие ссылки на удалённое правило от упоминания дыры:
теперь освободившиеся номера перечислены в `GUIDE.md`, раздел
«Освободившиеся номера», — проверке остаётся читать этот список, а не
угадывать. Реализации по-прежнему нет.
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: app-directories
prefix: DIRS prefix: DIRS
--- ---
@@ -11,8 +12,8 @@ prefix: DIRS
механически выводится состав бэкапа. механически выводится состав бэкапа.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
## Область действия ## Область действия
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: config
prefix: CONF prefix: CONF
--- ---
@@ -8,8 +9,8 @@ prefix: CONF
секретами и когда падает. секретами и когда падает.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
## Область действия ## Область действия
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: db-identifiers
prefix: KEYS prefix: KEYS
--- ---
@@ -7,8 +8,8 @@ prefix: KEYS
Как выбираются и как выглядят первичные ключи сущностей. Как выбираются и как выглядят первичные ключи сущностей.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
## Область действия ## Область действия
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: time
prefix: TIME prefix: TIME
--- ---
@@ -8,8 +9,8 @@ prefix: TIME
берётся значение и где появляется не-UTC. берётся значение и где появляется не-UTC.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
## Область действия ## Область действия
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: config
prefix: GCFG prefix: GCFG
extends: arch/config.md extends: arch/config.md
--- ---
@@ -9,8 +10,8 @@ extends: arch/config.md
запрета на окружение. запрета на окружение.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
Секретов Go-специфика не касается: они приходят из деплоя уже в файле, а Секретов Go-специфика не касается: они приходят из деплоя уже в файле, а
проверка их непустоты идёт вместе с остальной валидацией — как описано в проверка их непустоты идёт вместе с остальной валидацией — как описано в
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: db-identifiers
prefix: GKEY prefix: GKEY
extends: arch/db-identifiers.md extends: arch/db-identifiers.md
--- ---
@@ -8,8 +9,8 @@ extends: arch/db-identifiers.md
Как базовый слой выглядит в Go-приложении. Как базовый слой выглядит в Go-приложении.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
Единая точка из `KEYS-3` — пакет `internal/ident`: он Единая точка из `KEYS-3` — пакет `internal/ident`: он
порождает идентификаторы (`NewID`, `NewIDAt`) и он же их разбирает порождает идентификаторы (`NewID`, `NewIDAt`) и он же их разбирает
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: db-schema
prefix: MIGR prefix: MIGR
--- ---
@@ -8,8 +9,8 @@ prefix: MIGR
Go-приложении. Go-приложении.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
## Область действия ## Область действия
+14 -10
View File
@@ -1,4 +1,5 @@
--- ---
topic: errors
prefix: GERR prefix: GERR
--- ---
@@ -9,8 +10,8 @@ prefix: GERR
границе). границе).
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
Две границы, о которых говорят правила ниже: Две границы, о которых говорят правила ниже:
@@ -344,8 +345,9 @@ HTTP-клиентов, файловой системы, внешних SDK.
| № | Где перехвачена паника | Что дальше | | № | Где перехвачена паника | Что дальше |
|---|---|---| |---|---|---|
| GERR-26.1 | обработчик HTTP-запроса | ответ 500, если он ещё не начат; процесс и прочие запросы не затрагиваются | | GERR-26.1 | обработчик HTTP-запроса, паника любая, кроме сигнала намеренного прерывания | ответ 500, если он ещё не начат; процесс и прочие запросы не затрагиваются |
| GERR-26.2 | итерация цикла обработки — бот, воркер | цикл продолжается со следующего элемента, упавший элемент повторно не берётся | | GERR-26.2 | итерация цикла обработки — бот, воркер | цикл продолжается со следующего элемента, упавший элемент повторно не берётся |
| GERR-26.3 | обработчик HTTP-запроса, паника — сигнал намеренного прерывания (`http.ErrAbortHandler`) | значение пробрасывается дальше, ответ не подменяется |
**ПОЧЕМУ.** Паника внутри обработки одного элемента почти всегда говорит о **ПОЧЕМУ.** Паника внутри обработки одного элемента почти всегда говорит о
баге в работе с данными этого элемента, а не о порче общего состояния, — баге в работе с данными этого элемента, а не о порче общего состояния, —
@@ -356,10 +358,11 @@ HTTP-клиентов, файловой системы, внешних SDK.
обязательным (GERR-22): неперехваченная паника в любой горутине завершает весь обязательным (GERR-22): неперехваченная паника в любой горутине завершает весь
процесс. процесс.
Продолжать, не исключив упавший элемент, нельзя: детерминированная паника Исключение упавшего элемента в GERR-26.2 — не осторожность, а условие
даёт бесконечный цикл — тот же элемент, тот же стек, залитый лог и нулевой прогресса: детерминированная паника даёт бесконечный цикл — тот же элемент,
прогресс. Это классический poison message, и лекарство берём то же, что тот же стек, залитый лог и нулевой прогресс. Это классический poison message,
принято в очередях: элемент выводится из оборота, а не берётся снова. У и лекарство здесь то же, что принято в очередях: элемент выводится из
оборота, а не берётся снова. У
цикла, который и так подтверждает прогресс — сдвигает офсет, помечает цикла, который и так подтверждает прогресс — сдвигает офсет, помечает
строку состоянием, — механизм для этого уже есть, заводить отдельный не строку состоянием, — механизм для этого уже есть, заводить отдельный не
нужно. нужно.
@@ -369,9 +372,10 @@ HTTP-клиентов, файловой системы, внешних SDK.
клиент получит обрывок с кодом 200. Отсюда же общее предпочтение собирать клиент получит обрывок с кодом 200. Отсюда же общее предпочтение собирать
ответ целиком до записи там, где это возможно. ответ целиком до записи там, где это возможно.
Из GERR-26.1 есть одно исключение: `http.ErrAbortHandler`сигнал «прервать Отдельная строка GERR-26.3 нужна потому, что `http.ErrAbortHandler`не
обработку намеренно», и recover-обёртка пробрасывает его дальше, а не отказ, а сигнал «прервать обработку намеренно»: подмена его на 500 превратила
превращает в 500. Так поступают и стандартные обёртки вроде chi. бы штатный разрыв в ложную ошибку в логе и в метриках. Так поступают и
стандартные обёртки вроде chi.
### GERR-24. Независимые ошибки собираются `errors.Join` ### GERR-24. Независимые ошибки собираются `errors.Join`
+9 -5
View File
@@ -1,4 +1,5 @@
--- ---
topic: logging
prefix: SLOG prefix: SLOG
extends: arch/time.md extends: arch/time.md
--- ---
@@ -10,8 +11,8 @@ extends: arch/time.md
функциональности, живут в спеках. функциональности, живут в спеках.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
Лог читают инструментами, а не глазами: повседневно — `jq` Лог читают инструментами, а не глазами: повседневно — `jq`
(`jq 'select(.download_id=="a1b2")' app.jsonl`), тяжёлое (агрегации, JOIN) — (`jq 'select(.download_id=="a1b2")' app.jsonl`), тяжёлое (агрегации, JOIN) —
@@ -337,6 +338,7 @@ ctx = logctx.With(ctx, log) // достаём логгер из ctx в кажд
| SLOG-25.1 | штатный конфликт состояния или некорректный ввод | пользователю, он уже получил ответ | `DEBUG` | | SLOG-25.1 | штатный конфликт состояния или некорректный ввод | пользователю, он уже получил ответ | `DEBUG` |
| SLOG-25.2 | расхождение производного или учётного состояния, первичные данные целы | владельцу, «может стать проблемой» | `WARN` | | SLOG-25.2 | расхождение производного или учётного состояния, первичные данные целы | владельцу, «может стать проблемой» | `WARN` |
| SLOG-25.3 | сбой БД, ФС, недоступность зависимости | владельцу, в разбор | `ERROR` | | SLOG-25.3 | сбой БД, ФС, недоступность зависимости | владельцу, в разбор | `ERROR` |
| SLOG-25.4 | класса нет: отказ в классификацию не заведён | владельцу, как пропуск в классификации | `ERROR` с отметкой о непокрытом классе |
**ПОЧЕМУ.** Это применение SLOG-8 к отказам: пользователь уже увидел причину на **ПОЧЕМУ.** Это применение SLOG-8 к отказам: пользователь уже увидел причину на
экране — владельцу разбирать нечего; целостность первичных данных отделяет экране — владельцу разбирать нечего; целостность первичных данных отделяет
@@ -349,9 +351,11 @@ ctx = logctx.With(ctx, log) // достаём логгер из ctx в кажд
таблицу не входит: это не доменный отказ, и логирует его recover-граница таблицу не входит: это не доменный отказ, и логирует его recover-граница
вместе со стеком (конвенция `errors`). Искать его класс здесь не нужно. вместе со стеком (конвенция `errors`). Искать его класс здесь не нужно.
Мимо таблицы идёт и доменная ошибка, которой нет в маппинге: класса у неё Строка SLOG-25.4 говорит не о классе отказа, а о пропуске в самой
нет, потому что её просто забыли завести. Она логируется `ERROR` с классификации: ошибку забыли завести в маппинге. `ERROR` здесь — громкость,
признаком непокрытой (`GERR-25`). по которой пропуск находят фильтром, а не оценка тяжести отказа; саму отметку
о непокрытом классе ставит трансляция ошибки (`GERR-25` в конвенции
`errors`).
### SLOG-26. Тот же отказ в асинхронной стадии — уровнем выше ### SLOG-26. Тот же отказ в асинхронной стадии — уровнем выше
+5 -4
View File
@@ -1,4 +1,5 @@
--- ---
topic: time
prefix: GTIM prefix: GTIM
extends: arch/time.md extends: arch/time.md
--- ---
@@ -9,8 +10,8 @@ extends: arch/time.md
в каком виде время попадает в базу и в логи, что делать с зонами. в каком виде время попадает в базу и в логи, что делать с зонами.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
## Правила ## Правила
@@ -180,8 +181,8 @@ func utcTime(_ []string, a slog.Attr) slog.Attr {
текущего значения настройки: смена зоны задним числом сдвигает границы текущего значения настройки: смена зоны задним числом сдвигает границы
суток у того, что давно посчитано и сохранено. суток у того, что давно посчитано и сохранено.
Календарные вычисления бизнес-логики берут зону явно — как описано в Явную зону в календарных вычислениях требует TIME-12 — это его правило, а не
базовом слое. второе такое же здесь.
## Связано ## Связано
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: app-directories
prefix: ANSD prefix: ANSD
extends: arch/app-directories.md extends: arch/app-directories.md
--- ---
@@ -8,8 +9,8 @@ extends: arch/app-directories.md
Как категории из базового слоя раскладываются на сервере плейбуком. Как категории из базового слоя раскладываются на сервере плейбуком.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
## Область действия ## Область действия
+3 -2
View File
@@ -1,4 +1,5 @@
--- ---
topic: web-ui
prefix: HTMX prefix: HTMX
--- ---
@@ -9,8 +10,8 @@ prefix: HTMX
какие действия поддерживает — в спеках, не здесь. какие действия поддерживает — в спеках, не здесь.
Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
ПОЧЕМУ и МЕХАНИЗИРОВАНО толкуются как описано в языке конвенций версии 1 — ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
тогда и только тогда, когда написаны заглавными. конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
Логирование запросов — конвенция `logging` (HTTP-поля, рутинно-частое на Логирование запросов — конвенция `logging` (HTTP-поля, рутинно-частое на
`DEBUG`). Трансляция доменных ошибок наружу — конвенция `errors` `DEBUG`). Трансляция доменных ошибок наружу — конвенция `errors`
+113
View File
@@ -0,0 +1,113 @@
# Манифест набора конвенций.
#
# Манифестов в модели два, и они отвечают на разные вопросы:
#
# - этот, в наборе, описывает сам набор: какие в нём темы и какие префиксы
# правил заняты;
# - `.conventions.toml` в репозитории-потребителе описывает подключение:
# откуда взяты копии, какие темы выбраны, какие язык и стек.
#
# Оба идентификатора набора — тема и префикс — живут здесь, потому что
# правила у них общие: объявляются в шапке файла, сверяются с манифестом,
# не переиспользуются никогда, а снятые уходят в свой раздел `retired`
# вместе с причиной и датой.
# ─── Язык записи ────────────────────────────────────────────────────────────
#
# Набор объявляет версию языка, на котором записаны его правила, и два
# документа о нём. Полное описание остаётся у автора; в копию рядом с
# конвенциями едет короткое `READING.md` — то, что нужно читателю правил, без
# ссылок на правила ведения набора.
[language]
version = 1
description = "LANGUAGE.md"
reading = "READING.md"
# ─── Темы ───────────────────────────────────────────────────────────────────
#
# Тема — набор правил об одном фокусе разработки: время, конфигурация, схема
# БД. Имя записывается латиницей; рекомендуется нижний kebab-case, но годится
# любой идентификатор, пригодный для имени файла.
#
# Тема — единица подписки и единица сборки: потребитель перечисляет темы в
# своём манифесте, а сборщик складывает в один файл все слои темы в порядке
# arch → язык → стек. Слои узнают друг друга по объявленному имени, а не по
# имени файла: файл конвенции несёт тему в шапке (`topic: time`).
#
# Имя темы не переименовывается и не переиспользуется: на тему ссылаются
# словом — из текста конвенций («конвенция `logging`»), из подписки в
# манифесте потребителя, из шапки `origin:` каждой копии, — и такая ссылка
# обязана продолжать указывать на тот же набор правил. Тема живёт, пока в
# `conventions/` есть хотя бы один её слой.
#
# Описание — одна строка о том, про что тема: из него собирается таблица в
# README директории конвенций у потребителя (META-18).
[topics.live]
app-directories = "категории директорий приложения и что в каждой лежит"
config = "конфигурация: файл, валидация, секреты"
db-identifiers = "идентификаторы сущностей: вид ключа, генерация, границы"
db-schema = "схема БД и миграции: типы колонок, форма изменения"
errors = "ошибки: обёртки, границы трансляции, паники"
logging = "логирование: уровни, структура записи, что не логируем"
time = "время: хранение, зоны, форматы, календарные границы"
web-ui = "веб-UI: партиалы, свопы, поллинг"
[topics.retired]
# Пусто. Сюда попадают имена снятых и переименованных тем вместе с причиной
# и датой, чтобы их нельзя было выдать другой теме.
# ─── Префиксы правил ────────────────────────────────────────────────────────
#
# Префикс — четыре заглавные латинские буквы, уникальные по всему набору. Он
# выбирается под файл, а не выводится по формуле: префикс нужен, чтобы по нему
# искать, а не чтобы его разбирать. Поэтому подходящее слово лучше
# закономерности.
#
# Правила:
#
# - префикс не переименовывается и не переиспользуется никогда — ссылка
# из чужого репозитория обязана продолжать указывать на то же место;
# - при удалении или разделении файла префикс уходит в retired, а не
# освобождается;
# - переезд файла между осями префикс не меняет: идентификатор правила
# не зависит от таксономии;
# - вынос части правил в новый файл — это новый префикс и новая нумерация:
# перенос правила между документами есть смысловое изменение, а не
# переименование;
# - тот же префикс продублирован в шапке файла (`prefix:`), conv сверяет.
#
# Префикс принадлежит файлу, тема — набору файлов: у слоёв одной темы
# префиксы разные, а имя темы одно.
#
# Пути даются от корня репозитория, а не от `conventions/`: манифест покрывает
# и обвязку тоже.
#
# Буква `X` в начале префикса зарезервирована за репозиториями-потребителями:
# набор её не занимает никогда, локальные правила берут префиксы только на
# неё (XTIM, XLOG). Согласовывать их с манифестом не нужно — столкновение
# невозможно по построению.
[prefixes.live]
DIRS = "conventions/arch/app-directories.md"
CONF = "conventions/arch/config.md"
KEYS = "conventions/arch/db-identifiers.md"
TIME = "conventions/arch/time.md"
GCFG = "conventions/lang/go/config.md"
GKEY = "conventions/lang/go/db-identifiers.md"
MIGR = "conventions/lang/go/db-schema.md"
GERR = "conventions/lang/go/errors.md"
SLOG = "conventions/lang/go/logging.md"
GTIM = "conventions/lang/go/time.md"
ANSD = "conventions/stack/ansible/app-directories.md"
HTMX = "conventions/stack/htmx/web-ui.md"
# Документ, которым канон ведёт себя сам: к потребителю не едет, но правила в
# нём записаны тем же языком, цитируются по номерам и проверяются как
# конвенция — отсюда префикс. Темы у него нет: подписаться на него нельзя.
META = "GUIDE.md"
[prefixes.retired]
# Пусто. Сюда попадают префиксы удалённых и разделённых файлов вместе с
# причиной и датой, чтобы их нельзя было выдать повторно.
-49
View File
@@ -1,49 +0,0 @@
# Реестр префиксов правил.
#
# Префикс — четыре заглавные латинские буквы, уникальные по всему канону.
# Он выбирается под файл, а не выводится по формуле: префикс нужен, чтобы
# по нему искать, а не чтобы его разбирать. Поэтому подходящее слово лучше
# закономерности.
#
# Правила реестра:
#
# - префикс не переименовывается и не переиспользуется никогда — ссылка
# из чужого репозитория обязана продолжать указывать на то же место;
# - при удалении или разделении файла префикс уходит в [retired], а не
# освобождается;
# - переезд файла между осями префикс не меняет: идентификатор правила
# не зависит от таксономии;
# - вынос части правил в новый файл — это новый префикс и новая
# нумерация: перенос правила между документами есть смысловое
# изменение, а не переименование;
# - тот же префикс продублирован в шапке файла (`prefix:`), conv сверяет.
#
# Пути даются от корня репозитория, а не от `conventions/`: реестр покрывает
# и обвязку тоже.
#
# Буква `X` в начале префикса зарезервирована за репозиториями-потребителями:
# канон её не занимает никогда, локальные правила берут префиксы только на
# неё (XTIM, XLOG). Согласовывать их с этим реестром не нужно — столкновение
# невозможно по построению.
[live]
DIRS = "conventions/arch/app-directories.md"
CONF = "conventions/arch/config.md"
KEYS = "conventions/arch/db-identifiers.md"
TIME = "conventions/arch/time.md"
GCFG = "conventions/lang/go/config.md"
GKEY = "conventions/lang/go/db-identifiers.md"
MIGR = "conventions/lang/go/db-schema.md"
GERR = "conventions/lang/go/errors.md"
SLOG = "conventions/lang/go/logging.md"
GTIM = "conventions/lang/go/time.md"
ANSD = "conventions/stack/ansible/app-directories.md"
HTMX = "conventions/stack/htmx/web-ui.md"
# Обвязка канона: не синхронизируется в репозитории, но правила
# записаны тем же языком и цитируются по номерам, поэтому префикс нужен.
META = "GUIDE.md"
[retired]
# Пусто. Сюда попадают префиксы удалённых и разделённых файлов вместе с
# причиной и датой, чтобы их нельзя было выдать повторно.