обвязка: модель копий переписана под один маркер и манифест
- именованные регионы `<!-- local:имя -->` заменены на единственный `<!-- conv:local -->`: всё ниже него принадлежит репозиторию, всё выше пересобирается, поэтому имени-которое-можно-осиротить больше нет - лок-файла и `origin_hash` в шапке нет — «что было в прошлый раз» знает git, копии закоммичены, автоматического обновления не существует; транспорт назад (`push`) убран вместе с ними - заведены META-22 (репозиторное пишется ниже маркера) и META-23 (форк не носит `origin:`), META-17 переписан под маркер; буква `X` в префиксе зарезервирована за локальными правилами потребителей
This commit is contained in:
@@ -25,7 +25,7 @@ code in this repository.
|
|||||||
**ДОПУСКАЕТСЯ**, плюс не-модальная отметка **МЕХАНИЗИРОВАНО**. Английские
|
**ДОПУСКАЕТСЯ**, плюс не-модальная отметка **МЕХАНИЗИРОВАНО**. Английские
|
||||||
ключевые слова (SHALL, MUST) не используются — они заняты OpenSpec.
|
ключевые слова (SHALL, MUST) не используются — они заняты OpenSpec.
|
||||||
- Модальные слова не употребляются вне правил: ни в «Область действия», ни в
|
- Модальные слова не употребляются вне правил: ни в «Область действия», ни в
|
||||||
«Связано», ни в локальных регионах, ни во вводной прозе.
|
«Связано», ни в локальной части копии, ни во вводной прозе.
|
||||||
- «Почему» отвечает на «что сломается, если сделать иначе», а не
|
- «Почему» отвечает на «что сломается, если сделать иначе», а не
|
||||||
пересказывает норму. «Потому что так принято» — не обоснование.
|
пересказывает норму. «Потому что так принято» — не обоснование.
|
||||||
- Правило, классифицирующее ситуации, пишется таблицей «ситуация → вердикт»;
|
- Правило, классифицирующее ситуации, пишется таблицей «ситуация → вердикт»;
|
||||||
@@ -44,6 +44,8 @@ code in this repository.
|
|||||||
секция `[live]`, путём от корня репозитория.
|
секция `[live]`, путём от корня репозитория.
|
||||||
- Удаление или разделение файла: префикс уходит в `[retired]` с причиной и
|
- Удаление или разделение файла: префикс уходит в `[retired]` с причиной и
|
||||||
датой, а не освобождается.
|
датой, а не освобождается.
|
||||||
|
- Префиксы на букву `X` канон не занимает: они зарезервированы за локальными
|
||||||
|
правилами репозиториев-потребителей.
|
||||||
- Перенос правила в другой файл — смысловое изменение: новый префикс и новый
|
- Перенос правила в другой файл — смысловое изменение: новый префикс и новый
|
||||||
номер. Переезд самого файла между осями идентификаторы не трогает.
|
номер. Переезд самого файла между осями идентификаторы не трогает.
|
||||||
|
|
||||||
@@ -72,9 +74,9 @@ code in this repository.
|
|||||||
норма уехала в линтер.
|
норма уехала в линтер.
|
||||||
- META-1: один файл — ровно один повторяющийся выбор. META-2: конвенция
|
- META-1: один файл — ровно один повторяющийся выбор. META-2: конвенция
|
||||||
заводится, когда решение принимается третий раз.
|
заводится, когда решение принимается третий раз.
|
||||||
- Локальные регионы `<!-- local:имя --> … <!-- /local -->` в каноне остаются
|
- Репозиторного в каноне нет вовсе: механизация, отступления и ссылки на код
|
||||||
пустыми: их содержимое принадлежит репозиторию-потребителю. Имя региона и
|
живут в копии ниже маркера `<!-- conv:local -->` (META-22), который ставит
|
||||||
путь файла — API, переименование осиротит все копии.
|
сборщик. Заводить пустые местные разделы в каноне не нужно.
|
||||||
|
|
||||||
## Выбор оси
|
## Выбор оси
|
||||||
|
|
||||||
@@ -88,9 +90,9 @@ code in this repository.
|
|||||||
|
|
||||||
Шапка `prefix:` (плюс `extends:`) → `# Тема` → вводная проза со строкой
|
Шапка `prefix:` (плюс `extends:`) → `# Тема` → вводная проза со строкой
|
||||||
«Форма записи — `LANGUAGE.md`» → `## Область действия` (обязателен для
|
«Форма записи — `LANGUAGE.md`» → `## Область действия` (обязателен для
|
||||||
трудноизменяемых слоёв — META-11) → правила → `## Связано` с пустым
|
трудноизменяемых слоёв — META-11) → правила → `## Связано` только с
|
||||||
`<!-- local:связано -->`. Имя файла — kebab-case по теме. Проза переносится
|
каноническими ссылками (META-17). Имя файла — kebab-case по теме. Проза
|
||||||
по ~76 колонок; таблицы и блоки кода не переносятся.
|
переносится по ~76 колонок; таблицы и блоки кода не переносятся.
|
||||||
|
|
||||||
## Ревью формы
|
## Ревью формы
|
||||||
|
|
||||||
@@ -110,10 +112,14 @@ code in this repository.
|
|||||||
|
|
||||||
- Тестов, линтеров и CI нет. `conv` — python3 CLI на одной stdlib; запускают
|
- Тестов, линтеров и CI нет. `conv` — python3 CLI на одной stdlib; запускают
|
||||||
его из корня репозитория-потребителя (`~/projects/private/dev-conventions/`
|
его из корня репозитория-потребителя (`~/projects/private/dev-conventions/`
|
||||||
плюс `conv status`). `status` и `diff` всегда возвращают 0 — это отчёт, а не
|
плюс команда).
|
||||||
проверка.
|
- Модель копий, описанная в `README.md`, согласована, но не реализована:
|
||||||
|
`conv` собран под прежнюю (зеркальное дерево, именованные регионы,
|
||||||
|
`origin_hash`, команды `status`/`diff`/`push`), и в двенадцати файлах
|
||||||
|
канона ещё лежит 31 пустой регион `<!-- local:имя -->` — их предстоит
|
||||||
|
удалить. При правке обвязки истина — README, а не код `conv`.
|
||||||
- Ни один репозиторий-потребитель ещё не подключён: копий с шапкой `origin:`
|
- Ни один репозиторий-потребитель ещё не подключён: копий с шапкой `origin:`
|
||||||
в природе нет, все локальные регионы канона пусты.
|
в природе нет.
|
||||||
- `TODO.md` — площадка для обсуждения на будущее, а не принятые решения; при
|
- `TODO.md` — площадка для обсуждения на будущее, а не принятые решения; при
|
||||||
работе над обвязкой его стоит прочесть, но истина о текущем устройстве —
|
работе над обвязкой его стоит прочесть, но истина о текущем устройстве —
|
||||||
`README.md`.
|
`README.md`.
|
||||||
|
|||||||
@@ -41,16 +41,20 @@ prefix: META
|
|||||||
## Канон и копии
|
## Канон и копии
|
||||||
|
|
||||||
Файлы в `docs/conventions/` с шапкой `origin:` — копии из общего канона
|
Файлы в `docs/conventions/` с шапкой `origin:` — копии из общего канона
|
||||||
`dev-conventions`, а не собственные документы репозитория. Репозиторное
|
`dev-conventions`, а не собственные документы репозитория. Копия собирается
|
||||||
живёт только внутри локальных регионов `<!-- local:имя --> … <!-- /local -->`:
|
из канона целиком, поэтому репозиторное живёт ниже маркера локальной части
|
||||||
они исключены из сравнения с каноном, и расхождение по ним — норма, а не
|
в конце файла: обновление сохраняет всё, что ниже маркера, и переписывает
|
||||||
дрейф. Правка вне регионов означает одно из двух: улучшение, которое
|
всё, что выше. Откуда взяты копии и где брать обновления — в манифесте
|
||||||
возвращают в канон, или сознательное расхождение, записанное в ключ `local:`
|
`.conventions.toml` в корне репозитория.
|
||||||
шапки. Состояние копий показывает `conv status`, различия — `conv diff`,
|
|
||||||
обновление из канона — `conv pull`; всё через раннер репозитория.
|
|
||||||
|
|
||||||
Имя региона обязательно и стабильно: перенос содержимого при обновлении
|
Отдельного механизма отчёта о расхождении нет: обновление перезаписывает
|
||||||
идёт по именам, и переименование осиротит содержимое во всех копиях.
|
файлы в рабочем дереве, а что именно изменилось, показывает `git diff` до
|
||||||
|
коммита. Поэтому в шапке копии хранится только `origin:` — отпечатков
|
||||||
|
канона и дат синхронизации в ней нет, историю держит git.
|
||||||
|
|
||||||
|
Правка выше маркера означает одно из двух: улучшение, которое переносят в
|
||||||
|
канон, или документ, переставший быть копией, — тогда `origin:` из шапки
|
||||||
|
убирают.
|
||||||
|
|
||||||
## Правила
|
## Правила
|
||||||
|
|
||||||
@@ -58,11 +62,11 @@ prefix: META
|
|||||||
|
|
||||||
**ДОЛЖЕН.** Файл описывает ровно один повторяющийся выбор.
|
**ДОЛЖЕН.** Файл описывает ровно один повторяющийся выбор.
|
||||||
|
|
||||||
**Почему.** Подписка — это набор лежащих в репозитории файлов, и берут файл
|
**Почему.** Подписка перечисляется по темам, и тему берут целиком. Файл,
|
||||||
целиком. Файл, собравший две темы, вынуждает репозиторий взять правила,
|
собравший две темы, вынуждает репозиторий взять правила, которые ему не
|
||||||
которые ему не нужны, и получать шум в `diff` по чужой половине. Разрезать
|
нужны, и вычитывать чужую половину при каждом обновлении. Разрезать позже
|
||||||
позже дорого: путь файла — часть адреса правила, и после разреза внешние
|
дорого: перенос правила в другой файл — это новый префикс и новая
|
||||||
ссылки указывают не туда.
|
нумерация, поэтому после разреза все внешние ссылки обходят руками.
|
||||||
|
|
||||||
### META-2. Конвенция заводится, когда решение принимается третий раз
|
### META-2. Конвенция заводится, когда решение принимается третий раз
|
||||||
|
|
||||||
@@ -152,9 +156,9 @@ prefix: META
|
|||||||
(вкус формулировки, выбор границы, суждение о ситуации), не может быть
|
(вкус формулировки, выбор границы, суждение о ситуации), не может быть
|
||||||
ДОЛЖЕН — его модальность СЛЕДУЕТ по построению, а не по слабости.
|
ДОЛЖЕН — его модальность СЛЕДУЕТ по построению, а не по слабости.
|
||||||
|
|
||||||
### META-7. Факт механизации фиксируется в локальном регионе со ссылкой на правило
|
### META-7. Факт механизации фиксируется в копии со ссылкой на правило
|
||||||
|
|
||||||
**ДОЛЖЕН.** Регион `механизировано` называет идентификатор правила и
|
**ДОЛЖЕН.** Запись о механизации называет идентификатор правила и
|
||||||
конкретную проверку.
|
конкретную проверку.
|
||||||
|
|
||||||
**Почему.** Механизация — состояние конкретного репозитория, канон о ней не
|
**Почему.** Механизация — состояние конкретного репозитория, канон о ней не
|
||||||
@@ -181,7 +185,7 @@ prefix: META
|
|||||||
### META-9. Общая механизация разрешает удалить норму из канона
|
### META-9. Общая механизация разрешает удалить норму из канона
|
||||||
|
|
||||||
**ДОПУСКАЕТСЯ.** Правило, уехавшее в общий конфиг линтера или в общую роль,
|
**ДОПУСКАЕТСЯ.** Правило, уехавшее в общий конфиг линтера или в общую роль,
|
||||||
удаляется из канона одним `push`.
|
удаляется из канона.
|
||||||
|
|
||||||
**Почему.** Формулировка, дублирующая работающую у всех проверку,
|
**Почему.** Формулировка, дублирующая работающую у всех проверку,
|
||||||
размазывает внимание: файл на несколько сотен строк заставляет человека и
|
размазывает внимание: файл на несколько сотен строк заставляет человека и
|
||||||
@@ -232,15 +236,15 @@ prefix: META
|
|||||||
|
|
||||||
### META-14. Отступления перечисляются поимённо, со ссылкой на правила
|
### META-14. Отступления перечисляются поимённо, со ссылкой на правила
|
||||||
|
|
||||||
**ДОЛЖЕН.** В локальном регионе перечислены отступления, которые уже есть в
|
**ДОЛЖЕН.** В локальной части копии перечислены отступления, которые уже
|
||||||
коде, с идентификатором правила и причиной.
|
есть в коде, с идентификатором правила и причиной.
|
||||||
|
|
||||||
**Почему.** Иначе репозиторий выглядит соблюдающим конвенцию, а проверить
|
**Почему.** Иначе репозиторий выглядит соблюдающим конвенцию, а проверить
|
||||||
это можно только чтением всего кода. Со ссылками отступления счётны: видно,
|
это можно только чтением всего кода. Со ссылками отступления счётны: видно,
|
||||||
сколько правил конвенции репозиторий реально не соблюдает. Пустой регион при
|
сколько правил конвенции репозиторий реально не соблюдает. Пустой список при
|
||||||
этом почти всегда означает не отсутствие отступлений, а то, что их не искали.
|
этом почти всегда означает не отсутствие отступлений, а то, что их не искали.
|
||||||
|
|
||||||
### META-15. Запись в регионе отступлений разбирается по масштабу
|
### META-15. Запись об отступлении разбирается по масштабу
|
||||||
|
|
||||||
**ДОЛЖЕН.** Судьба записи зависит от того, что в ней сказано:
|
**ДОЛЖЕН.** Судьба записи зависит от того, что в ней сказано:
|
||||||
|
|
||||||
@@ -256,15 +260,35 @@ prefix: META
|
|||||||
правила в каноне, которую чинят один раз для всех, или лишнюю подписку,
|
правила в каноне, которую чинят один раз для всех, или лишнюю подписку,
|
||||||
где файл просто не нужен. Оставленная отступлением, она прячет обе.
|
где файл просто не нужен. Оставленная отступлением, она прячет обе.
|
||||||
|
|
||||||
### META-17. Репо-специфичная часть «Связано» — в локальном регионе
|
### META-17. Ссылки на код репозитория стоят в локальной части, а не в «Связано»
|
||||||
|
|
||||||
**ДОЛЖЕН.** Раздел «Связано» стоит в конце файла; канонические ссылки — в
|
**ДОЛЖЕН.** Раздел «Связано» канона содержит только ссылки, верные у всех
|
||||||
общем тексте, ссылки на ADR, код и файлы конкретного репозитория — в
|
потребителей; ссылки на ADR, код и файлы конкретного репозитория — в
|
||||||
локальном регионе.
|
локальной части копии.
|
||||||
|
|
||||||
**Почему.** Общий текст уезжает `push`-ем ко всем потребителям, и ссылка на
|
**Почему.** Текст канона приезжает ко всем потребителям, и ссылка на чужой
|
||||||
чужой файл у них битая с первого дня. Регион исключён из сравнения, поэтому
|
файл у них битая с первого дня. Ниже маркера та же ссылка никого не
|
||||||
та же ссылка внутри него никого не задевает и не даёт вечного шума в `diff`.
|
задевает и переживает обновление, потому что обновление её не трогает.
|
||||||
|
|
||||||
|
### META-22. Репозиторное в копии пишется ниже маркера локальной части
|
||||||
|
|
||||||
|
**ДОЛЖЕН.** Правки репозитория вносятся ниже маркера, а не в текст,
|
||||||
|
пришедший из канона.
|
||||||
|
|
||||||
|
**Почему.** Обновление перезаписывает всё, что выше маркера, поэтому правка
|
||||||
|
там живёт до первого `pull`. Заметить пропажу можно, только вычитав
|
||||||
|
`git diff` целиком — а он в этот момент и без того полон изменений канона,
|
||||||
|
и своя строка теряется среди чужих.
|
||||||
|
|
||||||
|
### META-23. Документ, переставший быть копией, не носит `origin:`
|
||||||
|
|
||||||
|
**НЕ ДОЛЖЕН.** Файл, который развели с каноном намеренно, шапку `origin:`
|
||||||
|
не сохраняет.
|
||||||
|
|
||||||
|
**Почему.** По `origin:` решается, какие файлы пересобирать из канона. Форк,
|
||||||
|
оставивший шапку, при первом же обновлении теряет ровно то, ради чего его
|
||||||
|
заводили. Происхождение такого документа остаётся в истории коммита, где оно
|
||||||
|
никого не вводит в заблуждение.
|
||||||
|
|
||||||
### META-18. README директории перечисляет конвенции с однострочным описанием
|
### META-18. README директории перечисляет конвенции с однострочным описанием
|
||||||
|
|
||||||
@@ -284,6 +308,3 @@ prefix: META
|
|||||||
идентификатором служит и напоминанием, и адресом, по которому за
|
идентификатором служит и напоминанием, и адресом, по которому за
|
||||||
подробностями идут; перенос деталей туда же вернул бы задачу поддержки двух
|
подробностями идут; перенос деталей туда же вернул бы задачу поддержки двух
|
||||||
текстов.
|
текстов.
|
||||||
|
|
||||||
<!-- local:точки-входа -->
|
|
||||||
<!-- /local -->
|
|
||||||
|
|||||||
+16
-15
@@ -12,10 +12,10 @@
|
|||||||
Не ради строгости. Три конкретные вещи, которые без адресуемых правил не
|
Не ради строгости. Три конкретные вещи, которые без адресуемых правил не
|
||||||
работают:
|
работают:
|
||||||
|
|
||||||
- **Механизация.** Регион `механизировано` должен говорить «правило
|
- **Механизация.** Запись о ней должна говорить «правило `MIGR-4` проверяет
|
||||||
`MIGR-4` проверяет `archrules`», а не «`AUTOINCREMENT` в новых миграциях —
|
`archrules`», а не «`AUTOINCREMENT` в новых миграциях — `archrules`»: во
|
||||||
`archrules`»: во втором случае читатель сам догадывается, к какому
|
втором случае читатель сам догадывается, к какому утверждению это
|
||||||
утверждению это относится, и догадывается по-разному.
|
относится, и догадывается по-разному.
|
||||||
- **Отступления.** «У нас не так» бесполезно, пока не сказано, что именно
|
- **Отступления.** «У нас не так» бесполезно, пока не сказано, что именно
|
||||||
«не так». Со ссылкой на правило отступления становятся счётными: видно,
|
«не так». Со ссылкой на правило отступления становятся счётными: видно,
|
||||||
сколько правил конвенции репозиторий реально не соблюдает.
|
сколько правил конвенции репозиторий реально не соблюдает.
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
Модальность живёт на **правиле**, а не на файле. Прежний файловый статус
|
Модальность живёт на **правиле**, а не на файле. Прежний файловый статус
|
||||||
(`status: рекомендуемая` / `обязательная` в шапке) отменён: он неизбежно
|
(`status: рекомендуемая` / `обязательная` в шапке) отменён: он неизбежно
|
||||||
врал, потому что один файл смешивает жёсткие требования с советами. В шапке
|
врал, потому что один файл смешивает жёсткие требования с советами. В шапке
|
||||||
остаются только `prefix`, `extends` и служебные ключи копии.
|
остаются только `prefix` и `extends`.
|
||||||
|
|
||||||
Мы **не используем SHALL и прочие английские ключевые слова**. Они заняты
|
Мы **не используем SHALL и прочие английские ключевые слова**. Они заняты
|
||||||
спецификациями (OpenSpec), и общий словарь стирал бы границу «конвенция —
|
спецификациями (OpenSpec), и общий словарь стирал бы границу «конвенция —
|
||||||
@@ -179,22 +179,21 @@ AND тик фонового цикла упал по той же причине
|
|||||||
(«новые таблицы; существующие не переписываются»). Это рамка для всех
|
(«новые таблицы; существующие не переписываются»). Это рамка для всех
|
||||||
правил файла, а не правило.
|
правил файла, а не правило.
|
||||||
- **Связано** — ссылки на смежные конвенции, ADR, код.
|
- **Связано** — ссылки на смежные конвенции, ADR, код.
|
||||||
- **Локальные регионы** — содержимое принадлежит репозиторию.
|
- **Локальная часть копии** — содержимое принадлежит репозиторию.
|
||||||
- Вводная проза, объясняющая предмет конвенции.
|
- Вводная проза, объясняющая предмет конвенции.
|
||||||
|
|
||||||
## Как на правила ссылаются копии
|
## Как на правила ссылаются копии
|
||||||
|
|
||||||
В репозитории:
|
Ниже маркера локальной части, в репозитории:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
<!-- local:механизировано -->
|
<!-- conv:local -->
|
||||||
MIGR-2, MIGR-4 — `internal/archrules` (проверяются в новых миграциях).
|
|
||||||
<!-- /local -->
|
|
||||||
|
|
||||||
<!-- local:отступления -->
|
MIGR-2, MIGR-4 механизированы — `internal/archrules` (проверяются в новых
|
||||||
MIGR-6 — не соблюдается в легаси-таблицах `show_history`, `queue`: составные
|
миграциях).
|
||||||
|
|
||||||
|
MIGR-6 не соблюдается в легаси-таблицах `show_history`, `queue`: составные
|
||||||
ключи там появились до конвенции, переписывание требует миграции данных.
|
ключи там появились до конвенции, переписывание требует миграции данных.
|
||||||
<!-- /local -->
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Отсюда видно и то, чего раньше не было видно: конвенция из восьми правил,
|
Отсюда видно и то, чего раньше не было видно: конвенция из восьми правил,
|
||||||
@@ -211,8 +210,10 @@ MIGR-6 — не соблюдается в легаси-таблицах `show_hi
|
|||||||
берёт следующий свободный, а не первый освободившийся);
|
берёт следующий свободный, а не первый освободившийся);
|
||||||
- у каждого `### <ПРЕФИКС>-<n>` есть модальное слово (или отметка
|
- у каждого `### <ПРЕФИКС>-<n>` есть модальное слово (или отметка
|
||||||
МЕХАНИЗИРОВАНО) и блок «Почему»;
|
МЕХАНИЗИРОВАНО) и блок «Почему»;
|
||||||
- ссылки вида `<ПРЕФИКС>-<n>` — хоть в тексте канона, хоть в локальных
|
- ссылки вида `<ПРЕФИКС>-<n>` — хоть в тексте канона, хоть в локальной части
|
||||||
регионах копии — указывают на правила, которые ещё существуют;
|
копии — указывают на правила, которые ещё существуют;
|
||||||
|
- префиксы локальных правил копии начинаются на `X` и не совпадают с
|
||||||
|
реестром канона;
|
||||||
- чужой префикс не встречается в абзаце с модальностью (META-20);
|
- чужой префикс не встречается в абзаце с модальностью (META-20);
|
||||||
- путь файла канона не встречается в тексте конвенции (META-21);
|
- путь файла канона не встречается в тексте конвенции (META-21);
|
||||||
- модальные слова не встречаются вне правил.
|
- модальные слова не встречаются вне правил.
|
||||||
|
|||||||
@@ -3,22 +3,21 @@
|
|||||||
Общие конвенции для личных проектов. Репозитории берут отсюда копии в свой
|
Общие конвенции для личных проектов. Репозитории берут отсюда копии в свой
|
||||||
`docs/conventions/`, коммитят их и живут дальше самостоятельно — как с
|
`docs/conventions/`, коммитят их и живут дальше самостоятельно — как с
|
||||||
`ansible-roles`: канон не источник истины во время работы, а лавка, из
|
`ansible-roles`: канон не источник истины во время работы, а лавка, из
|
||||||
которой берут и в которую возвращают улучшения.
|
которой берут.
|
||||||
|
|
||||||
Сами конвенции лежат в `conventions/`, обвязка — в корне:
|
Сами конвенции лежат в `conventions/`, обвязка — в корне:
|
||||||
|
|
||||||
| Файл | Что описывает |
|
| Файл | Что описывает |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `README.md` | устройство канона, оси, синхронизация, жизненный цикл |
|
| `README.md` | устройство канона, оси, сборка копий, жизненный цикл |
|
||||||
| [LANGUAGE.md](LANGUAGE.md) | язык записи правил: идентификаторы, модальность, «Почему» |
|
| [LANGUAGE.md](LANGUAGE.md) | язык записи правил: идентификаторы, модальность, «Почему» |
|
||||||
| [GUIDE.md](GUIDE.md) | как ведут конвенции: когда заводить, механизация, отступления |
|
| [GUIDE.md](GUIDE.md) | как ведут конвенции: когда заводить, механизация, отступления |
|
||||||
| `prefixes.toml` | реестр префиксов правил |
|
| `prefixes.toml` | реестр префиксов правил |
|
||||||
| `conv` | синхронизация копий |
|
| `conv` | сборка копий |
|
||||||
|
|
||||||
Обвязка живёт только в каноне и в репозитории не оказывается — `conv`
|
Обвязка живёт только в каноне и в репозитории не оказывается — в копию едет
|
||||||
синхронизирует лишь содержимое `conventions/`. Пока это осознанное
|
лишь содержимое `conventions/`. Пока это осознанное ограничение: копия
|
||||||
ограничение: копия конвенции ссылается на `LANGUAGE.md` как на внешний
|
конвенции ссылается на `LANGUAGE.md` как на внешний документ.
|
||||||
документ.
|
|
||||||
|
|
||||||
Правило то же, что у ролей: **деплоится и читается только то, что лежит в
|
Правило то же, что у ролей: **деплоится и читается только то, что лежит в
|
||||||
git репозитория**. Канон никем не подключается на лету.
|
git репозитория**. Канон никем не подключается на лету.
|
||||||
@@ -28,7 +27,7 @@ git репозитория**. Канон никем не подключаетс
|
|||||||
Конвенция формулируется независимо от конкретного приложения. Она задаёт
|
Конвенция формулируется независимо от конкретного приложения. Она задаёт
|
||||||
правило; код ему следует. Обратное направление запрещено: то, что
|
правило; код ему следует. Обратное направление запрещено: то, что
|
||||||
приложение уже делает иначе, **не является аргументом против правила** — это
|
приложение уже делает иначе, **не является аргументом против правила** — это
|
||||||
отступление, и его место в локальном регионе того репозитория, а не в
|
отступление, и его место в локальной части копии того репозитория, а не в
|
||||||
переформулировке канона.
|
переформулировке канона.
|
||||||
|
|
||||||
Отсюда практические следствия:
|
Отсюда практические следствия:
|
||||||
@@ -51,11 +50,12 @@ conventions/
|
|||||||
stack/<стек>/ привязка к инструменту, хранилищу, транспорту
|
stack/<стек>/ привязка к инструменту, хранилищу, транспорту
|
||||||
```
|
```
|
||||||
|
|
||||||
Пути **файлов** даются относительно `conventions/`: `arch/db-identifiers.md`,
|
Оси — раскладка **канона**; в репозитории копия лежит плоско, файлом на
|
||||||
а не `conventions/arch/…` — так же, как они лягут в `docs/conventions/`
|
тему. Пути файлов даются относительно `conventions/`
|
||||||
репозитория. На **правила** ссылаются идентификатором без пути: `KEYS-5`.
|
(`arch/db-identifiers.md`) и адресуют исходник канона, а не место в копии.
|
||||||
Префикс уникален по всему канону (реестр — `prefixes.toml`), поэтому
|
На **правила** ссылаются идентификатором без пути: `KEYS-5`. Префикс
|
||||||
идентификатор не зависит от того, на какой оси файл лежит сегодня.
|
уникален по всему канону (реестр — `prefixes.toml`), поэтому идентификатор
|
||||||
|
не зависит ни от оси, ни от того, как собран файл у потребителя.
|
||||||
|
|
||||||
Тест — по тому, замена чего убивает правило:
|
Тест — по тому, замена чего убивает правило:
|
||||||
|
|
||||||
@@ -93,6 +93,12 @@ prefix: KEYS
|
|||||||
по формуле, и не переиспользуется никогда. Правила адресуются идентификатором
|
по формуле, и не переиспользуется никогда. Правила адресуются идентификатором
|
||||||
`KEYS-5` — без пути к файлу. Подробности формы — `LANGUAGE.md`.
|
`KEYS-5` — без пути к файлу. Подробности формы — `LANGUAGE.md`.
|
||||||
|
|
||||||
|
Буква `X` в начале префикса зарезервирована за репозиториями: канон её не
|
||||||
|
занимает никогда, а локальные правила потребителя берут префиксы только на
|
||||||
|
неё (`XTIM`, `XLOG`). Так столкновение локального префикса с будущим
|
||||||
|
префиксом канона невозможно по построению, и согласовывать заранее ничего не
|
||||||
|
нужно.
|
||||||
|
|
||||||
## Расширение
|
## Расширение
|
||||||
|
|
||||||
Файл в `lang/` или `stack/` может объявить в шапке ещё и базу:
|
Файл в `lang/` или `stack/` может объявить в шапке ещё и базу:
|
||||||
@@ -106,67 +112,88 @@ extends: arch/db-identifiers.md
|
|||||||
неверно сформулировано условие применимости (чинится в каноне), либо
|
неверно сформулировано условие применимости (чинится в каноне), либо
|
||||||
репозиторий на базу просто не подписан.
|
репозиторий на базу просто не подписан.
|
||||||
|
|
||||||
`extends` — документация связи, а не механизм: `conv` о ней только
|
`extends` — документация связи, а не механизм: за тем, чтобы база лежала
|
||||||
напоминает при `add` и никак не следит за тем, чтобы база лежала рядом.
|
рядом, никто не следит.
|
||||||
|
|
||||||
## Служебная разметка
|
## Копия в репозитории
|
||||||
|
|
||||||
**Шапка копии** ставится при `conv add` и в каноне не хранится:
|
Копия плоская: **один файл на тему**, слои осей идут внутри него секциями в
|
||||||
|
порядке `arch` → язык → стек. Пути канона в копии не воспроизводятся.
|
||||||
```yaml
|
|
||||||
---
|
|
||||||
origin: arch/time.md # откуда взято
|
|
||||||
origin_hash: a1b2c3d4 # отпечаток канона на момент синхронизации
|
|
||||||
synced: 2026-07-25
|
|
||||||
local: нет # или: чем и почему разошлись
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
`origin_hash` — контент-отпечаток, а не git-SHA. Он позволяет отличать
|
|
||||||
«канон обновился» от «изменено локально»; без него `status` умеет только
|
|
||||||
«differs», а такой отчёт быстро перестают читать. Прочие ключи шапки
|
|
||||||
(`prefix`, `extends`) — часть документа: они сравниваются наравне с телом.
|
|
||||||
|
|
||||||
**Локальные регионы** — куски, принадлежащие репозиторию по определению.
|
|
||||||
Из сравнения исключаются, поэтому вечного шума в `diff` не дают:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
<!-- local:механизировано -->
|
|
||||||
`AUTOINCREMENT` в новых миграциях — `internal/archrules`.
|
|
||||||
<!-- /local -->
|
|
||||||
```
|
|
||||||
|
|
||||||
Имя обязательно — перенос при `pull` идёт по именам, безымянные регионы
|
|
||||||
`conv` отвергает. Что всегда локально:
|
|
||||||
|
|
||||||
- **механизация** — канон не знает, у кого линтер уже настроен;
|
|
||||||
- **отступления** — «у нас пока не так», честно и поимённо;
|
|
||||||
- **разрешение условия** — «Здесь: INTEGER PK, id наружу не выходят»;
|
|
||||||
- **эталоны и ссылки** — имена функций, файлов, ADR конкретного репозитория;
|
|
||||||
- **список конвенций** в README репозитория.
|
|
||||||
|
|
||||||
Путь файла в каноне и имя региона — это API: переименование осиротит все
|
|
||||||
копии (`origin` строковый). Переименовывать — только вместе с обходом
|
|
||||||
потребителей.
|
|
||||||
|
|
||||||
После `pull` копию нужно перечитать глазами: содержимое региона могло
|
|
||||||
устареть относительно переписанного вокруг текста, и автоматика этого не
|
|
||||||
увидит.
|
|
||||||
|
|
||||||
## Раскладка в репозитории
|
|
||||||
|
|
||||||
Копии повторяют структуру канона:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
docs/conventions/
|
docs/conventions/
|
||||||
README.md собственный, не синхронизируется
|
README.md собственный, не собирается
|
||||||
arch/db-identifiers.md
|
time.md arch/time.md + lang/go/time.md
|
||||||
lang/go/db-identifiers.md
|
db-identifiers.md arch/db-identifiers.md + lang/go/db-identifiers.md
|
||||||
|
app-directories.md arch/… + stack/ansible/…
|
||||||
```
|
```
|
||||||
|
|
||||||
Подписка не описана отдельным файлом — она **и есть** набор лежащих файлов,
|
Так конвенция остаётся самодостаточным документом: тот, кто проверяет по ней
|
||||||
видимый в `git ls-files`. README директории перечисляет их одной плоской
|
код — человек или агент, — читает один файл и не собирает тему из трёх мест.
|
||||||
таблицей, чтобы вложенность не мешала навигации.
|
|
||||||
|
**Шапка копии** ставится при сборке и в каноне не хранится:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
origin: time
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
Больше в шапке ничего нет. Отпечатка канона и даты синхронизации в ней не
|
||||||
|
хранится: обновление перезаписывает файл в рабочем дереве, и что именно
|
||||||
|
изменилось, показывает `git diff` до коммита. Второй механизм сравнения
|
||||||
|
рядом с git не нужен.
|
||||||
|
|
||||||
|
**Маркер локальной части** — единственная машинно значимая разметка внутри
|
||||||
|
файла:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
<!-- conv:local -->
|
||||||
|
|
||||||
|
MIGR-2, MIGR-4 механизированы — `internal/archrules`.
|
||||||
|
MIGR-6 не соблюдается в `show_history`, `queue`: составные ключи там
|
||||||
|
появились до конвенции, миграция данных не окупается.
|
||||||
|
```
|
||||||
|
|
||||||
|
Всё ниже маркера принадлежит репозиторию и переживает обновление; всё выше —
|
||||||
|
пересобирается из канона. Маркер один и безымянный, поэтому у него нет
|
||||||
|
имени, которое можно осиротить переименованием.
|
||||||
|
|
||||||
|
Ниже маркера живёт то, чего канон о репозитории не знает: механизация,
|
||||||
|
отступления, разрешение условий («Здесь: INTEGER PK, id наружу не выходят»),
|
||||||
|
ссылки на ADR и код, а также **собственные правила** — с префиксом на `X`,
|
||||||
|
по тем же правилам формы, что и канон.
|
||||||
|
|
||||||
|
Если местных правок стало больше, чем каноничного текста, копия перестаёт
|
||||||
|
быть копией: `origin:` из шапки убирают, и дальше это обычный документ
|
||||||
|
репозитория. Файл, оставивший шапку, при следующем обновлении потеряет
|
||||||
|
всё, что выше маркера.
|
||||||
|
|
||||||
|
## Манифест
|
||||||
|
|
||||||
|
Откуда взяты копии и где брать обновления — `.conventions.toml` в корне
|
||||||
|
репозитория-потребителя:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
source = "ssh://git@git.vakhrushev.me:2222/av/dev-conventions.git"
|
||||||
|
|
||||||
|
lang = ["go"]
|
||||||
|
stack = ["sqlite", "htmx"]
|
||||||
|
|
||||||
|
topics = ["time", "config", "db-identifiers"]
|
||||||
|
```
|
||||||
|
|
||||||
|
`lang` и `stack` выбирают строку разреженной матрицы: файл темы собирает
|
||||||
|
только те слои, которые репозиторию подходят. `topics` — подписка; списка
|
||||||
|
подписчиков у канона по-прежнему нет, список тем есть только у потребителя.
|
||||||
|
|
||||||
|
Как именно инструмент добирается до канона — путь на диске, git, HTTP —
|
||||||
|
дело инструмента, а не модели. Манифест отвечает на два вопроса: откуда это
|
||||||
|
взято и где искать обновления.
|
||||||
|
|
||||||
|
Отдельного лок-файла нет. Он отвечал бы на «что было в прошлый раз», а на
|
||||||
|
это отвечает git: копии закоммичены, автоматического обновления не
|
||||||
|
существует, и любое изменение проходит через чтение диффа человеком.
|
||||||
|
|
||||||
## Контракт с агентом
|
## Контракт с агентом
|
||||||
|
|
||||||
@@ -175,45 +202,53 @@ docs/conventions/
|
|||||||
`AGENTS.md` каждого потребителя должен явно говорить:
|
`AGENTS.md` каждого потребителя должен явно говорить:
|
||||||
|
|
||||||
> Файлы в `docs/conventions/` с шапкой `origin:` — копии из канона
|
> Файлы в `docs/conventions/` с шапкой `origin:` — копии из канона
|
||||||
> `dev-conventions`. Репозиторное пишется только внутрь
|
> `dev-conventions`. Репозиторное пишется только ниже `<!-- conv:local -->`;
|
||||||
> `<!-- local:… -->`. Правка вне регионов — либо `conv push` в канон, либо
|
> всё выше маркера перезаписывается при обновлении. Своё правило — с
|
||||||
> запись причины в `local:`.
|
> префиксом на `X`.
|
||||||
|
|
||||||
## Команды
|
## Команды
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
conv list # что есть в каноне
|
conv list # какие темы есть в каноне
|
||||||
conv add arch/time.md # взять к себе (можно несколько за раз)
|
conv add time # добавить тему в манифест и собрать файл
|
||||||
conv status # ok / изменено локально / канон обновился / разошлись
|
conv pull # пересобрать всё, что перечислено в манифесте
|
||||||
conv diff [arch/time.md] # чем копия отличается, без учёта локальных регионов
|
|
||||||
conv pull arch/time.md # забрать обновление канона (регионы переносятся)
|
|
||||||
conv push arch/time.md # вернуть локальное улучшение в канон
|
|
||||||
conv push --new lang/go/x.md # завести в каноне новую конвенцию
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`status` и `diff` всегда завершаются кодом 0: это отчёт, а не проверка.
|
Отчёт о том, что изменилось, отдельной командой не выдаётся: после `pull`
|
||||||
Расхождение — нормальное состояние, а постоянный шум в `diff` означает не
|
его показывает `git diff`, а решение — принять, поправить или откатить —
|
||||||
«догони канон», а «пора разрезать файл». Симметрично: разросшийся до спора
|
принимает человек перед коммитом.
|
||||||
с базой локальный регион означает «пора чинить условие применимости в
|
|
||||||
каноне».
|
Транспорт обратно в канон не предусмотрен. Улучшение, найденное в
|
||||||
|
репозитории, переносится в канон руками: это редкая операция, и её цена —
|
||||||
|
не аргумент против того, чтобы направление оставалось односторонним.
|
||||||
|
|
||||||
Запускать из корня репозитория:
|
Запускать из корня репозитория:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
~/projects/private/dev-conventions/conv status
|
~/projects/private/dev-conventions/conv pull
|
||||||
```
|
```
|
||||||
|
|
||||||
Обёртка в раннере репозитория (`inv conventions -- status` для ansible,
|
Обёртка в раннере репозитория (`inv conventions -- pull` для ansible,
|
||||||
`task conventions -- status` для Go) — тонкий проброс аргументов, чтобы
|
`task conventions -- pull` для Go) — тонкий проброс аргументов, чтобы
|
||||||
логика не размножалась по репозиториям в двух диалектах.
|
логика не размножалась по репозиториям в двух диалектах.
|
||||||
|
|
||||||
## Жизненный цикл
|
## Жизненный цикл
|
||||||
|
|
||||||
- **В канон.** Новая конвенция пишется в том репозитории, где заболело, и
|
- **В канон.** Новая конвенция пишется в том репозитории, где заболело, и
|
||||||
продвигается `conv push --new`. Локальные регионы при этом опустошаются:
|
переносится в канон, когда стало ясно, что общего в ней больше, чем
|
||||||
в канон едет только норма.
|
местного. Локальная часть при этом не едет: в канон попадает только норма,
|
||||||
|
а префикс на `X` меняется на канонический — то есть правила получают новые
|
||||||
|
идентификаторы.
|
||||||
- **Из канона.** Устаревшая конвенция удаляется вместе с обходом
|
- **Из канона.** Устаревшая конвенция удаляется вместе с обходом
|
||||||
потребителей — тихо осиротить копии нельзя.
|
потребителей — тихо осиротить копии нельзя.
|
||||||
- **История.** Канон коммитится при каждом `push`: `origin_hash` отвечает
|
- **История.** Канон коммитится при каждой правке: только git канона
|
||||||
на «отличается ли», но только git канона отвечает на «почему база
|
отвечает на вопрос, почему база сформулирована так.
|
||||||
сформулирована так».
|
|
||||||
|
## Состояние
|
||||||
|
|
||||||
|
Модель выше — согласованная, а не реализованная. `conv` пока собран под
|
||||||
|
прежнюю: зеркальное дерево копий вместо плоского, именованные регионы
|
||||||
|
`<!-- local:имя -->` вместо одного маркера, `origin_hash` в шапке и команды
|
||||||
|
`status`, `diff`, `push`. Двенадцать файлов канона всё ещё несут 31 пустой
|
||||||
|
именованный регион. Ни один репозиторий-потребитель не подключён, поэтому
|
||||||
|
переход никого не ломает.
|
||||||
|
|||||||
+4
-2
@@ -21,8 +21,10 @@
|
|||||||
# Пути даются от корня репозитория, а не от `conventions/`: реестр покрывает
|
# Пути даются от корня репозитория, а не от `conventions/`: реестр покрывает
|
||||||
# и обвязку тоже.
|
# и обвязку тоже.
|
||||||
#
|
#
|
||||||
# Локальные правила репозиториев берут свои префиксы и объявляют их в
|
# Буква `X` в начале префикса зарезервирована за репозиториями-потребителями:
|
||||||
# `.conventions.toml` копии. Они обязаны не пересекаться с этим реестром.
|
# канон её не занимает никогда, локальные правила берут префиксы только на
|
||||||
|
# неё (XTIM, XLOG). Согласовывать их с этим реестром не нужно — столкновение
|
||||||
|
# невозможно по построению.
|
||||||
|
|
||||||
[live]
|
[live]
|
||||||
DIRS = "conventions/arch/app-directories.md"
|
DIRS = "conventions/arch/app-directories.md"
|
||||||
|
|||||||
Reference in New Issue
Block a user