Проверка формы знала имя схемы и перечень артефактов константами — и это не наше решение, а состояние чужого инструмента. OpenSpec переименует артефакт: правила под прежним именем перестанут применяться, конфиг останется выглядеть написанным, канон продолжит требовать прежнее. Молчат при этом все три стороны, и заметить расхождение было некому. Сторожем поставлено сравнение версий. check спрашивает openspec --version — десятые доли секунды — и сравнивает major.minor с той, на которой форма сверялась. Разошлось — замечание, не отказ, с именем команды, которая перепроверяет. Патч-версия в сравнение не берётся намеренно: формы она не меняет, а нагоняй на каждый багфикс приучает пролистывать весь блок. Перепроверяет docs.py openspec-form: берёт openspec templates --json, то есть перечень артефактов текущей схемы, и печатает, что разошлось с константами. Дорогой вызов вынесен из check сознательно — он стоит втрое дороже опроса версии, а ответ меняется только вместе с версией. Дешёвая проверка служит воротами дорогой, и дорогая не ржавеет, потому что зовут её не по памяти. Чинится расхождение в плагине, а не в проекте, и команда печатает три адреса правки списком: константы скрипта, скелет, журнал версий канона. Пятой проверкой формы стали ключи под rules: — это имена артефактов, и правило, адресованное несуществующему, не применяется молча. rules.spec вместо rules.specs даёт конфиг, выглядящий написанным и не работающий. Первый вариант этой проверки искал ключи отступом по всему файлу и нашёл их внутри литерального блока context: строки «Language: Russian» и «av-dev-pm:review-pipeline» выглядят ключами. Оба живых проекта из-за этого покраснели на правде. Теперь разбор идёт от строки rules: до следующего ключа нулевой колонки; на тех же проектах чисто, а опечатка в имени артефакта по-прежнему находится. Решение — 48. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
515 lines
48 KiB
Markdown
515 lines
48 KiB
Markdown
# Журнал версий канона
|
||
|
||
Одна запись на версию. Проект знает свою версию из `docs/.pm.json`; `canon
|
||
upgrade` идёт по записям снизу вверх от версии проекта до текущей и делает то,
|
||
что в них названо.
|
||
|
||
Правило записи: **что добавилось, что переехало, что удалено, что сделать
|
||
проекту**. Без последнего пункта запись бесполезна — по ней и работает
|
||
`upgrade`.
|
||
|
||
Версия — целое число. Обратной совместимости у канона нет: есть «приведён» и «не
|
||
приведён».
|
||
|
||
---
|
||
|
||
## Версия 7 — 2026-08-07
|
||
|
||
`openspec/` был предпосылкой, о которой канон говорил, но за которой не следил.
|
||
Каталог назван в раскладке, `openspec/specs/` объявлен домом темы `requirements`,
|
||
`config.yaml` описан абзацем — а заводил всё это человек руками, и проверялось
|
||
из перечисленного ничего. Заведение нового проекта проходило мимо: `init`
|
||
собирал документы канона и оставлял проект без каталога, без которого не работают
|
||
ни `opsx:propose`, ни ревью дизайна, ни сверка требований.
|
||
|
||
Хуже отсутствия оказался файл из коробки. `openspec init` кладёт `config.yaml`,
|
||
где `context` и `rules` — закомментированный пример на английском. Такой файл
|
||
читается как настроенный: он есть, он валиден, имя правильное. Работает он как
|
||
пустой, и узнаётся это по предложению, написанному на другом языке, с
|
||
capability по имени пакета и без единого `SHALL`.
|
||
|
||
**Что изменилось:**
|
||
|
||
1. **`init` заводит OpenSpec сам** — `openspec init --tools claude`, до первого
|
||
документа канона. Команда названа в каноне поимённо, потому что её печатает
|
||
отказ `docs.py`.
|
||
2. **У `openspec/config.yaml` появилась каноническая форма** и скелет в
|
||
`skeletons.md`. Содержание — только то, что нужно **в момент порождения
|
||
артефакта**: язык, правила именования capability, придирки валидатора и
|
||
**адреса** документов канона. Пересказ паспорта, инвариантов, конвенций и
|
||
правил ревью в него не переносится.
|
||
3. **`docs.py check` проверяет пять вещей:** каталог `openspec/` есть; файл
|
||
называется `config.yaml` (`config.yml` OpenSpec читать не станет и об этом не
|
||
сообщит); `context` и `rules.specs` не остались примером, а правила для
|
||
`specs` называют `SHALL`; `context` называет `passport` и `CLAUDE.md`; ключи
|
||
под `rules:` — имена артефактов схемы, а не опечатки.
|
||
4. **За свежестью формы следит машина, а не память.** Схема и перечень
|
||
артефактов — слепок чужого инструмента; `check` сравнивает `major.minor`
|
||
установленного OpenSpec с версией, на которой форма сверялась, и при
|
||
расхождении даёт замечание. Перепроверяет `docs.py openspec-form`, и чинится
|
||
расхождение **в плагине, а не в проекте**.
|
||
5. **Шестое проверяет агент.** Отличить ссылку на документ от пересказа документа
|
||
машина не умеет — это работа `doc-consistency`, и в таблице «Что проверяет
|
||
машина, а что человек» она стоит строкой.
|
||
|
||
**Что переехало:** ничего в раскладке `docs/`. Ни один файл не переименовывается
|
||
и не перемещается.
|
||
|
||
**Что сделать проекту:**
|
||
|
||
1. Нет `openspec/` — завести: `openspec init --tools claude`. Команда кладёт ещё
|
||
и `.claude/skills/openspec-*` с `.claude/commands/opsx/*`; это её нормальная
|
||
работа, удалять их не надо.
|
||
2. Открыть `openspec/config.yaml` и привести к скелету из
|
||
[skeletons.md](skeletons.md): блок `context` с языком, правилами именования
|
||
capability, требованием `SHALL` и **адресами** `docs/passport.md` и
|
||
`CLAUDE.md`; блок `rules` с четырьмя правилами для `specs`.
|
||
3. **Вычистить из `context` пересказ.** Инварианты, перечень конвенций, состав
|
||
шагов гейта, правило выбора метки и состав проходов ревью — заменить ссылкой
|
||
на дом. Признак пересказа простой: строку можно опровергнуть, открыв другой
|
||
файл проекта.
|
||
4. Проверить имя файла: `config.yml` переименовать в `config.yaml`. Если жили оба
|
||
— содержимое `.yml` до сих пор не читалось никем, и переносить из него нужно
|
||
именно то, чего нет в `.yaml`.
|
||
5. `docs/.pm.json`: `"canon": 7`.
|
||
|
||
---
|
||
|
||
## Версия 6 — 2026-08-07
|
||
|
||
Версия 5 объявила: **каждый документ `docs/` — тема ревью**. Правило оказалось
|
||
верным ровно наполовину и потому вредным целиком. Паспорт и схему хранилища
|
||
ревью читает, но темами они не являются — они задают границу, по которой судит
|
||
чужая тема. Журнал решений и журнал наблюдений ревью изменения не нужны вовсе:
|
||
ADR объясняет прошлое решение, а не предъявляет требование к изменению.
|
||
|
||
Разметчик, применявший плоское правило буквально, обязан был либо завести
|
||
фантомные темы `passport`, `adr`, `database`, `research` и продублировать ими
|
||
работу тем `architecture` и `operations`, либо потерять четыре документа молча —
|
||
а молчащая потеря и есть то, против чего канон написан.
|
||
|
||
**Что изменилось:**
|
||
|
||
1. **Три категории документов вместо одной.** Разрез проверяемый: можно ли по
|
||
документу сказать «в этом изменении сделано не так»? **Тема** — да, прямо
|
||
(`conventions`, `security`, `architecture`, свои документы проекта).
|
||
**Источник темы** — нет, но он задаёт границу для чужой темы (`passport.*` →
|
||
`architecture`, `database.*` → `operations`, `CLAUDE.md` → `autotests`,
|
||
`openspec/specs/` → `requirements`). **Процессный документ** — нет, он про то,
|
||
как мы работаем (`tasks/`, `review.*`, `adr.*`, `research.*`, `.pm.json`).
|
||
2. **Категории `источник` и `процессный` закрыты, категория `тема` открыта.**
|
||
Прежде открытым был весь список, и «не темы ровно две» противоречило
|
||
собственной раскладке канона. Теперь пополняется только одно множество, и
|
||
документ, которого нет в раскладке, — однозначно своя тема проекта.
|
||
3. **`adr/` и `research/` уходят из входа ревью изменения.** Прогон их больше не
|
||
открывает. Проверяться они не перестали: ADR без ссылки на архивный
|
||
`design.md`, замена без парного статуса, число без провенанса — это по-прежнему
|
||
работа `doc-consistency` и `doc-code-drift`, на сессии между спринтами.
|
||
4. **`docs.py` печатает категорию в отказе.** «Нет источника passport» читается
|
||
иначе, чем «нет темы security». Обязательность при этом не изменилась:
|
||
заводятся все документы одинаково и с первого дня.
|
||
5. **У задачи появилась метка — `small`, `medium` или `large`.** Это итог
|
||
классификации и **единственный вход, по которому конвейер выбирает
|
||
исполнителей** на обеих стадиях ревью. Прежние имена `quick`, `standard` и
|
||
`wide` описывали глубину прогона, то есть свойство ревью; метка описывает
|
||
**задачу** — а выбирают по ней одно и то же. Слово «ступень» уходит:
|
||
у одной вещи одно имя.
|
||
6. **Метка выводится из двух осей и не равна ни одной из них.** Размер (малое,
|
||
среднее, крупное) и сложность (знакомое, незнакомое); метка — максимум по
|
||
ним. Малое **незнакомое** изменение получает `large`, трогая один узел, —
|
||
поэтому размер и метка пишутся отдельными строками, и выводить одно из
|
||
другого нельзя.
|
||
|
||
**Цена, записанная явно:** расхождение изменения с записанным решением прогоном
|
||
больше не ловится. Раньше архитектурный проход мог сказать «здесь отменено
|
||
решение ADR-2026-03-11, парного статуса нет»; теперь это скажет только сверка
|
||
документации. Сделка сознательная: чтение всего каталога решений оплачивалось на
|
||
каждой задаче, а срабатывало на единицах.
|
||
|
||
**Что переехало:** ничего в раскладке. Ни один файл не переименовывается и не
|
||
перемещается.
|
||
|
||
**Что сделать проекту:**
|
||
|
||
1. `docs/review.*`, подраздел «Вопросы по темам»: убрать вопросы, адресованные
|
||
`passport`, `database`, `adr`, `research` и `review` — **ни одно из этих имён
|
||
больше не тема**. Под каноном 5 темой был каждый документ `docs/`, поэтому
|
||
такие вопросы там законны и почти наверняка есть. Переадресовать:
|
||
про границу домена и про решение → `architecture`; про хранилище, настройку и
|
||
измеренное число → `operations`. Вопрос, который никуда не переадресовывается,
|
||
удалить, а не оставить висеть: адресованный несуществующей теме, он не
|
||
задаётся никем и молча.
|
||
2. Там же, «Недоступно проверке»: те же пять имён убрать из разнесения по темам,
|
||
переразнеся содержимое по оставшимся.
|
||
3. Там же: подраздел **«Триггеры профиля» → «Триггеры метки»**, и разнести его
|
||
на **три** списка вместо двух — «крупное здесь» (про объём), «незнакомое
|
||
здесь» (про форму решения) и «мелкое здесь» (опускает до `small`). Раньше
|
||
первые две оси были склеены в один список, и потому объём в правило по факту
|
||
не входил.
|
||
4. **Переименовать метки прогона везде, где проект их называет** — в «Триггерах
|
||
метки», в «Недоступно проверке», в журнале дефектов: `quick` → **`small`**,
|
||
`standard` → **`medium`**, `wide` → **`large`**. Метка это итог классификации
|
||
задачи, и три её значения — часть общего словаря канона и конвейера. Слово
|
||
«ступень» из документов уходит: у одной вещи одно имя.
|
||
5. Проверить, что свои темы проекта не совпадают именем с закрытыми категориями:
|
||
`docs/passport/`, `docs/adr/`, `docs/research/`, `docs/database/`,
|
||
`docs/review/` — это слоты канона, а не свои темы, и своим смыслом их
|
||
наполнять нельзя.
|
||
6. Ничего не заводить и не удалять: раскладка канона 6 совпадает с раскладкой
|
||
канона 5 файл в файл.
|
||
7. `docs/.pm.json`: `"canon": 6`.
|
||
|
||
---
|
||
|
||
## Версия 5 — 2026-08-06
|
||
|
||
Канон перестал быть списком файлов и стал **списком тем ревью**. Раскладка та же,
|
||
но читается иначе: документ в `docs/` — это направление проверки, а не просто
|
||
текст. Отсюда три правки, и все три развязывают то, что раньше было жёстко
|
||
сцеплено.
|
||
|
||
**Что изменилось:**
|
||
|
||
1. **Тема живёт файлом или каталогом, на выбор проекта.** `docs/security.md` и
|
||
`docs/security/` — одно и то же; тема разрослась, стала каталогом с
|
||
`README.md` — канон не сменился и версия не двинулась. Прежде форма была
|
||
задана поимённо: `conventions`, `research` и `adr` обязаны были быть
|
||
каталогами, остальные — файлами, и обосновать это было нечем. Обе формы сразу
|
||
— ошибка: два дома для одного факта расходятся молча.
|
||
2. **Список тем открытый.** Всё, что проект кладёт в `docs/`, становится темой
|
||
ревью и попадает в план каждого прогона; именной оптики у такой темы нет, её
|
||
разбирает общий проход конвейера, заведённый ровно за этим.
|
||
Прежде `docs.py` называл незнакомый файл «вне канона» — теперь называет своей
|
||
темой проекта и перечисляет их в отчёте. Не темы ровно две: `docs/tasks/` и
|
||
`docs/review.*`.
|
||
3. **`AGENTS.md` рядом с `CLAUDE.md` — законно.** Он почти стандарт; обязателен
|
||
по-прежнему только `CLAUDE.md`, но если лежат оба, читаются оба, и проверки
|
||
канона смотрят на второй так же, как на первый.
|
||
|
||
**Что переехало:**
|
||
|
||
- в `docs/review.*`: **«Вопросы к проходам» → «Вопросы по темам»**, форма
|
||
`<тема>: <вопрос> (<провенанс>)`. Причина не косметическая: вопрос,
|
||
адресованный проходу, перестал задаваться молча в тот день, когда тот уехал в
|
||
верхнюю ступень ревью. Тема переезд прохода переживает, имя прохода — нет;
|
||
- там же **«Недоступно проверке» — по темам**, оба подраздела.
|
||
|
||
**Что сделать проекту:**
|
||
|
||
1. Ничего не переименовывать, если всё уже разложено по канону 4: обе формы
|
||
дома законны, и текущая — одна из них.
|
||
2. `docs/review.*`, подраздел «Вопросы к проходам»: переименовать в «Вопросы по
|
||
темам» и переадресовать каждый вопрос теме вместо имени прохода. Темы ядра —
|
||
`requirements`, `autotests`, `conventions`, `architecture`, `security`,
|
||
`operations`.
|
||
3. Там же «Недоступно проверке»: разнести обе половины по темам.
|
||
4. Проверить, не лежит ли в `docs/` документ, который раньше считался лишним и
|
||
потому не заводился. Теперь он законен и станет темой ревью — это и есть
|
||
способ добавить проверку, которой в конвейере нет.
|
||
5. `docs/.pm.json`: `"canon": 5`.
|
||
6. Позвать судей `doc-consistency` и `doc-code-drift` — шагом 6 `upgrade`.
|
||
|
||
## Версия 4 — 2026-08-05
|
||
|
||
Две правки, обе про то, как читается каталог задач. Первая — секция роадмапа
|
||
переименована, и вместе с именем расширен её смысл; достигнутое переехало вниз.
|
||
Вторая — **у каждой записи появился тип, и тип определяет, что с записью можно
|
||
делать**. Раскладка не меняется, файлов канона не прибавляется.
|
||
|
||
**Что переехало:**
|
||
|
||
- секция роадмапа `Разработка` → **`Сопровождение`** (англ. `Tooling` →
|
||
**`Operations`**). Прежнее имя называло слишком много: роадмап **весь** про
|
||
разработку, и секция с таким именем не отличалась от остальных ничем;
|
||
- **тип записи** — из префикса заголовка (`[goal]`/`[idea]`) и тега
|
||
`kind:<род>` в **поле меты `Тип`** первой строкой. Эмодзи в заголовке от него
|
||
производна;
|
||
- **поле места** у задачи: `Секция` → **`Категория`**. У цели остаётся `Секция`:
|
||
у задачи поле называет полку домена, в которую она вернётся из спринта, у цели
|
||
— часть роадмапа, то есть состояние очереди. Одно имя на два смысла их и
|
||
смешивало.
|
||
|
||
**Что добавилось:**
|
||
|
||
1. **Смысл секции расширен.** Было «инструмент и процесс», стало «чем держат
|
||
проект: инструмент, процесс, эксплуатация». Метрики, логи, инфраструктура,
|
||
выкладка и дежурство — сюда же. Расширение не косметическое: английское
|
||
`Operations` при узком смысле обещало бы эксплуатацию, а внутри лежал бы
|
||
линтер.
|
||
2. **Общий словарь трёх мест** — [canon.md](canon.md), раздел «Сопровождение и
|
||
эксплуатация». Сопровождение — всё, чем держат проект; эксплуатация — его
|
||
часть, работа системы на проде. `ROADMAP.md`, секция `Сопровождение` — план
|
||
работ; `architecture.md`, раздел «Эксплуатация» — как устроено сейчас;
|
||
эксплуатационный проход ревью — оптика проверки. Слить их в одно слово
|
||
нельзя: они отвечают на разные вопросы. Слово **«поддержка» не употребляется
|
||
вовсе** — в нём слышится помощь пользователю.
|
||
3. **Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
||
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
||
целей. «Дежурный видит состояние на одном экране» — сопровождение. Одни и те
|
||
же метрики попадают в разные секции роадмапа, и это верно.
|
||
4. **Порядок секций стал каноническим**, и `Готово` переехало **вниз**:
|
||
`Запланировано` | `Направления` | `Сопровождение` | `Готово`. Достигнутое
|
||
копится — через год этой секции больше, чем всех остальных вместе, — и стоя
|
||
первой она отодвигает за экран то, ради чего роадмап открывают чаще всего.
|
||
Порядок проверяет `tasks.py check`, переставляет `check --fix`.
|
||
5. **Заголовок секции отбивается пустой строкой с обеих сторон.** Прежде
|
||
проверялась только строка после заголовка; перестановка секций двигает целые
|
||
блоки, и два заголовка оказываются вплотную. Правит `check --fix`.
|
||
6. **Тип — единственная ось записи, закрытый словарь из пяти значений:**
|
||
`goal` | `feature` | `fix` | `chore` | `research`. Осей было две — тип записи
|
||
(`goal`/`idea`/`task`) и род работы (`kind:` тегом), — но из двенадцати
|
||
клеток произведения законны были шесть, а алгоритм работы крепится к роду, а
|
||
не к типу. Оси схлопнуты.
|
||
7. **Тип задаёт схему тела:** какие разделы обязательны, какие допустимы, нужна
|
||
ли цель, берётся ли запись в спринт. Проверяет `sprint take`, замечания даёт
|
||
`check`. Два раздела новые: **`Воспроизведение`** у `fix` (не
|
||
воспроизводится — это `research`, а не `fix`; правило было записано и не
|
||
проверялось) и **`Вопрос` + `Куда ляжет ответ`** у `research` вместо
|
||
критериев приёмки (приёмка разведки — записанный ответ, и критерии в форме
|
||
«оракул: тест» ей натянуты).
|
||
8. **Тип `idea` упразднён.** Он значил не род работы, а состояние
|
||
незаполненности, а состояние типом быть не может. Теперь оно называется
|
||
честно: `research` без раздела «Вопрос» — **сырьё**. В спринт не берётся, как
|
||
и прежняя идея, лежит **в конце своей категории** (проверяет `check`,
|
||
переставляет `--fix`) и отбирается `list --raw`. Порядка «по важности» в
|
||
беклоге по-прежнему нет: этот порядок производен от типа, а не назначен
|
||
человеком.
|
||
9. **Алгоритм работы над каждым типом** — отдельным файлом,
|
||
`skills/tasks/references/task-<тип>.md`: схема, что проверяет машина, что
|
||
человек, и порядок шагов.
|
||
10. **Имена файлов проверяются.** Правило «текст русский, имена английские»
|
||
стояло в каноне и не было подкреплено ничем: `docs.py` имён не смотрел вовсе.
|
||
Теперь смотрит — кириллица и не-kebab-case **жёстко**, форма имени
|
||
`ADR-ГГГГ-ММ-ДД-slug.md` жёстко, транслит **эвристикой**, то есть
|
||
замечанием. Заодно из раскладки канона убраны плейсхолдеры `<тема>.md`,
|
||
приглашавшие называть файлы по-русски.
|
||
11. **Два агента вместо обещания.** В каноне была таблица «Что проверяет машина,
|
||
а что человек», и её правая колонка три версии описывала судью, которого не
|
||
существовало. Судьи заведены и разведены по глубине: **`doc-consistency`**
|
||
(документ ↔ документ ↔ openspec: факт в двух домах, прямое противоречие,
|
||
поведение в `architecture.md` вместо спек, ADR без ссылки и парного статуса,
|
||
число без провенанса, заглушка вместо честной строки); **`doc-code-drift`**
|
||
(документ ↔ код по закрытому перечню фактов). Оба зовутся раз в спринт на
|
||
сессии, а также после adopt и после upgrade, на весь канон разом.
|
||
|
||
**Что сделать проекту:**
|
||
|
||
1. Переименовать заголовок секции в `docs/tasks/ROADMAP.md`: `## Разработка` →
|
||
`## Сопровождение` (или `## Tooling` → `## Operations`, если индекс
|
||
английский). **`check --fix` этого не сделает**: регистр канонической секции
|
||
он правит сам, а чужую секцию только называет ошибкой — смысл за человеком.
|
||
2. Поправить поле `- **Секция:**` в файлах целей, которые в ней лежат. Порядок
|
||
именно такой: сперва заголовок, потом `python3 tasks.py check --dir
|
||
docs/tasks` покажет расхождение поимённо.
|
||
3. Перечитать состав секции: цели про выкладку, метрики, логи и инфраструктуру,
|
||
если они лежали в `Направлениях` за неимением места, переезжают сюда.
|
||
4. Прогнать `python3 tasks.py check --dir docs/tasks --fix`. За один проход он
|
||
переставит секции роадмапа в канонический порядок (`Готово` уедет вниз вместе
|
||
со всем содержимым), поправит отбивку заголовков и **переведёт записи на
|
||
типы**: перенесёт значение из тега `kind:` и префикса `[goal]`/`[idea]` в поле
|
||
`Тип`, снимет тег, поставит эмодзи в заголовок, переименует `Секция` →
|
||
`Категория` у задач и снесёт сырьё в конец категорий.
|
||
5. Разобрать то, что `--fix` вернул пометкой `НЕОДНОЗНАЧНО`. Главный случай —
|
||
**записи без типа**: заведённые до появления рода работы, они не несут ни
|
||
тега, ни префикса, и машина их не угадывает (`feature` от `chore` не
|
||
отличает). Проставить руками: `edit <слаг> --type …`.
|
||
6. Дописать новые обязательные разделы у задач, которые собираются в спринт:
|
||
`Воспроизведение` у каждого `fix`, `Вопрос` и `Куда ляжет ответ` у каждого
|
||
`research`. Не «заодно по всему беклогу», а порциями переоценки: `check`
|
||
ошибкой это не считает, отказывает только `sprint take`. Сколько задач готово
|
||
к взятию, печатает блок здоровья `check`.
|
||
7. Прогнать `python3 docs.py check`: он назовёт имена файлов не по правилу.
|
||
Кириллицу и не-kebab-case править обязательно, транслит — по решению
|
||
человека. **Переименование ADR это перенос ссылок**: слаг стоит в
|
||
`adr/README.md`, в `architecture.md` и в чужих документах, и делается одним
|
||
проходом, иначе останутся битые ссылки (их `docs.py` потом и покажет).
|
||
8. `docs/review.md`, подраздел «Триггеры профиля» — переписать целиком, он
|
||
отстал дважды. Снести перечень мест для `deep`: профиль упразднён вместе с
|
||
проходом независимой реализации, и перечень стал указателем в пустоту.
|
||
Оставшийся перечень перевести на новое правило: `wide` теперь означает не
|
||
«новое понятие», а **крупное или незнакомое** изменение и рассчитан на 5–10%
|
||
задач; отдельным списком назвать, что здесь считается **мелким** (это `quick`).
|
||
Форма подраздела — в [skeletons.md](skeletons.md). Там же проверить журнал
|
||
дефектов и «Недоступно проверке» на упоминания независимой реализации: класс
|
||
«форма решения, где спека выбора не сделала» переезжает в подраздел «перестали
|
||
проверять сознательно», а рядом с ним встаёт вторая честная строка — на
|
||
`quick` и `standard` не проверяется ничего, что требует запуска.
|
||
9. `docs/.pm.json`: `"canon": 4`.
|
||
10. Позвать **обоих судей** — `doc-consistency` и `doc-code-drift`, шагом 6
|
||
`upgrade`. Пунктов выше десять, половина из них ручная, и именно здесь видно,
|
||
какие сделаны только наполовину: переименования секций и полей разводят
|
||
документы, а `check` сверяет число версии, а не существо. Первый прогон на
|
||
живом проекте вдобавок самый урожайный — правило единственного дома до сих пор
|
||
никто не проверял. Разбирать порциями, а не одним заходом.
|
||
|
||
## Версия 3 — 2026-08-04
|
||
|
||
Роадмап стал **состоянием проекта**, а не очередью работ: цель — возможность
|
||
приложения, задача — шаг к ней, достигнутое из роадмапа не исчезает. Плюс род
|
||
работы, раздел «Затрагивает» и новое умолчание профиля ревью. Раскладка меняется
|
||
в одном файле, но переименование и смена секций тянут за собой ссылки, поэтому
|
||
шаги делаются одним заходом.
|
||
|
||
**Что добавилось:**
|
||
|
||
1. **Род работы** — тег `kind:<род>` в мете задачи, словарь закрыт:
|
||
`feature` | `fix` | `chore` | `research`. Обязателен у задачи, у цели
|
||
запрещён. `sprint take` без него отказывает, `check` о пропаже напоминает
|
||
замечанием. Определение — [canon.md](canon.md), раздел `tasks/`; смысл и
|
||
причина, почему тегом, — в SKILL.md скилла `tasks`, раздел «Род работы».
|
||
2. **Раздел «Затрагивает»** в теле задачи — перечень границ, которых изменение
|
||
касается (эндпоинт, таблица и миграция, формат на диске, публичный тип). Как
|
||
и критерии приёмки, требуется к взятию в спринт, а не к заведению.
|
||
3. **Секции роадмапа** — четыре вместо двух и **канонические**, в отличие от
|
||
секций беклога: `Готово` (достигнутые цели строкой с датой, без ссылки на
|
||
файл), `Запланировано` (очередь значима), `Направления` (очереди нет),
|
||
`Разработка` (инструмент и процесс, не возможности приложения). Английский
|
||
вариант — `Done` | `Planned` | `Directions` | `Tooling`, один язык на весь
|
||
индекс. Переименованию проектом не подлежат: у каждой свой смысл, и в первую
|
||
пишет сам `close`; `tasks.py check` проверяет состав.
|
||
4. **Форма заголовка записи** — по типу: задача отвечает на «что нужно сделать»
|
||
и пишется глаголом в неопределённой форме («Не отбрасывать молча лишние
|
||
символы»), цель — на «что приложение будет уметь», идея просто называет, о
|
||
чём она. `check` считает заголовки не в форме действия и печатает число в
|
||
блоке здоровья. Годность формулировки — не машине: её смотрит новый агент
|
||
`task-form` (форма записи, только чтение), а язык текста — `doc-wording`.
|
||
5. **Заголовки секций — с прописной, после заголовка пустая строка**, во всех
|
||
индексах. Написание канонических секций и отбивку правит `check --fix`; он
|
||
же сводит написание секции в мете файла с заголовком индекса.
|
||
6. **Язык проектных текстов** — [language.md](language.md), общий дом для
|
||
документов канона, задач, решений ADR и записок разведки: информационный
|
||
стиль (глагол вместо отглагольного существительного, активный залог, факт
|
||
вместо оценки, стоп-слова, параллельность), таблицы англицизмов и жаргона и
|
||
то, что из стиля отброшено намеренно. Проектных файлов не добавляет и
|
||
раскладку не меняет — это правила письма, а не новый слот.
|
||
7. **Умолчание профиля ревью сменилось** — это не раскладка, но проектный текст
|
||
под него уже написан. `standard` стал рабочим умолчанием: миграция схемы,
|
||
публичный контракт и инвариант ступень больше **не** поднимают, `wide`
|
||
означает новое понятие или структурную единицу. Подраздел «Триггеры профиля»
|
||
в `docs/review.md` остаётся на месте, но его содержимое надо перечитать.
|
||
|
||
**Что переехало:** `docs/tasks/PLAN.md` → `docs/tasks/ROADMAP.md`; достигнутая
|
||
цель — из небытия в секцию `Готово`: `close <цель> --implemented` удаляет файл, но
|
||
**оставляет строку с датой**. Прежде роадмап отвечал только «что осталось», и
|
||
половину его вопроса вели прозой руками. Вместе с
|
||
файлом переименован ключ конфига `tasks.plan` → `tasks.roadmap` и токены
|
||
команд: `--index plan` → `--index roadmap`, `init --plan-sections` →
|
||
`--roadmap-sections`, `init --plan` → `--roadmap`. Старый ключ в
|
||
`docs/.pm.json` не игнорируется молча — `tasks.py` останавливается и называет
|
||
переименование.
|
||
|
||
**Что удалено:** тип `[epic]`. Он был зонтиком между целью и задачами; зонтиком
|
||
стала цель, а слишком крупный шаг дробится на шаги помельче под ней. Ноль
|
||
употреблений на 97 записей двух живых проектов.
|
||
|
||
**Что сделать проекту:**
|
||
|
||
1. `git mv docs/tasks/PLAN.md docs/tasks/ROADMAP.md`.
|
||
2. Починить ссылки на прежнее имя: `grep -rn 'PLAN\.md' docs/ CLAUDE.md` —
|
||
заголовок самого файла («# План» → «# Роадмап»), строка в `docs/tasks/BACKLOG.md`,
|
||
упоминания в `docs/passport.md` и в телах задач.
|
||
3. `docs/.pm.json`: ключ `tasks.plan`, если он там был, — в `tasks.roadmap`.
|
||
4. Проставить род работы живым задачам: `python3 tasks.py check --dir docs/tasks`
|
||
перечислит те, у кого его нет. Задним числом весь беклог не переоформляется —
|
||
род нужен к взятию, так что порядок такой: сперва то, что берётся в ближайший
|
||
спринт, остальное по ходу переоценки.
|
||
5. Дописать раздел «Затрагивает» — тем же порядком и по той же причине: сперва
|
||
набор спринта, остальное по мере того, как задача попадает в работу.
|
||
6. Перечитать «Триггеры профиля» в `docs/review.md`: строки вида «миграция →
|
||
`deep`» теперь дублируют умолчание с обратным знаком. Оставить там только то,
|
||
что для этого проекта считается **новым понятием** и **правилом
|
||
идентичности**, — и убрать остальное, иначе проект возвращает себе прежнюю
|
||
частоту полного набора уточнением.
|
||
7. Переименовать секции роадмапа: `порядок` → `Запланировано`, `темы` →
|
||
`Направления`; завести `Готово` **первой** и `Разработка` последней
|
||
(порядок секций поменялся в версии 4 — если едешь сразу на неё, заводи
|
||
`Готово` последней и не переставляй дважды).
|
||
Прозаические разделы вроде «Что уже пройдено», которые велись руками,
|
||
разложить: звенья — строками в `Готово` (дата, слаг, что стало возможно),
|
||
обоснование очереди оставить прозой в `Запланировано`. Любой `##` в индексе
|
||
проверка считает секцией, и теперь `check` называет чужую секцию ошибкой.
|
||
8. Переформулировать цели ответом на **«что приложение будет уметь»**: не
|
||
«Работа со слиянием», а «Исход слияния не зависит от порядка доставки».
|
||
Свойство поведения — законная цель. Цель, которая не про приложение
|
||
(процесс, инструмент), переезжает в `Разработка`.
|
||
9. `[epic]`, если он в проекте заводился: это либо цель, либо набор задач под
|
||
общей целью. `check` назовёт его неизвестным типом.
|
||
10. Прогнать `python3 tasks.py check --dir docs/tasks --fix`: он поднимет
|
||
написание канонических секций, поставит отбивку после заголовков и сведёт
|
||
секцию в мете файлов с заголовками индексов. Секции беклога проект
|
||
переименовывает сам — их имена он выбирал, и трогать их скрипт не вправе.
|
||
11. Переписать заголовки задач в форму действия — по мере того, как задача
|
||
попадает в работу, а не «заодно»: `check` печатает их число, а `task-form`
|
||
предложит формулировки на замену пачкой.
|
||
12. Прочитать [language.md](language.md) — и **ничего не переписывать задним
|
||
числом**. Правила языка применяются к тому, что пишется и правится сейчас;
|
||
сплошная вычитка старых документов стоит дороже, чем даёт.
|
||
13. `docs/.pm.json`: `"canon": 3`.
|
||
|
||
## Версия 2 — 2026-08-03
|
||
|
||
Шапка записи ADR — мета-блоком общей формы, и у статуса появился объявленный
|
||
дом. Раскладка не менялась: правка касается одного шаблона.
|
||
|
||
**Что добавилось:** поле `- **Статус:**` в шапке `docs/adr/template.md` —
|
||
`заменено на ADR-…` либо `устарело`, у активной записи поля нет. Правило
|
||
«старая запись получает статус» было и раньше ([canon.md](canon.md), `adr/`),
|
||
но места под него шаблон не отводил: каждая запись изобретала своё, а колонка
|
||
«Статус» таблицы `adr/README.md` брала его оттуда, где он у каждого свой.
|
||
|
||
**Что переехало:** поля `Дата` и `Источник` в шаблоне стали жирными
|
||
(`- **Дата:**`, `- **Источник:**`) — та же форма, что у меты задачи и у записи
|
||
журнала дефектов: поле на строку, имя жирным.
|
||
|
||
**Что удалено:** ничего.
|
||
|
||
**Что сделать проекту:**
|
||
|
||
1. Привести `docs/adr/template.md` к скелету версии 2
|
||
([skeletons.md](skeletons.md), раздел `docs/adr/template.md`).
|
||
2. В существующих записях `docs/adr/ADR-*.md`: жирным поля шапки; если статус
|
||
записан прозой или заголовком — перенести его полем `- **Статус:**` в шапку
|
||
и сверить с колонкой «Статус» таблицы в `docs/adr/README.md`.
|
||
3. `docs/.pm.json`: `"canon": 2`.
|
||
|
||
## Версия 1 — 2026-08-03
|
||
|
||
Первая версия. Проект любой прежней раскладки приводится к ней скиллом `canon`
|
||
в режиме `adopt`, а не `upgrade`.
|
||
|
||
**Что вводится:** раскладка целиком — см. [canon.md](canon.md).
|
||
|
||
**Что сделать проекту, который приходит из свободной раскладки:**
|
||
|
||
1. `docs/.pm.json` с `{"canon": 1}` и путём миграций, если БД есть.
|
||
2. Скелет канона целиком; незаполненное — одной честной строкой.
|
||
3. `docs/specs/` разобрать: поведение — в `openspec/specs/`, обзор — в
|
||
`docs/architecture.md`, знание о чужих системах — в `docs/research/`.
|
||
Дубли capability удалить, сверив поимённо.
|
||
4. `docs/plan.md` → `docs/tasks/PLAN.md`, шаги плана — целями в «порядок».
|
||
5. `BRIEF.md` → `docs/passport.md`.
|
||
6. `docs/backlog/` → `docs/tasks/`.
|
||
7. `docs/review-journal.md` или `docs/review/journal.md` → `docs/review.md`,
|
||
плюс раздел настройки конвейера.
|
||
8. `docs/drafts/` растворить: идея → задача `[idea]`, намеренный отказ → ADR,
|
||
порядок работ → `PLAN.md`.
|
||
9. `docs/review-brief.md`, если заводился, удалить: его разделы разошлись по
|
||
документам канона.
|
||
10. `conventions.md` → `conventions/`, `local-research.md` → `research/`.
|
||
11. Завести `docs/security.md` с периметром первой строкой и `docs/adr/`.
|
||
12. В `CLAUDE.md`: severity рядом с каждым инвариантом; семантика гейта (чем
|
||
краснеет безусловно, где логи, чего в нём нет и кто тогда гоняет дорогое);
|
||
**имя основной ветки**; запреты с путями; где `testdata` и куда писать
|
||
временное; **что считается необратимым**; общий станок; ориентир по размеру
|
||
спринта. Убрать раздел «Процесс», если он пересказывает пайплайн.
|
||
13. В `openspec/config.yaml` оставить только нужды генерации и ссылки.
|
||
14. Добавить шаг `docs.py check` в гейт проекта.
|
||
|
||
**Копии правил в шаблонах, которые версия 1 уносит в проект** — их правка в
|
||
каноне обязана появляться здесь отдельной версией:
|
||
|
||
| Что копируется | Дом определения |
|
||
| --- | --- |
|
||
| форма записи журнала дефектов в `docs/review.md` | `av-dev-pipeline/skills/review-pipeline/references/review-journal.md` |
|
||
| правило заведения ADR в `docs/adr/README.md` | [canon.md](canon.md), раздел `adr/` |
|