язык: короткое описание для читателя копии едет в репозиторий

- заведён READING.md: словарь со значениями, форма правила и её граница,
  ссылки, локальная часть — без разделов о ведении набора и без META-ссылок,
  примеры на X-префиксах
- сборщик кладёт его в docs/conventions/ и перезаписывает целиком; в манифест
  набора добавлена секция [language] с версией и двумя документами
- META-30: правка словаря или состава частей правила доходит до READING.md,
  иначе потребитель толкует ДОПУСКАЕТСЯ по прежней версии
This commit is contained in:
av
2026-07-26 15:51:54 +03:00
parent 67d51db212
commit 170c06c1da
7 changed files with 205 additions and 39 deletions
+5 -2
View File
@@ -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`
документа, который едет к потребителю. Словари двух описаний совпадают.
- Заглавные модальные слова не употребляются вне правил: ни в «Область
действия», ни в «Связано», ни в локальной части копии, ни во вводной прозе.
Исключение — строка о версии языка, которая их перечисляет.
+13
View File
@@ -99,6 +99,19 @@ prefix: META
а по содержанию: файл обновится штатно, изменится текст. Та же дисциплина и по
той же причине действует для префиксов правил.
### META-30. Правка словаря или формы правила доходит до документа для читателя
**ДОЛЖЕН.** Изменение ключевых слов, их значений или состава частей правила
вносится и в короткое описание языка, которое едет в копию.
**ПОЧЕМУ.** Полное описание языка остаётся у автора набора, а по правилам код
проверяет читатель копии — человек или агент в чужом репозитории, у которого
из двух документов есть только короткий. Разошедшись, он начинает толковать
слова по прежней версии: ДОПУСКАЕТСЯ читается как бытовое «можно», отступление
от ДОЛЖЕН перестаёт требовать записи — то есть отказывает ровно то, ради чего
слова вводились, и молча. Проверить расхождение дёшево: словари в двух
документах либо совпадают, либо нет.
### META-2. Конвенция заводится, когда решение принимается третий раз
**СЛЕДУЕТ.** Поводом служит одно и то же решение, принятое третий раз и
+8
View File
@@ -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), префикс другого языка
или стека — нет;
+118
View File
@@ -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
<!-- conv:local -->
```
Всё выше маркера приезжает из общего набора и перезаписывается при
обновлении. Всё ниже принадлежит этому репозиторию и обновление переживает.
Там живёт:
- **отступления** — какое правило не соблюдается, где и почему: «`XMIG-6` не
соблюдается в `queue`: составные ключи там появились до конвенции»;
- **механизация** — кто проверяет правило машинно: «`XMIG-4`
МЕХАНИЗИРОВАНО: `internal/archrules`»;
- **разрешение условий**, которые правило оставило открытыми;
- **свои правила** — по той же форме, но с префиксом на `X` (`XLOG-1`).
Префиксы на `X` общий набор не занимает никогда, так что столкнуться они не
могут.
Правка выше маркера живёт до первого обновления и исчезает молча. Если
исправить нужно приехавший текст — либо правку переносят в общий набор, либо
файл перестаёт быть копией: из шапки убирают `origin:`.
## Чего в конвенции не бывает
- **Утверждений о том, как сейчас устроен этот репозиторий.** Правило пишется
в предписывающем времени; «у нас пока не так» — это отступление, и его
место ниже маркера.
- **Заглавных ключевых слов вне правил.** Во вводной прозе, в разделах
«Область действия» и «Связано» их нет, поэтому искать там требования не
нужно.
Язык записи — версия 1. Полное описание живёт в наборе конвенций, у автора;
здесь ровно то, что нужно читателю.
+34 -7
View File
@@ -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` пока собран под
прежнюю: зеркальное дерево копий вместо плоского, именованные регионы
`<!-- local:имя -->` вместо одного маркера, `origin_hash` в шапке и команды
`status`, `diff`, `push`. Сами конвенции уже приведены к новой модели —
именованных регионов в каноне нет. Ни один репозиторий-потребитель не
`status`, `diff`, `push`; `READING.md` рядом с копиями он тоже пока не
кладёт. Сами конвенции уже приведены к новой модели — именованных регионов в
каноне нет. Ни один репозиторий-потребитель не
подключён, поэтому переход никого не ломает.
+15 -30
View File
@@ -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 при этом
дешевле заложить сразу, чем отпиливать потом.
+12
View File
@@ -12,6 +12,18 @@
# не переиспользуются никогда, а снятые уходят в свой раздел `retired`
# вместе с причиной и датой.
# ─── Язык записи ────────────────────────────────────────────────────────────
#
# Набор объявляет версию языка, на котором записаны его правила, и два
# документа о нём. Полное описание остаётся у автора; в копию рядом с
# конвенциями едет короткое `READING.md` — то, что нужно читателю правил, без
# ссылок на правила ведения набора.
[language]
version = 1
description = "LANGUAGE.md"
reading = "READING.md"
# ─── Темы ───────────────────────────────────────────────────────────────────
#
# Тема — набор правил об одном фокусе разработки: время, конфигурация, схема