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

Канон 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
@@ -27,7 +27,7 @@
```mermaid
stateDiagram-v2
state "проход в профиле" as live
state "проход в составе метки" as live
state "retune №1 — правка charter'а" as r1
state "retune №2 — последняя попытка" as r2
state "проход удалён" as dead
@@ -101,7 +101,7 @@ stateDiagram-v2
## Когда калибровать
- при заведении нового прохода — **до** включения в профиль по умолчанию;
- при заведении нового прохода — **до** включения в состав метки по умолчанию;
- при правке charter'а существующего — иначе непонятно, правка помогла или нет;
- при появлении записи в журнале проскочивших дефектов — калибруем тот проход,
который должен был поймать;
@@ -13,7 +13,7 @@
- Оракул: <падающий тест / команда с выводом / положение руководства / нет>
- Последствие: <что произойдёт и при каких условиях>
- Предложение: <конкретное изменение>
- Найдено проходом: <имя агента>
- Найдено проходом: <имя агента; у проходов с раздельными потолками — имя и половина, например `code/техника`>
```
## Правила
@@ -76,10 +76,16 @@
4. `Promote candidates` — кандидаты в конвенцию или правило линтера;
5. `Границы покрытия` — сводная, обязательная.
Перед секциями — сводка для человека: профиль и режим прогона, состояние гейта,
**перечень запущенных проходов поимённо с исходом каждого**, сколько находок
пришло на вход и сколько осталось. Перечень обязателен: пропуск прохода не
отличим от прохода без находок, и назвать его больше некому.
Перед секциями — сводка для человека: размер, сложность, метка и режим
прогона, состояние гейта, **план разметки задачи с исходом по каждой теме**,
сколько находок пришло на вход и сколько осталось.
**Реестр сводки — темы, а не проходы, и это не оформление.** Перечень запущенных
проходов отвечает «все, кто должен был, отработали» и молчит о том, что именно
осталось непроверенным: уехавший в старшую метку проход уносит тему с собой
беззвучно. План же называет тему, её дом, глубину и исполнителя — и тема,
оставшаяся без отчёта, видна сразу. Перечень проходов из сводки не исчезает, но
идёт **внутри** плана, колонкой «кто закрывает».
Каждая находка в секциях 1–2 несёт дополнительное поле:
@@ -15,7 +15,7 @@
## Карта тем
**Дом бывает файлом или каталогом**`docs/security.md` и `docs/security/`
называют одну и ту же тему. Форму дома называет план разметчика; проход её не
называют одну и ту же тему. Форму дома называет план разметки задачи; проход её не
угадывает.
| Тема | Дом | Что оттуда берётся |
@@ -24,13 +24,21 @@
| `autotests` | `CLAUDE.md`, семантика гейта | команда гейта, чем краснеет безусловно, чего в нём нет, кто гоняет дорогое |
| `conventions` | `docs/conventions.*` | конвенции прозой и **что уже механизировано** правилом |
| `architecture` | `docs/architecture.*` | компоненты и capability, единые точки проекта |
| | `docs/passport.*` | что система делает и **чего не делает**, граница домена |
| | `docs/adr/` | почему решено так, отвергнутые варианты |
| | источник `docs/passport.*` | что система делает и **чего не делает**, граница домена |
| `security` | `docs/security.*` | периметр, недоверенный вход, из чего строятся пути и ключи, что вне модели |
| `operations` | `docs/architecture.*`, раздел эксплуатации | окружение, внешние зависимости поимённо, наблюдатель, характер потока |
| | `docs/database.*` | чем физически лежит запись, что при чтении и записи, настройки с числовым значением |
| | `docs/research/` | измеренные числа **с провенансом**, поведение внешних систем на самом деле |
| *тема проекта* | её документ в `docs/` | то, что проект счёл нужным записать |
| | источник `docs/database.*` | чем физически лежит запись, что при чтении и записи, настройки с числовым значением |
| *тема проекта* | её **свой** документ в `docs/` | то, что проект счёл нужным записать |
**`docs/adr.*` и `docs/research.*` в этой карте нет намеренно.** Они процессные
документы: прогон ревью их не открывает. Раньше первый питал тему `architecture`,
второй — `operations` и `requirements`; обе строки убраны, и цена этого названа в
`SKILL.md`, раздел «Честный предел».
**Дом темы зависит ещё и от метки.** На `small` темы `security`, `operations` и
`architecture` смотрятся не против домов из этой таблицы, а против **инвариантов
`CLAUDE.md`**, и закрывает их `code`. Таблица описывает полный дом темы; сколько
из него открыто на этом прогоне, говорит план разметки задачи.
Сквозное, не привязанное к теме:
@@ -38,12 +46,12 @@
| --- | --- |
| инварианты **с severity рядом с формулировкой** | `CLAUDE.md``AGENTS.md`, если он рядом), раздел инвариантов |
| что запускать запрещено, с путями; `testdata`; куда писать временное; имя основной ветки | `CLAUDE.md` |
| типовые узлы, типовые ложноположительные, **вопросы по темам**, триггеры профиля, недоступно проверке | `docs/review.*`, раздел настройки |
| типовые узлы, типовые ложноположительные, **вопросы по темам**, триггеры метки, недоступно проверке | `docs/review.*`, раздел настройки |
| прецеденты: воспроизведённые дефекты с оракулом | `docs/review.*`, журнал |
**Вопросы проекта привязаны к теме, а не к имени прохода.** Раньше блок в
`docs/review.md` адресовался поимённо (`ops: <вопрос>`), и когда проход уехал в
верхнюю ступень, вопрос перестал задаваться молча. Тема переезд прохода
старшую метку, вопрос перестал задаваться молча. Тема переезд прохода
переживает.
## Сшивать обязаны проходы
@@ -57,8 +65,10 @@
- **замер + настройка.** «Пик 768 МиБ» — аномалия только рядом со строкой
«запись лежит сжатой и распаковывается целиком»; «блокировка удерживалась
5.019 с» — гарантированный отказ соседа только рядом с известным таймаутом
занятости. Числа в `docs/research/`, настройки в `docs/database.md`, и оба
читает `ops` и `adversary`.
занятости. **Число проход снимает сам, на этом прогоне**, настройки берёт из
`docs/database.md`, и сшивают их `ops` и `adversary`. Раньше числа брались из
`docs/research/`; теперь этот документ процессный, и замер неизвестной свежести
больше не выдаёт себя за оракул.
- **инвариант + обратимость.** severity берётся из `CLAUDE.md`; если её там
нет — она **выводится по обратимости последствия** и помечается «выведена по
обратимости», а не выдаётся за решение проекта.
@@ -67,7 +77,9 @@
с настройкой ему нечего; единственное его основание для `critical` — инвариант из
`CLAUDE.md`, всё остальное он формулирует условиями и оставляет гипотезой. Его
вход намеренно узкий: дома тем из плана плюс инварианты и журнал. Широкий вход —
это профиль `wide`, и там он есть у `architecture`.
это метка `large`, и там он есть у `architecture`. Греп по базе ему разрешён
точечный — «есть ли второй вызывающий», — но обход всей базы и инвентарь
концепций не его работа.
**У `scope` стыков нет по другой причине: он не читает содержимого.** Его дело —
найти дома и раздать темы, а не пересказать написанное. Пересказ сделал бы его
@@ -83,7 +95,7 @@
**Кто какой документ читает — из документа не выводится, а назначается планом.**
Документ питает тему (это записано на стороне канона, таблица «Роли документов и
темы ревью»), а тему на этом прогоне закрывает тот, кого назвал разметчик; вся
темы ревью»), а тему на этом прогоне закрывает тот, кого назвала разметка задачи; вся
раскладка «тема → проход → глубина» — в `SKILL.md` этого скилла и больше нигде.
**Списка читателей не ведёт никто, и это не пробел.** Он жил бы на стороне
канона, а документ живёт дольше, чем раскладка проходов: список разошёлся бы с
@@ -96,10 +108,8 @@
| --- | --- |
| `CLAUDE.md` без инвариантов | `critical` по основанию «нарушен инвариант проекта» не присваивается никем |
| `docs/security.*` | тема `security` остаётся без дома: вопросы задаются по коду, `critical` не ставится, периметр неизвестен |
| `docs/research/` | числа неизвестны темам `operations` и `requirements` — формулируют условиями, а `specs` теряет проверку «требование против наблюдения» |
| `docs/database.*` | замер не с чем сравнить: находка темы `operations` не поднимается выше гипотезы |
| `docs/passport.*` | тема `architecture` теряет границу домена и вырождается в общее мнение |
| `docs/adr/` | «не отменяет ли изменение записанное решение» не спрашивает никто |
| `docs/review.*` | `triage` отсеивает вслепую: типовых ложноположительных нет; вопросы проекта по темам не задаются |
| `docs/conventions.*` | вторая половина `code` идёт вхолостую: записанных конвенций нет |
| `docs/architecture.*` | «не появился ли второй способ» не проверяется — единых точек не знает никто; тема `operations` теряет перечень внешних зависимостей |
@@ -6,7 +6,7 @@
и один и тот же класс проскакивает второй раз.
Тот же файл держит **настройку конвейера под проект** — типовые узлы, типовые
ложноположительные, вопросы к проходам, недоступно проверке. Это не соседство по
ложноположительные, вопросы по темам, недоступно проверке. Это не соседство по
случаю: все четыре раздела — производные калибровки, а журнал им источник.
## Что туда попадает
@@ -29,7 +29,7 @@
и `docs/adr/`.
Отдельно сюда попадают **решения о составе прогонов**: перестали звать проход,
понизили профиль правилом, сузили класс проверяемого. Не потому, что это промах,
понизили метку правилом, сузили класс проверяемого. Не потому, что это промах,
а потому, что здесь лежит цена: если что-то теперь проскочит, первый вопрос —
«не тот ли это класс, который мы перестали проверять».
@@ -77,13 +77,13 @@
Три адреса, и выбор между ними — половина ценности журнала:
- **в документ проекта** — если проход не мог знать факта. Адрес зависит от рода
факта, и карта их всех — [project-facts.md](project-facts.md): объём и
измеренное число → `docs/research/`; настройка хранилища → `docs/database.md`;
факта, и карта их всех — [project-facts.md](project-facts.md):
настройка хранилища → `docs/database.md`;
что необратимо и какой шаг гейта красит безусловно → `CLAUDE.md`; периметр и
недоверенный вход → `docs/security.*`. **Вопрос по теме**, если промах лечится
не фактом, а заданным вопросом, → раздел «Вопросы по темам» того же
`docs/review.*`; адресуй теме, а не имени прохода — проход уедет между
ступенями, тема останется. Самый частый адрес и самый дешёвый. Прежде чем
метками, тема останется. Самый частый адрес и самый дешёвый. Прежде чем
править charter, проверь, не хватит ли факта или вопроса: charter общий для
всех проектов, документ — про этот.
- **в конвенции или в правило линтера** — если свойство выражается