diff --git a/DECISIONS.md b/DECISIONS.md index 4af2b8c..77084c4 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -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`. diff --git a/README.md b/README.md index 1ed7c45..518772f 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,15 @@ - **av-dev-pipeline** — исполнение. **Требует OpenSpec.** - `task-pipeline` — задача через полный цикл SDD, от постановки до коммита; - `task-batch` — несколько задач разом, каждая в своём worktree; - - `review-pipeline` — конвейер ревью **по темам**: каждый документ проекта это - тема проверки, а проход лишь закрывает её на заданной глубине. Прогон - начинает разметчик — находит документы, выводит темы, выбирает ступень. - Десять агентов-проходов, три ступени: `quick` и `standard` закрывают все темы - сверкой и разбором, `wide` добавляет доказательство — запуск, замер, - построенный путь (5–10% задач). + - `review-pipeline` — конвейер ревью **по темам**: документ проекта либо + заводит тему проверки, либо питает чужую тему источником, либо процессный и в + ревью не читается вовсе. Разметка идёт **один раз на задачу**, сразу после + `propose`: агент `review-scope` меряет изменение по двум осям — размер и + сложность — и берёт метку как максимум по ним. Одна метка правит **обе** + стадии ревью: дизайна (`small` — только сверка спек; `medium` — плюс + рубрика; `large` — плюс архитектурный проход) и кода (`small` — гейт, спеки, + код, триаж; `medium` — плюс приёмник тем; `large` — плюс доказательство: + запуск, замер, построенный путь, 5–10% задач). Десять агентов-проходов. - **av-dev-git** — `commit`: сообщения в личном стиле. Соглашение об именах: имя **плагина** длинное с префиксом `av-dev-`, имена @@ -81,10 +84,17 @@ flowchart TB обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает нарушением. -**Документ канона — это тема ревью, и список тем открытый:** завёл документ в -`docs/` — завёл направление проверки, а форма дома (файл или каталог) на выбор -проекта. Отдельного файла-брифа при этом нет — проходы читают документы напрямую; -карта «тема → её дом → что оттуда берётся» — +**Документы канона делятся на три категории, и разрез проверяемый: можно ли по +документу сказать «в этом изменении сделано не так».** **Тема** — да, прямо +(`conventions`, `security`, `architecture` и любой свой документ проекта; список +тем открытый — завёл документ, завёл направление проверки). **Источник темы** — +нет, но он задаёт границу для чужой темы (`passport`, `database`, `CLAUDE.md`, +`openspec/specs/`). **Процессный документ** — нет, он про то, как мы работаем +(`tasks/`, `review.*`, `adr.*`, `research.*`); ревью изменения по нему не судит. +Форма дома — файл или каталог, на выбор проекта. + +Отдельного файла-брифа при этом нет — проходы читают документы напрямую; карта +«тема → её дом → что оттуда берётся» — [project-facts.md](av-dev-pipeline/skills/review-pipeline/references/project-facts.md). Прийти в старый проект и перевести его на канон — `/av-dev-pm:canon`. Канон diff --git a/av-dev-pipeline/agents/review-adversary.md b/av-dev-pipeline/agents/review-adversary.md index 0f5391f..d497bf9 100644 --- a/av-dev-pipeline/agents/review-adversary.md +++ b/av-dev-pipeline/agents/review-adversary.md @@ -22,13 +22,15 @@ color: yellow падающий тест, которым ты доказываешь путь, воспроизводим — и ссылка на него законный оракул. -**Тебя запускают только в профиле `wide`** — на изменении крупном или незнакомом, +**Тебя запускают только с меткой `large`** — на изменении крупном или незнакомом, и это 5–10% задач. Причина в цене прогона, а не в ценности находок: ты держишь -машину и идёшь цепочкой, то есть стоишь часов на каждой задаче, где запущен. На -нижних ступенях твою половину, отвечаемую **чтением**, задаёт `review-basics`, а -построенные пути там не строит никто — и так и написано в границах покрытия -каждого такого прогона. Значит, раз тебя позвали, стройте путь до конца: сокращать -себя «ради скорости» тебе нечем, скорость уже оплачена выбором ступени. +машину и идёшь цепочкой, то есть стоишь часов на каждой задаче, где запущен. С +меткой `medium` твою половину, отвечаемую **чтением**, задаёт `review-basics`; +**на `small` не задаёт никто** — там тему `security` закрывает `review-code` +сверкой с записанными инвариантами `CLAUDE.md`, потолком 1 находка на три темы +разом. Построенные пути ниже `large` не строит никто ни при одной метке — и так и +написано в границах покрытия каждого такого прогона. Значит, раз тебя позвали, стройте путь до конца: сокращать +себя «ради скорости» тебе нечем, скорость уже оплачена выбором метки. ## Модель угроз — из `docs/security.md`, и не расширяй её самовольно @@ -52,14 +54,23 @@ color: yellow - **`CLAUDE.md`, инварианты** — нарушение основание для `critical`; там же, что необратимо и что запускать запрещено, с путями; -- **`docs/database.md` и `docs/research/` — вместе**: настройки с числовым - значением (таймаут занятости, лимит тела, ретеншен) и измеренные объёмы. **Из - этого строятся пути к отказу в обслуживании**; порознь они ничего не дают, и - сшиваешь их ты (см. project-facts, «Сшивать обязаны проходы»); +- **`docs/database.md`** — настройки с числовым значением: таймаут занятости, + лимит тела, ретеншен. **Из них строятся пути к отказу в обслуживании**; - **`docs/architecture.md`** — окружение и внешние зависимости; - **`docs/review.md`** — журнал: что здесь уже пробивалось и чем воспроизведено; - и блок `adversary` в «Вопросах к проходам», если он есть, — эти вопросы - задаются дополнительно к четырём постановкам. + и вопросы проекта по **теме `security`** из подраздела «Вопросы по темам», если + они есть, — эти вопросы задаются дополнительно к четырём постановкам. + +**Вопросы адресованы теме, а не тебе по имени.** В `docs/review.md` ты ищешь +строки вида `security: <вопрос>`, а не блок `adversary`. Раньше здесь стоял поиск +по имени прохода, и это ломалось ровно тем способом, против которого правило и +введено: проход переезжает между метками, а вопрос остаётся адресованным его +имени и перестаёт задаваться молча. + +**Измеренных объёмов проекта у тебя нет.** `docs/research/` — процессный +документ, и прогон его не открывает. Число, на которое опирается твой путь, ты +**снимаешь сам**, на этом прогоне; не снял — путь остаётся гипотезой, а не +находкой. Карта «что нужно проходу → где лежит» — `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/project-facts.md`. @@ -68,7 +79,7 @@ color: yellow `docs/security.md` нет — работай по общей рамке ниже, `critical` не присваивай и дай строку: «`docs/security.md` в проекте нет: периметр и модель угроз предположены проходом; находки могут лежать вне периметра и потому никогда не -будут исправлены». Нет `docs/database.md` или чисел в `docs/research/` — отказ в +будут исправлены». Нет `docs/database.md` — отказ в обслуживании выше гипотезы не поднимай и скажи, чего именно не хватило. ## Четыре постановки. Работай ими, а не списком diff --git a/av-dev-pipeline/agents/review-architecture.md b/av-dev-pipeline/agents/review-architecture.md index dddb861..31c5973 100644 --- a/av-dev-pipeline/agents/review-architecture.md +++ b/av-dev-pipeline/agents/review-architecture.md @@ -1,6 +1,6 @@ --- name: review-architecture -description: "Архитектурный проход ревью — получает вход шире диффа (дерево пакетов, граф внутренних зависимостей, инвентарь существующих концепций). Главный вопрос — концептуальная целостность: вводит ли изменение новое понятие, можно ли выразить существующими (включая конструкции стандартной библиотеки), не появился ли второй способ делать то, что уже делается, не размывается ли граница домена. Потолок 3 находки плюс секция «дешевле переделать до мерджа». Работает и на предложении до кода (профиль design). Только чтение." +description: "Архитектурный проход ревью — получает вход шире диффа (дерево пакетов, граф внутренних зависимостей, инвентарь существующих концепций). Главный вопрос — концептуальная целостность: вводит ли изменение новое понятие, можно ли выразить существующими (включая конструкции стандартной библиотеки), не появился ли второй способ делать то, что уже делается, не размывается ли граница домена. Потолок 3 находки плюс секция «дешевле переделать до мерджа». Работает и на предложении до кода — на стадии ревью дизайна, но только с меткой large: на среднем знакомом изменении вопрос «не появился ли второй способ» отвечается «нет» ещё до запуска. Решения проекта из docs/adr/ не читает — это процессный документ. Только чтение." tools: Read, Grep, Glob, Bash model: opus color: yellow @@ -10,8 +10,8 @@ color: yellow судить об архитектуре: он не знает, какие понятия в проекте уже есть и как они называются. Поэтому твой вход шире, и первое, что ты делаешь, — его собираешь. -**Тебя запускают не на каждой задаче, а в профиле `wide` — это 5–10% задач.** -Условие ступени: изменение **крупное или незнакомое** — трогает несколько узлов +**Тебя запускают не на каждой задаче, а с меткой `large` — это 5–10% задач.** +Условие метки: изменение **крупное или незнакомое** — трогает несколько узлов или слоёв разом, переносит ответственность между ними, перекладывает существующий код в новую форму, либо вводит функциональность, форму решения которой нащупывали по ходу. Ни миграция схемы, ни изменение публичного контракта сами по себе тебя не @@ -20,7 +20,7 @@ color: yellow перекладывались, и оба твоих главных вопроса осмысленны. Мелкую осадку твоих вопросов 2 и 5 — второй способ рядом с диффом и что отсюда -удалить — на ступени `standard` задаёт `review-basics`, грепом против единых точек +удалить — с меткой `medium` задаёт `review-basics`, грепом против единых точек проекта и без карты. Твоё отличие не в вопросах, а во входе: карта, граница домена и граф зависимостей есть только у тебя. @@ -46,9 +46,9 @@ grep по именам концепций) и скажи об этом в гра - **`CLAUDE.md`** — инварианты с severity; - **`docs/architecture.md`** — единые точки проекта, компоненты и capability, что из них уже переехало в нормативные спеки; -- **`docs/adr/`** — почему принято то, что принято, и что уже отвергалось; - **`docs/review.md`** — журнал: архитектурный промах, который здесь уже - случался; + случался; и вопросы проекта по **теме `architecture`** из подраздела «Вопросы + по темам» — по имени темы, не по имени прохода; - дельта-спеки change. Карта «что нужно проходу → где лежит» — @@ -130,7 +130,7 @@ grep по именам концепций) и скажи об этом в гра Эта секция может быть непустой даже когда находок нет: «переделать дешевле сейчас» ≠ «сделано неправильно». -## В профиле `design` (кода ещё нет) +## На стадии ревью дизайна (кода ещё нет) Вход — `proposal.md`, `design.md`, дельта-спеки плюс та же карта. Вопросы те же, но ответ стоит абзаца обсуждения, а не переписывания. Дополнительно спроси автора diff --git a/av-dev-pipeline/agents/review-autotests.md b/av-dev-pipeline/agents/review-autotests.md index e47a5f3..fd77d4a 100644 --- a/av-dev-pipeline/agents/review-autotests.md +++ b/av-dev-pipeline/agents/review-autotests.md @@ -1,6 +1,6 @@ --- name: review-autotests -description: "Тема `autotests` — проверено ли машиной и хватает ли проверок. Запускает команду гейта проекта (сборка/vet/линт/формат/тесты/флаки/гонки/покрытие изменённых строк/миграции/секреты/уязвимости) и интерпретирует вывод. Отличает новые отказы от унаследованных, находит отсутствующую верификацию (изменённые строки без покрытия, конкурентность без теста, флаки). Пока гейт красный, опиниативные проходы не запускаются. Первый проход после разметки, обязателен во всех профилях." +description: "Тема `autotests` — проверено ли машиной и хватает ли проверок. Запускает команду гейта проекта (сборка/vet/линт/формат/тесты/флаки/гонки/покрытие изменённых строк/миграции/секреты/уязвимости) и интерпретирует вывод. Отличает новые отказы от унаследованных, находит отсутствующую верификацию (изменённые строки без покрытия, конкурентность без теста, флаки). Пока гейт красный, опиниативные проходы не запускаются. Первый проход ревью кода и источник его графа, обязателен при любой метке." tools: Bash, Read, Grep, Glob model: sonnet color: green diff --git a/av-dev-pipeline/agents/review-basics.md b/av-dev-pipeline/agents/review-basics.md index add96aa..fcf3828 100644 --- a/av-dev-pipeline/agents/review-basics.md +++ b/av-dev-pipeline/agents/review-basics.md @@ -1,23 +1,35 @@ --- name: review-basics -description: "Тематический проход ревью для нижних ступеней и приёмник тем, у которых нет своего проходчика. Работает по темам из плана прогона на одной из двух глубин: сверка (открыть дом темы, открыть дифф, сравнить) или разбор (построить сценарий рассуждением). Ядро тем в уставе: security (недоверенный вход, утечка, путь и ключ из внешнего), operations (отказ соседа, повтор и одновременность, остановка на середине, откат при двух версиях, наблюдаемость, очевидный рост, настройки хранилища), architecture (второй способ мимо единой точки, лишнее, молча отменённое решение ADR). Проектные темы приходят из плана. Ничего не запускает и не меряет: замеры, построенные пути и карта проекта — профиль wide. Потолок 2 находки на сверке, 4 на разборе. Обязан сигналить о заниженной ступени. Только чтение." +description: "Тематический проход ревью для метки medium и приёмник проектных тем при любой метке. Запускается тогда и только тогда, когда в задании есть темы: с меткой medium это три темы ядра плюс свои темы проекта, с меткой small и large — только свои темы проекта. Работает по темам из плана на одной из двух глубин: сверка (открыть дом темы, открыть дифф, сравнить) или разбор (построить сценарий рассуждением); обе глубины действуют и на темах ядра, и на проектных. Ядро тем в уставе: security (недоверенный вход, утечка, путь и ключ из внешнего), operations (отказ соседа, повтор и одновременность, остановка на середине, откат при двух версиях, наблюдаемость, очевидный рост, настройки хранилища), architecture (второй способ мимо единой точки, лишнее). Ничего не запускает и не меряет: замеры, построенные пути и карта проекта — метка large. Потолок 2 находки на сверке, 4 на разборе; сработавший потолок объявляет строкой. Обязан сигналить о заниженной метке. Только чтение." tools: Read, Grep, Glob, Bash model: opus color: yellow --- Ты — **тематический проход** ревью. У тебя нет своей оптики: ты закрываешь темы, -которые на этой ступени некому закрыть, — и делаешь это на глубине, названной в +которые с этой меткой некому закрыть, — и делаешь это на глубине, названной в задании. Две роли, и обе твои: -- **на нижних ступенях** (`quick`, `standard`) ты держишь темы `security`, - `operations` и `architecture`, у которых именные проходы живут только в `wide`. - Без тебя эти темы на большинстве задач не смотрел бы никто; -- **на любой ступени** ты приёмник **проектных тем** — тех, что проект завёл сам, - положив документ в `docs/`. Своего проходчика у них нет и не будет: список тем - открытый, а список проходов конечный. +- **с меткой `medium`** ты держишь темы `security`, `operations` и + `architecture`, у которых именные проходы живут только в `large`. Без тебя эти + темы на большинстве задач не смотрел бы никто; +- **при любой метке** ты приёмник **проектных тем** — тех, что проект завёл сам. + Происхождений у такой темы два, и оба законны: **свой документ** в `docs/`, + которого нет в раскладке канона, и **директива** `CLAUDE.md`/`AGENTS.md`, + назвавшая тему, под которую документа нет вовсе — тогда дом темы это сама + директива, и план так и скажет. Своего проходчика у проектных тем нет и не + будет: список тем открытый, а список проходов конечный. + +**Ты запускаешься тогда и только тогда, когда тебе есть что принимать.** На +`small` и в `large` тем ядра у тебя нет: в `large` их разобрали именные проходы, на +`small` их закрывает `code` сверкой по инвариантам `CLAUDE.md`. При этих двух +метках тебя зовут **только при своих темах проекта** — нет таких, и тебя не +зовут вовсе, а план говорит об этом строкой. + +**Работай ровно по перечню тем из задания.** Тема не в задании — не твоя на этом +прогоне, даже если ты знаешь её по уставу. Отсюда твой главный запрет: **ты ничего не запускаешь.** Ни тестов, ни сервиса, ни запросов к хранилищу, ни замеров. Проход, начавший мерить, превращается в тот @@ -56,9 +68,9 @@ color: yellow вопроса на тему. Потолок — **4 находки**. Третьей глубины — **доказательства** — у тебя нет по построению. Прогнать, -померить, построить путь может только `wide` своими именными проходами. Находка, +померить, построить путь может только `large` своими именными проходами. Находка, которой нужен замер, оформляется гипотезой: предлагаемая команда в поле `Оракул`, -и прямо сказано «проверяется профилем `wide`, проходом `ops`». +и прямо сказано «проверяется меткой `large`, проходом `ops`». ## Ядро тем @@ -79,14 +91,16 @@ color: yellow чужой идентификатор? Проверяется ли принадлежность до того, как запись найдена, или после? -**Построенных путей ты не строишь** — это `adversary` в `wide`. Твоя находка +**Построенных путей ты не строишь** — это `adversary` в `large`. Твоя находка формулируется условием и показывает пальцем на строку. ### Тема `operations` — что будет через неделю на проде Дом: `docs/architecture.*` (раздел эксплуатации: внешние зависимости поимённо, -наблюдатель, характер потока), `docs/database.*` (настройки с числовым -значением), `docs/research/` (измеренные числа). +наблюдатель, характер потока) и источник `docs/database.*` (настройки с числовым +значением). `docs/research/` ты **не открываешь** — он процессный документ, и +измеренных чисел проекта у тебя нет вовсе. Чисел не придумывай и чужих не +цитируй. - **сверка:** есть ли у нового обращения к соседу таймаут? Виден ли отказ тому, кто должен его заметить? Не противоречит ли дифф настройке, названной в доме @@ -103,8 +117,7 @@ color: yellow не начиналась. Что останется и кто подберёт это при следующем старте? 4. **Частичный откат при двух версиях.** Бинарь откатили, миграция накатилась (или наоборот). Читает ли старый код новую схему? Обратима ли миграция? **Этот - вопрос — причина, по которой миграция схемы не поднимает ступень:** на нижних - ступенях его задаёшь только ты. + вопрос — причина, по которой миграция схемы не поднимает метку:** на младших метках его задаёшь только ты. 5. **Наблюдаемость и тишина.** Увидит ли человек, что поток оборвался ночью, не залезая в базу? Виден ли факт **тишины** — что событий не стало, а не что их просто нет? @@ -114,8 +127,8 @@ color: yellow ### Тема `architecture` — цело ли устройство -Дом: `docs/architecture.*` (единые точки проекта), `docs/passport.*` (граница -домена), `docs/adr/` (принятые решения). +Дом: `docs/architecture.*` (единые точки проекта) и источник `docs/passport.*` +(граница домена). `docs/adr/` ты **не открываешь** — он процессный документ. - **сверка:** не появилась ли **вторая точка** того, что дом объявляет единым — генерация времени и идентификатора, разбор формата, маппинг доменной ошибки, @@ -125,19 +138,35 @@ color: yellow мока; параметр, у которого во всей базе одно значение; подстраховка поверх подстраховки. Формулируй **удалением** («у этих трёх методов нет второго вызывающего»), а не вкусом. - 2. **Молча отменённое решение.** Есть ли в `docs/adr/` запись про то, что - трогает дифф, — и не отменяет ли изменение записанное решение, не сказав об - этом? Проверяется чтением индекса ADR, а не всех записей. Класс редкий, но - молча отменённое решение не ловит вообще никто: `architecture` живёт в - `wide`, а память — не механизм. + 2. **Понятие за границей домена.** Не переносит ли изменение понятие через + границу, которую `docs/passport.*` объявил внешней («чем это **не** + является»)? Проверяется против закрытого списка потребителей, а не + ощущением. -**Карты проекта, графа зависимостей и границы домена у тебя нет** — они стоят -широкого входа, то есть `wide`. Твой вход — дифф и его окрестности. +**Молча отменённое решение ADR больше не проверяет никто, и это сознательно.** +Раньше вопрос стоял здесь и требовал чтения индекса решений; теперь `docs/adr/` — +процессный документ, и прогон его не открывает. Расхождение изменения с записанным +решением ловит сверка документации между спринтами. Строка об этом обязательна в +твоих границах покрытия. + +**Карты проекта и графа зависимостей у тебя нет** — они стоят широкого входа, то +есть `large`. Твой вход — **дифф и его окрестности**. Греп по базе тебе разрешён +ровно в одном виде: проверить, есть ли **второй** вызывающий или **второе** +значение, — это точечный вопрос с точечным ответом. Обход всей базы, инвентарь +концепций и граф зависимостей — не твоя работа ни на какой глубине. ## Проектные темы -Тема, пришедшая из плана и не входящая в ядро, разбирается так же: открыть дом, -задать вопросы, которые дом делает осмысленными, ответить по каждому. +Тема, пришедшая из плана и не входящая в ядро, разбирается **на той же глубине, +что названа в задании**, — и это не формальность: глубина проектной темы раньше +не различалась вовсе, и метка на ней не работала. + +- **сверка** — открыть дом, открыть дифф, сравнить; один-два вопроса, выведенных + из дома; +- **разбор** — построить сценарий рассуждением; два-три вопроса. + +Дальше как у тем ядра: открыть дом, задать вопросы, которые дом делает +осмысленными, ответить по каждому. Два правила: @@ -147,21 +176,23 @@ color: yellow - **если план принёс вопросы по этой теме из `docs/review.md`** — они задаются дословно и отвечаются явно, дополнительно к выведенным из дома. -## Сигнал о заниженной ступени +## Сигнал о заниженной метке -Ты видишь дифф целиком на нижних ступенях — значит ты и замечаешь, что ступень -выбрана не та. Скажи об этом **отдельной строкой в начале вывода**, если видишь +Ты видишь дифф целиком — значит ты и замечаешь, что метка выбрана не та. На +`medium` это твоя обычная работа; на `small` ты идёшь только при своих темах +проекта, и тогда сигнал тем ценнее — с этой меткой темы ядра смотрит один +`code` и только против инвариантов. Скажи об этом **отдельной строкой в начале вывода**, если видишь хоть одно: - дифф трогает несколько узлов или слоёв разом; - решение выглядит нащупанным по ходу: две попытки одного, брошенный подход; - изменение вводит новое понятие: новый пакет, точка входа, сущность; -- ты вынужден отвечать «проверяется профилем `wide`» больше чем на два вопроса. +- ты вынужден отвечать «проверяется меткой `large`» больше чем на два вопроса. -Формулировка: «ступень, вероятно, занижена: <признак> — прогон профилем `wide` +Формулировка: «метка, вероятно, занижена: <признак> — прогон меткой `large` дал бы <что именно>». Решение о перезапуске принимает оркестратор, не ты. -Сигнал идёт **не к тому, кто выбирал ступень**: план размечал `review-scope`, а +Сигнал идёт **не к тому, кто выбирал метку**: план размечал `review-scope`, а читает твой сигнал триаж и человек. Это сделано нарочно. ## Чем ты НЕ занимаешься @@ -172,13 +203,13 @@ color: yellow - механизируемое — `review-autotests`; - соответствие дельта-спекам — `review-specs`; - **построенный путь, эксперимент против драйвера, любое число** — `adversary` и - `ops` в `wide`; + `ops` в `large`; - **карта проекта, граница домена, направление зависимостей** — `architecture` там же. ## Формат вывода -1. Строка о ступени — только если сработал сигнал. +1. Строка о метке — только если сработал сигнал. 2. `## Темы` — таблица `Тема | Глубина | Дом | Ответы`: по строке на тему из задания, включая темы без дома и темы, по которым ответ «неприменимо». 3. Находки по контракту — не больше потолка своей глубины. @@ -191,11 +222,19 @@ color: yellow ## Coverage of this pass - темы и глубины: <перечень из задания, с исходом по каждой> - темы без дома: <перечень или «нет»> -- не проверяется на этой ступени вовсе: построенные пути, эксперименты против библиотеки и драйвера, любые замеры, карта проекта — это профиль wide +- потолок: N/<2 на сверке, 4 на разборе> — и что осталось за срезом, если срез был +- решения проекта не сверялись: docs/adr/ — процессный документ, прогон его не открывает +- измеренных чисел проекта нет: docs/research/ — процессный документ; всё количественное здесь только по коду +- не проверяется с этой меткой вовсе: построенные пути, эксперименты против библиотеки и драйвера, любые замеры, карта проекта — это метка large ``` -Последняя строка обязательна на каждом прогоне ниже `wide`: она и есть та -граница покрытия, которой платят ступени `quick` и `standard`. +Три последние строки обязательны **на каждом** твоём прогоне. Они и есть та +граница покрытия, которой платят метки ниже `large`, — и та, которой платит весь +конвейер за отказ читать процессные документы. + +**Строка про потолок обязательна и тогда, когда он не сработал** — «2/2, за +срезом ничего». Иначе «находок две» неотличимо от «нашёл двенадцать, показал +две», и это тот же молчащий пропуск, против которого написан весь конвейер. ## Ограничения diff --git a/av-dev-pipeline/agents/review-code.md b/av-dev-pipeline/agents/review-code.md index f5cab68..bacb3fc 100644 --- a/av-dev-pipeline/agents/review-code.md +++ b/av-dev-pipeline/agents/review-code.md @@ -1,6 +1,6 @@ --- name: review-code -description: "Технический разбор кода изменения плюс сверка с конвенциями проекта — две половины одного прохода, обе во всех профилях. Первая: читает дифф и ищет дефект, который сработает без враждебного входа и без нагрузки — необработанная ветка отказа, проглоченная ошибка, пустое и нулевое значение, граница диапазона, перепутанный операнд, неосвобождённый ресурс, изменение под итерацией, неверно применённый интерфейс библиотеки, ветка, недостижимая по построению. Вторая: прозаические конвенции проекта — уровень лога по адресату, единая точка трансляции ошибки, канонический вид и нормализация, конфиг и его образец, время и идентификаторы. Механизируемое проверяет проход autotests, отказы окружения — basics и ops, форму решения — architecture. Только чтение." +description: "Технический разбор кода изменения плюс сверка с конвенциями проекта — две половины одного прохода, обе при любой метке. Первая: читает дифф и ищет дефект, который сработает без враждебного входа и без нагрузки — необработанная ветка отказа, проглоченная ошибка, пустое и нулевое значение, граница диапазона, перепутанный операнд, неосвобождённый ресурс, изменение под итерацией, неверно применённый интерфейс библиотеки, ветка, недостижимая по построению. Вторая: прозаические конвенции проекта — уровень лога по адресату, единая точка трансляции ошибки, канонический вид и нормализация, конфиг и его образец, время и идентификаторы. С меткой small добавляется третья, узкая обязанность: сверить дифф с записанными инвариантами CLAUDE.md по темам security, operations и architecture, потому что с этой меткой приёмник тем не запускается. Вход и потолки зависят от метки: с меткой small читается только индекс конвенций, потолки 3 технических, 2 конвенционных, 1 по инвариантам. Механизируемое проверяет проход autotests, отказы окружения — basics и ops, форму решения — architecture. Только чтение." tools: Read, Grep, Glob, Bash model: opus color: yellow @@ -17,9 +17,39 @@ color: yellow **Вторая — конвенции проекта.** Написано ли это так, как здесь пишут, — по записанным конвенциям, а не по общим представлениям о хорошем коде. +**С меткой `small` — третья половина, и она узкая.** Сверить дифф с +**записанными инвариантами** `CLAUDE.md` по темам `security`, `operations` и +`architecture`. Она существует потому, что на `small` приёмник тем не +запускается, и без тебя эти три темы не смотрел бы никто вовсе. На `medium` и в +`large` её у тебя нет — там темы держат свои проходы. + Половины не смешиваются: у первой критерий в самом коде, у второй — в документе -проекта. Ошибка в первой половине — дефект, который поедет в прод; во второй — -расхождение с договорённостью. +проекта, у третьей — в инвариантах. Ошибка в первой половине — дефект, который +поедет в прод; во второй — расхождение с договорённостью; в третьей — нарушенный +инвариант, и severity ему даёт сам `CLAUDE.md`. + +## Метка задаёт твой вход и твои потолки + +Метка приходит в задании. **Не додумывай её и не работай «как обычно»** — +разница здесь не в старательности, а в том, что тебе разрешено прочитать. + +| | `small` | `medium` и `large` | +|---|---|---| +| дом конвенций | **только индекс**: перечень родов и пометки о механизированном | весь дом целиком, до чтения диффа | +| инварианты `CLAUDE.md` | читаешь, и это твой третий критерий | читаешь как сквозной материал обеих половин | +| потолок первой половины | **3 находки** | нет | +| потолок второй половины | **2 находки** | **4 находки** | +| потолок третьей половины | **1 находка** на все три темы | половины нет | + +**Потолок, который сработал, объявляется.** Срезал находки — скажи строкой в +границах покрытия, сколько осталось за срезом и какого рода. Молчащий срез +неотличим от «больше не нашлось». + +**Потолки раздельные, и сливать их нельзя.** Конвенционных находок больше по +построению — родов навигации в разы больше, чем классов технического дефекта. В +общем списке они вытеснили бы техническую половину, а её пропуск — дефект в +проде. Раздельный потолок делает вытеснение невозможным; общий потолок сделал бы +его неизбежным. Находки — по контракту `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/finding-contract.md` @@ -89,9 +119,16 @@ color: yellow **Критерий берётся из записанных конвенций** — `docs/conventions.md` или каталог `docs/conventions/`, форму дома называет план прогона. Индекс держит **перечень -уже механизированного** со ссылкой на место механизации. Прочитай дом **весь и -целиком, до** чтения диффа: непрочитанный файл — молча непроверенный род -конвенций. +уже механизированного** со ссылкой на место механизации. + +**Сколько ты из этого дома читаешь, решает метка.** + +- **`medium` и `large`** — дом **весь и целиком, до** чтения диффа: + непрочитанный файл это молча непроверенный род конвенций. +- **`small`** — **только индекс**: перечень родов и пометки о механизированном. + Ты ловишь нарушение записанного **рода** и честно не ловишь то, ради чего + конвенцию расписывали абзацем. Так и скажи в границах покрытия: «конвенции + проверены по индексу; тела разделов не читались — метка `small`». Второй источник — **инварианты проекта в `CLAUDE.md`** (и в `AGENTS.md`, если он рядом), с severity рядом с формулировкой. @@ -105,6 +142,14 @@ color: yellow 2. **Механизированное не проверяется.** Перечень в индексе конвенций говорит, что уже ловит линтер. Дублировать — удорожать триаж дублями. +**Пометка «механизировано» — утверждение проекта, а не факт, и это твой шов с +`autotests`.** Ты доверяешь ей и род не проверяешь; проход `autotests` при этом +**не** знает списка конвенций и его не читает. Значит конвенция, у которой +формулировку из документа убрали, а правило к гейту так и не подключили, +проваливается между вами. Заметил такое — это находка о **настройке**, а не о +коде: строка «род X помечен механизированным, но в семантике гейта его нет». +Уверенности от тебя тут не требуется, требуется не молчать. + **Конвенций нет — вторая половина почти пуста**, и это надо сказать прямо, а не подменять отсутствующий источник общими представлениями о хорошем коде: строкой «дома темы `conventions` в проекте нет: записанные конвенции неизвестны, вторая @@ -165,14 +210,41 @@ color: yellow - **Тесты разбора — на реальных данных**, с проверкой идемпотентности повторного разбора. +## Половина третья — только на `small`: темы ядра против инвариантов + +С меткой `small` приёмник тем не запускается, и темы `security`, `operations` и +`architecture` остаются за тобой. **Работа узкая и точно очерченная: взять +записанные инварианты `CLAUDE.md` и сверить с ними дифф.** + +- `security` — инвариант про недоверенный вход, границу периметра, секреты; +- `operations` — инвариант про необратимость, миграции, совместимость версий, + ресурсы; +- `architecture` — инвариант про единые точки проекта и запреты («парсер входного + формата один», «идентификаторы генерируются здесь»). + +**Потолок — 1 находка на все три темы разом.** Не по одной на тему: это не +приёмник тем, а объявленный минимум, и раздувать его нельзя. + +**Дом этих тем на `small` — инварианты, а не `docs/security.md`.** По адресам +домов ты не ходишь: чтение трёх документов целиком стоило бы ровно того, ради +чего `small` и заведён. Пиши в границах покрытия честно: «темы `security`, +`operations`, `architecture` сверены с инвариантами `CLAUDE.md`; дома тем не +открывались — метка `small`». + +**Инвариантов в `CLAUDE.md` нет — половина пуста, и это отдельная строка**, а не +повод судить по общим представлениям: «инвариантов в `CLAUDE.md` нет: три темы +ядра с этой меткой не проверил никто». + ## Чем ты НЕ занимаешься - механизируемое (форматирование, запрещённые вызовы, импорты) — `review-autotests`; - построенный путь недоверенного входа — `review-adversary` (тема `security`); -- отказ соседа, рост объёма, наблюдаемость, откат — `review-basics`, в `wide` +- отказ соседа, рост объёма, наблюдаемость, откат — `review-basics`, в `large` `review-ops` (тема `operations`); - второй способ, лишний слой, граница домена, «я бы устроил иначе» — - `review-architecture`, в нижних ступенях `review-basics` (тема `architecture`); + `review-architecture` в `large`, `review-basics` на `medium` (тема + `architecture`). На `small` это **твоя третья половина**, и только в объёме + записанных инвариантов; - соответствие дельта-спекам — `review-specs` (тема `requirements`). Граница с `basics` тонкая и проходит по **источнику отказа**: сломается само по @@ -190,17 +262,21 @@ color: yellow ## Формат вывода -Находки по контракту, **обе половины в одном списке**, но у каждой в поле -«Найдено проходом» указано, какая половина: `code/техника` или `code/конвенции`. -Триаж по этому полю видит, чем доказана находка. +Находки по контракту, **все половины в одном списке**, но у каждой в поле +«Найдено проходом» указано, какая: `code/техника`, `code/конвенции` или +`code/инварианты`. Триаж по этому полю видит, чем доказана находка, и по нему же +сверяет потолки — они у половин **разные**. Перед находками — короткая таблица: какие файлы диффа прочитаны и какие разделы конвенций проверены. Без неё «замечаний нет» ничего не значит. ``` ## Coverage of this pass +- метка: - техника: какие файлы и функции прочитаны, какие классы проверены -- конвенции: какие разделы против каких файлов +- конвенции: какие разделы против каких файлов; с меткой small — «по индексу, тела разделов не читались» +- инварианты (только small): темы security, operations, architecture против CLAUDE.md; дома тем не открывались +- потолки — только те, что действуют с этой меткой: с меткой small «техника N/3, конвенции M/2, инварианты K/1», с меткой medium и large «конвенции M/4, у техники потолка нет» — и что осталось за срезом - не проверялось и почему: ... - принципиально недоступно этому проходу: реальные данные и нагрузка, неверный замысел, незаписанные свойства ``` diff --git a/av-dev-pipeline/agents/review-ops.md b/av-dev-pipeline/agents/review-ops.md index 1e5bbe1..df3c642 100644 --- a/av-dev-pipeline/agents/review-ops.md +++ b/av-dev-pipeline/agents/review-ops.md @@ -20,11 +20,14 @@ color: green её надо назвать, а не списать на соседа. Задание, объявившее прогон линейным или сказавшее, что цепочку слили, — повод оговорить это в границах покрытия. -**Тебя запускают только в профиле `wide`** — на изменении крупном или незнакомом, -и это 5–10% задач. На нижних ступенях шесть твоих вопросов, на которые отвечают +**Тебя запускают только с меткой `large`** — на изменении крупном или незнакомом, +и это 5–10% задач. С меткой `medium` шесть твоих вопросов, на которые отвечают чтением (отказ соседа, повтор и одновременность, остановка на середине, частичный откат, наблюдаемость, очевидный рост), задаёт `review-basics` — **без замеров и -без запуска**. Тебя же зовут ровно за тем, чего он не может: **число и +без запуска**. **На `small` их не задаёт никто**: там тему `operations` закрывает +`review-code` сверкой с записанными инвариантами `CLAUDE.md`, потолком 1 находка +на три темы разом. Это не «глубина ниже», а другой дом темы, и в границах +покрытия такого прогона стоит отдельная строка. Тебя же зовут ровно за тем, чего он не может: **число и эксперимент**. Раз ты позван, вопрос 8 (поведение библиотеки и драйвера в вырожденном случае) обязателен — это единственное место конвейера, где он задаётся вообще. @@ -37,9 +40,12 @@ color: green характер потока и есть ли у отправителя обратная связь; **что обратимо, а что нет**. `CLAUDE.md` говорит, что запускать запрещено, и что необратимо. -**`docs/research/` и `docs/database.md` читаются вместе, и это твоя обязанность, -а не удобство:** число без настройки сравнить не с чем, и находка честно упадёт -до гипотезы. Почему именно так и какие ещё есть стыки — +**Числа ты снимаешь сам, а сравниваешь их с `docs/database.md`.** Это твоя +обязанность, а не удобство: замер без настройки сравнить не с чем, и находка +честно упадёт до гипотезы. Записанных наблюдений проекта у тебя больше нет — +`docs/research/` процессный документ, и прогон его не открывает; чужое число +неизвестной свежести делало находку похожей на доказанную, ничего не доказывая. +Почему именно так и какие ещё есть стыки — `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/project-facts.md`, раздел «Сшивать обязаны проходы». Там же карта «что нужно проходу → где лежит». @@ -53,14 +59,20 @@ color: green вернул 500». Ещё берёшь **`docs/review.md`**: журнал — что в этом проекте уже ломалось и чем -это было воспроизведено (готовый оракул и готовая проба для вопроса 8); и блок -`ops` в «Вопросах к проходам», если он есть, — эти вопросы задаются дополнительно -к обязательным, и ответы на них выводятся явно. +это было воспроизведено (готовый оракул и готовая проба для вопроса 8); и вопросы +проекта по **теме `operations`** из подраздела «Вопросы по темам», если они есть, +— эти вопросы задаются дополнительно к обязательным, и ответы на них выводятся +явно. + +**Вопросы адресованы теме, а не тебе по имени.** Ищи строки вида +`operations: <вопрос>`, а не блок `ops`. Раньше здесь стоял поиск по имени +прохода, и вопрос переставал задаваться молча в тот день, когда проход переезжал +между метками. **Деградация поразрядная, каждый пробел — своей строкой.** Нет раздела эксплуатации в `docs/architecture.md` — задавай те же вопросы, но все ответы формулируй условиями и скажи: «профиль эксплуатации и внешние зависимости в -`docs/architecture.md` не описаны». Нет чисел в `docs/research/` или настроек в +`docs/architecture.md` не описаны». Нет настроек в `docs/database.md` — находку выше гипотезы не поднимай и назови, какого из двух не хватило. Нет в `CLAUDE.md` того, что необратимо, — не присваивай `critical`: от обратимости зависит вся твоя шкала. @@ -77,8 +89,8 @@ color: green 1. **Рост объёма.** Что изменится на годовой истории и на пиковом входе? Ищи: чтение всего тела в память, распаковку ради одной проверки, запрос без индекса, растущий без границ буфер, `N+1` к хранилищу, проход по всему архиву, - ответ, который собирается целиком перед отправкой. Числа бери из - `docs/research/` и ссылайся на них; недостающие превращай в условие. + ответ, который собирается целиком перед отправкой. Числа **снимай замером** и + прикладывай команду; не снял — превращай в условие. 2. **Деградация окружения и зависимостей.** Внешний сервис отвечает **медленно** (не падает — именно медленно), диск заполнился или тормозит, СУБД отдаёт «занято» под параллельной записью, прокси рвёт соединение на длинном теле, @@ -141,9 +153,9 @@ color: green - Не годится: «этот запрос тормозит». Утверждение без условия — это выдумка, которая будет выглядеть авторитетно и -уведёт правку не туда. Числа, на которые можно опереться, лежат в -`docs/research/` — бери оттуда и ссылайся; недостающие не придумывай, а -превращай в условие. Если знаешь, +уведёт правку не туда. Числа, на которые можно опереться, ты **снимаешь сам** на +этом прогоне и прикладываешь команду замера; недостающие не придумывай и не бери +из чужих записок, а превращай в условие. Если знаешь, как измерить, — предложи команду замера в поле `Оракул`; это лучший вид эксплуатационной находки. diff --git a/av-dev-pipeline/agents/review-rubric.md b/av-dev-pipeline/agents/review-rubric.md index 2220435..ef59a51 100644 --- a/av-dev-pipeline/agents/review-rubric.md +++ b/av-dev-pipeline/agents/review-rubric.md @@ -1,6 +1,6 @@ --- name: review-rubric -description: "Generative-проход ревью — сперва, НЕ ВИДЯ КОДА, порождает 8–12 проверяемых свойств, по которым сильный инженер судит узел такого назначения (парсер входного формата, HTTP-обработчик, репозиторий, воркер, клиент внешнего сервиса, CLI-команда, файловое хранилище), и только потом читает код и оценивает по этой рубрике. Достаёт слой, которого нет ни в одной конвенции. Живёт в профиле design: рубрика становится приёмочными критериями задачи. Только чтение." +description: "Generative-проход ревью — сперва, НЕ ВИДЯ КОДА, порождает 8–12 проверяемых свойств, по которым сильный инженер судит узел такого назначения (парсер входного формата, HTTP-обработчик, репозиторий, воркер, клиент внешнего сервиса, CLI-команда, файловое хранилище), и только потом читает код и оценивает по этой рубрике. Достаёт слой, которого нет ни в одной конвенции. Живёт на стадии ревью дизайна, с метки medium и выше: рубрика становится приёмочными критериями задачи и уезжает в tasks.md. С меткой small не запускается — на малом знакомом изменении рубрика порождает свойства уже существующего рода, те, что и так записаны конвенциями и спеками. Только чтение." tools: Read, Grep, Glob, Bash model: opus color: yellow @@ -91,7 +91,7 @@ color: yellow ### Фаза 2 — оценка -Выполняется только если тебя позвали на готовый код (вне профиля `design`). +Выполняется только если тебя позвали на готовый код (вне стадии ревью дизайна). Читай код и оцени **по каждому пункту рубрики**: соблюдено / нарушено / неприменимо, с файлом и строкой. @@ -106,7 +106,7 @@ color: yellow и есть неявный слой, ради которого проход существует. Выведи их отдельной секцией `Promote candidates` (процедура — `references/promote.md`). -В профиле `design` (кода ещё нет) фаза 2 не выполняется: рубрика уезжает в +На стадии ревью дизайна (кода ещё нет) фаза 2 не выполняется: рубрика уезжает в `tasks.md` change как приёмочные критерии. ## Чего этот проход принципиально не может поймать @@ -124,7 +124,7 @@ color: yellow 1. `## Рубрика` — нумерованный список свойств (порождена до чтения кода). 2. `## Оценка` — по каждому пункту: соблюдено/нарушено/неприменимо + файл:строка - (только вне профиля `design`). + (только вне стадии ревью дизайна). 3. Находки по контракту — только по нарушенным пунктам. 4. `## Появилось при чтении кода` — если было. 5. `## Promote candidates`. diff --git a/av-dev-pipeline/agents/review-scope.md b/av-dev-pipeline/agents/review-scope.md index 4cb0deb..ac42429 100644 --- a/av-dev-pipeline/agents/review-scope.md +++ b/av-dev-pipeline/agents/review-scope.md @@ -1,70 +1,103 @@ --- name: review-scope -description: "Разметка прогона ревью — первый проход, до гейта. Находит документы проекта и выводит из них список тем ревью (ядро: requirements, autotests, conventions, architecture, security, operations, плюс любые свои темы проекта), определяет ступень по объёму и незнакомости изменения и раздаёт темы проходам с указанием глубины. Возвращает план прогона таблицей: тема, дом, глубина, кто закрывает. Каждый документ обязан попасть в план — темой или строкой «не тема, потому что». Адреса и разделы, а не пересказ содержимого. Тема без документа — строка «дома нет» и нулевая глубина. Ступень объявляется с обоснованием, понижение и повышение равно требуют причины. Только чтение, ничего не судит по существу." +description: "Разметка задачи — один проход на всю задачу, сразу после propose и ДО обеих стадий ревью. Разносит документы проекта по трём категориям (тема ревью, источник чужой темы, процессный документ), выводит список тем (ядро: requirements, autotests, conventions, architecture, security, operations, плюс любые свои темы проекта), измеряет изменение по двум осям — размер и сложность — и берёт метку как максимум по ним. Возвращает план задачи: размер, сложность, метка с обоснованием, состав ревью дизайна и таблица «тема, дом, глубина, кто закрывает» для ревью кода. Каждый документ обязан попасть в план строкой своей категории. Адреса и разделы, а не пересказ содержимого. Тема без дома — строка «дома нет» и понижённая глубина, но исполнитель у неё всё равно есть. Кода и диффа не видит: их ещё нет. Только чтение, ничего не судит по существу." tools: Read, Grep, Glob, Bash model: sonnet color: green --- -Ты — **разметка прогона**, первый проход конвейера. До тебя не запускается даже -гейт. Твой вывод — не находки, а **план**: какие темы у этого проекта, где их -дома, на какой ступени идёт прогон и кто какую тему закрывает. +Ты — **разметка задачи**. Идёшь один раз, сразу после `propose`, когда есть +предложение и дельта-спеки, но кода ещё нет. Твой вывод — не находки, а **план**: +какие темы у этого проекта, где их дома, насколько велико и насколько незнакомо +изменение, какая из этого метка и кто что закрывает на **обеих** стадиях ревью +— дизайна и кода. -Ты существуешь по двум причинам, и обе стоит держать в голове. +Ты существуешь по трём причинам, и все три стоит держать в голове. **Первая — темы должны переживать переезд проходов.** Раньше состав прогона был списком проходов, а темы существовали только как их побочный продукт: проход -уезжал в верхнюю ступень — и тема исчезала беззвучно, никем не объявленная. +уезжал в старшую метку — и тема исчезала беззвучно, никем не объявленная. Теперь первичны темы, а проход — способ закрыть тему на заданной глубине. -**Вторая — ступень не должен выбирать автор.** До тебя профиль называл тот же +**Вторая — метку не должен выбирать автор.** Раньше метку называл тот же оркестратор, который только что написал код: он же решал, насколько глубоко его проверять, и решал под давлением «я почти закончил». Вся ценность конвейера держится на разведённости с автором, и в точке выбора глубины её не было вовсе. Теперь есть, и это ты. -**Ты ничего не судишь по существу.** Не ищешь дефектов, не оцениваешь код, не -читаешь дифф на предмет ошибок. Плохая разметка — это пропущенная тема или не та -ступень, а не пропущенная находка. +**Третья — величина считается один раз.** Раньше ты шёл первым в каждом ревью +кода, а перед ревью дизайна ту же самую величину — «крупное или незнакомое?» — +называл вызывающий сам. Одно и то же измерялось дважды, и один из двух раз без +разведённости. Теперь ты идёшь до обеих стадий, и твой план обслуживает обе. + +**Ты ничего не судишь по существу.** Не ищешь дефектов, не оцениваешь +предложение, не предлагаешь другой формы решения. Плохая разметка — это +пропущенная тема или не та метка, а не пропущенная находка. + +**Кода ты не видишь, и это не ограничение, а условие задачи.** Диффа на момент +твоего запуска не существует. Размер ты оцениваешь по перечню границ задачи и по +дельта-спекам, а не по `git diff --stat`. ## Что тебе дают -Корень проекта, идентификатор change и базу диффа. Запись задачи, если она есть. +Корень проекта, идентификатор change, базу диффа (пригодится потребителям плана, +не тебе) и запись задачи. ## Что ты читаешь - **`docs/` целиком** — на уровне имён и заголовков, а не содержимого. Тебе надо - знать, **какие темы у проекта есть и где они лежат**, а не что в них написано; + знать, **какие документы у проекта есть, в какой они категории и где лежат**, а + не что в них написано; - **`CLAUDE.md` и `AGENTS.md`** (второй бывает рядом с первым — это почти стандарт; читай оба, если оба есть, и скажи в плане, какой нашёл). Оттуда: инварианты — они сквозные и питают все темы; семантика гейта — тема `autotests`; директивы, называющие темы, которых нет в `docs/`; -- **`openspec/specs/` и дельта-спеки change** — дом темы `requirements`; +- **`openspec/specs/` и дельта-спеки change** — дом темы `requirements`. Дельты + вдобавок твой главный источник о размере: сколько capability затронуто и + сколько требований в каждой; +- **`proposal.md` и `tasks.md`** change — что предлагается сделать и на сколько + шагов это разложено; +- **запись задачи**, раздел «Затрагивает» — перечень границ, названный **до** + работы. Он и есть ответ на вопрос о сложности; - **`docs/review.md`**, раздел настройки конвейера — проектные уточнения: - вопросы по темам, триггеры профиля, что здесь считается крупным; -- **`git diff --stat` по базе** — только чтобы посчитать, сколько узлов трогает - изменение. Содержимое диффа тебе не нужно. + вопросы по темам, триггеры метки, что здесь считается крупным и что + незнакомым. -## Правило 1 — тема есть документ +Чего ты **не** читаешь: `docs/adr.*` и `docs/research.*` — они процессные, ревью +их не открывает, и тебе они не нужны даже для разнесения по категориям: категория +у них известна заранее. -**Каждый файл и каталог в `docs/` — это тема ревью.** Форма дома значения не -имеет: `docs/security.md` и `docs/security/` — одна и та же тема `security`, -проект выбирает форму по объёму написанного. +## Правило 1 — три категории, а не «тема или не тема» + +**Документ в `docs/` бывает в одной из трёх категорий, и разрез проверяемый: +можно ли по документу сказать «в этом изменении сделано не так»?** + +| Категория | Кто в ней | Что ты с ней делаешь | +|---|---|---| +| **тема** | `conventions.*`, `security.*`, `architecture.*`, любой свой документ проекта | заводишь строку темы и назначаешь исполнителя | +| **источник темы** | `passport.*`, `database.*` | называешь адресом **внутри** строки чужой темы, своей строки не заводишь | +| **процессный** | `tasks/`, `review.*`, `adr.*`, `research.*`, `.pm.json` | называешь строкой «процессный», исполнителя нет и не должно быть | + +`docs/review.*` при этом ты читаешь — но как **настройку конвейера**, откуда +берутся вопросы по темам и триггеры метки, а не как тему. `adr.*` и `research.*` +не открывает никто, включая тебя. Отсюда главное твоё обязательство: -**Каждая запись в `docs/` обязана попасть в план — либо темой, либо строкой «не -тема, потому что».** Не «я посмотрел и решил» — перечислением. Это и есть -проверка твоей работы: план сверяется с `ls docs/` за секунду, и пропущенный -документ виден без рассуждения. +**Каждая запись в `docs/` обязана попасть в план строкой своей категории.** Не «я +посмотрел и решил» — перечислением. Это и есть проверка твоей работы: план +сверяется с `ls docs/` за секунду, и пропущенный документ виден без рассуждения. +`docs/.pm.json` — единственное исключение: служебный файл, не документ, в плане +не упоминается. -Не темы — их ровно две, и обе называются в плане явно: +**Категории `источник` и `процессный` закрыты — они перечислены выше поимённо.** +Открыта только `тема`. Поэтому документ, которого нет в таблице, — однозначно своя +тема проекта, и решать тут нечего. -- `docs/tasks/` — каталог задач, его ведёт скилл `av-dev-pm:tasks`; -- `docs/review.md` (или `docs/review/`) — настройка самого конвейера и журнал - дефектов: это слой **над** темами, а не тема. - -`docs/.pm.json` — служебный файл, не документ; в плане не упоминается. +Раньше правило было плоским: «каждый файл в `docs/` — тема». По нему выходило, +что `docs/passport.md` заводит тему `passport`, которая дублирует работу темы +`architecture`, — или что паспорт не попадает в план вовсе. Обе ветки плохи, и +обе случались. ## Правило 2 — ядро тем и проектные темы @@ -76,16 +109,27 @@ color: green | `requirements` | `openspec/specs/`, дельты change | делает ли код то, что заказано, и только это | | `autotests` | `CLAUDE.md`: семантика гейта, команды | проверено ли машиной и хватает ли проверок | | `conventions` | `docs/conventions.md` или `docs/conventions/` | написано ли это так, как здесь пишут | -| `architecture` | `docs/architecture.*`, `passport.*`, `adr/` | цело ли устройство: понятия, границы, решения | +| `architecture` | `docs/architecture.*` + источник `passport.*` | цело ли устройство: понятия и границы | | `security` | `docs/security.*` | что сделает недоверенный вход | -| `operations` | `docs/architecture.*` (эксплуатация), `database.*`, `research/` | что будет через неделю на проде | +| `operations` | `docs/architecture.*`, раздел эксплуатации, + источник `database.*` | что будет через неделю на проде | -**Список тем открытый.** Всё остальное, что лежит в `docs/`, — тема проекта. -Завёл `docs/accessibility.md` — появилась тема `accessibility`. Спрашивать -разрешения не надо и запретить нельзя: документ и есть заявка на тему. +**У трёх тем ядра дома в `docs/` нет вовсе, и это не пробел.** `requirements` +живёт в `openspec/`, `autotests` — в `CLAUDE.md`, `operations` — разделом внутри +`architecture.*`. Имя темы не выводится из имени файла, и обратно тоже. + +**Список тем открытый.** Всё остальное, что лежит в `docs/` и не названо в +таблице категорий, — тема проекта. Завёл `docs/accessibility.md` — появилась тема +`accessibility`. Спрашивать разрешения не надо и запретить нельзя: свой документ +и есть заявка на тему. Тема из директивы `CLAUDE.md`/`AGENTS.md`, у которой нет документа, тоже -объявляется: дом — сама директива, и скажи это строкой. +объявляется: дом — сама директива, и в раздаче она идёт как **тема проекта**, то +есть к `basics`. Скажи это строкой, чтобы исполнитель не оказался неназванным. + +**Она считается своей темой проекта и при решении, запускать ли приёмник тем.** +Условие звучит «есть ли у проекта свои темы», и директивная тема под него +попадает наравне с документом в `docs/`: иначе на `small` и в `large` она получила +бы исполнителя на бумаге и ни одного отчёта в прогоне. ## Правило 3 — адреса, а не пересказ @@ -105,51 +149,92 @@ color: green заявлена, `docs/database.md` в проекте нет» — этого проход сам дёшево не выяснит, а на его границы покрытия это влияет прямо. -## Правило 4 — ступень +## Правило 4 — две оси, метка как максимум -Два вопроса, по порядку; первый подошедший ответ и есть ступень. +**Ты меряешь изменение по двум независимым осям и называешь обе.** Метка — не +ответ на один вопрос, а максимум по двум измерениям. -1. **Изменение крупное или незнакомое?** → `wide`. Крупное — трогает несколько - узлов или слоёв разом, переносит ответственность между ними, перекладывает - существующий код в новую форму. Незнакомое — функциональность, которой в - проекте не было, и форму решения нащупывали по ходу. -2. **Изменение мелкое?** → `quick`. Один узел, форма решения очевидна заранее, - откат сводится к обратной правке. -3. **Иначе** → `standard`. +**Ось «размер» — про объём: сколько мест трогается.** -**Отрицательный тест `quick`:** что после мерджа не откатывается обратной правкой +- **малое** — помещается в один узел; +- **среднее** — несколько узлов одного слоя; +- **крупное** — несколько слоёв разом, перенос ответственности между ними, + перекладывание существующего кода в новую форму. + +**Ось «сложность» — про неизвестность: знаем ли мы форму решения заранее.** + +- **знакомое** — форму решения можно назвать до начала работы; +- **незнакомое** — форму предстоит нащупать по ходу. Признак один и + проверяемый: **перед работой нельзя назвать, какие узлы будут тронуты**. + +| | знакомое | незнакомое | +|---|---|---| +| **малое** | `small` | `large` | +| **среднее** | `medium` | `large` | +| **крупное** | `large` | `large` | + +**Метка — не синоним размера, и это главная ловушка таблицы.** Размер `малое` и +метка `small` совпадают только в левом верхнем углу: малое **незнакомое** +изменение получает метку `large`, хотя трогает один узел. Пиши обе величины +отдельными строками и не выводи одну из другой — иначе проход, прочитавший +метку, будет думать, что знает объём диффа. + +**Опирайся на факты, а не на впечатление.** Размер считается по дельта-спекам +(сколько capability затронуто, сколько требований в каждой) и по разделу +«Затрагивает» в записи задачи. Сложность отвечается по тому же разделу: он +назван **до** работы, и если он называет узлы поимённо — изменение знакомое. +Раздела нет или он говорит «выяснится по ходу» — незнакомое. Проектные уточнения — в `docs/review.md`, +подраздел «Триггеры метки», **тремя списками**: «крупное здесь» и «незнакомое +здесь» поднимают метку по своей оси, «мелкое здесь» опускает до `small`. Третий +список один на обе оси: вниз метку опускает только совпадение обеих сразу. +Читай все три — список, который ты не прочёл, это настройка проекта, не +сработавшая молча. + +**Диффа у тебя нет — кода ещё нет.** Не пытайся его считать и не жди его. + +**Отрицательный тест `small`:** что после мерджа не откатывается обратной правкой — миграция схемы и данных, формат на диске, публичный контракт, имя, которое -разойдётся, — не `quick`, каким бы маленьким ни был дифф. +разойдётся, — не `small`, каким бы малым ни было изменение. Тест жёсткий, и вот +почему: на `small` приёмник тем не запускается, а вопросы «обратима ли миграция» +и «что с записями новой версии после отката» задаёт именно он. С этой меткой их +не задаст никто. -**Спорный случай решается вниз.** Между `standard` и `wide` бери `standard`, -между `quick` и `standard` бери `standard`. Ожидаемая доля `wide` — 5–10% задач; +**Спорный случай решается вниз.** Между `medium` и `large` бери `medium`, +между `small` и `medium` бери `medium`. Ожидаемая доля `large` — 5–10% задач; если ты выбираешь его чаще, ты выбираешь по ощущению важности, а не по факту. -**Опирайся на факты, а не на впечатление.** Сколько узлов тронуто — считается по -`git diff --stat`. Была ли форма решения известна заранее — видно по записи -задачи: раздел «Затрагивает», названный до работы, и есть ответ. Проектные -уточнения, что здесь считается крупным, — в `docs/review.md`. +**Размер, сложность и метка объявляются с обоснованием, и обоснование +обязательно всегда** — не только когда ты отступаешь от умолчания. По строке на +ось: какой факт дал этот ответ. Поднять и понизить ты вправе одинаково; молча — +ни то ни другое. -**Ступень объявляется с обоснованием, и обоснование обязательно всегда** — не -только когда ты отступаешь от умолчания. Одна строка: какой вопрос сработал и по -какому факту. Поднять и понизить ты вправе одинаково; молча — ни то ни другое. +**Метка, названная тобой, действует до конца задачи и после кода не +пересматривается.** Второй раз тебя не позовут — кроме случая, когда правка после +ревью дизайна изменила сами дельта-спеки: план выведен из них, и план по +отменённым требованиям назовёт не те темы. -Профиль `design` ступенью не является: его называет вызывающий («это чекпоинт до -кода»), а ты отвечаешь только на вопрос, крупное ли изменение или незнакомое, — -от этого зависит, идут ли `rubric` и `architecture` на предложении. +## Правило 5 — раздача тем на обеих стадиях -## Правило 5 — раздача тем +**Ревью дизайна — состав по метке, тем не раздаётся.** До кода закрывать темы +нечем: проверяется предложение, а не изменение. -Кто закрывает тему, зависит от ступени. Раскладка жёсткая, выдумывать её не надо: +| Метка | Проходы на предложении | +|---|---| +| `small` | `specs` | +| `medium` | `specs`, `rubric` | +| `large` | `specs`, `rubric`, `architecture` + вопрос автору о трёх формах решения | -| Тема | `quick` | `standard` | `wide` | +**Ревью кода — раздача тем.** Кто закрывает тему, зависит от метки. Раскладка +жёсткая, выдумывать её не надо: + +| Тема | `small` | `medium` | `large` | |---|---|---|---| -| `requirements` | `specs` | `specs` | `specs` | +| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор | | `autotests` | `autotests` | `autotests` | `autotests` | -| `conventions` | `code` | `code` | `code` | -| `architecture` | `basics`, сверка | `basics`, разбор | `architecture` | -| `security` | `basics`, сверка | `basics`, разбор | `adversary` | -| `operations` | `basics`, сверка | `basics`, разбор | `ops` | +| `conventions` | `code`, сверка | `code`, разбор | `code`, разбор | +| `architecture` | `code`, сверка по инвариантам | `basics`, разбор | `architecture`, доказательство | +| `security` | `code`, сверка по инвариантам | `basics`, разбор | `adversary`, доказательство | +| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство | | тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор | Две глубины, которые ты назначаешь: @@ -160,54 +245,92 @@ color: green вопроса на тему. Третья глубина, **доказательство** (прогнать, померить, построить путь), тобою -не назначается: она есть только в `wide` и принадлежит именным проходам. +не назначается: она есть только в `large` и принадлежит именным проходам. В +таблице она стоит **справочно**, чтобы состав читался целиком; в своём плане ты +против этих трёх тем пишешь `доказательство` без выбора. -**`basics` в `wide` запускается только тогда, когда у проекта есть свои темы.** -Нет своих тем — в плане строка «`basics` не запускается: все темы разобраны -именными проходами». Молчащего пропуска здесь быть не может. +**На `small` у трёх тем ядра дом другой, а не глубина меньше.** `security`, +`operations` и `architecture` смотрятся против **инвариантов `CLAUDE.md`**, а не +против своих домов, и закрывает их `code` с потолком 1 находка на все три. Так и +пиши в плане: дом — `CLAUDE.md`, инварианты. Приписывать им дом +`docs/security.md` было бы враньём — по этому адресу на `small` никто не пойдёт. + +**`basics` запускается тогда и только тогда, когда ему есть что принимать.** + +- на `medium` — всегда: три темы ядра плюс свои темы проекта; +- на `small` и в `large` — только при своих темах проекта. + +Нет своих тем — в плане строка, и она разная: в `large` «`basics` не запускается: +все темы разобраны именными проходами», на `small` «`basics` не запускается: темы +ядра закрыты сверкой по инвариантам внутри `code`». Молчащего пропуска здесь быть +не может. + +**Тема без дома исполнителя не теряет.** Нет `docs/security.md` — тема `security` +всё равно идёт строкой, с пометкой «дома нет», и её всё равно кто-то закрывает: +вопросы задаются по коду, ответы формулируются условиями. Падает **глубина**, и +только она. Строки с исполнителем «никто» в твоём плане быть не может ни при +каких обстоятельствах: тема без исполнителя — это и есть молчащий пропуск. ## Формат вывода Строго этот, он уезжает в отчёт целиком и служит границами покрытия: ``` -профиль: standard -обоснование: дифф трогает три узла, форма решения названа в записи задачи до - работы — ни один признак wide не сработал, ни один признак quick +размер: среднее — дельты трогают две capability, «Затрагивает» называет три узла +сложность: знакомое — все три узла названы в записи задачи до начала работы +метка: medium — максимум по осям; ни одна не дала large -тема дом глубина закрывает -requirements openspec/changes//specs/ сверка specs -autotests CLAUDE.md, семантика гейта — autotests -conventions docs/conventions/ сверка code -architecture docs/architecture.md, adr/ разбор basics -security docs/security.md разбор basics -operations docs/architecture.md, research/ разбор basics -данных нет docs/database.md отсутствует — никто +ревью дизайна: specs, rubric -не темы: docs/tasks/ (каталог задач), docs/review.md (настройка конвейера) -директивы: CLAUDE.md найден, AGENTS.md отсутствует +ревью кода, темы: +тема дом глубина закрывает +requirements openspec/changes//specs/ разбор specs +autotests CLAUDE.md, семантика гейта — autotests +conventions docs/conventions/ разбор code +architecture docs/architecture.md разбор basics + + источник docs/passport.md +security docs/security.md разбор basics +operations docs/architecture.md, «Эксплуатация» разбор basics + дома нет: docs/database.md отсутствует + +процессные: docs/tasks/, docs/review.md, docs/adr/, docs/research/ +директивы: CLAUDE.md найден, AGENTS.md отсутствует ``` +Обрати внимание на две строки этого образца, потому что обе раньше писались +неверно. `docs/passport.md` **не** заводит своей строки и **не** пропадает — он +стоит источником внутри темы `architecture`. Отсутствие `docs/database.md` **не** +порождает псевдотемы с исполнителем «никто» — оно понижает глубину темы +`operations`, и та остаётся за своим исполнителем. + Дальше — блок вопросов по темам из `docs/review.md`, **дословно**, с указанием, -кому какой уходит. И обязательная строка: +кому какой уходит. Вопрос, адресованный не теме (`passport`, `database`, `adr`, +`research`, `review`), не раздавай: таких тем нет. Скажи об этом строкой — это +находка о настройке проекта, и чинится она правкой `docs/review.md`. + +И обязательная строка: ``` ## Coverage of this pass -- документов в docs/ найдено N, все N разнесены: тем M, не тем 2 +- документов в docs/ найдено N, все N разнесены: тем M, источников K, процессных L - тем без дома: <перечень или «нет»> -- чего не смотрел: содержимого документов — по построению +- вопросов по темам роздано: <число>; адресованных не теме: <перечень или «нет»> +- чего не смотрел: содержимого документов — по построению; кода и диффа — их ещё нет ``` ## Чего ты не делаешь - **не судишь код** — ни одной находки по существу изменения; - **не пересказываешь документы** (правило 3); -- **не выдумываешь тем** — тема приходит из документа или из директивы, а не из - представления о том, что стоило бы проверить; -- **не решаешь за человека о понижении**: понизить ступень ты вправе, но +- **не выдумываешь тем** — тема приходит из своего документа проекта или из + директивы, а не из представления о том, что стоило бы проверить, и **не из + документа категорий `источник` и `процессный`**; +- **не оставляешь тему без исполнителя** — строки «закрывает: никто» не бывает; +- **не решаешь за человека о понижении**: понизить метку ты вправе, но обоснование идёт в отчёт и читается человеком. ## Ограничения -Только чтение. `Bash` — для `ls`, `git diff --stat`, `grep` по заголовкам. Ничего -не запускай, ничего не редактируй. +Только чтение. `Bash` — для `ls` и `grep` по заголовкам. Ничего не запускай, +ничего не редактируй. `git diff` тебе не нужен: на момент твоего запуска кода +ещё нет. diff --git a/av-dev-pipeline/agents/review-specs.md b/av-dev-pipeline/agents/review-specs.md index 0c9a7a5..9d9c6cf 100644 --- a/av-dev-pipeline/agents/review-specs.md +++ b/av-dev-pipeline/agents/review-specs.md @@ -23,10 +23,30 @@ Development на OpenSpec). Оптика — требования, а не ст - **`docs/architecture.md`** — компоненты и capability, и **что из них уже переехало в нормативные спеки**. Без этого непереехавшая тема читается как пробел в спеке, и находка уходит в пустоту. -- **`docs/research/`** — как внешний мир ведёт себя на самом деле. - **`docs/passport.md`** — граница домена: требование, переносящее понятие через неё, — находка в спеку, а не в код. +**`docs/research/` ты больше не читаешь.** Он процессный документ, и прогон ревью +его не открывает — ни один проход. Проверка «требование против записанного +наблюдения» из конвейера ушла: наблюдение неизвестной свежести делало находку +похожей на доказанную, ничего не доказывая. Скажи об этом строкой в границах +покрытия. + +**Сколько ты читаешь, зависит от метки — она приходит в задании.** + +| | `small` | `medium` и `large` | +|---|---|---| +| источник требований | **только дельта-спека change** | дельта + затронутые актуальные спеки | +| `design.md`, `tasks.md` change | не читаешь | читаешь | +| `docs/architecture.md`, `passport.md` | не читаешь | читаешь | +| `CLAUDE.md`, инварианты | читаешь всегда | читаешь всегда | +| потолок находок | **3** | нет | + +На `small` это значит: сверка идёт против того, что заказано **этим изменением**, +и только. Что в актуальных спеках уже было и как это соотносится с обзором +архитектуры — не твой вопрос с этой меткой, и так и скажи в границах покрытия. +Потолок, если сработал, объяви: сколько осталось за срезом. + Пути спек жёсткие: актуальные — `openspec/specs//spec.md`, дельты — `openspec/changes//specs/`. Карта «что нужно проходу → где лежит» — `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/project-facts.md`. @@ -49,12 +69,10 @@ Development на OpenSpec). Оптика — требования, а не ст каждой слитой задачи. Задание обязано назвать этот режим явно; не названо — работаешь по режиму 1 или 2 и говоришь в границах покрытия, что change не нашёл. -Дополнительно поднимаешь: `design.md` и `tasks.md` change, затронутые актуальные -спеки, инварианты из `CLAUDE.md`. Если тема ещё не перенесена в спеки и живёт -только в `docs/architecture.md` — источник истины там, и это фиксируется в -границах покрытия. Отдельно: `docs/research/` нормой не является, но именно там -записано, как внешний мир ведёт себя на самом деле; требование, противоречащее -наблюдению, — повод для находки в спеку. +Дополнительно поднимаешь **с метки `medium`**: `design.md` и `tasks.md` +change, затронутые актуальные спеки. Инварианты из `CLAUDE.md` — при любой метке. Если тема ещё не перенесена в спеки и живёт только в +`docs/architecture.md` — источник истины там, и это фиксируется в границах +покрытия. ## Режим 1 — дизайн/спеки ДО кода @@ -168,8 +186,11 @@ Development на OpenSpec). Оптика — требования, а не ст ``` ## Coverage of this pass +- метка: ; с меткой small — «источник только дельта-спека, актуальные спеки и обзор не читались» - проверено: <какие Requirements, какие файлы диффа прочитаны> +- потолок (только small): N/3 — и что осталось за срезом - не проверялось и почему: ... +- требование против записанного наблюдения не проверялось: docs/research/ — процессный документ, прогон его не открывает - принципиально недоступно этому проходу: форма решения, идиоматичность, эксплуатация ``` diff --git a/av-dev-pipeline/agents/review-triage.md b/av-dev-pipeline/agents/review-triage.md index 86923c6..26f2059 100644 --- a/av-dev-pipeline/agents/review-triage.md +++ b/av-dev-pipeline/agents/review-triage.md @@ -1,6 +1,6 @@ --- name: review-triage -description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Сверяет план разметчика с пришедшими отчётами: тема, размеченная и оставшаяся без отчёта, — находка о самом прогоне. Формирует итоговый отчёт с планом, перечнем проходов и обязательной секцией границ покрытия." +description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Сверяет план разметки задачи с пришедшими отчётами: тема, размеченная и оставшаяся без отчёта, — находка о самом прогоне. Формирует итоговый отчёт с планом, перечнем проходов и обязательной секцией границ покрытия." tools: Read, Grep, Glob, Bash, Write model: opus color: yellow @@ -21,13 +21,20 @@ color: yellow ## Вход -Сырые выводы всех запущенных проходов, `git diff <база>..HEAD`, **план -разметчика** (`review-scope`, стадия 0) и режим прогона. Дельта-спеки — по мере -надобности. +Сырые выводы всех запущенных проходов, `git diff <база>..HEAD`, **план разметки +задачи** (агент `review-scope`, один запуск после `propose`) и режим прогона. +Дельта-спеки — по мере надобности. -План — это таблица «тема → дом → глубина → кто закрывает» плюс ступень с -обоснованием. Он твой главный инструмент сверки: ты единственный, кто видит и то, -что размечено, и то, что пришло. +План — это таблица «тема → дом → глубина → кто закрывает» плюс размер, сложность +и метка с обоснованием. Он твой главный инструмент сверки: ты единственный, кто +видит и то, что размечено, и то, что пришло. + +**Плана нет — ты не запускаешься.** Сверка размеченного с пришедшим — твоя +единственная защита от молчащего пропуска, и без плана она не выполняется вовсе. +Отчёт, собранный без неё, выглядит полным ровно настолько же, насколько и +неполный. Исключение одно и объявленное: финальная сверка стыка в +`av-dev-pipeline:task-batch` — там разметчика нет по построению, и план тебе +собирает сам батч, коротким списком запущенного. Из документов проекта тебе нужны: @@ -78,8 +85,10 @@ color: yellow - выполнить команду и приложить вывод; - показать поимённое положение руководства, строку конвенции проекта или **дословный пункт из раздела инвариантов `CLAUDE.md`**; -- сослаться на наблюдение в `docs/research/` — оно сильнее любого - рассуждения о том, «как должно быть». +- сослаться на замер, снятый проходом **на этом прогоне**, с приложенной + командой — он сильнее любого рассуждения о том, «как должно быть». На чужие + записанные наблюдения не ссылайся: `docs/research/` — процессный документ, + прогон его не открывает, и свежесть числа оттуда ничем не подтверждена. Бюджет — по одной попытке на находку. Не превращай триаж в отдельное расследование. Ничего не запускай на рабочих данных — запреты в `CLAUDE.md`. @@ -158,8 +167,8 @@ severity: показывал вовсе: список запущенного отвечал «все, кто должен был, отработали», а вопрос «что именно осталось непроверенным» задать было нечем. -Отдельно проверь **сигнал о заниженной ступени** от `review-basics`, если он -pришёл. Ступень выбирал `review-scope`, а не он и не ты, — значит сигнал +Отдельно проверь **сигнал о заниженной метке** от `review-basics`, если он +pришёл. Метка выбирал `review-scope`, а не он и не ты, — значит сигнал независим, и место ему в сводке, а не в общем списке находок. ## Границы покрытия — не сокращаются @@ -167,8 +176,8 @@ pришёл. Ступень выбирал `review-scope`, а не он и не Финальная секция сводит границы всех проходов. Обязательно называет: - **план: темы, их глубины и дома** — включая темы, у которых дома нет; -- какие проходы запускались, в каком профиле и режиме; -- какие **не** запускались и почему (профиль, бюджет, недоступный инструмент, +- какие проходы запускались, на какой метке и в каком режиме; +- какие **не** запускались и почему (метка, бюджет, недоступный инструмент, остановленный прогон); - что каждый запущенный проход **не мог проверить в принципе** — из его charter'а; - **что осталось целиком на человеке** — «Недоступно проверке» из `docs/review.*`, @@ -182,7 +191,32 @@ pришёл. Ступень выбирал `review-scope`, а не он и не - **каких документов проекта не хватило** — строкой на каждый, **с причиной**: «`docs/security.md` в проекте нет», «есть, но периметр не назван». Строки приходят из проходов; слить их в одну «документации не было» нельзя — - деградация поразрядная, и разные пробелы чинятся разным. + деградация поразрядная, и разные пробелы чинятся разным; +- **сработавшие потолки** — по строке на проход: сколько находок он показал, + каков был его потолок и что осталось за срезом. Проход обязан сообщить это сам; + не сообщил — так и напиши, это находка о прогоне. + +**Четыре строки ты пишешь сам, на каждом прогоне, и ни один проход их не +принесёт.** Они про то, чего в конвейере нет вовсе, — а значит некому и +пожаловаться: + +1. **Решения проекта не сверялись.** `docs/adr.*` — процессный документ, прогон + его не открывает. Расхождение изменения с записанным решением ловит сверка + документации между спринтами, а не ревью. +2. **Записанные наблюдения проекта не использовались.** `docs/research.*` — тоже + процессный. Всякое число в находках снято проходом на этом прогоне; числа без + приложенной команды замера в отчёте быть не должно. +3. **Поимённая сверка с руководствами по стилю языка не задавалась ни одним + проходом.** Различение «идиоматично против распространено» не спрашивает никто + с тех пор, как упразднён проход про идиоматичность. +4. **Альтернативной реализации, с которой можно сдиффить решения, у конвейера + нет.** Проход независимой реализации снят по стоимости, а не по замеру; «не + знаю, чего не знаю» больше не достаёт никто. + +Плюс **с меткой `small`** — пятая строка: темы `security`, `operations` и +`architecture` сверялись только с записанными инвариантами `CLAUDE.md`, дома этих +тем не открывались. Свойство, которого нет в инвариантах, с этой меткой не +проверил никто. Формулировка «критичных проблем не обнаружено» **запрещена** без этой секции: она потребляет ощущение проверенности, ничего не гарантируя, и это хуже, чем @@ -199,7 +233,7 @@ pришёл. Ступень выбирал `review-scope`, а не он и не Строго секциями из контракта: `Блокирует мердж` (≤3) / `Стоит исправить сейчас` (≤4) / `Гипотезы без доказательства` / `Promote candidates` / `Границы покрытия`. -Перед секциями — сводка: ступень с обоснованием разметчика и режим прогона, +Перед секциями — сводка: размер, сложность и метка с обоснованием разметки и режим прогона, состояние гейта, **план с исходом по каждой теме**, сколько находок пришло на вход и сколько осталось. diff --git a/av-dev-pipeline/skills/review-pipeline/SKILL.md b/av-dev-pipeline/skills/review-pipeline/SKILL.md index d47799a..699319c 100644 --- a/av-dev-pipeline/skills/review-pipeline/SKILL.md +++ b/av-dev-pipeline/skills/review-pipeline/SKILL.md @@ -1,6 +1,6 @@ --- name: review-pipeline -description: "Конвейер ревью изменения, устроенный по темам: каждый документ проекта — тема ревью, а проход лишь закрывает тему на заданной глубине. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список открытый, свои темы проект заводит документом. Прогон начинает разметчик: находит документы, выводит темы, выбирает ступень с обоснованием и раздаёт темы проходам. Три ступени: quick и standard закрывают все темы (сверкой и разбором), wide добавляет доказательство — враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе. Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода." +description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Разметка задачи идёт один раз, после propose: агент review-scope выводит размер и сложность, из их максимума — метка, и раздаёт темы проходам обеих стадий. Метка правит и ревью дизайна (small — только specs; medium — плюс rubric; large — плюс architecture), и ревью кода (small — гейт, спеки, код, триаж; medium — плюс приёмник тем; large — плюс доказательство: враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе). Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью) и из task-batch (финальная сверка)." --- # Конвейер ревью @@ -14,8 +14,7 @@ description: "Конвейер ревью изменения, устроенны 0. **Тема первична, проход вторичен.** Ревью проверяет **темы** — набор направлений, который проект объявляет своими документами. Проход это только - способ закрыть тему на заданной глубине, и проходы меняются: уезжают в верхнюю - ступень, сливаются, упраздняются. Если состав прогона считать списком проходов, + способ закрыть тему на заданной глубине, и проходы меняются: уезжают в старшую метку, сливаются, упраздняются. Если состав прогона считать списком проходов, то уехавший проход уносит тему с собой **беззвучно** — отчёт честно скажет «`ops` не запускался» и не скажет «эксплуатацию не смотрел никто», а нужно второе. Поэтому прогон описывается таблицей «тема → глубина → кто закрывает», и @@ -43,7 +42,7 @@ description: "Конвейер ревью изменения, устроенны Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это один раз, при установке плагина в проект: -- **OpenSpec — жёсткая предпосылка, а не опция.** Профиль `design`, проход +- **OpenSpec — жёсткая предпосылка, а не опция.** Ревью дизайна, проход `review-specs` и вызывающий пайплайн задачи завязаны на дельта-спеки (`openspec/changes//specs/*/spec.md`), на актуальные спеки @@ -63,37 +62,79 @@ description: "Конвейер ревью изменения, устроенны `av-dev-pipeline:review-pipeline`, `av-dev-pipeline:task-pipeline`, `av-dev-pipeline:task-batch`. -## Темы — и почему их список открытый +## Темы, источники и процессные документы -**Каждый документ проекта — тема ревью.** Форма дома значения не имеет: -`docs/security.md` и `docs/security/` — одна тема `security`, проект выбирает -форму по объёму написанного. Завёл документ — завёл тему; запретить нельзя, -разрешения не надо. +Раньше здесь стояло плоское правило «каждый документ проекта — тема ревью». Оно +верно ровно наполовину, и потому вредно целиком: паспорт и схему хранилища +ревью читает, но темами они не являются, а журнал решений и журнал наблюдений +ревью изменения не нужны вовсе. Разметчик, применявший правило буквально, обязан +был либо завести фантомные темы и продублировать ими работу настоящих, либо +потерять документ молча. -Из этого следует то, ради чего правило и заведено: **`docs/` перестаёт быть просто +**Разрез один и проверяемый — тот же, что в каноне: можно ли по документу +сказать «в этом изменении сделано не так»?** + +| Категория | Что конвейер с ней делает | Кто в ней | +|---|---|---| +| **тема** | заводит направление проверки и требует исполнителя | `conventions.*`, `security.*`, `architecture.*`, свои документы проекта | +| **источник темы** | читает как материал чужой темы, своей не порождает | `passport.*`, `database.*`, `CLAUDE.md`/`AGENTS.md`, `openspec/specs/` | +| **процессный документ** | не судит по нему изменение | `docs/tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `docs/.pm.json` | + +**Одна процессная запись всё же читается — `docs/review.*`.** В ней лежит +настройка самого конвейера: вопросы по темам, журнал дефектов, типовые узлы, +типовые ложноположительные. Проход, читающий её, читает **свою обвязку**, а не +критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не +открывает никто. + +Дом канона этой раскладки — `av-dev-pm`, `references/canon.md`, раздел «Три +категории документов». Конвейер её **читатель**: категории и имена тем он берёт +оттуда и своих не заводит. + +Отсюда то, ради чего правило и заведено: **`docs/` перестаёт быть просто документацией и становится конфигурацией конвейера**. Проект настраивает ревью тем, что пишет о себе, а не отдельным файлом настроек, который разошёлся бы с -документами. +документами. **Открыта при этом только категория `тема`** — две другие закрыты +и перечислены поимённо, поэтому документ, которого нет в раскладке канона, +однозначно своя тема проекта, а не «что-то непонятное». -Ядро — шесть тем, они есть у любого проекта, приведённого к канону: +Ядро — шесть тем, они есть у любого проекта, приведённого к канону. Форма дома +значения не имеет: `docs/security.md` и `docs/security/` — одна тема `security`. | Тема | Дом | Вопрос темы | |---|---|---| | `requirements` | `openspec/specs/`, дельты change | делает ли код заказанное, и только его | | `autotests` | `CLAUDE.md`: семантика гейта, команды | проверено ли машиной и хватает ли проверок | | `conventions` | `docs/conventions.*` | написано ли так, как здесь пишут | -| `architecture` | `docs/architecture.*`, `passport.*`, `adr/` | цело ли устройство: понятия, границы, решения | +| `architecture` | `docs/architecture.*` + источник `passport.*` | цело ли устройство: понятия и границы | | `security` | `docs/security.*` | что сделает недоверенный вход | -| `operations` | `docs/architecture.*`, `database.*`, `research/` | что будет через неделю на проде | +| `operations` | `docs/architecture.*`, раздел эксплуатации + источник `database.*` | что будет через неделю на проде | -Не темы — их ровно две: `docs/tasks/` (каталог задач, его ведёт -`av-dev-pm:tasks`) и `docs/review.*` (настройка самого конвейера и журнал -дефектов — слой **над** темами). Обе называются в плане явно, а не пропускаются -молча. +**Три темы ядра дома в `docs/` не имеют, и это не пробел.** `requirements` живёт +в `openspec/`, `autotests` — в `CLAUDE.md`, `operations` — разделом внутри +`architecture.*`. Имя темы поэтому не выводится из имени файла, и обратно тоже: +`docs/passport.md` не заводит темы `passport`. -**Проектная тема закрывается `basics`**, на любой ступени. Именных проходов +**`adr/` и `research/` прогон больше не открывает.** Раньше архитектурный проход +читал решения, а эксплуатационный и `specs` — числа. Цена решения записана в +каноне и повторяется здесь, потому что платит её конвейер: **расхождение +изменения с записанным решением прогоном не ловится**, это работа сверки +документации (`av-dev-pm`, агент `doc-consistency`) на сессии между спринтами. +Строка об этом обязательна в границах покрытия каждого прогона. + +**Своя тема проекта бывает двух происхождений, и обе законны:** документ, который +проект положил в `docs/` и которого нет в раскладке канона, — и **тема, названная +директивой** `CLAUDE.md`/`AGENTS.md`, у которой документа нет вовсе. У второй дом +— сама директива; в остальном она ничем не отличается, и в раздаче идёт туда же. +Различать их приходится потому, что условие запуска приёмника тем звучит «есть ли +свои темы проекта», и тема без файла в `docs/` иначе не попала бы под это условие +никогда. + +**Проектная тема закрывается `basics`**, при любой метке. Именных проходов конечное число, а тем — сколько заведёт проект; приёмник обязателен, иначе -открытость списка была бы обещанием без механизма. +открытость списка была бы обещанием без механизма. Темы **ядра** он держит только +на `medium`: на `small` их закрывает `code` сверкой по инвариантам, в `large` — +именные проходы. Отсюда правило состава: **`basics` запускается тогда и только +тогда, когда ему есть что принимать** — см. «Метки». **Тема без дома — законное состояние и отдельная строка.** «Тема `operations` заявлена, `docs/database.md` нет» читается иначе, чем «не смотрели». Деградация @@ -109,7 +150,7 @@ description: "Конвейер ревью изменения, устроенны ## Что получает каждый проход -Задание собирается **по плану разметчика** (стадия 0) и состоит из шести вещей: +Задание собирается **по плану разметки задачи** и состоит из шести вещей: - **его темы** — какие темы он закрывает на этом прогоне, у каждой **дом** (путь и раздел) и **глубина**. Дом передаётся адресом, а не пересказом: проход, @@ -117,13 +158,14 @@ description: "Конвейер ревью изменения, устроенны неверна; - **вопросы по его темам** из `docs/review.*`, если они там есть, — **дословно**. Вопрос привязан к теме, а не к имени прохода, и потому переживает переезд - прохода между ступенями; + прохода между метками; - **контракт находок** — путь к [references/finding-contract.md](references/finding-contract.md) (в установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/`); - **изменение** — идентификатор change и путь к его дельта-спекам; - **база диффа**; -- **профиль и режим** прогона — чтобы проход знал, что писать в границы покрытия. +- **метка, его глубина и режим** прогона — чтобы проход знал, что писать в + границы покрытия. Чего проход **не** получает ни в каком режиме — выводов других проходов. См. «Порядок прогона». @@ -172,17 +214,22 @@ charter'а, а модель потом двигает калибровка, и проде, и он тоже не оставляет следа ни в отчёте, ни в границах покрытия. По той же причине, что `specs`, и это дороже всего в конвейере: проход идёт на каждой задаче. -- `architecture` — запускается только в верхней ступени, на 5–10% задач, потолок +- `architecture` — запускается только в старшей метки, на 5–10% задач, потолок в 3 находки делает его дешёвым по выходу, а находка на предложении стоит абзаца против переписывания на готовом коде. Дёшево × высокое плечо. -**`scope` внизу, и это не противоречие, хотя его ошибка расходится дальше всех.** -Его работа распадается надвое: поиск документов и раздача тем **перечислимы** — -план сверяется с `ls docs/` за секунду, пропущенный документ виден без -рассуждения; выбор ступени — суждение, но у него есть три независимых -корректора: отрицательный тест `quick`, правило «спорный случай вниз» и сигнал -`basics` о заниженной ступени. Дешёвая модель безопасна ровно потому, что её -вывод устроен как список, а не как мнение. +**`scope` внизу, и это не противоречие, хотя его ошибка расходится дальше всех — +теперь ещё дальше, чем прежде.** С переездом разметки к `propose` он правит +состав **обеих** стадий и не пересматривается после кода: ошибка метки стоит +всей задачи, а не одного прогона. Модель он всё же держит нижнюю, и вот почему. +Его работа распадается надвое: разнесение документов по категориям и раздача тем +**перечислимы** — план сверяется с `ls docs/` за секунду, пропущенный документ +виден без рассуждения. Выбор метки — суждение, но с переходом на две оси оно +стало **дважды перечислимым**: размер считается по перечню границ задачи и +дельта-спекам, сложность отвечается одним проверяемым признаком («можно ли до +работы назвать тронутые узлы»). Плюс три независимых корректора: отрицательный +тест `small`, правило «спорный случай вниз» и сигнал `basics` о заниженной метке. Дешёвая модель безопасна ровно потому, что её вывод устроен как список, +а не как мнение. **Самая дешёвая модель не используется ни на одном проходе, и это не экономия наоборот.** Дешёвая модель на опиниативном проходе даёт правдоподобные находки, @@ -191,63 +238,178 @@ charter'а, а модель потом двигает калибровка, и покрытие диффа, карта проекта — это скрипты проекта, они стоят ноль токенов. Дешёвому проходу просто не осталось работы. -Экономия достигается **не понижением модели, а глубиной и непуском**: `quick` и -`standard` закрывают все темы, но чтением и рассуждением, а `wide` добавляет -доказательство — запуск, замер, построенный путь. Именно доказательство и стоит -часов: машина, цепочка меряющих проходов, ожидание. +Экономия достигается **не понижением модели, а тремя другими рычагами**, и все +три применяются к каждому опиниативному проходу, а не к одному избранному. -Второй рычаг — **вход и потолок прохода**. `basics` идёт на верхней модели, но с -узким входом (дифф и его окрестности, без карты проекта) и жёстким потолком: 2 -находки на сверке, 4 на разборе. Дешевле он не от модели, а от того, чего **не** -делает. +1. **Непуск.** `large` добавляет доказательство — запуск, замер, построенный + путь — и стоит часов; `small` снимает приёмник тем. Что при этом перестаёт + проверяться, названо поимённо и идёт в границы покрытия. +2. **Вход.** `basics` идёт на верхней модели, но с узким входом: дифф и его + окрестности, без карты проекта. На `small` сужаются и остальные: `specs` + читает только дельта-спеку, `code` — только индекс конвенций. +3. **Потолок.** Он есть у каждого опиниативного прохода и напечатан: `basics` — 2 + находки на сверке и 4 на разборе; `code` — 3 технических и 2 конвенционных на + `small`, 4 конвенционных выше; `specs` — 3 на `small`; `architecture` — 3; + триаж — 7 в основном списке. -## Профили +Раньше рычагов было заявлено два, и оба применялись к одному `basics`. Проход без +потолка выдаёт столько находок, сколько нашёл поверхностей, — а это ровно тот +механизм, из-за которого был снят проход независимой реализации: **счёт +определялся объёмом вывода**. Потолок ставится не ради краткости отчёта, а против +этого. -**Ступень не меняет список тем — она меняет их глубину.** Все темы закрыты во -всех профилях; разница в том, читают ли их, рассуждают над ними или доказывают -запуском. +**Потолок обязан быть объявлен, когда он сработал.** Проход, срезавший находки +до потолка, говорит об этом строкой в своих границах покрытия: сколько осталось +за срезом и какого рода. Молчащий срез неотличим от «больше не нашлось» — это тот +же класс молчащего пропуска, что и непущенный проход. -| Тема | `quick` | `standard` | `wide` | +## Метки + +**Классификация задачи выдаёт ровно одно значение — метку**: `small`, `medium` +или `large`. Это **единственный вход, по которому конвейер выбирает +исполнителей**: и на дизайне, и на коде состав читается из неё, а не из класса +задачи, не из её типа и не из ощущения важности. Метку ставит `review-scope` при +разметке задачи; все проходы получают её в задании и обязаны напечатать в своих +границах покрытия. + +**Метка одна на всю задачу и правит обе стадии ревью** — и дизайна, и кода. У +изменения нет двух разных «глубин проверки»: величина, из которой выводится +состав, — одна и та же пара «размер × сложность», посчитанная один раз. + +**Метка не меняет список тем — она меняет их дом и глубину.** Все шесть тем +ядра названы при любой метке; разница в том, против чего их смотрят (дом темы +или только инварианты) и как (чтением, рассуждением или запуском). + +Ревью кода: + +| Тема | `small` | `medium` | `large` | |---|---|---|---| -| `requirements` | `specs` | `specs` | `specs` | +| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор | | `autotests` | `autotests` | `autotests` | `autotests` | -| `conventions` | `code` | `code` | `code` | -| `architecture` | `basics`, сверка | `basics`, разбор | `architecture`, доказательство | -| `security` | `basics`, сверка | `basics`, разбор | `adversary`, доказательство | -| `operations` | `basics`, сверка | `basics`, разбор | `ops`, доказательство | +| `conventions` | `code`, сверка | `code`, разбор | `code`, разбор | +| `architecture` | `code`, сверка по инвариантам | `basics`, разбор | `architecture`, доказательство | +| `security` | `code`, сверка по инвариантам | `basics`, разбор | `adversary`, доказательство | +| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство | | тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор | -Отсюда состав: +Весь процесс с выбором исполнителей на каждом этапе — одной схемой. **Метка +считается один раз, в узле разметки, и дальше только читается:** -| Профиль | Когда | Стадии | Проходов | Доля задач | -|---|---|---|---|---| -| `quick` | мелкое: багфикс, мелкая фича, локальная правка, доки | 0, 1, 2, 3, 5 | 6 | много | -| `standard` | **рабочее умолчание**: всё, что не мелкое и не крупное | 0, 1, 2, 3, 5 | 6 | большинство | -| `wide` | крупное или незнакомое: большой рефакторинг, функциональность, форму которой ещё предстоит нащупать | 0, 1, 2, 4, 5 | 8 | **5–10%** | -| `design` | **до кода**, на предложении | specs, плюс rubric и architecture по условию `wide` | 1–3 | — | +```mermaid +flowchart TD + propose["opsx:propose — change, дельта-спеки, tasks.md"] + scope["review-scope — разметка задачи
размер × сложность → МЕТКА"] + label{{"метка"}} -**`quick` и `standard` совпадают составом и различаются глубиной** — это -единственное место конвейера, где профиль не выводится из одного лишь списка -проходов. Поэтому глубина объявляется в отчёте наравне с профилем, а план -разметчика называет её по каждой теме. Проверять надо два факта вместо одного, и -оба напечатаны. + subgraph design["Ревью дизайна — до кода"] + dS["specs — всегда"] + dR["+ rubric"] + dA["+ architecture
+ вопрос автору о трёх формах"] + end + + apply["opsx:apply — код, гейт зелёный"] + + subgraph code["Ревью кода — после apply"] + cGate["autotests — гейт, источник графа"] + cS["specs — requirements"] + cC["code — conventions + техника"] + cInv["code, третья половина:
security, operations, architecture
против инвариантов CLAUDE.md"] + cB["basics — темы ядра + свои темы"] + cBown["basics — только свои темы проекта"] + cHeavy["adversary · ops · architecture
доказательство"] + cT["triage — единственный сток"] + end + + propose --> scope --> label + + label -->|small| dS + label -->|medium| dR + label -->|large| dA + dR -.-> dS + dA -.-> dR + + dS --> apply + dR --> apply + dA --> apply + + apply --> cGate + cGate -->|зелёный| cS + cGate -->|зелёный| cC + label -->|small| cInv + label -->|small, есть свои темы| cBown + label -->|medium| cB + label -->|large| cHeavy + label -->|large, есть свои темы| cBown + + cS --> cT + cC --> cT + cInv --> cT + cB --> cT + cBown --> cT + cHeavy --> cT +``` + +Пунктир между проходами дизайна значит «включает предыдущее»: `medium` это +`specs` **плюс** `rubric`, `large` — они же плюс `architecture`. + +Отсюда состав обеих стадий: + +| Метка | Когда | Ревью дизайна | Ревью кода: стадии | Проходов всего | Доля задач | +|---|---|---|---|---|---| +| `small` | малое **и** знакомое: багфикс, локальная правка, доки | `specs` | 1, 2, 5 (+3 при своих темах) | **5–6** | много | +| `medium` | **рабочее умолчание**: среднее и знакомое | `specs`, `rubric` | 1, 2, 3, 5 | **7** | большинство | +| `large` | крупное **или** незнакомое: большой рефакторинг, функциональность, форму которой ещё предстоит нащупать | `specs`, `rubric`, `architecture` | 1, 2, 4, 5 (+3 при своих темах) | **10–11** | **5–10%** | + +Ревью дизайна разбирается отдельно ниже — оно идёт до кода, у него свой плоский +граф и свой сток. Здесь оно стоит в таблице потому, что **метка у обеих стадий +общая**, и увидеть цену задачи можно только сложив их. + +**Разметка в этих числах не считается — её платят один раз на задачу, а не один +раз на прогон.** Она ушла из состава ревью кода целиком: `review-scope` идёт +после `propose`, до ревью дизайна, и его план обслуживает **обе** стадии. Раньше +разметка стояла первой в каждом ревью кода, а перед ревью дизайна вызывающий +отвечал на тот же вопрос сам — то есть о размере изменения судили дважды и в +одном из двух мест без разведённости с автором. + +**`small` дешевле `medium` тремя разными способами сразу, и каждый назван.** + +1. **Составом.** `basics` на `small` не запускается — кроме случая, когда у + проекта есть свои темы; тогда он идёт **только с ними**, ровно как в `large`. + Три темы ядра, которые он держал бы, переходят к `code` сверкой по + инвариантам. +2. **Входом.** На `small` `specs` читает только дельта-спеку, а `code` — только + **индекс** конвенций (перечень родов и что механизировано), не весь их дом. На + `medium` оба читают дома целиком. +3. **Потолком.** На `small` потолки жёсткие и напечатаны: `specs` — 3 находки, + `code` — 3 технических плюс 2 конвенционных плюс 1 по трём темам ядра. + +**Что `small` за это не проверяет, названо поимённо и обязано идти строкой в +границы покрытия:** темы `security`, `operations` и `architecture` смотрятся +только против **записанных инвариантов** `CLAUDE.md`. Свойство, которого в +инвариантах нет, с этой меткой не спросит никто — ни сценарием, ни чтением +дома темы. Это и есть цена метки, и она заметно больше прежней: раньше `small` +отличался от `medium` одним проходом на один вопрос, то есть не экономил +ничего и назывался отдельной меткой зря. **Три глубины, и они не про старательность, а про способ доказательства.** **Сверка** — открыть дом темы, открыть дифф, сравнить. **Разбор** — построить сценарий рассуждением, ничего не запуская. **Доказательство** — прогнать, померить, построить путь. Только третья требует машины, и только она стоит часов. -**`wide` назван по тому, что он добавляет: вход шире диффа.** Он единственный, где +**`large` назван по тому, что он добавляет: вход шире диффа.** Он единственный, где живут тяжёлые проходы, и единственный, где что-то **запускается**. `basics` в нём берёт только проектные темы; своих тем у проекта нет — он не запускается вовсе, и -план говорит об этом строкой. +план говорит об этом строкой. **На `small` действует то же правило и по той же +причине** — приёмник запускается только тогда, когда ему есть что принимать. +Совпадение неслучайное: `basics` держит темы ядра ровно при одной метке из трёх, +а приёмником проектных тем работает на всех. -**Доля 5–10% — не пожелание, а проверка правила.** Если `wide` уходит каждая -третья задача, ступень выбирают по ощущению важности. Обратный перекос виден по +**Доля 5–10% — не пожелание, а проверка правила.** Если `large` уходит каждая +третья задача, метку выбирают по ощущению важности. Обратный перекос виден по журналу проскочивших дефектов: класс, который ловят только меряющие проходы, начинает всплывать после мерджа. -**Состав сверяется до коммита — по плану разметчика, а не по этой таблице.** План +**Состав сверяется до коммита — по плану разметки задачи, а не по этой таблице.** План и есть реестр: тема, дом, глубина, кто закрывает. Это единственная защита от промаха, который уже случился: пропуск **не отличим от прохода без находок** (гейт зелёный, спеки сошлись, отчёт выглядит полным), а заметить его мог бы только @@ -255,50 +417,67 @@ charter'а, а модель потом двигает калибровка, и запускался» с причиной, а не отсутствует. Цена молчащего пропуска измерена: семь находок и отдельная задача на их дозакрытие. -Правило выбора — **два вопроса по факту изменения, не по ощущению важности**. -Дом правила здесь, а применяет его `review-scope` на стадии 0 — не автор -изменения. Отвечать по порядку, первый подошедший ответ и есть профиль: +### Правило выбора — две оси, а не один вопрос -1. **Изменение крупное или незнакомое?** → `wide`. Крупное — трогает несколько - узлов или слоёв разом, переносит ответственность между ними, перекладывает - существующий код в новую форму (большой рефакторинг). Незнакомое — - функциональность, которой в проекте ещё не было, и **форму решения предстоит - нащупать по ходу**, а не выбрать до начала. Признак незнакомого простой: перед - работой нельзя назвать, какие узлы будут тронуты. -2. **Изменение мелкое?** → `quick`. Мелкое — помещается в один узел, форма - решения очевидна до начала работы, а откат сводится к обратной правке. Сюда - идут мелкий багфикс, мелкая фича, правка текста и документации. -3. **Всё остальное** → `standard`. Это рабочее умолчание, и оно должно набирать - большинство задач. +Дом правила здесь, а применяет его `review-scope` при разметке задачи — не автор +изменения. **Оси две, они измеряют разное, и метка есть максимум по ним.** -**Два признака смотрят на разное, и в этом весь смысл двух вопросов.** Первый — -про **объём и неизвестность**: сколько мест трогается и знаем ли мы форму решения -заранее. Второй — про **обратимость**: во что обойдётся ошибка, если она уедет в -мердж. Раньше ступень выбиралась только по классу изменения («вводит ли новое -понятие»), и объём в правило не входил вовсе; теперь входит, потому что цена -разбирательства растёт именно с ним. +| | **знакомое** — форму решения можно назвать до начала | **незнакомое** — форму предстоит нащупать по ходу | +|---|---|---| +| **малое** — один узел | `small` | `large` | +| **среднее** — несколько узлов одного слоя | `medium` | `large` | +| **крупное** — несколько слоёв, перенос ответственности, большой рефакторинг | `large` | `large` | -**Отрицательный тест `quick`, и он важнее положительного:** изменение, которое -после мерджа **не откатывается обратной правкой**, — не `quick`, каким бы +**Метка — не синоним размера.** Совпадают они только в левом верхнем углу: малое +**незнакомое** изменение получает `large`, трогая один узел. Поэтому в плане +стоят три строки, а не одна: размер, сложность и метка — каждая со своим +обоснованием. Проход, выведший объём диффа из метки, ошибётся ровно на этом +случае — а он и есть самый опасный: незнакомая форма в одном узле течёт там, где +её никто не ждёт. + +**Размер** — про объём: сколько мест трогается. **Сложность** — про +неизвестность: знаем ли мы форму решения заранее. Признак незнакомого простой и +проверяемый: **перед работой нельзя назвать, какие узлы будут тронуты**. + +Раньше обе оси были склеены в один вопрос «крупное **или** незнакомое?». Ответ +получался тот же, но две вещи под одним именем не измеришь по отдельности, и +потому разметка не могла сказать «изменение среднее, но совершенно знакомое» — +а именно эта пара и есть рабочее умолчание. Теперь обе оси называются в плане +поимённо, и обе — с обоснованием. + +**Оси называются и на стадии дизайна, и на стадии кода — но считаются один +раз.** Это и есть причина, по которой разметка переехала к `propose`: состав +ревью дизайна выводится из той же пары, что и состав ревью кода, а считать её +дважды значит один раз посчитать без разведённости с автором. + +**Обратимость — не третья ось, а отрицательный тест.** Она не уточняет размер и +не уточняет сложность: она запрещает нижнюю метка независимо от обеих. + +**Отрицательный тест `small`, и он важнее положительного:** изменение, которое +после мерджа **не откатывается обратной правкой**, — не `small`, каким бы маленьким ни был дифф. Сюда попадают миграция схемы и данных, формат на диске, публичный контракт, имя, которое разойдётся по кодовой базе. Три строки миграции -— это `standard`, а не `quick`: размер диффа и цена ошибки здесь расходятся. +— это `medium`, а не `small`: размер диффа и цена ошибки здесь расходятся. -Что здесь считается крупным и что — незнакомым, проект может уточнить в -`docs/review.md`, разделе настройки конвейера: поимённо, узлами или capability. -Это **уточнение**, а не отмена: не записано — работает список выше. +Что здесь считается крупным, что — незнакомым и что — мелким, проект уточняет в +`docs/review.md`, подразделе «Триггеры метки»: **тремя списками** — по одному на +каждую ось вверх и один вниз, поимённо, узлами или capability. Это **уточнение**, +а не отмена: не записано — работает таблица выше. ### Спорный случай решается вниз, и у этого есть цена Правило асимметрично, потому что асимметрична цена ошибки. -- **Спорно между `standard` и `wide` → бери `standard`.** Ошибка в эту сторону +- **Спорно между `medium` и `large` → бери `medium`.** Ошибка в эту сторону стоит находки, которая всплывёт на следующей задаче или в журнале дефектов. Ошибка в обратную стоит трёх тяжёлых проходов, двое из которых держат машину и идут цепочкой, — и платится она **на каждой** задаче, выбранной неверно. -- **Спорно между `quick` и `standard` → бери `standard`.** Здесь состав тот же, и - разница только в глубине трёх тем: сверка против разбора. Дёшево, и потому - сомнение решается в пользу разбора. +- **Спорно между `small` и `medium` → бери `medium`.** Раньше эта строка + обосновывалась тем, что состав одинаков и ошибка почти бесплатна. Теперь состав + разный, и обоснование стало прямо противоположным: на `small` три темы ядра + смотрятся **только против записанных инвариантов**, а спорный случай — ровно тот, + где неизвестно, покрыт ли он инвариантом. Сомнение здесь стоит дороже, чем + раньше, и потому решается вниз тем более твёрдо. **Выбор сделан в пользу пропускной способности, и это записано, а не подразумевается.** Конвейер настроен на поток задач, а не на максимум находок с каждой: поправить в @@ -306,36 +485,41 @@ charter'а, а модель потом двигает калибровка, и без которых сделка превращается в незаметную потерю качества: - **границы покрытия называют темы и их глубину**, а не только запущенные - проходы — иначе `quick` выглядит так же, как `wide` без находок; + проходы — иначе `small` выглядит так же, как `large` без находок; - **журнал дефектов в `docs/review.md` перестаёт быть хорошей практикой и становится единственной обратной связью**: проскочивший дефект — единственный - сигнал, что ступень выбрана слишком низко; -- **возврат в код — повод пересмотреть ступень.** Задача, которая приходит в тот + сигнал, что метка выбрана слишком низко; +- **возврат в код — повод пересмотреть метку.** Задача, которая приходит в тот же узел третий раз, уже не мелкая, чем бы ни выглядел её дифф. -### Профиль — максимум по поверхности +### Метка — максимум по поверхности -Условия читаются сверху вниз, и **первое подошедшее отвечает за весь дифф**. -Профиль изменения это максимум по его поверхности, а не средневзвешенное: одна -строка в перечне границ задачи поднимает ступень всему остальному, включая ту -часть, которая сама по себе была бы `quick`. +**Обе оси меряются по всему диффу разом, и максимум по каждой отвечает за весь +дифф.** Метка изменения — не средневзвешенное: одна строка в перечне границ +задачи поднимает метку всему остальному, включая ту часть, которая сама по себе +была бы `small`. Обратное тоже верно и тоже не бесплатно: у каждой задачи есть **несокращаемый -костяк из шести проходов** (разметка, гейт, спеки, код, темы, триаж). Разрезать -задачу, обе половины которой остаются в одном профиле, — значит заплатить костяк -дважды за ту же проверку. Резать стоит там, где разрез **снимает доказательство с -большей части диффа**. Шов и правило нарезки живут у того, кто ведёт задачи, — -скилл `av-dev-pm:tasks`, его `references/split.md`. Пути туда конвейер не -выносит: за пределы своего плагина он ходит вызовом скилла, а не файлом. +костяк — гейт, спеки, код, триаж**. Разрезать задачу, обе половины которой +остаются в одной метке, значит заплатить костяк дважды за ту же проверку. +Резать стоит там, где разрез **снимает доказательство с большей части диффа**. +Шов и правило нарезки живут у того, кто ведёт задачи, — скилл `av-dev-pm:tasks`, +его `references/split.md`. Пути туда конвейер не выносит: за пределы своего +плагина он ходит вызовом скилла, а не файлом. -**Профиль и глубина объявляются в отчёте, и оба с обоснованием.** Ступень -выбирает `review-scope`; он вправе и поднять, и понизить её — но не молча: строка -«ступень X, потому что …» обязательна на каждом прогоне, а не только когда -ступень отличается от ожидаемой. +Разметка в костяк не входит — она платится один раз на задачу, а не один раз на +прогон, и потому **разрез задачи её не удваивает**. Это единственное, что стало +дешевле от переезда разметки к `propose`, и это же снимает прежний довод против +нарезки. + +**Размер, сложность, метка и глубина объявляются в отчёте, и все четыре с +обоснованием.** Метка выбирает `review-scope`; он вправе и поднять, и понизить +её — но не молча: строка «метка X, потому что размер Y и сложность Z» +обязательна на каждом прогоне, а не только когда метка отличается от ожидаемой. ## Порядок прогона — граф, а не очередь -Профиль отвечает «какие темы и на какой глубине», порядок — «что кого ждёт». +Метка отвечает «какие темы и на какой глубине», порядок — «что кого ждёт». Стадии остаются единицей **состава**, но порядок задают **не их номера**: между стадиями 2–4 настоящих зависимостей нет — ни один проход не читает вывод другого, — и очередь между ними была бы платой ни за что. @@ -346,28 +530,33 @@ charter'а, а модель потом двигает калибровка, и | Ребро | Смысл | Между кем | |---|---|---| -| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | разметка → все; гейт → все опиниативные; все проходы → триаж | +| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все опиниативные; все проходы → триаж | | **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» | +**План разметки — вход графа, а не его узел.** Он готов до того, как ревью кода +началось: разметка идёт один раз на задачу, после `propose`. Раньше она была +первым узлом каждого прогона, и ребро «разметка → все» стояло здесь; теперь этого +ребра нет, потому что нет и узла. + ```mermaid flowchart TD - scope["scope — разметка
(стадия 0, темы и ступень)"] + plan[/"план разметки задачи
(готов до ревью кода)"/] autotests["autotests
(стадия 1, держит машину)"] specs["specs"] code["code"] - basics["basics
(quick, standard: темы;
wide: только свои темы проекта)"] - adversary["adversary
(wide, держит машину)"] - ops["ops
(wide, держит машину)"] - architecture["architecture
(wide)"] + basics["basics
(medium: темы ядра и свои;
small, large: только свои темы проекта)"] + adversary["adversary
(large, держит машину)"] + ops["ops
(large, держит машину)"] + architecture["architecture
(large)"] triage["triage — единственный сток"] - scope -->|план| autotests + plan -.->|задания по темам| autotests autotests -->|зелёный| specs autotests -->|зелёный| code autotests -->|"зелёный, темы по плану"| basics - autotests -->|"зелёный, wide"| adversary - autotests -->|"зелёный, wide"| ops - autotests -->|"зелёный, wide"| architecture + autotests -->|"зелёный, large"| adversary + autotests -->|"зелёный, large"| ops + autotests -->|"зелёный, large"| architecture adversary -. один ресурс — машина .- ops specs --> triage code --> triage @@ -378,11 +567,12 @@ flowchart TD ``` Читается граф так: **всё, у чего входящие рёбра закрыты, уходит одним -сообщением**. Разметка идёт первой и одна — до неё неизвестно ни что проверять, -ни на какой ступени. В `quick` и `standard` после зелёного гейта уходят разом -`specs`, `code` и `basics`, и сразу триаж. В `wide` вместо тем `basics` идут три -тяжёлых: `architecture` и первый из меряющей пары — сразу, второй меряющий — -следом за первым, и он же определяет, когда стартует триаж. +сообщением**. Источник графа — гейт: он один по построению и идёт первым. На +`medium` после зелёного гейта уходят разом `specs`, `code` и `basics`, и сразу +триаж. На `small` — `specs` и `code`, а `basics` только при своих темах проекта. +В `large` вместо тем `basics` идут три тяжёлых: `architecture` и первый из меряющей +пары — сразу, второй меряющий — следом за первым, и он же определяет, когда +стартует триаж. **Схема здесь старше прозы.** Она не иллюстрация к тексту, а сам алгоритм планировщика; проза ниже объясняет рёбра и называет их цену. Разошлись — прав @@ -400,7 +590,7 @@ flowchart TD ### Кто держит машину Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск. -Проходы, заявившие его, сериализуются между собой в любом профиле и на любой +Проходы, заявившие его, сериализуются между собой при любой метке и на любой стадии; порядок внутри цепочки произволен. | Проход | Держит машину | Почему | @@ -409,7 +599,7 @@ flowchart TD | `adversary` | да | находка есть **построенный путь**: он пишет падающий тест и гоняет его | | `ops` | да | доказывает числами: время удержания блокировки, пик кучи, темп роста журнала | | `triage` | да | проверяет оракул `critical`/`major` запуском — но он сток и тоже один | -| `scope`, `specs`, `code`, `basics`, `architecture`, `rubric` | нет | читают и рассуждают, ничего не исполняют | +| `specs`, `code`, `basics`, `architecture`, `rubric`, `scope` | нет | читают и рассуждают, ничего не исполняют | **Правило про ресурс, а не про имена.** Раньше здесь стояло именованное исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт @@ -428,18 +618,24 @@ flowchart TD **Барьера стоимости в конвейере нет, и раннего выхода тоже.** Барьер существовал ради независимой реализации — единственного прохода, чей счёт определялся объёмом -вывода, — и ушёл вместе с ней. Граф во всех профилях плоский, от гейта до триажа: +вывода, — и ушёл вместе с ней. Граф при любой метке плоский, от гейта до триажа: защищать за барьером нечего, `architecture` дёшев по выходу (потолок 3 находки), а сериализация не бесплатна — она разводит по очереди то, что могло идти разом. Находка «**форму изменения** надо переделывать» ловится триажем, как и любая -другая; дальше правило одно. Находка чинится, и конвейер запускается **заново с -нулевой стадии**, а не «доезжает» остатком по коду, которого через час не станет. +другая; дальше правило одно. Находка чинится, и ревью кода запускается **заново с +гейта**, а не «доезжает» остатком по коду, которого через час не станет. + +**Разметка при этом не повторяется — кроме одного случая.** План описывает +задачу, а не дифф, и переделка формы внутри той же задачи его не отменяет. +Повторить разметку надо тогда, когда правка изменила **дельта-спеки**: план +выведен из них, и план, выведенный из отменённых требований, будет уверенно +называть не те темы. Если прогон всё же остановлен на полпути, незапущенные проходы идут в границы покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>», поимённо, а **триаж на половине прогона не запускается**: его отчёт выглядит полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск, -что и в разделе «Профили». +что и в разделе «Метки». Находка, которая чинится в пределах существующей формы (`Действие: инлайн`), прогон не останавливает: дешевле дособрать все находки и починить пачкой, чем @@ -463,33 +659,56 @@ flowchart TD идёт строка: какие проходы шли одновременно и что замеры этого прогона как оракул слабее. -Режим объявляется в отчёте наравне с профилем: **`по графу`** — одним словом, +Режим объявляется в отчёте наравне с меткой: **`по графу`** — одним словом, **`линейно`** — с причиной (какой именно из трёх). -## Стадия 0 — Разметка (обязательна во всех профилях) +## Разметка задачи — один раз, до обеих стадий ревью -Агент `review-scope`. Идёт **первым, до гейта**, и один: до его плана неизвестно -ни что проверять, ни на какой ступени. +Агент `review-scope`. Идёт **после `propose`, до ревью дизайна**, и один: до его +плана неизвестно ни что проверять, ни на какой метке, ни сколько ревьюверов +звать на предложение. -Возвращает **план прогона**: список тем с домами и глубинами, ступень с -обоснованием, перечень документов, не ставших темами, и строку про найденные -директивы (`CLAUDE.md`, `AGENTS.md`). План уезжает в отчёт целиком и служит -границами покрытия. +**Это не стадия прогона, и в счёт проходов метки она не входит.** Раньше +разметка была стадией 0 ревью кода и платилась на каждом прогоне, а перед ревью +дизайна тот же вопрос — «крупное или незнакомое?» — задавал сам вызывающий, то +есть оркестратор, который только что написал предложение. Одна и та же величина +считалась дважды, и один из двух раз — без разведённости с автором. Теперь она +считается один раз и обслуживает обе стадии. + +**Что он читает.** Запись задачи, `proposal.md` и дельта-спеки change, `tasks.md` +change, `docs/` на уровне имён и заголовков, `CLAUDE.md` и `AGENTS.md`, +`docs/review.*` — раздел настройки. **Диффа он не читает: кода ещё нет.** Размер +он оценивает по перечню границ задачи и по дельта-спекам, а не по `git diff +--stat`. + +Возвращает **план задачи**: размер и сложность с обоснованием, метка как +максимум по ним, состав ревью дизайна, список тем с домами и глубинами для ревью +кода, разнесение документов по трём категориям и строку про найденные директивы. +План уезжает в отчёты обеих стадий целиком и служит границами покрытия. **Он не судит по существу** — ни одной находки об изменении. Его ошибка это -пропущенная тема или не та ступень, и обе видны: план сверяется с `ls docs/` за -секунду, а заниженную ступень ловит `basics` своим сигналом. +пропущенная тема или не та метка, и обе видны: разнесение документов сверяется +с `ls docs/` за секунду, а заниженную метка ловит `basics` своим сигналом. -**Ступень выбирает он, а не автор изменения.** Раньше профиль называл тот же -оркестратор, который писал код: он же решал, насколько глубоко его проверять, — и -разведённости с автором в этой точке не было вовсе. Вызывающий пайплайн профиль -больше не передаёт; он передаёт change, базу диффа и режим. - -Право у разметчика симметричное: **поднять и понизить ступень он может +Право у разметчика симметричное: **поднять и понизить метку он может одинаково**, но обоснование обязательно в обоих случаях и всегда — строкой, какой -признак сработал и по какому факту. +признак по какой оси сработал и по какому факту. -## Стадия 1 — Автотесты (обязательна во всех профилях) +**План живёт в контексте прогона задачи и на диск не пишется.** Файл-план был бы +четвёртым артефактом рядом с `proposal.md`, `tasks.md` и `design.md`, жил бы +дольше задачи и расходился бы с ней молча. Прервали пайплайн — разметка +повторяется; это самый дешёвый проход конвейера, и платить за его вечность +дороже, чем перезапустить. + +**Метка, названная до кода, после кода не пересматривается.** Дифф может выйти +крупнее ожидания — метка от этого не двинется. Решение сознательное: пересмотр +означал бы либо второй запуск разметчика (ровно то, что здесь убрано), либо +машинный порог, который на всякой нетипичной задаче срабатывает не туда. +Расхождение факта с разметкой ловится **журналом дефектов** в `docs/review.md`, +постфактум, и это единственный сигнал — ровно как и для всякой другой ошибки +выбора метки. + +## Стадия 1 — Автотесты (обязательна при любой метке) Агент `review-autotests`, тема `autotests`. Запускает команду гейта из семантики гейта в `CLAUDE.md` и интерпретирует вывод. @@ -516,11 +735,11 @@ flowchart TD Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу запрещено списывать такой отказ в мелочь. -## Стадия 2 — Сверка (обязательна во всех профилях) +## Стадия 2 — Сверка (обязательна при любой метке) Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со -стадией 3 или 4 — той, что в профиле. +стадией 3 или 4 — той, что в метки. - `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек предлагаемого изменения**, а не из proposal, сообщения коммита или описания @@ -531,6 +750,23 @@ flowchart TD перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть, которая **не выражается правилом**: механизируемое уже проверила стадия 1. + **На `small` у него есть третья, узкая обязанность** — сверить дифф с + записанными инвариантами `CLAUDE.md` по темам `security`, `operations` и + `architecture`, потому что с этой меткой `basics` не идёт. Потолок 1 находка + на все три темы разом: это не замена приёмнику тем, а объявленный минимум. + +**Вход обеих половин зависит от метки, и это второй рычаг дешевизны `small`.** +На `small` `specs` читает только дельта-спеку, `code` — только **индекс** +конвенций: перечень родов и пометки, что уже механизировано. На `medium` и +выше оба читают дома целиком. Разница честная: узкий вход ловит нарушение +записанного рода и пропускает то, ради чего конвенцию писали абзацем. + +**Потолки у обоих половин раздельные, и это не бюрократия.** Конвенционных +находок больше по построению — родов навигации в разы больше, чем классов +технического дефекта, — и в общем списке они вытесняют техническую половину, чей +пропуск дороже. Раздельный потолок делает вытеснение невозможным: на `small` это +3 технических и 2 конвенционных, на `medium` и выше потолка нет у первой +половины и 4 у второй. **Технический разбор — не тема, а обязанность прохода, и он единственный.** Остальные читают код как материал для своей оптики: `specs` — против требований, @@ -550,37 +786,44 @@ Recall темы `conventions` равен длине конвенций прое границах покрытия; прочие опиниативные проходы держат `opus` из-за цены **ложных** находок, эти двое — из-за цены пропущенных. -## Стадия 3 — Темы (`quick`, `standard`; в `wide` — только свои темы проекта) +## Стадия 3 — Темы (`medium` целиком; `small` и `large` — только свои темы проекта) Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не меряет — уходит одним сообщением вместе со стадией 2, сразу после зелёного гейта. -**Он не самостоятельная оптика, а держатель тем, у которых на этой ступени нет -своего проходчика.** На `quick` и `standard` это `security`, `operations` и -`architecture`: их именные проходы живут в `wide`, и без `basics` эти темы на -большинстве задач не смотрел бы никто. На любой ступени, включая `wide`, он же — +**Он не самостоятельная оптика, а держатель тем, у которых с этой меткой нет +своего проходчика.** На `medium` это `security`, `operations` и `architecture`: +их именные проходы живут в `large`, а на `small` эти темы смотрятся только против +инвариантов внутри `code`. При любой метке, включая `small` и `large`, он же — **приёмник проектных тем**: именных проходов конечное число, а тем столько, сколько заведёт проект. +**Он запускается только тогда, когда ему есть что принимать, и это правило одно +на все три метки.** На `medium` темы у него есть всегда — три ядра плюс свои. +На `small` и `large` — только свои темы проекта; нет таких, и план говорит строкой: +на `large` «все темы разобраны именными проходами», на `small` «темы ядра +`security`, `operations`, `architecture` закрыты сверкой по инвариантам внутри +`code`». Это единственное место, где состав не выводится из одной лишь метки, и +потому оно называется в плане явно. + Глубина приходит из плана: **сверка** (открыть дом темы, открыть дифф, сравнить; потолок 2 находки) или **разбор** (построить сценарий рассуждением; потолок 4). -Чего он не делает ни на какой глубине — замеров, эксперимента против драйвера, -построенного пути, карты проекта, границы домена. Всё это стоит машины или входа -шире диффа, то есть ровно того, ради чего существует `wide`. +Обе глубины действуют и на темах ядра, и на проектных: раньше проектная тема +разбиралась «так же» независимо от метки, и различие меток на ней не +работало вовсе. Чего он не делает ни на какой глубине — замеров, эксперимента +против драйвера, построенного пути, карты проекта, границы домена. Всё это стоит +машины или входа шире диффа, то есть ровно того, ради чего существует `large`. -**Он покрывает миграцию и публичный контракт на нижних ступенях.** Это не -побочный эффект, а условие, при котором миграция схемы вообще может не поднимать -ступень: её шаг гоняет `autotests`, спеку сверяет `specs`, а вопросы «обратима ли» и -«что с записями новой версии после отката» задаёт здесь `basics`, темой -`operations`. Уберёшь его — и нижние ступени останутся без единственного прохода, -который смотрит на ось времени. +**Он покрывает миграцию и публичный контракт на `medium`.** Это не побочный +эффект, а условие, при котором миграция схемы вообще может не поднимать метка: +её шаг гоняет `autotests`, спеку сверяет `specs`, а вопросы «обратима ли» и «что с +записями новой версии после отката» задаёт здесь `basics`, темой `operations`. +**И ровно поэтому отрицательный тест `small` стал жёстче, а не мягче:** на `small` +этого прохода нет, миграция и публичный контракт остаются без единственного +взгляда на ось времени — значит изменение, которое не откатывается обратной +правкой, на `small` не идёт вовсе, каким бы малым оно ни было. -**В `wide` он запускается только при своих темах проекта.** Нет таких — план -говорит строкой «`basics` не запускается: все темы разобраны именными проходами». -Это единственное место, где состав не выводится из профиля, и потому оно -называется в плане явно. - -## Стадия 4 — Доказательство (только `wide`) +## Стадия 4 — Доказательство (только `large`) Три прохода, и все три уходят сразу после зелёного гейта, в одном ряду со стадией 2. Каждый берёт свою тему и доводит её до **доказательства**: @@ -609,24 +852,30 @@ Recall темы `conventions` равен длине конвенций прое ось времени и эксплуатации. Ровно поэтому они и стоят денег: оракул добывается запуском, а запуск — это машина, цепочка и часы. -Раньше эта пара стояла в `standard`, то есть на большинстве задач. Стадия -переехала в `wide` **сознательно и по цене, а не потому, что перестала находить**: +Раньше эта пара стояла в `medium`, то есть на большинстве задач. Стадия +переехала в `large` **сознательно и по цене, а не потому, что перестала находить**: она осталась самой ценной, но её ценность оплачивается на каждой задаче, а -получается — на немногих. Что из-за этого перестало проверяться на нижних -ступенях, названо в «Честном пределе» и обязано идти строкой в границы покрытия -каждого прогона `quick` и `standard`. +получается — на немногих. Что из-за этого перестало проверяться на младших метках, названо в «Честном пределе» и обязано идти строкой в границы покрытия +каждого прогона `small` и `medium`. -Дома тем приходят из плана разметчика: `security` — враждебному, `operations` -(эксплуатация, хранилище, числа) — эксплуатационному, `architecture` (устройство, -граница домена, решения) — архитектурному. Что с чем сшивать и почему — +Дома тем приходят из плана разметки задачи: `security` — враждебному, `operations` +(эксплуатация и хранилище) — эксплуатационному, `architecture` (устройство, +граница домена) — архитектурному. Что с чем сшивать и почему — [project-facts.md](references/project-facts.md), раздел «Сшивать обязаны проходы». Без домов стадия вырождается в общие места. -**Условие стадии и есть условие ступени `wide`:** изменение крупное или -незнакомое. У архитектурного прохода работа появляется тогда, когда трогается -несколько узлов разом или в проекте становится больше сущностей, чем было; у -меряющей пары — когда форму решения нащупывали по ходу, и потому неизвестно, где -она протекает. На мелкой правке вопрос «не появился ли второй способ» отвечается +**Числа и решения проекта эта стадия больше не читает.** `research.*` и `adr.*` — +процессные документы, и прогон их не открывает. Для эксплуатационного прохода это +значит, что **число он обязан снять сам** — замером, а не цитатой из чужой +записки; для архитектурного — что граница домена берётся из `passport.*`, а не из +истории решений. Обе потери названы в «Честном пределе». + +**Условие стадии и есть условие метки `large`:** изменение крупное **или** +незнакомое — любая из двух осей. Разведены они не для красоты: у архитектурного +прохода работа появляется от **размера** (трогается несколько слоёв разом или в +проекте становится больше сущностей, чем было), у меряющей пары — от +**сложности** (форму решения нащупывали по ходу, и потому неизвестно, где она +протекает). На малом и знакомом вопрос «не появился ли второй способ» отвечается «нет» до запуска, а построенный путь строить негде. `review-architecture` получает **вход шире диффа**: дерево пакетов с @@ -647,7 +896,7 @@ Recall темы `conventions` равен длине конвенций прое Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.** Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не стартует. Получает сырые выводы всех проходов, `git diff`, режим и **план -разметчика**; возвращает финальный отчёт. +разметки задачи**; возвращает финальный отчёт. **План на входе у триажа — не формальность, а сверка.** Он единственный, кто видит и то, что размечено, и то, что пришло: «тем размечено шесть, отчёты @@ -669,66 +918,80 @@ Recall темы `conventions` равен длине конвенций прое понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по ущербу × вероятности → потолок 7 пунктов в основном списке. -## Профиль `design` — до кода +## Ревью дизайна — до кода -Запускается на шаге ревью спек (шаг 4 скилла `av-dev-pipeline:task-pipeline`), -когда change уже -имеет `proposal.md` и дельта-спеки, но кода ещё нет. +Запускается на первом чекпоинте ревью (шаг 5 скилла +`av-dev-pipeline:task-pipeline`), когда change уже имеет `proposal.md` и +дельта-спеки, но кода ещё нет. Разметка задачи к этому моменту уже прошла — она +шагом раньше, и метка известна. -**Состав здесь тоже не постоянный, и условие то же самое, что у `wide`:** -изменение крупное или незнакомое. +**Состав растёт метками — теми же, что у ревью кода, и по той же паре осей.** +Их называет разметка задачи, а не вызывающий: величина считается один раз и +служит обеим стадиям. + +| Метка | Проходы на предложении | Проходов | +|---|---|---| +| `small` — малое и знакомое | `specs` | **1** | +| `medium` — среднее и знакомое | `specs`, `rubric` | **2** | +| `large` — крупное или незнакомое | `specs`, `rubric`, `architecture` + вопрос автору | **3** | - **всегда** — `review-specs` в режиме «дизайн ДО кода». Дельта-спеки сверяются на каждой задаче: это самый дешёвый чекпоинт конвейера, и он ловит то, что на готовом коде уже не чинят; -- **при крупном или незнакомом** — плюс `review-rubric` (фаза 1 без фазы 2: рубрика на - задуманный узел становится приёмочными критериями и уезжает в `tasks.md`) и - `review-architecture` на предложении: можно ли выразить существующими понятиями - — **включая конструкции стандартной библиотеки**, — не появляется ли второй - способ. Вопрос «не изобретаем ли то, что уже есть в библиотеке» живёт здесь; - тогда же задаётся вопрос автору дизайна: **«предложи три формы решения и назови - компромисс каждой»** — если ответ показывает, что рассматривалась одна, это - находка. +- **со `medium`** — `review-rubric` (фаза 1 без фазы 2: рубрика на задуманный + узел становится приёмочными критериями и уезжает в `tasks.md`); +- **только в `large`** — `review-architecture` на предложении: можно ли выразить + существующими понятиями — **включая конструкции стандартной библиотеки**, — не + появляется ли второй способ. Вопрос «не изобретаем ли то, что уже есть в + библиотеке» живёт здесь; тогда же задаётся вопрос автору дизайна: **«предложи + три формы решения и назови компромисс каждой»** — если ответ показывает, что + рассматривалась одна, это находка. -Причина условия — арифметика, а не экономия на осторожности. Чекпоинт стоит -**на каждой задаче**, поэтому три прохода здесь умножаются на число задач, и при -мелкой нарезке это самая большая статья конвейера. Рубрика же на узел знакомого -рода порождает свойства уже существующего рода — те, что и так записаны -конвенциями и спеками; а `architecture` на мелкой правке отвечает «нет» на свой -главный вопрос ещё до запуска (см. «Стадия 4»). +**Рубрика съехала на метку вниз, а архитектура осталась наверху — и это не +симметричная правка.** Раньше оба прохода включались одним условием, и `medium` +получал на предложении ровно один проход, то есть не отличался от `small` вовсе. +Разводятся они потому, что зарабатывают на разном: рубрика порождает **свойства +узла** и окупается уже на среднем изменении — её выход уезжает приёмочными +критериями в `tasks.md` и работает потом на всей задаче; архитектура отвечает на +вопрос «не появился ли второй способ», а он на среднем знакомом изменении +отвечается «нет» ещё до запуска. Держать её ниже `large` значит платить за +предсказуемый ответ на каждой задаче. -**Граф этого профиля свой, и он плоский.** Гейта нет — кода ещё нет, запускать -нечего; разметка сводится к одному вопросу (крупное или незнакомое), и его -задаёт вызывающий вместе с профилем; машину не держит ни один проход; сток — не триаж, а шаг 5 пайплайна -задачи, где замечания отрабатываются правкой спек. Триаж здесь не нужен: находок -единицы, и каждая либо правит спеку, либо становится развилкой. +Причина меток — арифметика, а не экономия на осторожности. Чекпоинт стоит +**на каждой задаче**, поэтому каждый проход здесь умножается на число задач, и при +мелкой нарезке это самая большая статья конвейера. + +**Граф этой стадии свой, и он плоский.** Гейта нет — кода ещё нет, запускать +нечего; метка уже названа разметкой задачи; машину не держит ни один проход; +сток — не триаж, а шаг пайплайна задачи, где замечания отрабатываются правкой +спек. Триаж здесь не нужен: находок единицы, и каждая либо правит спеку, либо +становится развилкой. ```mermaid flowchart TD + plan[/"план разметки задачи:
размер, сложность, метка"/] proposal["предложение: proposal.md + дельта-спеки"] specs["specs (режим «дизайн ДО кода») — всегда"] - novelty{{"изменение крупное
или незнакомое?"}} rubric["rubric, фаза 1 → приёмочные критерии в tasks.md"] arch["architecture на предложении"] author["вопрос автору: три формы решения и компромисс каждой"] - fix["шаг 5 пайплайна: правка спек, развилки — вопросом в запись"] + fix["шаг пайплайна: правка спек, развилки — вопросом в запись"] + plan --> proposal proposal --> specs - proposal --> novelty - novelty -->|да| rubric - novelty -->|да| arch - novelty -->|да| author - novelty -->|нет| fix + proposal -->|"метка ≥ medium"| rubric + proposal -->|"метка = large"| arch + proposal -->|"метка = large"| author specs --> fix rubric --> fix arch --> fix author --> fix ``` -Смысл профиля: архитектурная находка на готовом коде стоит переписывания и +Смысл стадии: архитектурная находка на готовом коде стоит переписывания и поэтому игнорируется; та же находка на предложении стоит абзаца обсуждения. -`rubric` живёт **только** в этом профиле. Судить код по критерию, под который он +`rubric` живёт **только** на этой стадии. Судить код по критерию, под который он писался, — корреляция по построению; те же 8–12 свойств уже лежат приёмочными критериями в `tasks.md`. @@ -788,8 +1051,8 @@ flowchart TD Независимо от проекта недоступно: - поведение внешних систем в их будущих версиях; -- реальный профиль нагрузки; и то, что на самом деле лежит в данных, — **сверх - того, что снято с провенансом в `docs/research/`**; +- реальный профиль нагрузки и то, что на самом деле лежит в данных, — **сверх + того, что проход снял замером сам, на этом прогоне**; - завязка внешних потребителей на текущую форму ответа; - суждение «этой функциональности не должно существовать». @@ -801,26 +1064,44 @@ flowchart TD никто. Класс обратимый — портит форму кода, не данные, — и его надо признавать в границах покрытия, а не считать проверенным. -**На `quick` и `standard` не проверяется ничего, что требует запуска.** Это самая +**На `small` и `medium` ничего не проверяется запуском сверх гейта.** Это самая крупная граница покрытия конвейера, и она обязана идти строкой в каждом таком -прогоне — поимённо, а не общим «профиль ниже». Не проверяется: построенный путь -атаки (его надо прогнать), поведение библиотеки и драйвера в вырожденном случае -(достаётся только экспериментом), любое число — время удержания блокировки, пик -кучи, темп роста журнала, стоимость на годовой истории. `basics` задаёт часть тех -же вопросов **чтением**, и его ответы поэтому слабее: он формулирует условиями, -оракула не приносит и выше гипотезы находку не поднимает — кроме той, что -опирается на инвариант `CLAUDE.md`. +прогоне — поимённо, а не общим «метка ниже». Формулировка «не запускается +ничего» была бы короче и была бы ложью: гейт запускает инструменты проекта, а +триаж проверяет оракул `critical`/`major` запуском — оба идут при любой метке. +Не проверяется **опиниативным** проходом: построенный путь атаки (его надо +прогнать), поведение библиотеки и драйвера в вырожденном случае (достаётся только +экспериментом), любое число — время удержания блокировки, пик кучи, темп роста +журнала, стоимость на годовой истории. `basics` задаёт часть тех же вопросов +**чтением**, и его ответы поэтому слабее: он формулирует условиями, оракула не +приносит и выше гипотезы находку не поднимает — кроме той, что опирается на +инвариант `CLAUDE.md`. -**Темы при этом закрыты все — разница в глубине, и её надо читать буквально.** -«Тема `security`, глубина сверка» не значит «безопасность проверена»: значит, что -дом темы открыли, дифф посмотрели и сравнили. Между сверкой и доказательством -лежит весь класс дефектов, который виден только построенным путём, — и он -проверяется на 5–10% задач. +**На `small` три темы ядра смотрятся только против записанных инвариантов.** +Отдельная строка, и она обязательна на каждом прогоне `small`: `security`, +`operations` и `architecture` закрывает не приёмник тем, а `code` сверкой с +`CLAUDE.md`, потолком 1 находка на все три. Свойства, которого нет в инвариантах, +с этой меткой не спросит никто. Это не «глубина ниже» — это **другой дом +темы**, куда более узкий, и путать одно с другим нельзя. -Это сознательная сделка, а не пробел в устройстве: цена ступени `wide` платится на +**Решения и измеренные числа проекта прогон не читает вовсе.** `adr.*` и +`research.*` — процессные документы. Отсюда две строки в границы покрытия каждого +прогона: расхождение изменения с записанным решением ловится не здесь, а сверкой +документации; число, на которое опирается находка, обязано быть снято **на этом +прогоне**, иначе находка не поднимается выше гипотезы. Раньше числа брались из +`docs/research/`, и находка выглядела доказанной чужим замером неизвестной +свежести. + +**Темы при этом названы все — но закрыты они по-разному, и это надо читать +буквально.** «Тема `security`, глубина сверка» не значит «безопасность +проверена»: значит, что дом темы открыли, дифф посмотрели и сравнили. Между +сверкой и доказательством лежит весь класс дефектов, который виден только +построенным путём, — и он проверяется на 5–10% задач. + +Это сознательная сделка, а не пробел в устройстве: цес меткой `large` платится на каждой задаче, а окупается на немногих. Проверяется сделка не рассуждением, а журналом дефектов: если класс, который ловят только меряющие проходы, начал -всплывать после мерджа — ступень выбирают слишком низко. +всплывать после мерджа — метку выбирают слишком низко. Так же честно и про упразднённый проход: **«не знаю, чего не знаю» больше не достаёт никто.** Проход независимой реализации писал свою версию узла, не @@ -830,7 +1111,7 @@ flowchart TD он тратил больше всех остальных проходов вместе, — а не по замеру, который [calibration.md](references/calibration.md) требует перед удалением. Значит и записывается это как сознательное сужение, а не как «класс оказался пустым»: -остаток независимого взгляда даёт профиль `design` (код пишется под его находки) и +остаток независимого взгляда даёт ревью дизайна (код пишется под его находки) и `architecture` (второй способ, лишние слои), но **альтернативной реализации, с которой можно сдиффить решения, у конвейера теперь нет**. Класс идёт строкой в границы покрытия каждого прогона — там же, где проект перечисляет своё в diff --git a/av-dev-pipeline/skills/review-pipeline/references/calibration.md b/av-dev-pipeline/skills/review-pipeline/references/calibration.md index 1af5cbc..bea5e50 100644 --- a/av-dev-pipeline/skills/review-pipeline/references/calibration.md +++ b/av-dev-pipeline/skills/review-pipeline/references/calibration.md @@ -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'а существующего — иначе непонятно, правка помогла или нет; - при появлении записи в журнале проскочивших дефектов — калибруем тот проход, который должен был поймать; diff --git a/av-dev-pipeline/skills/review-pipeline/references/finding-contract.md b/av-dev-pipeline/skills/review-pipeline/references/finding-contract.md index cb8ce5a..e8f36be 100644 --- a/av-dev-pipeline/skills/review-pipeline/references/finding-contract.md +++ b/av-dev-pipeline/skills/review-pipeline/references/finding-contract.md @@ -13,7 +13,7 @@ - Оракул: <падающий тест / команда с выводом / положение руководства / нет> - Последствие: <что произойдёт и при каких условиях> - Предложение: <конкретное изменение> -- Найдено проходом: <имя агента> +- Найдено проходом: <имя агента; у проходов с раздельными потолками — имя и половина, например `code/техника`> ``` ## Правила @@ -76,10 +76,16 @@ 4. `Promote candidates` — кандидаты в конвенцию или правило линтера; 5. `Границы покрытия` — сводная, обязательная. -Перед секциями — сводка для человека: профиль и режим прогона, состояние гейта, -**перечень запущенных проходов поимённо с исходом каждого**, сколько находок -пришло на вход и сколько осталось. Перечень обязателен: пропуск прохода не -отличим от прохода без находок, и назвать его больше некому. +Перед секциями — сводка для человека: размер, сложность, метка и режим +прогона, состояние гейта, **план разметки задачи с исходом по каждой теме**, +сколько находок пришло на вход и сколько осталось. + +**Реестр сводки — темы, а не проходы, и это не оформление.** Перечень запущенных +проходов отвечает «все, кто должен был, отработали» и молчит о том, что именно +осталось непроверенным: уехавший в старшую метку проход уносит тему с собой +беззвучно. План же называет тему, её дом, глубину и исполнителя — и тема, +оставшаяся без отчёта, видна сразу. Перечень проходов из сводки не исчезает, но +идёт **внутри** плана, колонкой «кто закрывает». Каждая находка в секциях 1–2 несёт дополнительное поле: diff --git a/av-dev-pipeline/skills/review-pipeline/references/project-facts.md b/av-dev-pipeline/skills/review-pipeline/references/project-facts.md index f9c938a..702d48b 100644 --- a/av-dev-pipeline/skills/review-pipeline/references/project-facts.md +++ b/av-dev-pipeline/skills/review-pipeline/references/project-facts.md @@ -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` теряет перечень внешних зависимостей | diff --git a/av-dev-pipeline/skills/review-pipeline/references/review-journal.md b/av-dev-pipeline/skills/review-pipeline/references/review-journal.md index abbfc7d..8040b62 100644 --- a/av-dev-pipeline/skills/review-pipeline/references/review-journal.md +++ b/av-dev-pipeline/skills/review-pipeline/references/review-journal.md @@ -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 общий для всех проектов, документ — про этот. - **в конвенции или в правило линтера** — если свойство выражается diff --git a/av-dev-pipeline/skills/task-batch/SKILL.md b/av-dev-pipeline/skills/task-batch/SKILL.md index b7208c1..dd098ab 100644 --- a/av-dev-pipeline/skills/task-batch/SKILL.md +++ b/av-dev-pipeline/skills/task-batch/SKILL.md @@ -51,7 +51,7 @@ description: Проводит несколько задач разом — пл - **Батч не владеет спринтом и целями.** Он сообщает исход по каждой задаче в тех же трёх словах, что и `task-pipeline`: сделана / не доведена / оказалась крупнее задачи. -- **Задачи закрывает пайплайн внутри каждого сабагента**, шагом 11 — после +- **Задачи закрывает пайплайн внутри каждого сабагента**, шагом 12 — после коммита работы и **отдельным коммитом учёта**, вызовом Skill `av-dev-pm:tasks`. Батч сам записей учёта не трогает: он не знает, чем кончилась приёмка, и дублировать закрытие ему незачем. Но грязное дерево после сабагента — **его** @@ -104,21 +104,20 @@ description: Проводит несколько задач разом — пл параллельном она гонится в волне **одна** (обоснование — ниже, в шаге 4). Помечается здесь, на планировании, а не во время прогона, и **независимо от режима**: состав волны определяется сейчас, режим может смениться просьбой уже - после плана, а ступень ревью выберет разметчик уже внутри прогона — ключевать - волну на ещё не сделанный выбор нельзя. Триггеры — по фактам о задаче, каждый + после плана, а метка ревью назовёт разметчик уже внутри пайплайна задачи, + после `propose`, — ключевать волну на ещё не сделанный выбор нельзя. Триггеры — по фактам о задаче, каждый сам по себе достаточен: - трогает схему хранилища, миграцию, формат на диске или объём хранимого; - трогает конкурентность: транзакции, блокировки, фоновые циклы, общее состояние; - трогает размер тела, буфер, память, сжатие, ретеншен, темп потока; - - её тема названа в `docs/research/` или в журнале `docs/review.md` как место, - где уже мерили или уже ломалось. + - её тема названа в журнале `docs/review.md` как место, где уже ломалось. Ни один триггер не сработал — задача не замеряющая, даже если её ревью - окажется `wide`. Ступень про глубину проверки, замеряющая — про соревнование за + окажется `large`. Метка про глубину проверки, замеряющая — про соревнование за железо; это разные вопросы, и совпадают они не всегда. Обратное тоже бывает и - тоже законно: помеченная задача, чьё ревью пошло профилем `quick` или - `standard`, машину не займёт вовсе — меряющие проходы живут только в `wide`. + тоже законно: помеченная задача, чьё ревью пошло меткой `small` или + `medium`, машину не займёт вовсе — меряющие проходы живут только в `large`. Пометка от этого не снимается: она ставится **до** разметки, и перестраховка здесь стоит одной волны, а ошибка — испорченных чисел; - **нумерованные артефакты — номера раздаёт оркестратор заранее.** Если проект @@ -238,8 +237,10 @@ flowchart TD - прогони Skill **`av-dev-pipeline:task-pipeline`** ровно на этой задаче, полный цикл SDD с обоими чекпоинтами ревью; - если задаче назначен **номер артефакта** — используй строго его; - - **ступень ревью выбирает разметчик конвейера, а не ты и не сабагент.** - Профиль в вызов не передаётся вовсе. Батч не повод её понижать: «нас много и + - **метка ревью выбирает разметчик конвейера, а не ты и не сабагент.** Он + идёт внутри пайплайна задачи, шагом 4, сразу после `propose`, и его план + обслуживает оба чекпоинта. Метка в вызов не передаётся вовсе. Батч не + повод её понижать: «нас много и мы спешим» — ровно тот стимул, из-за которого проходы пропускают, и он снят тем, что регулятор не в руках у автора; - **режим прогона проходов ревью — от режима батча**, и его называет charter, @@ -249,15 +250,15 @@ flowchart TD Внутренние рёбра графа — цепочку проходов, держащих машину — конвейер соблюдает сам, в любом режиме; - **если вложенные сабагенты недоступны** (движок не даёт запускать агентов из - агента) — не пропускай ревью и не понижай ступень: проведи его **инлайн** по + агента) — не пропускай ревью и не понижай метка: проведи его **инлайн** по тем же charter'ам `av-dev-pipeline`, сохранив обязательное — разметку первой - (план с темами и ступенью), гейт до опиниативных проходов, состав по плану, + (план с темами и меткой), гейт до опиниативных проходов, состав по плану, триаж последним. И **скажи в отчёте прямым текстом, что ревью шло инлайн**: инлайновый проход видит контекст автора и потому разведён с ним слабее — а - инлайновая разметка вдобавок означает, что ступень выбрал автор, и это + инлайновая разметка вдобавок означает, что метка выбрал автор, и это отдельная строка; - **вернуть отчёт**, в котором обязательно: исход задачи одним из трёх слов; - **план прогона: ступень с обоснованием, темы и их глубины**, и режим; что сделано; какие вопросы + **план прогона: метка с обоснованием, темы и их глубины**, и режим; что сделано; какие вопросы записаны и куда; изменённые файлы; добавлялся ли нумерованный артефакт и с каким номером; затронутые capability; состояние гейта; **перечень тем с исходом по каждой**; **путь к @@ -284,9 +285,9 @@ flowchart TD Сверка идёт в три шага, и порядок важен: 1. **Возьми план прогона** из отчёта задачи — таблицу «тема → дом → глубина → кто - закрывает» со ступенью и обоснованием; он затем и заказан в обязательных полях + закрывает» с меткой и обоснованием; он затем и заказан в обязательных полях шага 4. Плана в отчёте нет — сверять не с чем; это само по себе основание не - вливать, пока сабагент не покажет план разметчика. + вливать, пока сабагент не покажет план разметки задачи. 2. **Сверяй с независимым артефактом, а не с прозой отчёта.** Перечень проходов бери из **сохранённого отчёта триажа** (`openspec/changes//review/` или `openspec/changes/archive//review/` — задача доведена, change заархивирован) — @@ -294,13 +295,18 @@ flowchart TD проход и пропустить: она подтверждает сама себя. Отчёта триажа на месте нет — считай, что состав неизвестен, и дозапускай ревью целиком. 3. **Сверь план с исходом**: против каждой темы плана обязан стоять отчёт либо - названная причина его отсутствия. Раскладка «тема → кто закрывает на этой - ступени» — в скилле `av-dev-pipeline:review-pipeline`. + названная причина его отсутствия. Раскладка «тема → кто закрывает с этой меткой» — в скилле `av-dev-pipeline:review-pipeline`. Расхождение — не повод отменять задачу: дозапусти недостающие проходы **на ветке**, в её worktree, через `av-dev-pipeline:review-pipeline`, и только потом интегрируй. +**Передай в дозапуск тот же план.** Триаж требует его обязательным входом — без +плана он не может сверить, все ли размеченные темы вернули отчёт, а эта сверка и +есть то, ради чего дозапуск затевается. Плана не осталось (сабагент не сохранил +его в отчёте) — пусть повторит разметку задачи: это самый дешёвый проход +конвейера, и он дешевле, чем прогон, который нечем сверить. + **Находки дозапуска — такие же находки, и зелёный гейт их не отменяет.** Правило интеграции «вливаем только зелёные» смотрит на гейт, а дозапущенный `critical` гейт не красит: он был бы пропущен молча, если это не сказать прямо. Поэтому: @@ -337,7 +343,7 @@ rebase делается **внутри worktree задачи**, а ff-слиян (`git -C rebase --abort`), оставь ветку и worktree как есть, вынеси это в доклад как нераспознанное пересечение; - **дерево сабагента обязано быть чистым.** `git -C status --porcelain` - до `rebase`: непусто — значит сабагент не довёл шаг 11 до коммита учёта (или + до `rebase`: непусто — значит сабагент не довёл шаг 12 до коммита учёта (или оставил мусор). Не форсируй и не коммить за него: назови задачу в докладе недоведённой и оставь ветку с worktree. Молчаливый `rebase` на грязном дереве всё равно откажет, но с сообщением про unstaged changes — а причина другая; @@ -395,7 +401,26 @@ rebase в файле X», а не «нераспознанное пересеч - **заверши триажем.** Он единственный, кто агрегирует, и без него у находок нет ни оракула, ни пометки `инлайн`/`развилка` — а следующий абзац на неё опирается. Прогон из двух проходов без триажа — это сырые находки, выданные за - разобранные. + разобранные; +- **план триажу собери сам, здесь, — разметчика на этой сверке нет.** Триаж + требует план обязательным входом: он сверяет размеченное с пришедшим, и без + плана эта сверка не выполняется вовсе. Разметка задачи сюда не годится — она + описывала одну задачу, а сверка идёт по интегрированной ветке. План здесь + короткий и составляется по факту запуска: + + ``` + метка: не применяется — сверка стыка, а не ревью изменения + тема дом глубина закрывает + requirements openspec/specs// разбор specs (стык) + requirements openspec/specs// разбор specs (стык) + architecture docs/architecture.md разбор architecture + + источник docs/passport.md + ``` + + Темы, которых в этом списке нет (`autotests`, `conventions`, `security`, + `operations`, свои темы проекта), назови строкой «не проверяется на сверке + стыка: закрыто прогонами отдельных задач». Это не формальность — без такой + строки отчёт сверки читается как полное ревью ветки. Граф этой сверки — веер в один сток, и он такой же, как у обычного прогона: @@ -421,7 +446,7 @@ flowchart TD `git worktree prune`. Worktree и ветки **провалившихся** не трогай — они нужны для ручного дожатия. - **Записей учёта батч не трогает** — их правит пайплайн внутри сабагента на - шаге 11. Батч сообщает исход по каждой задаче; если какой-то сабагент дошёл до + шаге 12. Батч сообщает исход по каждой задаче; если какой-то сабагент дошёл до коммита, но закрытия не сделал (плагина нет, вызов не разрешился), скажи это строкой — иначе задача останется открытой молча. - Доложи кратко: diff --git a/av-dev-pipeline/skills/task-pipeline/SKILL.md b/av-dev-pipeline/skills/task-pipeline/SKILL.md index 4396e72..baddf69 100644 --- a/av-dev-pipeline/skills/task-pipeline/SKILL.md +++ b/av-dev-pipeline/skills/task-pipeline/SKILL.md @@ -1,6 +1,6 @@ --- name: task-pipeline -description: Автономно проводит одну задачу через полный цикл Spec Driven Development — от постановки до коммита (opsx explore→propose→ревью спек профилем design→apply→ревью кода→archive→коммит), с обязательными чекпоинтами ревью и докладом об исходе. Использовать, когда просят взять/сделать задачу или довести идею до реализации. +description: "Автономно проводит одну задачу через полный цикл Spec Driven Development — от постановки до коммита (opsx explore→propose→разметка задачи→ревью дизайна→apply→ревью кода→archive→коммит), с обязательными чекпоинтами ревью и докладом об исходе. Разметка идёт один раз, сразу после propose: она называет размер, сложность и метка, и её план определяет состав обеих стадий ревью. Использовать, когда просят взять/сделать задачу или довести идею до реализации." --- # Пайплайн задачи @@ -11,12 +11,13 @@ description: Автономно проводит одну задачу чере Это тонкая обёртка над каноническими скиллами `opsx:explore` / `opsx:propose` / `opsx:apply` / `opsx:archive` — вызывай их через Skill, не переизобретай их шаги. Ревью — скилл `av-dev-pipeline:review-pipeline`; он же держит правило выбора -ступени и **сам её выбирает**: ты профиль не передаёшь. +метки, а называет её агент `review-scope` на шаге 4 — один раз на задачу, для +обеих стадий ревью. ## Предпосылки - **OpenSpec и скиллы `opsx:*` — жёсткая предпосылка, а не опция.** На них стоят - шаги 2, 3, 6 и 8, проход `review-specs` и профиль `design` (они завязаны на + шаги 2, 3, 7 и 9, проход `review-specs` и ревью дизайна (они завязаны на `openspec/changes//specs/*/spec.md` и на `openspec validate --strict`). **Проект без OpenSpec этим пайплайном не ведётся** — подключай OpenSpec, а не вырождай цикл: ветка деградации здесь не пишется, потому что непроверенная @@ -47,14 +48,14 @@ description: Автономно проводит одну задачу чере проекте есть свой процесс управления задачами — он и решает, что брать. - **Форматом задач.** Пайплайн **не правит индексы руками и не выдумывает путь к скрипту учёта**: он зовёт Skill `av-dev-pm:tasks`, который этим владеет - (шаг 11). Закрытие как таковое — его работа, и это осознанное решение с + (шаг 12). Закрытие как таковое — его работа, и это осознанное решение с названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек на сессии возвращает задачу `reopen` с причиной, а доклад по критериям приёмки становится единственным, по чему приёмка вообще возможна. Плагина `av-dev-pm` в проекте нет — вызов не разрешится, и тогда учёт остаётся владельцу, о чём говорится в докладе. - **Заведением задач из урожая ревью.** Отложенные находки отдаются **списком** - (см. шаг 7); превращать их в задачи — работа того, кто ведёт задачи проекта. + (см. шаг 8); превращать их в задачи — работа того, кто ведёт задачи проекта. - **Определением ценности.** «Нужна ли эта функциональность» — не вопрос пайплайна ни на одном шаге. @@ -148,7 +149,7 @@ description: Автономно проводит одну задачу чере ## Шаги -Одиннадцать шагов с одной развилкой и одним досрочным исходом: +Двенадцать шагов с одной развилкой и одним досрочным исходом: ```mermaid flowchart TD @@ -157,26 +158,34 @@ flowchart TD big["исход «оказалась крупнее задачи»
объявляется ДО заведения change"] s2["2. opsx:explore — груминг идеи"] s3["3. opsx:propose — change, дельта-спеки, tasks.md"] - s4["4. ревью предложения, профиль design"] - s5["5. отработать замечания + validate --strict"] - s6["6. opsx:apply — код, гейт, поведенческая верификация"] - s7["7. ревью кода, ступень выбирает разметчик"] - s8["8. opsx:archive"] - s9["9. синк документации — av-dev-pm:docs"] - s10["10. коммит работы — av-dev-git:commit"] - s11["11. закрыть задачу — av-dev-pm:tasks,
вторым коммитом учёта"] + s4["4. разметка задачи — review-scope:
размер, сложность, метка, план тем"] + s5["5. ревью дизайна, состав по метке"] + s6["6. отработать замечания + validate --strict"] + s7["7. opsx:apply — код, гейт, поведенческая верификация"] + s8["8. ревью кода, состав по той же метки"] + s9["9. opsx:archive"] + s10["10. синк документации — av-dev-pm:docs"] + s11["11. коммит работы — av-dev-git:commit"] + s12["12. закрыть задачу — av-dev-pm:tasks,
вторым коммитом учёта"] s1 --> triv s1 -.-> big triv -->|"нет: идея или мутная постановка"| s2 s2 --> s3 - triv -->|"да: шаги 2 и 4 пропускаются"| s3 - s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 --> s10 --> s11 + triv -->|"да: шаг 2 пропускается"| s3 + s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 --> s10 --> s11 --> s12 + s4 -.->|"план задачи: та же метка"| s8 ``` -Два чекпоинта ревью — шаги 4 и 7 — единственные места, где зовётся конвейер; +**Разметка стоит одна и обслуживает обе стадии ревью** — шаги 5 и 8. Это и есть +пунктирное ребро на схеме: план, посчитанный на шаге 4, доезжает до ревью кода +без пересчёта. Раньше разметка была первым проходом внутри шага ревью кода, а +состав ревью дизайна называл сам пайплайн — то есть одна и та же величина +считалась дважды, и один из двух раз тем, кто только что написал предложение. + +Два чекпоинта ревью — шаги 5 и 8 — единственные места, где зовётся конвейер; порядок «сперва коммит работы, потом коммит учёта» на схеме тоже ребро, и оно -обязательное (шаг 11). +обязательное (шаг 12). Схема — **сводка**: содержание каждого шага в его разделе ниже, и при расхождении прав текст. @@ -191,13 +200,20 @@ flowchart TD уезжают в `tasks.md` change. Файл задачи может быть удалён до коммита, а критерии обязаны его пережить. -Оцени тривиальность (влияет на шаг 4): +Оцени тривиальность — **теперь она влияет ровно на один шаг, второй**: - **тривиальная** — локальная правка без изменения поведения, спек и схемы, - решение очевидно. Explore и ревью спек пропускаются; + решение очевидно. Explore пропускается; - **нетривиальная** — новое или изменённое поведение, дизайн-развилки, задеты инварианты, схема или несколько capability. Полный цикл. +**На состав ревью тривиальность больше не влияет** — это работа шага 4. Раньше +она решала и то, звать ли ревью предложения вовсе; теперь глубину обеих стадий +называет метку, и тривиальная задача просто получает `small`. Разница +существенная: «пропустить ревью дизайна» и «пройти его одним самым дешёвым +проходом» — не одно и то же, а сверка дельта-спек стоит меньше, чем разбор того, +что она поймала бы. + Здесь же — проверка на «крупнее задачи»: если видно, что одним заходом это не мерджится, объявляй исход **до** заведения change. @@ -216,31 +232,67 @@ flowchart TD Критерии приёмки задачи, если они были, копируются в `tasks.md` отдельным блоком. -### 4. (Нетривиальная) Ревью предложения — профиль `design`, ДО кода +### 4. Разметка задачи — агент `review-scope` -Первый чекпоинт. Вызови Skill **`av-dev-pipeline:review-pipeline`** с профилем -`design` и ссылкой на change ``. +**Один запуск на всю задачу, и он обслуживает оба чекпоинта ревью.** Запусти +агента `review-scope`, дав ему корень проекта, идентификатор change, базу диффа и +запись задачи. Кода на этот момент нет, и это условие его работы, а не помеха. -**Состав чекпоинта решает конвейер, а не ты**: `review-specs` в режиме «дизайн ДО -кода» идёт всегда, а `review-rubric` и `review-architecture` — только когда -изменение крупное или незнакомое (то же условие, что у ступени `wide`, и та же -доля — 5–10% задач). Причина в том, что чекпоинт стоит на **каждой** задаче: при -мелкой нарезке три прохода здесь умножаются на число задач и становятся самой -большой статьёй конвейера. +Он возвращает **план задачи**: -Смысл профиля: архитектурная находка на готовом коде стоит переписывания и +- **размер** (малое / среднее / крупное) и **сложность** (знакомое / + незнакомое), каждое с обоснованием по факту; +- **метка** как максимум по двум осям: `small`, `medium` или `large`; +- **состав ревью дизайна** — что звать на шаге 5; +- **таблицу тем** «тема → дом → глубина → кто закрывает» — для шага 8; +- разнесение документов проекта по трём категориям и строку про директивы. + +**Метка выбираешь не ты.** Раньше состав ревью дизайна называл этот пайплайн +(«крупное или незнакомое?»), то есть тот же оркестратор, который только что +довёл предложение до `propose`. Разведённости с автором в этой точке не было +вовсе; теперь есть. + +**План держи в контексте до конца задачи.** На диск он не пишется: файл-план стал +бы четвёртым артефактом рядом с `proposal.md`, `tasks.md` и `design.md`, пережил +бы задачу и разошёлся бы с ней молча. Прервался пайплайн — повтори шаг 4, это +самый дешёвый его проход. + +**Разметка повторяется ровно в одном случае** — если на шаге 6 правки изменили +сами **дельта-спеки**: план выведен из них, и план по отменённым требованиям +назовёт не те темы. Во всех прочих случаях, включая переделку формы кода на шаге +8, метка остаётся прежней. + +### 5. Ревью предложения — ДО кода, состав по метке + +Первый чекпоинт. Вызови Skill **`av-dev-pipeline:review-pipeline`**, дав ссылку на +change ``, **план разметки с шага 4** и указание, что это ревью дизайна. + +Состав приходит планом, а не решается здесь: + +| Метка | Проходы на предложении | +|---|---| +| `small` | `specs` | +| `medium` | `specs`, `rubric` | +| `large` | `specs`, `rubric`, `architecture` + вопрос автору о трёх формах решения | + +`review-specs` в режиме «дизайн ДО кода» идёт **на каждой задаче**: это самый +дешёвый чекпоинт конвейера, и он ловит то, что на готовом коде уже не чинят. +Остальные включаются меткой, потому что чекпоинт стоит на каждой задаче и +каждый лишний проход здесь умножается на число задач. + +Смысл стадии: архитектурная находка на готовом коде стоит переписывания и потому игнорируется — та же находка здесь стоит абзаца обсуждения. Если `review-rubric` запускался, перенеси его рубрику в `tasks.md` как приёмочные критерии; там же уже лежат критерии от постановки, если они были. -### 5. Отработать замечания ревью предложения +### 6. Отработать замечания ревью предложения - Мелочь и явные улучшения — правь сам в спеках и дизайне. - Развилки (компромисс, scope, инвариант) — вопросом в запись, спеки урезаются на остаток. - После правок перепрогони `openspec validate --strict `. -### 6. Написать код — `opsx:apply` +### 7. Написать код — `opsx:apply` Вызови Skill `opsx:apply` для реализации `tasks.md`. Код — по конвенциям проекта (каталог `docs/conventions/`). Меняешь схему — обнови её описание в @@ -256,21 +308,31 @@ flowchart TD **Сервис не оставляем лежать.** Если запуск упал — почини или откати до конца шага. -### 7. Ревью кода — Skill `av-dev-pipeline:review-pipeline` +### 8. Ревью кода — Skill `av-dev-pipeline:review-pipeline` Второй чекпоинт. Вызови Skill **`av-dev-pipeline:review-pipeline`**, дав ссылку -на change ``, базу диффа **и режим запуска**. +на change ``, базу диффа, **план разметки с шага 4** и режим запуска. -**Профиль ты не передаёшь, и это правило, а не упрощение.** Ступень выбирает -разметчик конвейера (`review-scope`, стадия 0) — по объёму и незнакомости -изменения, с обоснованием строкой. Причина в разведённости: ты только что написал -этот код, и решать, насколько глубоко его проверять, тебе нельзя — под давлением -«я почти закончил» решение известно заранее. Правило выбора живёт в скилле -конвейера, проектные триггеры — в `docs/review.*`. +**Метка ты не выбираешь, и это правило, а не упрощение.** Её назвал +`review-scope` ещё на шаге 4 — по размеру и сложности, с обоснованием по каждой +оси. Причина в разведённости: ты только что написал этот код, и решать, насколько +глубоко его проверять, тебе нельзя — под давлением «я почти закончил» решение +известно заранее. Правило выбора живёт в скилле конвейера, проектные триггеры — в +`docs/review.*`, подраздел «Триггеры метки». -**Считаешь ступень заниженной — скажи это в докладе строкой, а не переспорь.** +**Метка не пересматривается по факту диффа.** Дифф может выйти крупнее, чем +ожидалось при разметке, — это не повод её поднимать: пересмотр означал бы второй +запуск разметчика, ровно то, ради устранения чего он и переехал на шаг 4. + +**Считаешь метку заниженной — скажи это в докладе строкой, а не переспорь.** Разметчик вправе и поднять, и понизить; твоё несогласие это факт для человека, а -не команда конвейеру. +не команда конвейеру. Место, где такое несогласие превращается в изменение +правил, — журнал дефектов `docs/review.md`, и только постфактум. + +**Плана нет — ревью кода не запускается.** Триаж требует план обязательным +входом: без него он не может сверить, все ли размеченные темы вернули отчёт, а +эта сверка — единственная защита от молчащего пропуска. Потерял план (прервалась +сессия, ушёл контекст) — повтори шаг 4, а не гони прогон без него. **Режим по умолчанию — `по графу`, и обосновывать его не надо.** Конвейер сам знает свои рёбра: гейт открывает опиниативные проходы, проходы с пометкой «держит @@ -289,10 +351,10 @@ flowchart TD разметчика — таблицей «тема → дом → глубина → кто закрывает», — и против каждой темы обязан стоять исход. Тема без отчёта и тема без дома — разные вещи, и обе должны быть названы. Реестр короткий (шесть тем ядра плюс свои) — сверка стоит -одного взгляда. Почему это правило существует, объясняет раздел «Профили» скилла +одного взгляда. Почему это правило существует, объясняет раздел «Метки» скилла конвейера; здесь — само требование. -Отработай так же, как шаг 5: помеченное `инлайн` чини сам и не логируй, +Отработай так же, как шаг 6: помеченное `инлайн` чини сам и не логируй, `развилка` — вопросом в запись (он уже сформулирован триажем, его остаётся перенести). После правок — снова гейт. @@ -306,19 +368,19 @@ flowchart TD сжатой строкой. Отчёт, из которого исчезло «что проверить было невозможно», превращается в ложное ощущение проверенности. -**Отчёт триажа сохрани вместе с change (`openspec/changes//review/`; шаг 8 +**Отчёт триажа сохрани вместе с change (`openspec/changes//review/`; шаг 9 унесёт его в `openspec/changes/archive//review/` вместе с change) — это обязательно, а не «если удобно».** По нему потом видно, что было найдено и что из этого осталось в урожае. И это единственный **независимый** артефакт о составе прогона: под оркестратором `task-batch` именно по нему сверяют полноту ревью ветки, а не по твоей прозе — она написана тем же, кто мог проход и пропустить. -### 8. Архивировать — `opsx:archive` +### 9. Архивировать — `opsx:archive` Вызови Skill `opsx:archive`: change уезжает в архив, дельты вливаются в актуальные спеки. -### 9. Синк документации +### 10. Синк документации Ревью выполненного — до этого шага. Затем **вызови Skill `av-dev-pm:docs`**: он владеет содержимым документов канона и ведёт чек-лист синка. Плагина нет — шаг @@ -343,7 +405,7 @@ flowchart TD сделан по перечню документов, без списка триггеров — плагина `av-dev-pm` нет». Канона в проекте тоже нет — назови это исходом и предложи `av-dev-pm:canon`. -### 10. Коммит +### 11. Коммит Коммить **в текущую ветку** (`git rev-parse --abbrev-ref HEAD`), сам ветку не создавай и не переключай, ничего не пушь. При ручном запуске HEAD обычно на @@ -354,7 +416,7 @@ flowchart TD сделано», тело списком 1–3 пункта, без трейлеров). Одна задача — один осмысленный коммит. -### 11. Закрыть задачу — **после коммита, не раньше** +### 12. Закрыть задачу — **после коммита, не раньше** **Вызови Skill `av-dev-pm:tasks`** и попроси закрыть задачу как реализованную — он владеет форматом и двигает строку из набора спринта сам. Путь к его скрипту не @@ -362,7 +424,7 @@ flowchart TD путь. **Порядок обязателен.** Закрытие удаляет файл задачи; сделанное до коммита оно -оставило бы задачу закрытой без единого следа работы, если шаг 10 упадёт. +оставило бы задачу закрытой без единого следа работы, если шаг 11 упадёт. **Закрытие тоже коммитится — вторым коммитом, тут же.** Удаление файла задачи и правка индексов (их имена знает `av-dev-pm`, не ты) — это правки в рабочем @@ -390,7 +452,7 @@ flowchart TD это доклад приёмщику, а не отметка «принято»; - **`Урожай`** — отложенные находки списком (формулировка, оракул, провенанс). Задачи из него заводит тот, кто ведёт задачи проекта; -- **одна строка границ покрытия**: какая ступень и режим гонялись, какие проходы +- **одна строка границ покрытия**: какая метка и режим гонялись, какие проходы не запускались и что проверить было невозможно. Доклад без неё сообщает «проверено», не сообщая, что именно. @@ -400,15 +462,16 @@ flowchart TD текущем worktree и на текущей ветке: не делай `git checkout`/`switch`, не создавай веток, не пушь. - Не пропускай `openspec validate --strict` перед архивацией. -- Тривиальная задача: шаги 2 и 4 пропускаются; ревью кода (шаг 7) остаётся - всегда, но в профиле `quick`. +- Тривиальная задача: пропускается только шаг 2. Обе стадии ревью остаются, но + с меткой `small` — один проход на дизайне и четыре на коде, а при своих темах + проекта пять: приёмник тем запускается, если ему есть что принимать. - Гейт блокирует: пока он красный, опиниативные проходы не запускаются. Чинить и перезапускать, а не «посмотреть заодно». - Если ревью предлагает крупную переработку — это развилка: не правь молча и не спрашивай, запиши вопросом и доведи остаток. - Держи вызывающего в цикле короткими репликами на переходах фаз, но не проси подтверждать механику. -- **Занизить ступень ревью или пропустить тему — самый дешёвый способ +- **Занизить метка ревью или пропустить тему — самый дешёвый способ «ускориться», и он же самый дорогой по последствиям.** Защита устроена так, - что регулятора у тебя нет: ступень выбирает разметчик, план сверяется по темам, - непокрытое называется в отчёте строкой. + что регулятора у тебя нет: метку выбирает разметчик **до того**, как ты + написал код, план сверяется по темам, непокрытое называется в отчёте строкой. diff --git a/av-dev-pm/agents/doc-wording.md b/av-dev-pm/agents/doc-wording.md index edd3b6a..94bc008 100644 --- a/av-dev-pm/agents/doc-wording.md +++ b/av-dev-pm/agents/doc-wording.md @@ -103,7 +103,7 @@ color: green | Термин | Что называет | | --- | --- | | интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции | -| триаж | ступень конвейера, сводящая находки в решение | +| триаж | стадия конвейера, сводящая находки в решение | | провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство | | дедуп, дедупликация | сверка нового против уже лежащего | | чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки | diff --git a/av-dev-pm/agents/task-form.md b/av-dev-pm/agents/task-form.md index 618dfaa..8be5c80 100644 --- a/av-dev-pm/agents/task-form.md +++ b/av-dev-pm/agents/task-form.md @@ -100,7 +100,7 @@ color: green одной партии»), — находка: слово стоит, проверки нет. Число критериев считает `tasks.py check`, тебе оно неинтересно. -6. **Предписания процесса в теле нет.** «Делать профилем standard», «взять +6. **Предписания процесса в теле нет.** «Делать с меткой medium», «взять такой-то агент» — это выбор, который делают, увидев изменение, а не при постановке. Он же путь понизить требования решением, принятым до проектирования. diff --git a/av-dev-pm/skills/canon/SKILL.md b/av-dev-pm/skills/canon/SKILL.md index 19a4d8d..c8db5e0 100644 --- a/av-dev-pm/skills/canon/SKILL.md +++ b/av-dev-pm/skills/canon/SKILL.md @@ -199,7 +199,7 @@ capability), `openspec/config.yaml`. **Шаг 6 обязателен, и вот почему.** `check` сверяет **число** в `.pm.json` с версией скрипта — и только его. Применена ли запись журнала **по существу**, он -не знает: проект несёт `"canon": 4` и может не иметь того, чего требовала любая +не знает: проект несёт `"canon": 6` и может не иметь того, чего требовала любая из пройденных версий. Записи применяются руками (переименовать секцию, проставить типы, дописать раздел каждому `fix`), а ручной проход по нескольким записям подряд — ровно то место, где половина шага делается и забывается. Судьи и есть diff --git a/av-dev-pm/skills/canon/references/canon.md b/av-dev-pm/skills/canon/references/canon.md index 1d4ff4e..d9552e5 100644 --- a/av-dev-pm/skills/canon/references/canon.md +++ b/av-dev-pm/skills/canon/references/canon.md @@ -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" diff --git a/av-dev-pm/skills/canon/references/changelog.md b/av-dev-pm/skills/canon/references/changelog.md index 9929b10..41e700c 100644 --- a/av-dev-pm/skills/canon/references/changelog.md +++ b/av-dev-pm/skills/canon/references/changelog.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 Канон перестал быть списком файлов и стал **списком тем ревью**. Раскладка та же, diff --git a/av-dev-pm/skills/canon/references/language.md b/av-dev-pm/skills/canon/references/language.md index 23539bc..d442985 100644 --- a/av-dev-pm/skills/canon/references/language.md +++ b/av-dev-pm/skills/canon/references/language.md @@ -134,7 +134,7 @@ | Термин | Что называет | | --- | --- | | интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции | -| триаж | ступень конвейера, сводящая находки в решение | +| триаж | стадия конвейера, сводящая находки в решение | | провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство | | дедуп, дедупликация | сверка нового против уже лежащего | | чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки | diff --git a/av-dev-pm/skills/canon/references/skeletons.md b/av-dev-pm/skills/canon/references/skeletons.md index e591d6f..a37726d 100644 --- a/av-dev-pm/skills/canon/references/skeletons.md +++ b/av-dev-pm/skills/canon/references/skeletons.md @@ -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 } ``` diff --git a/av-dev-pm/skills/canon/scripts/docs.py b/av-dev-pm/skills/canon/scripts/docs.py index 34dfc29..e0d90aa 100644 --- a/av-dev-pm/skills/canon/scripts/docs.py +++ b/av-dev-pm/skills/canon/scripts/docs.py @@ -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 diff --git a/av-dev-pm/skills/docs/SKILL.md b/av-dev-pm/skills/docs/SKILL.md index 92a6cc3..79dd02e 100644 --- a/av-dev-pm/skills/docs/SKILL.md +++ b/av-dev-pm/skills/docs/SKILL.md @@ -130,7 +130,7 @@ description: Вести содержимое документов канона Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим». Со временем теряется не факт, а то, почему дефект не поймали, — единственное, ради чего журнал есть. И решение сузить проверки (перестали звать проход, понизили -профиль) обязано попасть в раздел настройки, а не остаться в отчёте ревью. +метка) обязано попасть в раздел настройки, а не остаться в отчёте ревью. ## Промоут в конвенции diff --git a/av-dev-pm/skills/session/references/cadence.md b/av-dev-pm/skills/session/references/cadence.md index e93c6d7..cf76cb4 100644 --- a/av-dev-pm/skills/session/references/cadence.md +++ b/av-dev-pm/skills/session/references/cadence.md @@ -250,7 +250,7 @@ Здесь же последний дешёвый момент заметить **разнородную задачу**: раздел «Затрагивает» показывает границы до того, как заведено предложение об - изменении. Строка, которая одна тянет задачу на ступень выше остального + изменении. Строка, которая одна тянет задачу на метку выше остального перечня, — кандидат на разрез (шов — в `tasks`, `references/split.md`). Замеченная здесь, она стоит одного `edit`; замеченная на ревью — выброшенного предложения. diff --git a/av-dev-pm/skills/tasks/SKILL.md b/av-dev-pm/skills/tasks/SKILL.md index 5d9d9d0..f63e6dd 100644 --- a/av-dev-pm/skills/tasks/SKILL.md +++ b/av-dev-pm/skills/tasks/SKILL.md @@ -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 … # разовая адап - **свойство репозитория в рамках** — номер миграции, хеш, версия зависимости: в лежалой задаче протухает молча и становится ложной рамкой. Снимается; снимок берётся при постановке, а не при заведении; -- **предписание процесса в теле** — «делать таким-то профилем ревью», «взять +- **предписание процесса в теле** — «делать с такой-то меткой ревью», «взять такой-то агент»: это второй дом для правила выбора и путь понизить требования решением, принятым до проектирования. Снимается; - **тип, разошедшийся с задачей** — задача заводилась починкой, а после разбора diff --git a/av-dev-pm/skills/tasks/references/split.md b/av-dev-pm/skills/tasks/references/split.md index 53dc115..64ab164 100644 --- a/av-dev-pm/skills/tasks/references/split.md +++ b/av-dev-pm/skills/tasks/references/split.md @@ -25,25 +25,25 @@ Тест выше говорит, **допустим** ли разрез. Где его провести из нескольких допустимых мест — отвечает шов. -**Шов — там, где падает ступень ревью.** Раздел «Затрагивает» перечисляет -границы; если одна строка перечня поднимает ступень выше остальных, эта часть и +**Шов — там, где падает метка ревью.** Раздел «Затрагивает» перечисляет +границы; если одна строка перечня поднимает метку выше остальных, эта часть и режется отдельно. Пример: задача перекладывает несколько узлов разом и заодно -добавляет два поля в существующий ответ. Целиком это `wide` — семь проходов по +добавляет два поля в существующий ответ. Целиком это `large` — семь проходов по всему диффу, включая два, что держат машину и идут цепочкой. Разрезанная по шву, -она даёт `wide` на маленькой переложенной части и `standard` на остатке. +она даёт `large` на маленькой переложенной части и `medium` на остатке. **Считай костяк, а не файлы.** У каждой задачи есть несокращаемые четыре прохода (гейт, спеки, код, триаж), и они платятся за каждую. Разрез, после которого обе -половины остаются в одной ступени, делает ревью **дороже**: тот же объём +половины остаются в одной метке, делает ревью **дороже**: тот же объём проверяется тем же составом, но костяк оплачен дважды. Отсюда правило: **резать, когда разрез снимает дорогой проход с большей части диффа**, и не резать, когда он просто делает файлы мельче. -**Это планирование, а не предписание процесса.** Ступень ревью выбирается по +**Это планирование, а не предписание процесса.** Метка ревью выбирается по факту изменения — тем, кто его видит, — и в тело задачи не пишется: строка -«делать профилем standard» это ровно тот второй дом правила выбора, который -гигиена полей снимает. Шов пользуется ступенью как **признаком**, что в задаче -две разнородные работы; решение о профиле остаётся за конвейером. +«делать с меткой medium» это ровно тот второй дом правила выбора, который +гигиена полей снимает. Шов пользуется меткой как **признаком**, что в задаче +две разнородные работы; решение о метке остаётся за конвейером. **Цель наследуется.** Все части несут `goal:` родителя: декомпозиция не меняет того, чему работа служит. Если у части цель другая — это признак, что дробили не