классификация задачи: три категории документов и метка вместо ступени
Канон 5 объявил «каждый документ docs/ — тема ревью». Правило верно ровно наполовину и потому вредно целиком. Паспорт и схему хранилища ревью читает, но темами они не являются: по ним нельзя сказать «в этом изменении сделано не так», они задают границу, по которой судит чужая тема. Журнал решений и журнал наблюдений ревью изменения не нужны вовсе — ADR объясняет прошлое, а не предъявляет требование. Разметчик, применявший правило буквально, обязан был либо завести фантомные темы passport, adr, database, research и продублировать ими работу architecture и operations, либо потерять четыре документа молча; случались обе ветки, и в собственном образце плана docs/passport.md не попадал ни строкой, а обязательная арифметика покрытия при этом не сходилась. Категорий теперь три, разрез проверяемый. Тема — да, прямо: conventions, security, architecture и любой свой документ проекта. Источник темы — нет, но он задаёт границу для чужой: passport, database, CLAUDE.md, openspec/specs. Процессный — нет, он про то, как мы работаем: tasks, review, adr, research, .pm.json. Открыта одна категория из трёх, две другие перечислены поимённо, так что документ вне раскладки — однозначно своя тема. adr и research прогон больше не открывает ни одним проходом; docs/review остаётся читаемым, но как настройка конвейера, а не критерий. Цена записана и стала обязательной строкой границ покрытия: расхождение с записанным решением ловит теперь только сверка документации, а число под находкой обязано быть снято на этом прогоне, с приложенной командой. Классификация выдаёт задаче метку — small, medium, large. Прежние quick, standard и wide назывались ступенью и описывали ревью: как глубоко смотрим. Классифицируется же задача, и пока величина называлась свойством прогона, её естественно было пересчитывать на каждом прогоне — что конвейер и делал. Слово «ступень» удалено, а не оставлено синонимом: два имени одной вещи расходятся. Выводится метка из двух разведённых осей — размер (малое, среднее, крупное) и сложность (знакомое, незнакомое), — и равна максимуму по ним. Метка не синоним размера: малое незнакомое изменение получает large, трогая один узел, поэтому план печатает три строки с обоснованием каждая и выводить одну из другой запрещено. Оси остались русскими словами — это суждение прозой; метка английская — это идентификатор, который проходы сравнивают. Разметка переехала из ревью кода в шаг 4 пайплайна, сразу после propose. Она шла первым проходом каждого ревью кода, а перед ревью дизайна ту же величину называл сам пайплайн — то есть оркестратор, который только что довёл предложение до propose. Одно и то же измерялось дважды, и один из двух раз без разведённости с автором, ровно в той точке, ради которой разметчик заведён. Теперь запуск один на задачу, диффа он не видит, план обслуживает обе стадии, и метка после кода не пересматривается: расхождение факта с разметкой ловит журнал дефектов постфактум, как и всякую другую ошибку выбора. На диск план не пишется — четвёртый артефакт рядом с proposal, tasks и design пережил бы задачу и разошёлся бы с ней молча. Ревью дизайна тоже растёт меткой: small — specs, medium — плюс rubric, large — плюс architecture и вопрос автору о трёх формах решения. Раньше rubric и architecture включались одним условием, и medium получал ровно один проход, то есть не отличался от quick ничем. Разведены они потому, что зарабатывают на разном: рубрика порождает свойства узла и окупается уже на среднем изменении, её выход уезжает приёмочными критериями в tasks.md; архитектура отвечает на вопрос про второй способ, а он на среднем знакомом изменении отвечается «нет» ещё до запуска. small подешевел тремя способами сразу. Составом: приёмник тем не запускается, три темы ядра переходят к code сверкой по записанным инвариантам CLAUDE.md с потолком в одну находку, и это не «глубина ниже», а другой дом темы. Входом: specs читает только дельта-спеку, code — только индекс конвенций. Потолком: он появился у каждого опиниативного прохода, а не у одного basics, и у половин code он раздельный, потому что конвенционных находок больше по построению и в общем списке они вытеснили бы техническую половину. Сработавший потолок обязан быть объявлен строкой — молчащий срез неотличим от «больше не нашлось». Отрицательный тест small от этого стал жёстче, а не мягче: вопросы про обратимость миграции задавал приёмник тем, и на этой метке их не задаст никто. Пайплайн задачи вырос до двенадцати шагов. Тривиальность перестала решать состав ревью — она влияет только на explore; глубину обеих стадий называет метка. Проверено прогоном ревьюверов по готовому результату: девять расхождений найдено и починено — контракт находок печатал старый перечень проходов вместо плана по темам, три ссылки в task-batch указывали на шаг коммита вместо закрытия, запись changelog не переводила вопросы, адресованные passport и database, ops и adversary утверждали, что на нижних метках их вопросы задаёт basics, шаблон покрытия в review-code зашивал потолки small намертво, триггеры метки рассыпались на два списка против трёх, тема из директивы CLAUDE.md могла остаться без запуска исполнителя. Гейт зелёный: фронтматтеры, копии, одиннадцать диаграмм, ruff, pyrefly; docs.py прогнан на живом фикстуре и печатает категорию в отказе. Канон повышен до версии 6 с записью, выполнимой upgrade. Решения — 40–44. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -103,7 +103,7 @@ color: green
|
||||
| Термин | Что называет |
|
||||
| --- | --- |
|
||||
| интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции |
|
||||
| триаж | ступень конвейера, сводящая находки в решение |
|
||||
| триаж | стадия конвейера, сводящая находки в решение |
|
||||
| провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство |
|
||||
| дедуп, дедупликация | сверка нового против уже лежащего |
|
||||
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
||||
|
||||
@@ -100,7 +100,7 @@ color: green
|
||||
одной партии»), — находка: слово стоит, проверки нет. Число критериев считает
|
||||
`tasks.py check`, тебе оно неинтересно.
|
||||
|
||||
6. **Предписания процесса в теле нет.** «Делать профилем standard», «взять
|
||||
6. **Предписания процесса в теле нет.** «Делать с меткой medium», «взять
|
||||
такой-то агент» — это выбор, который делают, увидев изменение, а не при
|
||||
постановке. Он же путь понизить требования решением, принятым до
|
||||
проектирования.
|
||||
|
||||
@@ -199,7 +199,7 @@ capability), `openspec/config.yaml`.
|
||||
|
||||
**Шаг 6 обязателен, и вот почему.** `check` сверяет **число** в `.pm.json` с
|
||||
версией скрипта — и только его. Применена ли запись журнала **по существу**, он
|
||||
не знает: проект несёт `"canon": 4` и может не иметь того, чего требовала любая
|
||||
не знает: проект несёт `"canon": 6` и может не иметь того, чего требовала любая
|
||||
из пройденных версий. Записи применяются руками (переименовать секцию, проставить
|
||||
типы, дописать раздел каждому `fix`), а ручной проход по нескольким записям
|
||||
подряд — ровно то место, где половина шага делается и забывается. Судьи и есть
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Канон документов проекта
|
||||
|
||||
**Версия 5.**
|
||||
**Версия 6.**
|
||||
|
||||
Это **единственный дом определения канона**. Скиллы `init`, `canon` и `docs`
|
||||
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
||||
@@ -47,10 +47,10 @@
|
||||
|
||||
## Раскладка
|
||||
|
||||
**Документ канона — это тема ревью, а тема живёт файлом или каталогом.**
|
||||
`docs/security.md` и `docs/security/` — одно и то же; форму выбирает проект по
|
||||
объёму написанного, и переход между формами не меняет ни канон, ни версию. Обе
|
||||
формы сразу — ошибка: два дома для одного факта расходятся молча.
|
||||
**Документ канона живёт файлом или каталогом.** `docs/security.md` и
|
||||
`docs/security/` — одно и то же; форму выбирает проект по объёму написанного, и
|
||||
переход между формами не меняет ни канон, ни версию. Обе формы сразу — ошибка:
|
||||
два дома для одного факта расходятся молча.
|
||||
|
||||
```
|
||||
CLAUDE.md памятка агенту: что это, стек, инварианты с
|
||||
@@ -75,21 +75,76 @@ openspec/
|
||||
changes/archive/ архив изменений с design.md — сырьё для ADR
|
||||
```
|
||||
|
||||
**У темы-каталога обязателен `README.md`** — вход, по которому её читают агенты.
|
||||
**У документа-каталога обязателен `README.md`** — вход, по которому его читают агенты.
|
||||
`adr/` в форме каталога держит ещё и `template.md`, а записи именуются
|
||||
`ADR-ГГГГ-ММ-ДД-slug.md`.
|
||||
|
||||
**Список тем открытый, и это не послабление, а механизм.** Всё, что проект
|
||||
кладёт в `docs/`, становится темой ревью: у конвейера есть приёмник для темы, к
|
||||
которой нет именной оптики, и заведён он ровно за этим. Завёл
|
||||
`docs/accessibility.md` — появилась тема `accessibility`, и она попадает в план
|
||||
каждого прогона. Не темы ровно две: `docs/tasks/` (его ведёт скилл `tasks`)
|
||||
и `docs/review.*` — это настройка самого конвейера, слой над темами.
|
||||
## Три категории документов
|
||||
|
||||
Раньше здесь стояло плоское правило «каждый документ `docs/` — тема ревью». Оно
|
||||
неверно ровно наполовину: паспорт и схема хранилища ревью нужны, но темами не
|
||||
являются, а журнал решений и журнал наблюдений ревью изменения не нужны вовсе.
|
||||
Плоское правило заставляло разметчика либо плодить фантомные темы, либо терять
|
||||
документы молча — а молчащая потеря и есть то, против чего канон написан.
|
||||
|
||||
**Разрез один и проверяемый: можно ли по документу сказать «в этом изменении
|
||||
сделано не так»?**
|
||||
|
||||
| Категория | Ответ на разрез | Что с ней делает ревью |
|
||||
| --- | --- | --- |
|
||||
| **тема** | да, прямо | заводит направление проверки и требует исполнителя |
|
||||
| **источник темы** | нет, но он задаёт границу, по которой судит чужая тема | читается как материал, своей темы не порождает |
|
||||
| **процессный документ** | нет: он про то, как мы работаем, а не про изменение | не судит по нему изменение |
|
||||
|
||||
| Документ | Категория | Куда питает |
|
||||
| --- | --- | --- |
|
||||
| `conventions.*` | тема | `conventions` |
|
||||
| `security.*` | тема | `security` |
|
||||
| `architecture.*` | тема | `architecture`; раздел эксплуатации — `operations` |
|
||||
| *свой документ проекта* | тема | своя тема, именем документа |
|
||||
| `passport.*` | источник | `architecture` — граница домена, «чем **не** является» |
|
||||
| `database.*` | источник | `operations` — схема и настройки с числами |
|
||||
| `CLAUDE.md`, `AGENTS.md` | источник | `autotests` (семантика гейта); инварианты — сквозные |
|
||||
| `openspec/specs/` | источник | `requirements` |
|
||||
| `tasks/` | процессный | — |
|
||||
| `review.*` | процессный | — (настройка самого конвейера, слой **над** темами) |
|
||||
| `adr.*` | процессный | — |
|
||||
| `research.*` | процессный | — |
|
||||
| `.pm.json` | процессный | — (служебный файл, не документ) |
|
||||
|
||||
**Список тем открытый, и это не послабление, а механизм.** Категории
|
||||
`источник` и `процессный` **закрыты** — они перечислены здесь поимённо и
|
||||
проектом не пополняются. Всё остальное, что проект кладёт в `docs/`, — тема: у
|
||||
конвейера есть приёмник для темы, к которой нет именной оптики, и заведён он
|
||||
ровно за этим. Завёл `docs/accessibility.md` — появилась тема `accessibility`, и
|
||||
она попадает в план каждого прогона.
|
||||
|
||||
Отсюда следствие, ради которого правило и заведено: **`docs/` — это конфигурация
|
||||
ревью.** Проект настраивает проверку тем, что пишет о себе, а не отдельным файлом
|
||||
настроек, который разошёлся бы с документами.
|
||||
|
||||
**«Не судит по нему» и «не открывает» — не одно и то же, и разница существенна.**
|
||||
`docs/review.*` проходы читают на каждом прогоне: там лежат вопросы по темам,
|
||||
журнал дефектов, типовые узлы и типовые ложноположительные. Это чтение конвейером
|
||||
**собственной настройки**, а не суждение об изменении, и потому оно законно.
|
||||
`adr/`, `research/` и `tasks/` не открывает никто: по ним изменение не судят, и
|
||||
настройкой конвейера они не являются.
|
||||
|
||||
**Процессный документ — не документ второго сорта.** `adr/` и `research/`
|
||||
проверяются наравне с остальными, но **сверкой документации**, а не прогоном
|
||||
ревью: ADR без ссылки на архивный `design.md`, замена без парного статуса, число
|
||||
без провенанса — это работа агентов `doc-consistency` и `doc-code-drift`, и она
|
||||
осталась там же, где была. Изменилось одно: прогон ревью не открывает их как
|
||||
критерий и не судит по ним изменение.
|
||||
|
||||
Цена этого решения записана, а не подразумевается: **расхождение изменения с
|
||||
записанным решением прогоном больше не ловится.** Раньше архитектурный проход
|
||||
читал `adr/` и мог сказать «здесь отменено решение ADR-2026-03-11, а парного
|
||||
статуса нет»; теперь это скажет только `doc-consistency` на сессии между
|
||||
спринтами. Сделка сознательная — ADR объясняет прошлое, а не предъявляет
|
||||
требование к изменению, и чтение всего каталога решений на каждой задаче
|
||||
оплачивалось на каждой, а срабатывало на единицах.
|
||||
|
||||
### Имена файлов английские, текст русский
|
||||
|
||||
**Текст документов русский; имена файлов, capability и задач — английские,
|
||||
@@ -116,30 +171,35 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
|
||||
## Роли документов и темы ревью
|
||||
|
||||
Одна строка на каждый — на какой вопрос он отвечает и какую тему ревью питает.
|
||||
**Кто именно закрывает тему, здесь не указано намеренно**: это зависит от ступени
|
||||
прогона и меняется вместе с конвейером, а документ живёт дольше. Раскладку
|
||||
«тема → проход → глубина» держит скилл `av-dev-pipeline:review-pipeline`.
|
||||
Одна строка на каждый — на какой вопрос он отвечает, в какой он категории и
|
||||
какую тему питает. **Кто именно закрывает тему, здесь не указано намеренно**: это
|
||||
зависит от метки прогона и меняется вместе с конвейером, а документ живёт
|
||||
дольше. Раскладку «тема → проход → глубина» держит скилл
|
||||
`av-dev-pipeline:review-pipeline`.
|
||||
|
||||
**Общего словаря у канона с конвейером ровно два вида имён: имена тем и имена
|
||||
ступеней.** Ими проект и настраивает ревью — вопросами по темам и триггерами
|
||||
профиля. **Имён проходов канон не называет нигде**, включая вывод `docs.py`:
|
||||
проход переименовывается и переезжает между ступенями, и канон, назвавший его, в
|
||||
**Общего словаря у канона с конвейером ровно три вида имён: имена категорий,
|
||||
имена тем и имена меток.** Категорий три — `тема`, `источник`, `процессный`;
|
||||
**меток тоже три, и они закрыты: `small`, `medium`, `large`.** Метка это итог
|
||||
классификации задачи, и по ней конвейер выбирает исполнителей на обеих стадиях
|
||||
ревью; проект её не выдумывает, а только уточняет триггеры. Ими проект и
|
||||
настраивает ревью — вопросами по темам и триггерами метки. **Имён проходов канон не называет нигде**, включая вывод `docs.py`:
|
||||
проход переименовывается и переезжает между метками, и канон, назвавший его, в
|
||||
этот день соврёт молча. Обратное направление законно — конвейер называет
|
||||
документы канона поимённо, потому что он их читатель.
|
||||
|
||||
| Документ | Вопрос | Тема ревью |
|
||||
| Документ | Вопрос | Категория и тема |
|
||||
| --- | --- | --- |
|
||||
| `CLAUDE.md`, `AGENTS.md` | что нельзя нарушать, чем краснеет гейт | `autotests`; инварианты — сквозные, во все темы |
|
||||
| `passport.*` | зачем и для кого, чем это **не** является | `architecture` |
|
||||
| `architecture.*` | как сложено и где что работает | `architecture`; раздел эксплуатации — `operations` |
|
||||
| `database.*` | что лежит в хранилище и какими настройками | `operations` |
|
||||
| `security.*` | против кого защищаемся и что вне модели | `security` |
|
||||
| `conventions.*` | как мы пишем код | `conventions` |
|
||||
| `research/` | что показала реальность, а не документация | `operations`, `requirements` |
|
||||
| `adr.*` | почему решено именно так | `architecture` |
|
||||
| `openspec/specs/` | что система делает — нормативно | `requirements` |
|
||||
| `review.*` | как настроен конвейер и что уже проскакивало | **не тема**: слой над всеми |
|
||||
| `CLAUDE.md`, `AGENTS.md` | что нельзя нарушать, чем краснеет гейт | источник: `autotests`; инварианты — сквозные, во все темы |
|
||||
| `passport.*` | зачем и для кого, чем это **не** является | источник: `architecture` |
|
||||
| `architecture.*` | как сложено и где что работает | тема `architecture`; раздел эксплуатации — `operations` |
|
||||
| `database.*` | что лежит в хранилище и какими настройками | источник: `operations` |
|
||||
| `security.*` | против кого защищаемся и что вне модели | тема `security` |
|
||||
| `conventions.*` | как мы пишем код | тема `conventions` |
|
||||
| `openspec/specs/` | что система делает — нормативно | источник: `requirements` |
|
||||
| `research.*` | что показала реальность, а не документация | процессный |
|
||||
| `adr.*` | почему решено именно так | процессный |
|
||||
| `review.*` | как настроен конвейер и что уже проскакивало | процессный: слой **над** темами |
|
||||
| `tasks/` | что делаем и в каком порядке | процессный |
|
||||
| *свой документ проекта* | что проект счёл нужным проверять | **своя тема**, именем документа |
|
||||
|
||||
### `passport.md`
|
||||
@@ -256,16 +316,22 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
- **Типовые ложноположительные** — находки, которые здесь выглядят убедительно и
|
||||
всегда неверны, каждая со строкой «почему здесь это не дефект»;
|
||||
- **Вопросы по темам** — в форме `<тема>: <вопрос> (<провенанс>)`. **Не по именам
|
||||
проходов**: проход уезжает между ступенями, а тема остаётся, и вопрос,
|
||||
проходов**: проход уезжает между метками, а тема остаётся, и вопрос,
|
||||
адресованный проходу, перестал бы задаваться молча в тот день, когда тот уехал
|
||||
в верхнюю ступень. Задаёт вопрос тот, кто закрывает тему на этом прогоне;
|
||||
- **Триггеры профиля** — проектная конкретизация правила выбора профиля ревью:
|
||||
что в этом проекте считается **крупным или незнакомым** изменением (поднимает
|
||||
прогон до `wide`, верхней ступени, — и она рассчитана на 5–10% задач) и что
|
||||
считается **мелким** (опускает до `quick`). Перечнем мест, узлами или
|
||||
capability, а не вторым определением класса. Уточняет умолчания, а не отменяет
|
||||
их. Рабочее умолчание — `standard`: миграция схемы и публичный контракт ступень
|
||||
**не** поднимают, их проверяют проходы, которые в `standard` и так есть;
|
||||
в старшую метку. Задаёт вопрос тот, кто закрывает тему на этом прогоне.
|
||||
Адресовать можно только теме: `passport`, `database`, `adr`, `research` и
|
||||
`review` — не темы, и вопрос, адресованный им, не задаст никто;
|
||||
- **Триггеры метки** — проектная конкретизация правила выбора метки ревью,
|
||||
**тремя списками**. Два поднимают, по одному на ось: что в этом проекте считается
|
||||
**крупным** (объём: сколько узлов и слоёв трогает) и что считается
|
||||
**незнакомым** (форма решения: известна до начала или нащупывается по ходу).
|
||||
Любая из двух осей поднимает прогон до `large`, старшей метки, — а она
|
||||
рассчитана на 5–10% задач. Третий список — что считается **мелким** (опускает
|
||||
до `small`); он один, потому что вниз метку опускает только совпадение обеих
|
||||
осей сразу. Перечнем мест, узлами или capability, а не вторым определением
|
||||
класса. Уточняет умолчания, а не отменяет их. Рабочее умолчание — `medium`:
|
||||
миграция схемы и публичный контракт метку **не** поднимают, их проверяют
|
||||
проходы, которые в `medium` и так есть;
|
||||
- **Недоступно проверке** — два подраздела, оба **по темам**: «не проверит ни
|
||||
один проход» (принципиальная граница, по факту промаха не пересматривается) и
|
||||
«перестали проверять сознательно» (пересматривается первым). Тема, у которой в
|
||||
@@ -441,7 +507,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
|
||||
```json
|
||||
{
|
||||
"canon": 4,
|
||||
"canon": 6,
|
||||
"migrations": "internal/store/migrations",
|
||||
"tasks": {
|
||||
"backlog": "INDEX.md"
|
||||
|
||||
@@ -13,6 +13,92 @@ upgrade` идёт по записям снизу вверх от версии п
|
||||
|
||||
---
|
||||
|
||||
## Версия 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
|
||||
|
||||
Канон перестал быть списком файлов и стал **списком тем ревью**. Раскладка та же,
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
| Термин | Что называет |
|
||||
| --- | --- |
|
||||
| интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции |
|
||||
| триаж | ступень конвейера, сводящая находки в решение |
|
||||
| триаж | стадия конвейера, сводящая находки в решение |
|
||||
| провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство |
|
||||
| дедуп, дедупликация | сверка нового против уже лежащего |
|
||||
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
||||
|
||||
@@ -282,30 +282,40 @@
|
||||
задаёт тот проход, который закрывает эту тему на текущем прогоне, дополнительно
|
||||
к обязательным.
|
||||
|
||||
**Адресуй теме, а не имени прохода.** Проходы переезжают между ступенями и
|
||||
**Адресуй теме, а не имени прохода.** Проходы переезжают между метками и
|
||||
упраздняются; вопрос, адресованный проходу, перестанет задаваться в тот день,
|
||||
когда тот уедет в верхнюю ступень, — и заметить это будет нечем. Тема переезд
|
||||
когда тот уедет в старшую метку, — и заметить это будет нечем. Тема переезд
|
||||
переживает.
|
||||
|
||||
Темы ядра: `requirements`, `autotests`, `conventions`, `architecture`,
|
||||
`security`, `operations`. Плюс любая своя — та, под которую в `docs/` лежит
|
||||
документ.
|
||||
`security`, `operations`. Плюс любая своя — та, под которую проект завёл в
|
||||
`docs/` **свой** документ. Документы категорий `источник` и `процессный` тем не
|
||||
порождают, и адресовать вопрос `passport`, `database`, `adr`, `research` или
|
||||
`review` нельзя — таких тем нет. Вопрос про границу домена адресуй
|
||||
`architecture`, вопрос про хранилище и числа — `operations`.
|
||||
|
||||
### Триггеры профиля
|
||||
### Триггеры метки
|
||||
|
||||
Проектная конкретизация правила выбора профиля, двумя списками и поимённо —
|
||||
узлами или capability.
|
||||
Проектная конкретизация правила выбора метки. **Списка три: по одному на
|
||||
каждую ось вверх и один вниз** — поимённо, узлами или capability.
|
||||
|
||||
**Крупное или незнакомое здесь** — поднимает прогон до `wide`, верхней ступени:
|
||||
там `security`, `operations` и `architecture` проверяют запуском, и там же
|
||||
единственные замеры. Ступень рассчитана на **5–10% задач**; если сюда попадает
|
||||
каждая третья, список написан слишком широко.
|
||||
**Крупное здесь** — про объём: что трогает несколько узлов или слоёв, переносит
|
||||
ответственность между ними, перекладывает существующий код в новую форму.
|
||||
|
||||
**Мелкое здесь** — опускает до `quick`. Помни отрицательный тест конвейера: что
|
||||
**Незнакомое здесь** — про форму решения: то, чего в проекте ещё не было и чью
|
||||
форму предстоит нащупать по ходу. Признак простой: перед работой нельзя назвать,
|
||||
какие узлы будут тронуты.
|
||||
|
||||
Любая из двух осей поднимает прогон до `large`, старшей метки: там `security`,
|
||||
`operations` и `architecture` проверяют запуском, и там же единственные замеры.
|
||||
Метка рассчитана на **5–10% задач**; если сюда попадает каждая третья, списки
|
||||
написаны слишком широко.
|
||||
|
||||
**Мелкое здесь** — опускает до `small`. Помни отрицательный тест конвейера: что
|
||||
после мерджа не откатывается обратной правкой (миграция, формат на диске,
|
||||
публичный контракт, имя), — не `quick`, каким бы маленьким ни был дифф.
|
||||
публичный контракт, имя), — не `small`, каким бы маленьким ни был дифф.
|
||||
|
||||
Уточняет умолчания конвейера, не отменяет их; рабочее умолчание — `standard`.
|
||||
Уточняет умолчания конвейера, не отменяет их; рабочее умолчание — `medium`.
|
||||
|
||||
### Недоступно проверке
|
||||
|
||||
@@ -409,7 +419,7 @@ severity стоит здесь, а не выводится каждым прох
|
||||
|
||||
```json
|
||||
{
|
||||
"canon": 4
|
||||
"canon": 6
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -25,55 +25,69 @@ from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
|
||||
CANON_VERSION = 5
|
||||
CANON_VERSION = 6
|
||||
|
||||
OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4
|
||||
|
||||
# --- Раскладка канона -------------------------------------------------------
|
||||
|
||||
# Тема канона: имя → на какой вопрос отвечает (для внятного отказа).
|
||||
# Документ канона: имя → (категория, на какой вопрос отвечает).
|
||||
#
|
||||
# **Тема живёт файлом `docs/<имя>.md` либо каталогом `docs/<имя>/` с README.md
|
||||
# внутри.** Форму выбирает проект: тема разрослась — стала каталогом, и это не
|
||||
# смена канона и не повод править скрипт. Обе формы сразу — ошибка: это два дома
|
||||
# для одного факта, ровно то, от чего канон и защищает.
|
||||
THEMES = {
|
||||
"passport": "зачем и для кого, чем НЕ является",
|
||||
"architecture": "как сложено — обзор, окружение, эксплуатация",
|
||||
"security": "периметр, недоверенный вход, что вне модели",
|
||||
"conventions": "как мы пишем код; индекс, промоут, что механизировано",
|
||||
"research": "что показала реальность: наблюдения и числа с провенансом",
|
||||
"adr": "почему решено так; индекс, статусы, правило замены",
|
||||
"review": "настройка конвейера + журнал дефектов",
|
||||
# Категории — из canon.md, раздел «Три категории документов». Разрез один: можно
|
||||
# ли по документу сказать «в этом изменении сделано не так»?
|
||||
# тема — да, прямо: документ заводит направление проверки изменения;
|
||||
# источник — нет, но он задаёт границу, по которой судит чужая тема;
|
||||
# процессный — нет: он про то, как мы работаем, а не про изменение.
|
||||
#
|
||||
# **Категория не меняет обязательности документа** — заводятся все три
|
||||
# одинаково и с первого дня. Она меняет только то, что с документом делает
|
||||
# конвейер ревью, и потому печатается в отказе: «нет источника passport»
|
||||
# читается иначе, чем «нет темы security», и чинится теми же руками, но с
|
||||
# другим приоритетом.
|
||||
#
|
||||
# **Документ живёт файлом `docs/<имя>.md` либо каталогом `docs/<имя>/` с
|
||||
# README.md внутри.** Форму выбирает проект: документ разросся — стал каталогом,
|
||||
# и это не смена канона и не повод править скрипт. Обе формы сразу — ошибка: это
|
||||
# два дома для одного факта, ровно то, от чего канон и защищает.
|
||||
DOCS = {
|
||||
"passport": ("источник", "зачем и для кого, чем НЕ является"),
|
||||
"architecture": ("тема", "как сложено — обзор, окружение, эксплуатация"),
|
||||
"security": ("тема", "периметр, недоверенный вход, что вне модели"),
|
||||
"conventions": ("тема", "как мы пишем код; индекс, промоут, что механизировано"),
|
||||
"research": ("процессный", "что показала реальность: наблюдения и числа"),
|
||||
"adr": ("процессный", "почему решено так; индекс, статусы, правило замены"),
|
||||
"review": ("процессный", "настройка конвейера + журнал дефектов"),
|
||||
}
|
||||
|
||||
# Тема, обязательная только при условии: имя → (ключ .pm.json, пояснение).
|
||||
CONDITIONAL_THEMES = {
|
||||
"database": ("migrations", "схема хранилища и настройки"),
|
||||
# Документ, обязательный только при условии: имя → (ключ .pm.json, категория,
|
||||
# пояснение).
|
||||
CONDITIONAL_DOCS = {
|
||||
"database": ("migrations", "источник", "схема хранилища и настройки"),
|
||||
}
|
||||
|
||||
# Обязательные файлы вне тем.
|
||||
# Обязательные файлы вне раскладки docs/.
|
||||
REQUIRED = {
|
||||
"CLAUDE.md": "памятка агенту: инварианты с severity, команды, семантика гейта",
|
||||
"docs/.pm.json": "версия канона и пути, нужные проверкам",
|
||||
}
|
||||
|
||||
# Файлы, которые тема-каталог обязана держать сверх README.md.
|
||||
THEME_EXTRA = {
|
||||
# Файлы, которые документ-каталог обязан держать сверх README.md.
|
||||
DOC_EXTRA = {
|
||||
"adr": {"template.md": "шаблон записи ADR"},
|
||||
}
|
||||
|
||||
# Служебное в docs/ и каталог, который ведёт tasks.py.
|
||||
NOT_THEMES = {".pm.json", "tasks"}
|
||||
# Служебное в docs/ и каталог, который ведёт tasks.py. Оба процессные, но
|
||||
# проверок формы у них нет: .pm.json не markdown, tasks/ ведёт другой скрипт.
|
||||
NOT_DOCS = {".pm.json", "tasks"}
|
||||
|
||||
# Слоты, которых в каноне нет, — с адресом, куда уезжает содержимое. Имена,
|
||||
# совпадающие с темой, отсюда убраны намеренно: `docs/conventions.md` и
|
||||
# `docs/review/` теперь законные формы своих тем.
|
||||
RETIRED = {
|
||||
"review-brief.md": "документы канона и есть бриф; остаток — в review",
|
||||
"review-journal.md": "→ тема review",
|
||||
"review-journal.md": "→ документ review",
|
||||
"plan.md": "→ docs/tasks/ROADMAP.md",
|
||||
"local-research.md": "→ тема research",
|
||||
"local-research.md": "→ документ research",
|
||||
"specs": "поведение → openspec/specs/, обзор → тема architecture",
|
||||
"drafts": "идея → запись research, отказ → ADR, порядок → ROADMAP.md",
|
||||
"backlog": "→ docs/tasks/",
|
||||
@@ -122,11 +136,11 @@ def check_slugs(root: Path, rep: Report) -> None:
|
||||
if not docs.is_dir():
|
||||
return
|
||||
# Имена, выбранные каноном, а не проектом: их форма задана здесь же.
|
||||
fixed = {"README.md", "template.md"} | {f"{name}.md" for name in THEMES}
|
||||
# Все темы-каталоги, включая свои темы проекта: правило имён общее, а
|
||||
fixed = {"README.md", "template.md"} | {f"{name}.md" for name in DOCS}
|
||||
# Все документы-каталоги, включая свои темы проекта: правило имён общее, а
|
||||
# перечислять их поимённо значило бы закрыть открытый список.
|
||||
for folder in sorted(docs.iterdir()):
|
||||
if not folder.is_dir() or folder.name in NOT_THEMES:
|
||||
if not folder.is_dir() or folder.name in NOT_DOCS:
|
||||
continue
|
||||
sub = folder.name
|
||||
for path in sorted(folder.rglob("*.md")):
|
||||
@@ -267,8 +281,8 @@ def check_version(root: Path, cfg: dict, rep: Report) -> None:
|
||||
)
|
||||
|
||||
|
||||
def theme_home(root: Path, name: str) -> tuple[Path | None, str | None]:
|
||||
"""Дом темы: файл `docs/<имя>.md` или каталог `docs/<имя>/`.
|
||||
def doc_home(root: Path, name: str) -> tuple[Path | None, str | None]:
|
||||
"""Дом документа: файл `docs/<имя>.md` или каталог `docs/<имя>/`.
|
||||
|
||||
Возвращает путь и жалобу. Обе формы сразу — это два дома для одного факта, и
|
||||
расходятся они молча: правят одну, читают другую.
|
||||
@@ -278,7 +292,7 @@ def theme_home(root: Path, name: str) -> tuple[Path | None, str | None]:
|
||||
as_dir = docs / name
|
||||
if as_file.is_file() and as_dir.is_dir():
|
||||
return as_file, (
|
||||
f"тема {name} живёт сразу двумя домами — docs/{name}.md и docs/{name}/:"
|
||||
f"{name} живёт сразу двумя домами — docs/{name}.md и docs/{name}/:"
|
||||
f" оставить один, иначе правят один, а читают другой"
|
||||
)
|
||||
if as_file.is_file():
|
||||
@@ -286,8 +300,8 @@ def theme_home(root: Path, name: str) -> tuple[Path | None, str | None]:
|
||||
if as_dir.is_dir():
|
||||
if not (as_dir / "README.md").is_file():
|
||||
return as_dir, (
|
||||
f"docs/{name}/ без README.md — у темы-каталога вход обязателен:"
|
||||
f" по нему её читают агенты"
|
||||
f"docs/{name}/ без README.md — у документа-каталога вход"
|
||||
f" обязателен: по нему его читают агенты"
|
||||
)
|
||||
return as_dir, None
|
||||
return None, None
|
||||
@@ -298,54 +312,60 @@ def check_required(root: Path, cfg: dict, rep: Report) -> None:
|
||||
if not (root / rel).exists():
|
||||
rep.error(f"нет {rel} — {what}")
|
||||
|
||||
for name, what in THEMES.items():
|
||||
home, complaint = theme_home(root, name)
|
||||
for name, (kind, what) in DOCS.items():
|
||||
home, complaint = doc_home(root, name)
|
||||
if home is None:
|
||||
rep.error(f"нет темы {name} (docs/{name}.md или docs/{name}/) — {what}")
|
||||
rep.error(
|
||||
f"нет документа {name} (docs/{name}.md или docs/{name}/),"
|
||||
f" категория «{kind}» — {what}"
|
||||
)
|
||||
continue
|
||||
if complaint:
|
||||
rep.error(complaint)
|
||||
if home.is_dir():
|
||||
for extra, why in THEME_EXTRA.get(name, {}).items():
|
||||
for extra, why in DOC_EXTRA.get(name, {}).items():
|
||||
if not (home / extra).is_file():
|
||||
rep.error(f"нет docs/{name}/{extra} — {why}")
|
||||
|
||||
for name, (key, what) in CONDITIONAL_THEMES.items():
|
||||
home, complaint = theme_home(root, name)
|
||||
for name, (key, kind, what) in CONDITIONAL_DOCS.items():
|
||||
home, complaint = doc_home(root, name)
|
||||
if complaint:
|
||||
rep.error(complaint)
|
||||
if key in cfg and home is None:
|
||||
rep.error(
|
||||
f"нет темы {name} (docs/{name}.md или docs/{name}/) — {what}"
|
||||
f" (обязательна: в .pm.json объявлен {key})"
|
||||
f"нет документа {name} (docs/{name}.md или docs/{name}/),"
|
||||
f" категория «{kind}» — {what}"
|
||||
f" (обязателен: в .pm.json объявлен {key})"
|
||||
)
|
||||
elif key not in cfg and home is None:
|
||||
rep.skip(f"тема {name} — в .pm.json нет ключа {key}, проверка неприменима")
|
||||
rep.skip(f"{name} — в .pm.json нет ключа {key}, проверка неприменима")
|
||||
|
||||
|
||||
def check_stray(root: Path, rep: Report) -> None:
|
||||
"""Лишнего в docs/ больше нет — есть темы проекта.
|
||||
"""Лишнего в docs/ больше нет — есть свои темы проекта.
|
||||
|
||||
Список тем **открытый**: каждый документ в docs/ и есть заявка на тему
|
||||
ревью, и запретить проекту завести свою нельзя. Проверяются только слоты,
|
||||
у которых дом в другом месте, — иначе переехавшее содержимое вернулось бы
|
||||
темой и выглядело законным.
|
||||
Категории `источник` и `процессный` **закрыты**: они перечислены в каноне
|
||||
поимённо и проектом не пополняются. Открыта только категория `тема` —
|
||||
поэтому любой документ в docs/, которого нет в раскладке, и есть заявка на
|
||||
свою тему, и запретить её нельзя. Проверяются только слоты, у которых дом в
|
||||
другом месте, — иначе переехавшее содержимое вернулось бы темой и выглядело
|
||||
законным.
|
||||
"""
|
||||
docs = root / "docs"
|
||||
if not docs.is_dir():
|
||||
rep.error("нет каталога docs/")
|
||||
return
|
||||
known = set(THEMES) | set(CONDITIONAL_THEMES)
|
||||
known = set(DOCS) | set(CONDITIONAL_DOCS)
|
||||
own: list[str] = []
|
||||
for entry in sorted(docs.iterdir()):
|
||||
name = entry.name
|
||||
if name in RETIRED:
|
||||
rep.error(f"docs/{name} — слота нет в каноне: {RETIRED[name]}")
|
||||
continue
|
||||
if name in NOT_THEMES:
|
||||
if name in NOT_DOCS:
|
||||
continue
|
||||
theme = name[:-3] if entry.is_file() and name.endswith(".md") else name
|
||||
if theme in known:
|
||||
topic = name[:-3] if entry.is_file() and name.endswith(".md") else name
|
||||
if topic in known:
|
||||
continue
|
||||
if entry.is_file() and not name.endswith(".md"):
|
||||
rep.error(f"docs/{name} — не markdown: тема ревью читается как текст")
|
||||
@@ -356,7 +376,7 @@ def check_stray(root: Path, rep: Report) -> None:
|
||||
f" по нему её читают агенты"
|
||||
)
|
||||
continue
|
||||
own.append(theme)
|
||||
own.append(topic)
|
||||
if own:
|
||||
rep.note(
|
||||
f"свои темы проекта: {', '.join(own)} — именной оптики у них нет,"
|
||||
@@ -418,13 +438,13 @@ def check_placeholders_and_debt(root: Path, rep: Report) -> None:
|
||||
rep.debt(f"{rel}: {what}")
|
||||
|
||||
|
||||
def theme_text(root: Path, name: str) -> str | None:
|
||||
"""Текст темы целиком: файл или все markdown каталога, склеенные.
|
||||
def doc_text(root: Path, name: str) -> str | None:
|
||||
"""Текст документа целиком: файл или все markdown каталога, склеенные.
|
||||
|
||||
Проверке всё равно, одним файлом написана тема или десятью: она ищет
|
||||
Проверке всё равно, одним файлом написан документ или десятью: она ищет
|
||||
упоминание, а упоминание живёт в любом из них.
|
||||
"""
|
||||
home, _ = theme_home(root, name)
|
||||
home, _ = doc_home(root, name)
|
||||
if home is None:
|
||||
return None
|
||||
if home.is_file():
|
||||
@@ -437,7 +457,7 @@ def theme_text(root: Path, name: str) -> str | None:
|
||||
|
||||
def check_capabilities(root: Path, rep: Report) -> None:
|
||||
specs = root / "openspec" / "specs"
|
||||
text = theme_text(root, "architecture")
|
||||
text = doc_text(root, "architecture")
|
||||
if not specs.is_dir():
|
||||
rep.skip("openspec/specs/ нет — сверка capability с архитектурой неприменима")
|
||||
return
|
||||
|
||||
@@ -130,7 +130,7 @@ description: Вести содержимое документов канона
|
||||
Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим».
|
||||
Со временем теряется не факт, а то, почему дефект не поймали, — единственное,
|
||||
ради чего журнал есть. И решение сузить проверки (перестали звать проход, понизили
|
||||
профиль) обязано попасть в раздел настройки, а не остаться в отчёте ревью.
|
||||
метка) обязано попасть в раздел настройки, а не остаться в отчёте ревью.
|
||||
|
||||
## Промоут в конвенции
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
|
||||
Здесь же последний дешёвый момент заметить **разнородную задачу**: раздел
|
||||
«Затрагивает» показывает границы до того, как заведено предложение об
|
||||
изменении. Строка, которая одна тянет задачу на ступень выше остального
|
||||
изменении. Строка, которая одна тянет задачу на метку выше остального
|
||||
перечня, — кандидат на разрез (шов — в `tasks`, `references/split.md`).
|
||||
Замеченная здесь, она стоит одного `edit`; замеченная на ревью — выброшенного
|
||||
предложения.
|
||||
|
||||
@@ -279,7 +279,7 @@ stateDiagram-v2
|
||||
**напоминает** — беклог, заведённый до появления типа, законен, и переоформлять
|
||||
его «заодно» здесь не просят.
|
||||
|
||||
**Тип не выбирает профиль ревью и вообще ничего не предписывает пайплайну.**
|
||||
**Тип не выбирает метку ревью и вообще ничего не предписывает пайплайну.**
|
||||
Профиль выбирается по факту изменения, а не по типу задачи: `chore` бывает
|
||||
миграцией схемы, `fix` — правкой публичного контракта. Правило «предписание
|
||||
процесса в теле задачи снимается» типом не отменяется, а подтверждается: он
|
||||
@@ -523,8 +523,8 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
**каждая давать видимую пользу**, а у штурма исход «выкинуть» — полноправный.
|
||||
|
||||
Там же **шов**: где резать, когда допустимых мест несколько. Коротко — по
|
||||
границе, которая одна поднимает ступень ревью выше остальных; и не резать, когда
|
||||
обе половины остаются в одной ступени, потому что несокращаемый костяк проверок
|
||||
границе, которая одна поднимает метку ревью выше остальных; и не резать, когда
|
||||
обе половины остаются в одной метке, потому что несокращаемый костяк проверок
|
||||
платится за каждую задачу отдельно.
|
||||
|
||||
### Вычитка: два прохода, а не один
|
||||
@@ -589,7 +589,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
- **свойство репозитория в рамках** — номер миграции, хеш, версия зависимости:
|
||||
в лежалой задаче протухает молча и становится ложной рамкой. Снимается;
|
||||
снимок берётся при постановке, а не при заведении;
|
||||
- **предписание процесса в теле** — «делать таким-то профилем ревью», «взять
|
||||
- **предписание процесса в теле** — «делать с такой-то меткой ревью», «взять
|
||||
такой-то агент»: это второй дом для правила выбора и путь понизить требования
|
||||
решением, принятым до проектирования. Снимается;
|
||||
- **тип, разошедшийся с задачей** — задача заводилась починкой, а после разбора
|
||||
|
||||
@@ -25,25 +25,25 @@
|
||||
Тест выше говорит, **допустим** ли разрез. Где его провести из нескольких
|
||||
допустимых мест — отвечает шов.
|
||||
|
||||
**Шов — там, где падает ступень ревью.** Раздел «Затрагивает» перечисляет
|
||||
границы; если одна строка перечня поднимает ступень выше остальных, эта часть и
|
||||
**Шов — там, где падает метка ревью.** Раздел «Затрагивает» перечисляет
|
||||
границы; если одна строка перечня поднимает метку выше остальных, эта часть и
|
||||
режется отдельно. Пример: задача перекладывает несколько узлов разом и заодно
|
||||
добавляет два поля в существующий ответ. Целиком это `wide` — семь проходов по
|
||||
добавляет два поля в существующий ответ. Целиком это `large` — семь проходов по
|
||||
всему диффу, включая два, что держат машину и идут цепочкой. Разрезанная по шву,
|
||||
она даёт `wide` на маленькой переложенной части и `standard` на остатке.
|
||||
она даёт `large` на маленькой переложенной части и `medium` на остатке.
|
||||
|
||||
**Считай костяк, а не файлы.** У каждой задачи есть несокращаемые четыре прохода
|
||||
(гейт, спеки, код, триаж), и они платятся за каждую. Разрез, после которого обе
|
||||
половины остаются в одной ступени, делает ревью **дороже**: тот же объём
|
||||
половины остаются в одной метке, делает ревью **дороже**: тот же объём
|
||||
проверяется тем же составом, но костяк оплачен дважды. Отсюда правило: **резать,
|
||||
когда разрез снимает дорогой проход с большей части диффа**, и не резать, когда
|
||||
он просто делает файлы мельче.
|
||||
|
||||
**Это планирование, а не предписание процесса.** Ступень ревью выбирается по
|
||||
**Это планирование, а не предписание процесса.** Метка ревью выбирается по
|
||||
факту изменения — тем, кто его видит, — и в тело задачи не пишется: строка
|
||||
«делать профилем standard» это ровно тот второй дом правила выбора, который
|
||||
гигиена полей снимает. Шов пользуется ступенью как **признаком**, что в задаче
|
||||
две разнородные работы; решение о профиле остаётся за конвейером.
|
||||
«делать с меткой medium» это ровно тот второй дом правила выбора, который
|
||||
гигиена полей снимает. Шов пользуется меткой как **признаком**, что в задаче
|
||||
две разнородные работы; решение о метке остаётся за конвейером.
|
||||
|
||||
**Цель наследуется.** Все части несут `goal:` родителя: декомпозиция не меняет
|
||||
того, чему работа служит. Если у части цель другая — это признак, что дробили не
|
||||
|
||||
Reference in New Issue
Block a user