ревью: цикл задачи проверяет механику, метки сняты

Состав прогона постоянный: гейт, спеки, код, триаж; приёмник тем идёт,
когда у проекта есть свои темы. Метка, разметка и проход review-scope
упразднены, review-levels.md удалён, ось «метка» снята из axes.md.

Ступень 4 ушла из цикла: review-proof упразднён через день после
заведения, review-architecture переехал в code-deep-review вслед за
adversary и ops. Темы security, operations и architecture закрывает
review-code сверкой с записанными инвариантами, потолком 1 находка.

Умолчание разметки действий перевёрнуто на инлайн; развилка осталась
за необратимым, изменением дельта-спек и нарушенным инвариантом.
Задачи из урожая заводятся по слову человека, а не шагом сценария.

Чекпоинт назван единственным местом, где решается форма решения.
Потеряны ось времени в цикле и суждение о форме после кода — обе
потери названы в «Честном пределе» строкой границ покрытия.

Журнал — тема 77.
This commit is contained in:
av
2026-08-23 17:26:07 +03:00
parent daf9f8b824
commit 3c89d7111d
30 changed files with 1055 additions and 1781 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
@@ -79,7 +79,6 @@ stateDiagram-v2
| Проход | Класс дефекта для инъекции | Заготовка пробы |
|---|---|---|
| `review-scope` | пропущенная тема | положить в `docs/` новый документ и проверить, попал ли он в план темой |
| `review-autotests` | отсутствующая верификация | убрать тест на изменённую ветку, оставить код рабочим |
| `review-specs` | поведение вне спеки | добавить незаказанный фолбэк-дефолт на пустом входе |
| `review-code` | нарушение прозаической конвенции | увести штатный отказ мимо единой точки трансляции ошибки |
@@ -88,7 +87,9 @@ stateDiagram-v2
| `review-basics` | отказ, видимый чтением | убрать обработку ошибки записи так, чтобы отказ считался успехом |
| `review-basics` | своя тема проекта | нарушить правило из документа, у которого нет именного прохода |
| `review-architecture` | второй способ | завести вторую точку генерации id мимо единой |
| `review-proof` | набросок пути и ось времени | принять внешний идентификатор без разбора до запроса в хранилище; убрать обработку недоступности внешней зависимости в фоновом цикле |
| `review-code` | инвариант проекта | нарушить записанный в `CLAUDE.md` запрет по темам `security`, `operations` или `architecture` |
| `review-adversary` | построенный путь | принять внешний идентификатор без разбора до запроса в хранилище |
| `review-ops` | ось времени | убрать обработку недоступности внешней зависимости в фоновом цикле |
| `review-triage` | шум | подать 20 находок, из них 15 вкусовщина и 3 дубля — проверить потолок и дедуп |
Метрик сверх этого не заводим. Precision, корреляция между проходами, стоимость
@@ -100,7 +101,7 @@ stateDiagram-v2
## Когда калибровать
- при заведении нового прохода — **до** включения в состав метки по умолчанию;
- при заведении нового прохода — **до** включения в состав прогона;
- при правке charter'а существующего — иначе непонятно, правка помогла или нет;
- при появлении записи в журнале проскочивших дефектов — калибруем тот проход,
который должен был поймать;
@@ -75,19 +75,23 @@ Severity — ось процесса; перечень осей — [shared/axes
1. `Блокирует мердж` (≤3, каждая с оракулом);
2. `Стоит исправить сейчас` (≤4);
3. `Гипотезы без доказательства` — что понижено и почему;
4. `Promote candidates` — кандидаты в конвенцию или правило линтера;
5. `Границы покрытия` — сводная, обязательная.
4. `Урожай` — реальные находки не для этого мерджа: формулировка, оракул,
происхождение. Задачи из них заводит человек своим словом, не отчёт;
5. `Отложено в av-dev:code-deep-review` — что доказывается только запуском,
замером или входом шире диффа: тема, место, чем проверяется;
6. `Promote candidates` — кандидаты в конвенцию или правило линтера;
7. `Границы покрытия` — сводная, обязательная.
Перед секциями — сводка для человека: размер, сложность, метка и режим
прогона, состояние гейта, **план разметки задачи с исходом по каждой теме**,
сколько находок пришло на вход и сколько осталось.
Перед секциями — сводка для человека: режим прогона, состояние гейта, **перечень
тем с исходом по каждой**, сколько находок пришло на вход и сколько осталось,
сколько из них помечено `инлайн` и сколько `развилка`.
**Реестр сводки — темы, а не проходы, и это не оформление.** Перечень запущенных
проходов отвечает «все, кто должен был, отработали» и молчит о том, что именно
осталось непроверенным: уехавший в старшую метку проход уносит тему с собой
беззвучно. План же называет тему, её дом, глубину и исполнителя — и тема,
осталось непроверенным: уехавший в другой скилл проход уносит тему с собой
беззвучно. Перечень тем называет тему, её дом, глубину и исполнителя — и тема,
оставшаяся без отчёта, видна сразу. Перечень проходов из сводки не исчезает, но
идёт **внутри** плана, колонкой «кто закрывает».
идёт **внутри** него, колонкой «кто закрывает».
Каждая находка в секциях 1–2 несёт дополнительное поле:
@@ -95,10 +99,12 @@ Severity — ось процесса; перечень осей — [shared/axes
- Действие: инлайн | развилка
```
`инлайн` — оркестратор чинит сам, не спрашивая и не логируя. `развилка` — цена
исправления сопоставима с переработкой, либо выбор меняет scope, либо решение
трогает инвариант: уезжает вопросом с вариантами и ценой каждого туда, где
проект держит вопросы, а работа продолжается на остатке.
`инлайн` — оркестратор чинит сам, не спрашивая и не логируя, **и это
умолчание**. `развилка` — узкий выход с тремя основаниями: правка меняет
дельта-спеки, находка сидит в необратимом месте (миграция, формат на диске,
публичный контракт), находка трогает инвариант. Она уезжает вопросом с вариантами
и ценой каждого туда, где проект держит вопросы, а работа продолжается на
остатке.
Потребитель отчёта — оркестратор, который **реализует прочитанное**. Поэтому
потолок в 7 пунктов — не забота о внимании читателя, а защита кодовой базы от
@@ -14,7 +14,7 @@
## Карта тем
**Дом бывает файлом или каталогом**`docs/security.md` и `docs/security/`
называют одну и ту же тему. Форму дома называет план разметки задачи; проход её не
называют одну и ту же тему. Форму дома называет задание прохода; проход её не
угадывает.
| Тема | Дом | Что оттуда берётся |
@@ -34,10 +34,11 @@
второй — `operations` и `requirements`; обе строки убраны, и цена этого названа в
`SKILL.md`, раздел «Честный предел».
**Дом темы зависит ещё и от метки.** На `small` темы `security`, `operations` и
`architecture` смотрятся не против домов из этой таблицы, а против **инвариантов
`CLAUDE.md`**, и закрывает их `code`. Таблица описывает полный дом темы; сколько
из него открыто на этом прогоне, говорит план разметки задачи.
**Дом темы зависит от того, кто её закрывает.** В цикле задачи темы `security`,
`operations` и `architecture` смотрятся не против домов из этой таблицы, а против
**инвариантов `CLAUDE.md`**, и закрывает их `code`. Полные дома открывает скилл
`av-dev:code-deep-review` своими проходами. Таблица описывает полный дом темы;
что из него открыто на этом прогоне, говорит состав прогона.
Сквозное, не привязанное к теме:
@@ -45,12 +46,12 @@
| --- | --- |
| инварианты **с severity рядом с формулировкой** | `CLAUDE.md``AGENTS.md`, если он рядом), раздел инвариантов |
| что запускать запрещено, с путями; `testdata`; куда писать временное; имя основной ветки | `CLAUDE.md` |
| типовые узлы, типовые ложноположительные, **вопросы по темам**, триггеры метки, недоступно проверке | `docs/review.*`, раздел настройки |
| типовые узлы, типовые ложноположительные, **вопросы по темам**, недоступно проверке | `docs/review.*`, раздел настройки |
| прецеденты: воспроизведённые дефекты с оракулом | `docs/review.*`, журнал |
**Вопросы проекта привязаны к теме, а не к имени прохода.** Раньше блок в
`docs/review.md` адресовался поимённо (`ops: <вопрос>`), и когда проход уехал в
старшую метку, вопрос перестал задаваться молча. Тема переезд прохода
другой скилл, вопрос перестал задаваться молча. Тема переезд прохода
переживает.
## Сшивать обязаны проходы
@@ -65,7 +66,8 @@
«запись лежит сжатой и распаковывается целиком»; «блокировка удерживалась
5.019 с» — гарантированный отказ соседа только рядом с известным таймаутом
занятости. **Число проход снимает сам, на этом прогоне**, настройки берёт из
`docs/database.md`, и сшивает их `proof`. Раньше числа брались из
`docs/database.md`, и сшивает их `ops` в глубоком ревью — в цикле задачи не
снимает чисел никто. Раньше числа брались из
`docs/research/`; теперь этот документ процессный, и замер неизвестной свежести
больше не выдаёт себя за оракул.
- **инвариант + обратимость.** severity берётся из `CLAUDE.md`; если её там
@@ -75,15 +77,16 @@
**У `basics` стыков нет, и это не упущение.** Он не меряет, поэтому сшивать число
с настройкой ему нечего; единственное его основание для `critical` — инвариант из
`CLAUDE.md`, всё остальное он формулирует условиями и оставляет гипотезой. Его
вход намеренно узкий: дома тем из плана плюс инварианты и журнал. Широкий вход
это метка `large`, и там он есть у `architecture`. Греп по базе ему разрешён
вход намеренно узкий: дома тем из задания плюс инварианты и журнал. Широкий вход
есть только у `architecture`, а он работает в глубоком ревью. Греп по базе ему разрешён
точечный — «есть ли второй вызывающий», — но обход всей базы и инвентарь
концепций не его работа.
**У `scope` стыков нет по другой причине: он не читает содержимого.** Его дело —
найти дома и раздать темы, а не пересказать написанное. Пересказ сделал бы его
посредником между документом и проходом, а посредник расходится с источником и при
этом выглядит актуальным.
**Дома передаются адресом, а не пересказом, и это правило пережило проход,
который его исполнял.** Прежде темы раздавал `review-scope`: он находил дома и
называл их путём с разделом, ничего не пересказывая. Прохода нет, состав
постоянный, но правило то же — проход, получивший проинтерпретированный периметр,
не заметит, что интерпретация неверна.
## Деградация — поразрядная
@@ -94,7 +97,7 @@
**Кто какой документ читает — из документа не выводится, а назначается планом.**
Документ питает тему (это записано на стороне канона, таблица «Роли документов и
темы ревью»), а тему на этом прогоне закрывает тот, кого назвала разметка задачи; вся
темы ревью»), а тему на этом прогоне закрывает тот, кто назван в составе прогона; вся
раскладка «тема → проход → глубина» — в `SKILL.md` этого скилла и больше нигде.
**Списка читателей не ведёт никто, и это не пробел.** Он жил бы на стороне
канона, а документ живёт дольше, чем раскладка проходов: список разошёлся бы с
@@ -29,7 +29,7 @@
и `docs/adr/`.
Отдельно сюда попадают **решения о составе прогонов**: перестали звать проход,
понизили метку правилом, сузили класс проверяемого. Не потому, что это промах,
переселили его в другой скилл, сузили класс проверяемого. Не потому, что это промах,
а потому, что здесь лежит цена: если что-то теперь проскочит, первый вопрос —
«не тот ли это класс, который мы перестали проверять».
@@ -81,8 +81,8 @@
что необратимо и какой шаг гейта красит безусловно → `CLAUDE.md`; периметр и
недоверенный вход → `docs/security.*`. **Вопрос по теме**, если промах лечится
не фактом, а заданным вопросом, → раздел «Вопросы по темам» того же
`docs/review.*`; адресуй теме, а не имени прохода — проход уедет между
метками, тема останется. Самый частый адрес и самый дешёвый. Прежде чем
`docs/review.*`; адресуй теме, а не имени прохода — проход уедет в другой
скилл, тема останется. Самый частый адрес и самый дешёвый. Прежде чем
править charter, проверь, не хватит ли факта или вопроса: charter общий для
всех проектов, документ — про этот.
- **в конвенции или в правило линтера** — если свойство выражается
@@ -1,175 +0,0 @@
# Метки задачи — выбор, цена, доли
**Дом правила выбора метки.** Состав проходов по каждой метке, схема процесса и
раздача тем живут в [SKILL.md](../SKILL.md) — там диспетчер, и на готовой задаче
его достаточно. Здесь то, что читают, когда метку **выбирают, оспаривают или
калибруют**.
Применяет правило `review-scope` при разметке задачи — не автор изменения. Сама
матрица уехала в его устав **помеченной копией**, и дословность её держит
`copies.py`, а не обещание: прежде здесь стояло «расходиться не вправе», и
подкреплено это было ничем. Проза вокруг матрицы — отрицательный тест `small`,
доли, цена — принадлежит месту и живёт только здесь.
## Правило выбора — две оси, а не один вопрос
**Оси две, они измеряют разное, и метка есть максимум по ним.**
<!-- дом: матрица-метки -->
| | **знакомое** — форму решения можно назвать до начала | **незнакомое** — форму предстоит нащупать по ходу |
|---|---|---|
| **малое** — один узел | `small` | `large` |
| **среднее** — несколько узлов одного слоя | `medium` | `large` |
| **крупное** — несколько слоёв, перенос ответственности, большой рефакторинг | `large` | `large` |
<!-- /дом: матрица-метки -->
**Метка — не синоним размера.** Совпадают они только в левом верхнем углу: малое
**незнакомое** изменение получает `large`, трогая один узел. Поэтому в плане
стоят три строки, а не одна: размер, сложность и метка — каждая со своим
обоснованием. Проход, выведший объём диффа из метки, ошибётся ровно на этом
случае — а он и есть самый опасный: незнакомая форма в одном узле течёт там, где
её никто не ждёт.
**Размер** — про объём: сколько мест трогается. **Сложность** — про
неизвестность: знаем ли мы форму решения заранее. Признак незнакомого простой и
проверяемый: **перед работой нельзя назвать, какие узлы будут тронуты**.
Раньше обе оси были склеены в один вопрос «крупное **или** незнакомое?». Ответ
получался тот же, но две вещи под одним именем не измеришь по отдельности, и
потому разметка не могла сказать «изменение среднее, но совершенно знакомое» —
а именно эта пара и есть рабочее умолчание. Теперь обе оси называются в плане
поимённо, и обе — с обоснованием.
**Обе оси считаются один раз — по готовому диффу.** Раньше разметка шла до кода,
и размер приходилось выводить из написанного о задаче: перечня границ, `tasks.md`,
дельта-спек. Теперь размер меряется по тому, что тронуто на самом деле, а
написанное о задаче остаётся источником **сложности**: признак незнакомого —
нельзя было назвать тронутые узлы заранее — проверяется сверкой обещанного с
сделанным.
**Обратимость — не третья ось, а отрицательный тест.** Она не уточняет размер и
не уточняет сложность: она запрещает нижнюю метку независимо от обеих.
**Отрицательный тест `small`, и он важнее положительного:** изменение, которое
после мерджа **не откатывается обратной правкой**, — не `small`, каким бы
маленьким ни был дифф. Сюда попадают миграция схемы и данных, формат на диске,
публичный контракт, имя, которое разойдётся по кодовой базе. Три строки миграции
— это `medium`, а не `small`: размер диффа и цена ошибки здесь расходятся.
Что здесь считается крупным, что — незнакомым и что — мелким, проект уточняет в
`docs/review.md`, подразделе «Триггеры метки»: **тремя списками** — по одному на
каждую ось вверх и один вниз, поимённо, узлами или capability. Это **уточнение**,
а не отмена: не записано — работает таблица выше.
## Спорный случай решается вниз, и у этого есть цена
Правило асимметрично, потому что асимметрична цена ошибки.
- **Спорно между `medium` и `large` → бери `medium`.** Ошибка в эту сторону
стоит находки, которая всплывёт на следующей задаче или в журнале дефектов.
Ошибка в обратную стоит двух лишних проходов на каждой задаче, выбранной
неверно. Цена этого шага заметно упала: тяжёлая пара, что держала машину и шла
цепочкой, переехала в `av-dev:code-deep-review`, и `large` теперь добавляет два
прохода чтением, а не часы запусков.
- **Спорно между `small` и `medium` → бери `medium`.** Раньше эта строка
обосновывалась тем, что состав одинаков и ошибка почти бесплатна. Теперь состав
разный, и обоснование стало прямо противоположным: на `small` три темы ядра
смотрятся **только против записанных инвариантов**, а спорный случай — ровно тот,
где неизвестно, покрыт ли он инвариантом. Сомнение здесь стоит дороже, чем
раньше, и потому решается вниз тем более твёрдо.
**Выбор сделан в пользу пропускной способности, и это записано, а не подразумевается.**
Конвейер настроен на поток задач, а не на максимум находок с каждой: поправить в
следующей задаче дешевле, чем держать одну два часа. Отсюда три обязанности,
без которых сделка превращается в незаметную потерю качества:
- **границы покрытия называют темы и их глубину**, а не только запущенные
проходы — иначе `small` выглядит так же, как `large` без находок;
- **журнал дефектов в `docs/review.md` перестаёт быть хорошей практикой и
становится единственной обратной связью**: проскочивший дефект — единственный
сигнал, что метка выбрана слишком низко;
- **возврат в код — повод пересмотреть метку.** Задача, которая приходит в тот
же узел третий раз, уже не мелкая, чем бы ни выглядел её дифф.
## Метка — максимум по поверхности
**Обе оси меряются по всему диффу разом, и максимум по каждой отвечает за весь
дифф.** Метка изменения — не средневзвешенное: одна строка в перечне границ
задачи поднимает метку всему остальному, включая ту часть, которая сама по себе
была бы `small`.
Обратное тоже верно и тоже не бесплатно: у каждой задачи есть **несокращаемый
костяк — гейт, спеки, код, триаж**. Разрезать задачу, обе половины которой
остаются в одной метке, значит заплатить костяк дважды за ту же проверку.
Резать стоит там, где разрез **снимает старшую метку с большей части диффа**.
Шов и правило нарезки живут у того, кто ведёт задачи, — скилл
`av-dev:task-track`, его раздел о нарезке. Пути туда конвейер не выносит: за
пределы своего скилла он ходит вызовом, а не файлом.
Разметка в костяк не входит — она платится один раз на задачу, а не один раз на
прогон, и потому **перезапуск прогона её не удваивает**. Разрез задачи, впрочем,
удваивает: у каждой половины свой дифф, и мерить его приходится порознь.
**Размер, сложность, метка и глубина объявляются в отчёте, и все четыре с
обоснованием.** Метка выбирает `review-scope`; он вправе и поднять, и понизить
её — но не молча: строка «метка X, потому что размер Y и сложность Z»
обязательна на каждом прогоне, а не только когда метка отличается от ожидаемой.
## Чем `small` дешевле `medium` и что это стоит
Экономят три рычага — непуск, вход, потолок, — и они общие для всех проходов и
всех меток; их дом и точные числа в [SKILL.md](../SKILL.md), раздел «Модель по
проходу». Здесь только то, что рычаги делают **с этой меткой**:
1. **Составом.** `basics` на `small` не запускается — кроме случая, когда у
проекта есть свои темы; тогда он идёт **только с ними**, ровно как в `large`.
Три темы ядра, которые он держал бы, переходят к `code` сверкой по
инвариантам.
2. **Входом.** На `small` `specs` читает только дельта-спеку, а `code` — только
**индекс** конвенций (перечень родов и что механизировано), не весь их дом. На
`medium` оба читают дома целиком.
3. **Потолком.** На `small` потолки самые жёсткие из трёх меток, и каждый
напечатан в границах покрытия своего прохода.
**Что `small` за это не проверяет, названо поимённо и обязано идти строкой в
границы покрытия:** темы `security`, `operations` и `architecture` смотрятся
только против **записанных инвариантов** `CLAUDE.md`. Свойство, которого в
инвариантах нет, с этой меткой не спросит никто — ни сценарием, ни чтением
дома темы. Это и есть цена метки, и она заметно больше прежней: раньше `small`
отличался от `medium` одним проходом на один вопрос, то есть не экономил
ничего и назывался отдельной меткой зря.
**`large` назван по тому, что он добавляет: вход шире диффа.** Он единственный, где
живут тяжёлые проходы, и единственный, где что-то **запускается**. `basics` в нём
берёт только проектные темы; своих тем у проекта нет — он не запускается вовсе, и
план говорит об этом строкой. **На `small` действует то же правило и по той же
причине** — приёмник запускается только тогда, когда ему есть что принимать.
Совпадение неслучайное: `basics` держит темы ядра ровно при одной метке из трёх,
а приёмником проектных тем работает на всех.
## Доли — не пожелание, а проверка правила, и проверок две
**Сверху: `large` — 510%.** Если туда уходит каждая третья задача, метку
выбирают по ощущению важности. Обратный перекос виден по двум следам: по журналу
проскочивших дефектов и по строкам «отложено» в отчётах — если по одному месту
повторяется один и тот же неснятый замер, дело не в метке, а в том, что глубокое
ревью области просрочено.
**Снизу: `small` не должен обгонять `medium`.** Ориентир — до трети задач, но
сравнение важнее числа: **перевес `small` над `medium` значит, что рабочее
умолчание сместилось, а решения об этом никто не принимал.** Проверка нужна
именно теперь: пока две нижние метки совпадали составом, дрейф между ними не
стоил ничего, и проверки не было. Сейчас он стоит трёх тем ядра, которые на
`small` смотрятся только против инвариантов, — то есть ровно того, чем `small` и
дёшев.
Считается это по журналу дефектов и по отчётам, а не по ощущению: метка
напечатана в каждом отчёте, и посчитать её за месяц — работа на минуту.
**У дрейфа вниз есть свой стимул, и его стоит назвать.** `small` дешевле по
времени и по деньгам, а выбирает метку хоть и не автор, но проход, читающий
описание, написанное автором. Занижённое описание даёт занижённую метку без
чьего-либо злого умысла — потому корректор и вынесен в `code`, который смотрит
уже на код, а не на описание.