классификация задачи: три категории документов и метка вместо ступени

Канон 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:
av
2026-08-07 10:57:15 +03:00
co-authored by Claude Opus 5
parent 61cd9fcd37
commit d5bee11a6b
31 changed files with 1809 additions and 689 deletions
+227
View File
@@ -2707,3 +2707,230 @@ JJJ): у профиля обязан быть один правильный от
147. **Снятая проверка называет, что осталось вместо неё.** Цель не проверяется
— значит, за состав отвечают показ человеку и строка доклада; иначе
послабление читается как «здесь можно не думать».
## 40. Три категории документов: не всякий документ — тема ревью (2026-08-07)
Решение 36 объявило: **каждый документ проекта — тема ревью**. Правило дало
открытый список тем и сделало `docs/` конфигурацией конвейера — это работает и
остаётся. Но оно же оказалось неверным ровно наполовину, и потому вредным
целиком.
Паспорт и схему хранилища ревью читает, но темами они не являются: по ним нельзя
сказать «в этом изменении сделано не так», они задают границу, по которой судит
**чужая** тема. Журнал решений и журнал наблюдений ревью изменения не нужны
вовсе: ADR объясняет прошлое решение, а не предъявляет требование к изменению.
Ломалось это механически. Разметчик, применявший правило буквально, обязан был
либо завести фантомные темы `passport`, `adr`, `database`, `research` и
продублировать ими работу тем `architecture` и `operations`, либо потерять четыре
документа молча. Обе ветки случались; в собственном образце плана разметчика
`docs/passport.md` не попадал ни строкой, а его же обязательная арифметика
покрытия («документов найдено N, все N разнесены») при этом не сходилась.
**АЕАБА. Разрез один и проверяемый: можно ли по документу сказать «в этом
изменении сделано не так».** Отсюда три категории. **Тема** — да, прямо
(`conventions`, `security`, `architecture`, свои документы проекта). **Источник
темы** — нет, но он задаёт границу для чужой темы (`passport`, `database`,
`CLAUDE.md`, `openspec/specs/`). **Процессный документ** — нет, он про то, как мы
работаем (`tasks/`, `review.*`, `adr.*`, `research.*`, `.pm.json`).
**АЕАББ. Открыта одна категория из трёх.** `источник` и `процессный` перечислены
поимённо и проектом не пополняются; открыта только `тема`. Прежняя формулировка
«не темы ровно две» противоречила собственной раскладке канона — `.pm.json` был
третьим, и правило-исправление жило в чужом плагине, в коде `docs.py`. Теперь
документ, которого нет в раскладке, — однозначно своя тема проекта, и решать
нечего.
**АЕАБВ. «Не судит по нему» и «не открывает» — разные вещи.** `docs/review.*`
проходы читают на каждом прогоне: там вопросы по темам, журнал дефектов, типовые
узлы, типовые ложноположительные. Это чтение конвейером **своей обвязки**, а не
критерия. `adr/`, `research/` и `tasks/` не открывает никто.
**АЕАБГ. Цена решения записана, а не подразумевается.** Расхождение изменения с
записанным решением прогоном больше не ловится — это работа сверки документации
между спринтами. Измеренные числа проекта из ревью тоже ушли: проход,
опирающийся на число, обязан **снять его сам, на этом прогоне**, и приложить
команду замера. Обе потери идут обязательными строками в границы покрытия
каждого прогона, и пишет их триаж — не проход, потому что проход о том, чего в
конвейере нет, пожаловаться не может.
### Что из этого следует
148. **Плоское правило, верное наполовину, хуже двух правил.** Оно не даёт
половине случаев легального ответа, и исполнитель выбирает между двумя
плохими ветками — фантомной сущностью и молчащей потерей. Заметно это
становится не на определении, а на первом же образце вывода.
149. **Открытым делается одно множество, а не все.** Открытый список ценен тем,
что в него попадает незнакомое; если открыты все категории, незнакомое
попадает в произвольную.
150. **Отказ читать документ — тоже граница покрытия, и её пишет сток.** Строку
«этого не смотрел никто» некому подать снизу: проход, которого нет, отчёта
не присылает.
## 41. Разметка задачи: одна величина, посчитанная один раз (2026-08-07)
Разметка была стадией 0 **ревью кода** и платилась на каждом прогоне. Перед ревью
дизайна ту же самую величину — «крупное или незнакомое?» — называл сам пайплайн
задачи, то есть оркестратор, который только что довёл предложение до `propose`.
Одно и то же измерялось дважды, и один из двух раз без разведённости с автором —
ровно в той точке, ради которой разметчик и заведён.
**АЕАВА. Разметка идёт один раз на задачу, сразу после `propose`.** Её план
обслуживает обе стадии ревью: состав ревью дизайна и таблицу тем для ревью кода.
Диффа она не видит — кода ещё нет; размер оценивается по дельта-спекам и перечню
границ задачи.
**АЕАВБ. Осей две, ступень — максимум по ним.** **Размер** (малое, среднее,
крупное) — про объём; **сложность** (знакомое, незнакомое) — про то, известна ли
форма решения заранее. Раньше обе были склеены в один вопрос «крупное **или**
незнакомое?»: ответ получался тот же, но разметка не могла сказать «среднее, но
совершенно знакомое» — а это и есть рабочее умолчание.
**АЕАВВ. Ступень после кода не пересматривается.** Дифф может выйти крупнее
ожидания — ступень не двинется. Пересмотр означал бы либо второй запуск
разметчика (то, ради устранения чего он и переехал), либо машинный порог, который
на нетипичной задаче срабатывает не туда. Расхождение факта с разметкой ловит
журнал дефектов, постфактум, — так же, как и всякую другую ошибку выбора ступени.
**АЕАВГ. План на диск не пишется.** Файл-план стал бы четвёртым артефактом рядом
с `proposal.md`, `tasks.md` и `design.md`, пережил бы задачу и разошёлся бы с ней
молча. Прервался пайплайн — разметка повторяется; это самый дешёвый его проход.
### Что из этого следует
151. **Величина, из которой выводится состав, считается один раз и одним
агентом.** Два места, считающие одно и то же, расходятся; расходятся они
молча, и побеждает то, у которого меньше разведённости с автором.
152. **Разведённость — свойство момента, а не роли.** Тот же агент, спрошенный
до написания кода и после, даёт разные ответы; переезд по времени сделал
больше, чем сделал бы любой запрет.
## 42. `quick` стал дешевле `standard` тремя способами (2026-08-07)
`quick` и `standard` совпадали составом (шесть проходов) и различались глубиной
трёх тем: сверка против разбора. На практике это означало один проход, задающий
на один вопрос меньше, и потолок 4 вместо 2. Нижняя ступень не экономила почти
ничего и называлась отдельной ступенью зря.
Отдельно выяснилось, что дешевизна конвейера держалась на двух заявленных
рычагах — узкий вход и потолок находок, — и **оба применялись к одному проходу
из шести**. У `specs` и `code` потолка не было вовсе, а вход `code` включал
чтение дома конвенций «весь и целиком» на каждой задаче.
**АЕАГА. `quick` теряет приёмник тем.** Темы `security`, `operations` и
`architecture` на этой ступени закрывает `code` сверкой с **записанными
инвариантами** `CLAUDE.md`, потолком 1 находка на все три. Это не «глубина
ниже» — это **другой дом темы**, куда более узкий, и в плане он так и называется.
**АЕАГБ. Приёмник тем запускается тогда и только тогда, когда ему есть что
принимать.** Правило было в `wide` («нет своих тем проекта — не запускается») и
теперь распространено на `quick`. Совпадение неслучайное: темы ядра `basics`
держит ровно на одной ступени из трёх, а приёмником проектных тем работает на
всех.
**АЕАГВ. Вход и потолок применены к каждому опиниативному проходу.** На `quick`
`specs` читает только дельта-спеку, `code` — только индекс конвенций. Потолки
напечатаны и раздельны по половинам `code`: 3 технических, 2 конвенционных, 1 по
инвариантам. Раздельность обязательна — конвенционных находок больше по
построению, и в общем списке они вытеснили бы техническую половину, чей пропуск
дороже.
**АЕАГГ. Сработавший потолок объявляется.** Проход, срезавший находки, говорит
строкой, сколько осталось за срезом и какого рода. Молчащий срез неотличим от
«больше не нашлось» — тот же класс молчащего пропуска, против которого написан
весь конвейер.
**АЕАГД. Отрицательный тест `quick` стал жёстче, а не мягче.** Вопросы «обратима
ли миграция» и «что с записями новой версии после отката» задавал приёмник тем; на
`quick` его нет. Значит изменение, которое не откатывается обратной правкой, на
`quick` не идёт вовсе — каким бы малым оно ни было.
### Что из этого следует
153. **Ступень, не дающая экономии, не нужна.** Две ступени, различающиеся одним
вопросом одного прохода, — это одна ступень с шумом в отчёте.
154. **Рычаг, применённый к одному исполнителю, — не рычаг, а исключение.**
Заявленный механизм экономии проверяется перечислением: к кому он применён и
к кому нет.
155. **Проход без потолка выдаёт столько находок, сколько нашёл поверхностей.**
Ровно из-за этого был снят проход независимой реализации; тот же механизм
работал у `code` и `specs` и не был замечен, потому что счёт никто не считал.
## 43. Ревью дизайна тоже растёт ступенями (2026-08-07)
Состав ревью дизайна включался одним условием: `specs` всегда, `rubric` и
`architecture` — вместе, «при крупном или незнакомом». Значит `standard` получал
на предложении ровно один проход, то есть не отличался от `quick` ничем.
**АЕАДА. Три ступени вместо двух: `quick``specs`; `standard` — плюс `rubric`;
`wide` — плюс `architecture` и вопрос автору о трёх формах решения.**
**АЕАДБ. Рубрика съехала вниз, архитектура осталась наверху, и это не
симметричная правка.** Они зарабатывают на разном. Рубрика порождает **свойства
узла** и окупается уже на среднем изменении: её выход уезжает приёмочными
критериями в `tasks.md` и работает потом на всей задаче. Архитектура отвечает на
вопрос «не появился ли второй способ», а он на среднем знакомом изменении
отвечается «нет» ещё до запуска — держать её ниже `wide` значит платить за
предсказуемый ответ на каждой задаче.
**АЕАДВ. Тривиальность задачи больше не решает состав ревью.** Раньше она решала,
звать ли ревью предложения вовсе; теперь глубину обеих стадий называет ступень, а
тривиальная задача просто получает `quick`. «Пропустить ревью дизайна» и «пройти
его одним самым дешёвым проходом» — разные вещи: сверка дельта-спек стоит
меньше, чем разбор того, что она поймала бы на готовом коде.
### Что из этого следует
156. **Проходы, включаемые одним условием, стоит разводить по тому, на чём они
зарабатывают.** Общее условие — признак того, что их не сравнивали между
собой, а не того, что они равноценны.
157. **Средняя ступень обязана отличаться от нижней на обеих стадиях.** Иначе
«рабочее умолчание» отличается от исключения только именем.
## 44. Метка задачи: одно значение, по которому выбираются все ревьюверы (2026-08-07)
Решения 41–43 развели классификацию на две оси и свели состав обеих стадий ревью
к их максимуму. Значения этого максимума назывались `quick`, `standard`, `wide`,
а сам он — «ступень». Оба имени описывали **ревью**: как глубоко смотрим, на
какой ступеньке идём. Классифицируется же при этом **задача**, и результат
классификации принадлежит ей, а не прогону.
Расхождение не косметическое. Пока величина называлась свойством ревью, её было
естественно пересчитать на каждом прогоне — что конвейер и делал, пока разметка
не переехала к `propose`. Имя тянуло назад к устройству, из которого её только
что вынули.
**АЕАЕА. Классификация выдаёт задаче метку: `small`, `medium` или `large`.**
Метка принадлежит задаче, ставится один раз при разметке и дальше только
читается. Все проходы обеих стадий получают её в задании и обязаны напечатать в
границах покрытия.
**АЕАЕБ. Метка — единственный вход выбора исполнителей.** Ни класс задачи, ни её
тип, ни тривиальность, ни ощущение важности состав больше не определяют. У
конвейера один переключатель, и он напечатан в каждом отчёте.
**АЕАЕВ. Слово «ступень» удалено, а не оставлено синонимом.** Два имени одной
вещи расходятся — это ровно решение #37 про тему и проход. Метка ordered: `small`
< `medium` < `large`, и там, где нужен порядок, говорится «младшая» и «старшая
метка», а не вводится второе существительное.
**АЕАЕГ. Метка — не синоним размера, и это записано там, где ошибиться легче
всего.** Совпадают они в одном углу таблицы из трёх: малое **незнакомое**
изменение получает `large`, трогая один узел. Поэтому план печатает три строки —
размер, сложность, метка, — каждую со своим обоснованием, и выводить одну из
другой запрещено. Проход, определивший объём диффа по метке, ошибётся именно на
том случае, ради которого верхняя метка и заведена.
### Что из этого следует
158. **Имя величины должно называть её носителя, а не потребителя.** «Ступень
ревью» звала пересчитывать себя на каждом прогоне ревью; «метка задачи»
считается там же, где живёт задача.
159. **Переключатель состава должен быть один и печатный.** Пока их два —
тривиальность и ступень, — состав выводится из пересечения, а пересечение
нигде не напечатано целиком.
160. **Русские слова для осей, английские для значения.** Оси — суждение и
читаются прозой (`малое`, `знакомое`); метка — идентификатор, который
проходы сравнивают, и потому она английская. Тот же разрез, что «имена
файлов английские, текст русский» в каноне, и он же снимает путаницу
«крупное» против `large`.