From 170c06c1da0ec80662ab6460ac18a14b90d79a96 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 26 Jul 2026 15:51:54 +0300 Subject: [PATCH] =?UTF-8?q?=D1=8F=D0=B7=D1=8B=D0=BA:=20=D0=BA=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=BE=D0=B5=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B4=D0=BB=D1=8F=20=D1=87=D0=B8=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D0=BA=D0=BE=D0=BF=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B5=D0=B4=D0=B5=D1=82=20=D0=B2=20=D1=80=D0=B5=D0=BF=D0=BE?= =?UTF-8?q?=D0=B7=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - заведён READING.md: словарь со значениями, форма правила и её граница, ссылки, локальная часть — без разделов о ведении набора и без META-ссылок, примеры на X-префиксах - сборщик кладёт его в docs/conventions/ и перезаписывает целиком; в манифест набора добавлена секция [language] с версией и двумя документами - META-30: правка словаря или состава частей правила доходит до READING.md, иначе потребитель толкует ДОПУСКАЕТСЯ по прежней версии --- CLAUDE.md | 7 ++- GUIDE.md | 13 ++++++ LANGUAGE.md | 8 ++++ READING.md | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 41 +++++++++++++++--- TODO.md | 45 +++++++------------ manifest.toml | 12 +++++ 7 files changed, 205 insertions(+), 39 deletions(-) create mode 100644 READING.md diff --git a/CLAUDE.md b/CLAUDE.md index 765f306..c7a8dfc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,8 +9,9 @@ code in this repository. Канон конвенций разработки для личных проектов. Сами конвенции лежат в `conventions/{arch,lang/<язык>,stack/<стек>}/`; обвязка канона (`README.md`, -`LANGUAGE.md`, `GUIDE.md`, `manifest.toml`, `conv`) живёт в корне и в -репозитории-потребители не едет. +`LANGUAGE.md`, `GUIDE.md`, `READING.md`, `manifest.toml`, `conv`) живёт в +корне. К потребителю из неё едет только `READING.md` — короткое описание языка +для читателя копий. Ниже — короткие инварианты с идентификаторами; детали и обоснования в `LANGUAGE.md` (форма записи) и `GUIDE.md` (процесс, префикс META). @@ -44,6 +45,8 @@ code in this repository. Механизация её не заменяет и не сокращает. - Метки правила — **ПОЧЕМУ** и **МЕХАНИЗИРОВАНО** — тоже словарь набора и перечислены в строке о версии языка наравне с модальными словами. +- META-30: правка словаря или состава частей правила доходит до `READING.md` — + документа, который едет к потребителю. Словари двух описаний совпадают. - Заглавные модальные слова не употребляются вне правил: ни в «Область действия», ни в «Связано», ни в локальной части копии, ни во вводной прозе. Исключение — строка о версии языка, которая их перечисляет. diff --git a/GUIDE.md b/GUIDE.md index 746dae6..ce903cf 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -99,6 +99,19 @@ prefix: META а по содержанию: файл обновится штатно, изменится текст. Та же дисциплина и по той же причине действует для префиксов правил. +### META-30. Правка словаря или формы правила доходит до документа для читателя + +**ДОЛЖЕН.** Изменение ключевых слов, их значений или состава частей правила +вносится и в короткое описание языка, которое едет в копию. + +**ПОЧЕМУ.** Полное описание языка остаётся у автора набора, а по правилам код +проверяет читатель копии — человек или агент в чужом репозитории, у которого +из двух документов есть только короткий. Разошедшись, он начинает толковать +слова по прежней версии: ДОПУСКАЕТСЯ читается как бытовое «можно», отступление +от ДОЛЖЕН перестаёт требовать записи — то есть отказывает ровно то, ради чего +слова вводились, и молча. Проверить расхождение дёшево: словари в двух +документах либо совпадают, либо нет. + ### META-2. Конвенция заводится, когда решение принимается третий раз **СЛЕДУЕТ.** Поводом служит одно и то же решение, принятое третий раз и diff --git a/LANGUAGE.md b/LANGUAGE.md index 4e766d7..5fd5913 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -12,6 +12,12 @@ version: 1 пополниться, и текст, написанный по предыдущей версии, должен читаться по той, по которой написан. +Документ адресован автору набора и в репозиторий-потребитель не едет. К +читателю копии едет короткое `READING.md`: словарь со значениями, форма +правила и её граница, ссылки, локальная часть — без разделов о ведении набора. +Словарь в двух документах обязан совпадать (META-30), и это единственное +место, где между ними возможен дрейф. + Описание языка ни на один набор конвенций не опирается, поэтому все примеры здесь — вымышленные правила с префиксами на `X` (`XKEY`, `XMIG`, `XLOG`). Такие префиксы канон не занимает никогда, в манифесте набора их нет — значит @@ -513,6 +519,8 @@ XMIG-6 не соблюдается в легаси-таблицах `show_histor - префиксы локальных правил копии начинаются на `X`; - отметки МЕХАНИЗИРОВАНО в тексте конвенции нет: её место — запись о механизации в локальной части копии (META-7); +- словарь в коротком описании языка совпадает с этим: те же ступени, те же + метки, те же значения (META-30); - префикс **чужой темы** не встречается в абзаце с модальностью (META-20); префикс арх-слоя своей темы там допустим (META-24), префикс другого языка или стека — нет; diff --git a/READING.md b/READING.md new file mode 100644 index 0000000..c16eef4 --- /dev/null +++ b/READING.md @@ -0,0 +1,118 @@ +--- +version: 1 +--- + +# Как читать конвенцию + +Файлы рядом с этим — конвенции: правила о том, как в этом репозитории пишут +код. Здесь сказано, как они записаны: что означают заглавные слова, из чего +состоит правило и как на него сослаться. Читается один раз, дальше нужен как +справка. + +Файл кладёт сюда сборщик конвенций и перезаписывает целиком при каждом +обновлении. Правки в нём не живут. + +## Ключевые слова + +Заглавное слово в начале абзаца задаёт обязательность правила. + +| Слово | Что означает | Если делаем иначе | +|---|---|---| +| **ДОЛЖЕН** | нарушение считается ошибкой | только с записью в отступления | +| **НЕ ДОЛЖЕН** | запрет, та же строгость | то же | +| **СЛЕДУЕТ** | сильная рекомендация: новый код пишем так | допустимо, причину записываем | +| **НЕ СЛЕДУЕТ** | обратное к СЛЕДУЕТ | то же | +| **ДОПУСКАЕТСЯ** | выбор за автором кода | ничего не требуется — правило не запрещает | + +Две вещи, которые легко прочитать неверно: + +- **ДОПУСКАЕТСЯ — не бытовое «можно».** У слова есть вторая половина: выбор, + помеченный им, на ревью не обсуждается. Возражение «сделай иначе» против + такого выбора не принимается — иначе разрешение ничего не значило бы. +- **Отступление от ДОЛЖЕН — не запрет на отступление.** Нарушать можно, но + тогда об этом появляется запись: какое правило, где именно, почему. Разница + между ДОЛЖЕН и СЛЕДУЕТ — в том, чем платят за отклонение, а не в том, + возможно ли оно. + +Ещё две метки заглавными: **ПОЧЕМУ** открывает обоснование правила, +**МЕХАНИЗИРОВАНО** стоит при записи о том, что правило проверяет линтер или +скрипт. + +**Нормативно только заглавное написание.** Строчное «должен» в прозе — обычная +речь, а не норма; спорить с ней как с правилом не нужно. + +## Из чего состоит правило + +Правило в примере вымышленное: префиксы на `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 + +``` + +Всё выше маркера приезжает из общего набора и перезаписывается при +обновлении. Всё ниже принадлежит этому репозиторию и обновление переживает. +Там живёт: + +- **отступления** — какое правило не соблюдается, где и почему: «`XMIG-6` не + соблюдается в `queue`: составные ключи там появились до конвенции»; +- **механизация** — кто проверяет правило машинно: «`XMIG-4` — + МЕХАНИЗИРОВАНО: `internal/archrules`»; +- **разрешение условий**, которые правило оставило открытыми; +- **свои правила** — по той же форме, но с префиксом на `X` (`XLOG-1`). + Префиксы на `X` общий набор не занимает никогда, так что столкнуться они не + могут. + +Правка выше маркера живёт до первого обновления и исчезает молча. Если +исправить нужно приехавший текст — либо правку переносят в общий набор, либо +файл перестаёт быть копией: из шапки убирают `origin:`. + +## Чего в конвенции не бывает + +- **Утверждений о том, как сейчас устроен этот репозиторий.** Правило пишется + в предписывающем времени; «у нас пока не так» — это отступление, и его + место ниже маркера. +- **Заглавных ключевых слов вне правил.** Во вводной прозе, в разделах + «Область действия» и «Связано» их нет, поэтому искать там требования не + нужно. + +Язык записи — версия 1. Полное описание живёт в наборе конвенций, у автора; +здесь ровно то, что нужно читателю. diff --git a/README.md b/README.md index 76a08ab..5336a75 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,15 @@ | `README.md` | устройство канона, оси, сборка копий, жизненный цикл | | [LANGUAGE.md](LANGUAGE.md) | язык записи правил: идентификаторы, модальность, обоснование | | [GUIDE.md](GUIDE.md) | как ведут конвенции: когда заводить, механизация, отступления | -| [manifest.toml](manifest.toml) | манифест набора: темы и префиксы правил | +| [READING.md](READING.md) | как читать конвенцию: то, что едет к потребителю | +| [manifest.toml](manifest.toml) | манифест набора: язык, темы, префиксы правил | | `conv` | сборка копий | -Обвязка живёт только в каноне и в репозитории не оказывается — в копию едет -лишь содержимое `conventions/`. Самодостаточность копии это не нарушает: -конвенция называет язык записи одной строкой с номером версии и не ссылается -на путь (`LANGUAGE.md`, раздел «Ссылка на язык из конвенции»). +К потребителю едет содержимое `conventions/` и один файл обвязки — +`READING.md`; остальная обвязка остаётся в каноне. Самодостаточность копии это +не нарушает: конвенция называет язык записи одной +строкой с номером версии и не ссылается на путь (`LANGUAGE.md`, раздел «Ссылка +на язык из конвенции»). Правило то же, что у ролей: **деплоится и читается только то, что лежит в git репозитория**. Канон никем не подключается на лету. @@ -151,6 +153,7 @@ extends: arch/db-identifiers.md ``` docs/conventions/ README.md собственный, не собирается + READING.md как читать конвенцию — приезжает из канона time.md arch/time.md + lang/go/time.md db-identifiers.md arch/db-identifiers.md + lang/go/db-identifiers.md app-directories.md arch/… + stack/ansible/… @@ -159,6 +162,10 @@ docs/conventions/ Так конвенция остаётся самодостаточным документом: тот, кто проверяет по ней код — человек или агент, — читает один файл и не собирает тему из трёх мест. +Имена в директории делятся на три вида: `README.md` принадлежит репозиторию и +сборщик его не трогает, `READING.md` принадлежит канону и перезаписывается +целиком, остальные файлы — копии тем с шапкой `origin:` и локальной частью. + **Шапка копии** ставится при сборке и в каноне не хранится: ```yaml @@ -198,6 +205,24 @@ MIGR-6 не соблюдается в `show_history`, `queue`: составны репозитория. Файл, оставивший шапку, при следующем обновлении потеряет всё, что выше маркера. +## Язык записи едет вместе с копиями + +Конвенция называет язык одной строкой с номером версии и без пути — строка +работает и сама по себе. Но семантика заглавных слов живёт в описании языка, а +описание в репозиторий-потребитель раньше не попадало: агент, читающий копию, +принимал ДОПУСКАЕТСЯ за бытовое «можно» и терял ровно то, ради чего слово +введено. + +Поэтому в `docs/conventions/` сборщик кладёт `READING.md` — короткое описание +для читателя правил: словарь со значениями, правило заглавных, из чего состоит +правило и где его граница, как ссылаться, что живёт ниже маркера. Полное +[LANGUAGE.md](LANGUAGE.md) остаётся в каноне: три его раздела адресованы +автору набора и ссылаются на правила `GUIDE.md`, которых у потребителя нет. + +Два документа — один словарь, и это единственное место, где возможен дрейф. +Правка ключевых слов или состава частей правила обязана дойти до `READING.md` +(META-30), а сверить их дёшево: таблицы либо совпадают, либо нет. + ## Два манифеста Манифестов в модели два, и они отвечают на разные вопросы: @@ -251,6 +276,7 @@ topics = ["time", "config", "db-identifiers"] conv list # какие темы есть в каноне conv add time # добавить тему в манифест и собрать файл conv pull # пересобрать всё, что перечислено в манифесте + # (и обновить READING.md рядом с копиями) ``` Отчёт о том, что изменилось, отдельной командой не выдаётся: после `pull` @@ -288,6 +314,7 @@ conv pull # пересобрать всё, что переч Модель выше — согласованная, а не реализованная. `conv` пока собран под прежнюю: зеркальное дерево копий вместо плоского, именованные регионы `` вместо одного маркера, `origin_hash` в шапке и команды -`status`, `diff`, `push`. Сами конвенции уже приведены к новой модели — -именованных регионов в каноне нет. Ни один репозиторий-потребитель не +`status`, `diff`, `push`; `READING.md` рядом с копиями он тоже пока не +кладёт. Сами конвенции уже приведены к новой модели — именованных регионов в +каноне нет. Ни один репозиторий-потребитель не подключён, поэтому переход никого не ломает. diff --git a/TODO.md b/TODO.md index 1da70f5..6fcced5 100644 --- a/TODO.md +++ b/TODO.md @@ -4,28 +4,12 @@ решения. Закрытый вопрос отсюда удаляется — принятое решение живёт в `README.md`, `GUIDE.md` или `LANGUAGE.md`, а не в этом файле. -Две секции: сначала язык и подход, потом канон с тулингом. Пункты 1–3 +Две секции: сначала язык и подход, потом канон с тулингом. Пункты 1–2 пришли из внешнего ревью описания языка и проверены по файлам на месте. # Язык и подход -## 1. Семантика ключевых слов в копию не едет - -Строка о версии языка перечисляет слова, но не их значения, а всё -нетривиальное в шкале живёт только в `LANGUAGE.md`, который в репозиторий не -едет: что ДОПУСКАЕТСЯ запрещает возражать на ревью, что отступление от -ДОЛЖЕН требует записи, что отступление от СЛЕДУЕТ требует причины. - -Аналогия с BCP 14 ломается именно там, где призвана работать: RFC 2119 -общедоступен и общеизвестен, «язык конвенций версии 1» — нет. Агент в -репозитории-потребителе прочитает ДОПУСКАЕТСЯ как бытовое «можно» и примет -возражение на ревью — ровно та потеря, ради которой слово вводилось. - -Для одного автора терпимо, для агентов — нет. Варианты: возить рядом с -копиями короткую выжимку семантики; расширить строку о версии до -двух-трёх предложений; или признать ограничение и записать его явно. - -## 2. Две «механические» проверки без источника данных +## 1. Две «механические» проверки без источника данных В списке «разбором текста» стоят два пункта, которые без дополнительного реестра нерешаемы: @@ -42,7 +26,7 @@ требуют, а манифест набора хранит только темы и префиксы. Пока реестр снятых номеров не объявлен частью языка, оба пункта принадлежат списку «чтением». -## 3. Натяжки в опоре на стандарты +## 2. Натяжки в опоре на стандарты Три места, где источнику приписано чуть больше, чем в нём есть: @@ -61,7 +45,7 @@ Остальное в таблице проверку выдержало, включая вторую половину `MAY` из BCP 14 и списки эквивалентных словесных форм ISO Directives. -## 4. Одиннадцать таблиц не прочитаны на взаимоисключительность +## 3. Одиннадцать таблиц не прочитаны на взаимоисключительность `LANGUAGE.md` объявил, что строки таблицы решений взаимоисключающи по умолчанию, а иной порядок объявляется явно. Объявление не делает таблицы @@ -76,7 +60,7 @@ BCP 14 и списки эквивалентных словесных форм IS Работа читательская, машине не даётся; в список проверок она уже записана в разделе «Чтением, потому что машине не даётся». -## 5. Описание языка отдельно от набора конвенций +## 4. Описание языка отдельно от набора конвенций `LANGUAGE.md` и `GUIDE.md` описывают, **как** пишутся конвенции; `conventions/` — **один конкретный** набор. Сейчас они склеены в одном @@ -101,7 +85,7 @@ BCP 14 и списки эквивалентных словесных форм IS # Канон, тулинг, подключение -## 6. Тулинг: две разные задачи в одном `conv` +## 5. Тулинг: две разные задачи в одном `conv` Сейчас в `conv` смешаны две категории работы, и они расходятся по всему — по частоте запуска, по тому, кто запускает, и по тому, что считается @@ -116,7 +100,8 @@ BCP 14 и списки эквивалентных словесных форм IS **Установка в проект.** Манифест `.conventions.toml`, сборка файла темы из слоёв (`arch` → язык → стек), сохранение при пересборке всего, что ниже -маркера, предупреждение о висячих ссылках на неподписанные темы. Запускается +маркера, `READING.md` рядом с копиями, предупреждение о висячих ссылках на +неподписанные темы. Запускается в репозитории-потребителе, изредка, провал — это чаще «посмотри глазами», чем «ошибка». Отчёта «канон ушёл вперёд» здесь нет: его делает `git diff` после пересборки. @@ -132,7 +117,7 @@ BCP 14 и списки эквивалентных словесных форм IS ссылках: это установка, а не целостность, но список подписок ему нужен из манифеста. -Часть проверок из этого списка сейчас нереализуема по причине из вопроса 2, +Часть проверок из этого списка сейчас нереализуема по причине из вопроса 1, так что порядок такой: сначала язык, потом чекер. Перед тем как переписывать, стоит посмотреть на два готовых прототипа: @@ -140,7 +125,7 @@ BCP 14 и списки эквивалентных словесных форм IS манифеста и `vendir.yml` — как пример того, где проходит граница между «чего хочу» и «что получил». -## 7. Пары слоёв и темы без базы +## 6. Пары слоёв и темы без базы Отложено сознательно, но список стоит держать перед глазами: @@ -161,7 +146,7 @@ BCP 14 и списки эквивалентных словесных форм IS - Вынос арх-ядра из `errors` и `logging` закроет две хрупкие ссылки из `web-ui` (стек) в go-слой — единственные ссылки стек → язык в каноне. -## 8. Подключение к репозиториям +## 7. Подключение к репозиториям Ничего ещё не подключено. Кандидаты — jellybit и pet-project-server. Понадобится: заполнить локальную часть копий тем, что сейчас в этих @@ -170,7 +155,7 @@ BCP 14 и списки эквивалентных словесных форм IS строка в `AGENTS.md` каждого потребителя про то, что файлы в `docs/conventions/` — копии. -## 9. Тулинг на Go, живущий независимо +## 8. Тулинг на Go, живущий независимо Сейчас `conv` — питоновский скрипт внутри канона, то есть тулинг и данные в одном репозитории и правятся одним движением. Мысль: вынести в отдельный @@ -179,10 +164,10 @@ Go-бинарь со своим релизным циклом, ставить ч Что за этим стоит помимо вкуса: независимый бинарь физически не даёт править инструмент «заодно» с правкой конвенции, работает против **любого** канона и -любого потребителя — что прямо требуется вопросом 5, — и снимает питон из +любого потребителя — что прямо требуется вопросом 4, — и снимает питон из зависимостей репозиториев-потребителей. -Порядок обратный ожидаемому: пока вопрос 5 не сделан, инструмент всё равно +Порядок обратный ожидаемому: пока вопрос 4 не сделан, инструмент всё равно работает против одного конкретного канона, и независимый релизный цикл ему -нечего обслуживать. Сначала 5, потом 9. Разделение из вопроса 6 при этом +нечего обслуживать. Сначала 4, потом 8. Разделение из вопроса 5 при этом дешевле заложить сразу, чем отпиливать потом. diff --git a/manifest.toml b/manifest.toml index 4778c9e..89c71d3 100644 --- a/manifest.toml +++ b/manifest.toml @@ -12,6 +12,18 @@ # не переиспользуются никогда, а снятые уходят в свой раздел `retired` # вместе с причиной и датой. +# ─── Язык записи ──────────────────────────────────────────────────────────── +# +# Набор объявляет версию языка, на котором записаны его правила, и два +# документа о нём. Полное описание остаётся у автора; в копию рядом с +# конвенциями едет короткое `READING.md` — то, что нужно читателю правил, без +# ссылок на правила ведения набора. + +[language] +version = 1 +description = "LANGUAGE.md" +reading = "READING.md" + # ─── Темы ─────────────────────────────────────────────────────────────────── # # Тема — набор правил об одном фокусе разработки: время, конфигурация, схема