Канон 5 объявил «каждый документ docs/ — тема ревью». Правило верно ровно наполовину и потому вредно целиком. Паспорт и схему хранилища ревью читает, но темами они не являются: по ним нельзя сказать «в этом изменении сделано не так», они задают границу, по которой судит чужая тема. Журнал решений и журнал наблюдений ревью изменения не нужны вовсе — ADR объясняет прошлое, а не предъявляет требование. Разметчик, применявший правило буквально, обязан был либо завести фантомные темы passport, adr, database, research и продублировать ими работу architecture и operations, либо потерять четыре документа молча; случались обе ветки, и в собственном образце плана docs/passport.md не попадал ни строкой, а обязательная арифметика покрытия при этом не сходилась. Категорий теперь три, разрез проверяемый. Тема — да, прямо: conventions, security, architecture и любой свой документ проекта. Источник темы — нет, но он задаёт границу для чужой: passport, database, CLAUDE.md, openspec/specs. Процессный — нет, он про то, как мы работаем: tasks, review, adr, research, .pm.json. Открыта одна категория из трёх, две другие перечислены поимённо, так что документ вне раскладки — однозначно своя тема. adr и research прогон больше не открывает ни одним проходом; docs/review остаётся читаемым, но как настройка конвейера, а не критерий. Цена записана и стала обязательной строкой границ покрытия: расхождение с записанным решением ловит теперь только сверка документации, а число под находкой обязано быть снято на этом прогоне, с приложенной командой. Классификация выдаёт задаче метку — small, medium, large. Прежние quick, standard и wide назывались ступенью и описывали ревью: как глубоко смотрим. Классифицируется же задача, и пока величина называлась свойством прогона, её естественно было пересчитывать на каждом прогоне — что конвейер и делал. Слово «ступень» удалено, а не оставлено синонимом: два имени одной вещи расходятся. Выводится метка из двух разведённых осей — размер (малое, среднее, крупное) и сложность (знакомое, незнакомое), — и равна максимуму по ним. Метка не синоним размера: малое незнакомое изменение получает large, трогая один узел, поэтому план печатает три строки с обоснованием каждая и выводить одну из другой запрещено. Оси остались русскими словами — это суждение прозой; метка английская — это идентификатор, который проходы сравнивают. Разметка переехала из ревью кода в шаг 4 пайплайна, сразу после propose. Она шла первым проходом каждого ревью кода, а перед ревью дизайна ту же величину называл сам пайплайн — то есть оркестратор, который только что довёл предложение до propose. Одно и то же измерялось дважды, и один из двух раз без разведённости с автором, ровно в той точке, ради которой разметчик заведён. Теперь запуск один на задачу, диффа он не видит, план обслуживает обе стадии, и метка после кода не пересматривается: расхождение факта с разметкой ловит журнал дефектов постфактум, как и всякую другую ошибку выбора. На диск план не пишется — четвёртый артефакт рядом с proposal, tasks и design пережил бы задачу и разошёлся бы с ней молча. Ревью дизайна тоже растёт меткой: small — specs, medium — плюс rubric, large — плюс architecture и вопрос автору о трёх формах решения. Раньше rubric и architecture включались одним условием, и medium получал ровно один проход, то есть не отличался от quick ничем. Разведены они потому, что зарабатывают на разном: рубрика порождает свойства узла и окупается уже на среднем изменении, её выход уезжает приёмочными критериями в tasks.md; архитектура отвечает на вопрос про второй способ, а он на среднем знакомом изменении отвечается «нет» ещё до запуска. small подешевел тремя способами сразу. Составом: приёмник тем не запускается, три темы ядра переходят к code сверкой по записанным инвариантам CLAUDE.md с потолком в одну находку, и это не «глубина ниже», а другой дом темы. Входом: specs читает только дельта-спеку, code — только индекс конвенций. Потолком: он появился у каждого опиниативного прохода, а не у одного basics, и у половин code он раздельный, потому что конвенционных находок больше по построению и в общем списке они вытеснили бы техническую половину. Сработавший потолок обязан быть объявлен строкой — молчащий срез неотличим от «больше не нашлось». Отрицательный тест small от этого стал жёстче, а не мягче: вопросы про обратимость миграции задавал приёмник тем, и на этой метке их не задаст никто. Пайплайн задачи вырос до двенадцати шагов. Тривиальность перестала решать состав ревью — она влияет только на explore; глубину обеих стадий называет метка. Проверено прогоном ревьюверов по готовому результату: девять расхождений найдено и починено — контракт находок печатал старый перечень проходов вместо плана по темам, три ссылки в task-batch указывали на шаг коммита вместо закрытия, запись changelog не переводила вопросы, адресованные passport и database, ops и adversary утверждали, что на нижних метках их вопросы задаёт basics, шаблон покрытия в review-code зашивал потолки small намертво, триггеры метки рассыпались на два списка против трёх, тема из директивы CLAUDE.md могла остаться без запуска исполнителя. Гейт зелёный: фронтматтеры, копии, одиннадцать диаграмм, ruff, pyrefly; docs.py прогнан на живом фикстуре и печатает категорию в отказе. Канон повышен до версии 6 с записью, выполнимой upgrade. Решения — 40–44. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
25 KiB
Сессия: четыре шага
Одна сессия между спринтами. Порядок шагов — зависимость, а не список: переоценивать задачи, не разобрав вопросы, значит переоценивать вслепую; набирать спринт, не переоценив, значит набирать из протухшего.
Начинается сессия с tasks.py check (и check --fix, если дрейф накопился) —
результат идёт строкой в доклад.
Шаг 1. Разбор вопросов
tasks.py list --questions — всё, что накопилось. Вопрос это решение человека,
и разбирается он пачкой, а не по одному, как только возник: по одному —
это дёрганье, пачкой — это сессия.
Порядок по каждому вопросу:
- Проверь, не отвечен ли он уже — решением, документом, соседним изменением, самим ходом прошедшего спринта. Отвеченный вопрос не выносится человеку: это самая частая находка и она не требует ничьего решения.
- Сформулируй развилку с вариантами и последствием каждого, рекомендация — первым вариантом.
- Вынеси пачкой через
AskUserQuestion, не больше трёх за раз. - Запиши ответ в тело задачи, опустоши раздел «Вопросы», сними тег
(
edit <slug> --rm-tag question), перепиши «зачем»: «Решено: …» на вопрос «почему это лежит в беклоге» уже не отвечает. Опустошение раздела — не уборка, а условие взятия: правило и причина в скиллеtasks, references/task-format.md.
Вопросы на задачах-кандидатах разбираются вне очереди порции — здесь же, на этой сессии, даже если сама задача в порцию переоценки не попала. Иначе правило «задача с открытым вопросом в набор не берётся» создаёт стимул вопрос не записывать, лишь бы не вычеркнуть задачу из ближайшего спринта.
Шаг 2. Разбор прошедшего спринта — про процесс, а не про задачи
Не «что мы сделали» (это доклад спринта, он уже был), а:
- что сломалось в процессе и почему не поймали — промах, доехавший до конца;
- что оказалось дороже, чем выглядело при заведении — не число, а сам факт и причина: чего не было видно в постановке;
- какие правила не сработали или сработали не так — в том числе правила этого плагина.
Замеров процесс не ведёт намеренно: оценки в очках и velocity не взяты («Почему не Scrum»), а спринт ограничен объёмом, а не временем — сравнивать «сколько заняло» не с чем. Разбор здесь качественный, и это не упущение.
Артефакт обязателен. Вывод, оставшийся в контексте сессии, не существует:
следующая сессия его не увидит. Дом у него один и известен из канона —
docs/review.md: вывод про конвейер и про то, что перестали проверять, идёт
в раздел настройки, вывод про воспроизведённый дефект — в журнал. Решение с
долгим следом — в docs/adr/.
Отдельным ритуалом ретроспектива не выделяется: процесс личный, синхронизировать некого.
Здесь же зовутся оба судьи документов — на весь канон разом, а не на пачку, отобранную работой:
doc-consistency— согласованность документов между собой и с openspec: факт в двух домах, прямое противоречие, поведение вarchitecture.mdвместо спек, ADR без парного статуса при замене, число без провенанса;doc-code-drift— сверка с кодом по закрытому перечню фактов: имя основной ветки, команды, пути, внешние зависимости поимённо, настройки с числовым значением, единые точки проекта, capability.
Раз в спринт, а не чаще. Дорог из них по-настоящему первый — doc-consistency
на opus: он сличает утверждения двух документов, и это суждение. Второй с
недавних пор на sonnet — у него закрытый перечень фактов и команда на каждый, —
но он читает репозиторий целиком, и дешёвым от смены модели не стал. Но и не
реже — спринт это ровно то, что двигает код и документы:
переименованная цель сборки, ушедшая зависимость, второй способ делать то, что
обзор объявил единственным; факт, дописанный в architecture.md, уже живущий в
CLAUDE.md. Протухшее и раздвоившееся неотличимо от свежего, и по нему принимают
решения, пока кто-нибудь не наткнётся.
Пачка — весь канон, и это не расточительство, а охват. Когда пачку отбирала работа, без присмотра оставалось ровно то, чего работа не касалась: правка, отменившая решение, живёт в одном документе, а парный статус нужен в другом. Канон мал, раз в спринт он читается целиком.
Находки обоих — обычный материал переоценки: строка на замену идёт в документ
сразу, работа больше чем на абзац становится задачей типа chore. Позвал —
скажи в докладе, кого именно позвал, и приложи границы покрытия; не позвал —
скажи и это, иначе доклад читается как «сверено».
Шаг 3. Переоценка задач
Цель — выкинуть то, что перестало быть задачей, и вернуть остальному честное состояние. Не «пересмотреть всё», а «пересмотреть порцию до конца».
Порция и правило остановки
Тридцать задач за один заход — это усталость и штамповка: последние десять получат «оставить» не потому, что живы, а потому, что сессия затянулась.
- 5–8 задач за порцию. Размер обоснован усталостью, а не пропускной способностью, и менять его не надо — надо брать несколько порций за сессию.
- Сколько порций: не меньше
⌈урожай прошедшего спринта / 8⌉. Урожай — это задачи, заведённые за спринт; при урожае в 15 это две-три порции. - Отбор порций по порядку:
- урожай спринта —
list --tag sprint:<слаг>: свежезаведённое ещё не проходило ни одной проверки на нужность. Тег на задачах проставлен автоматически при заведении — руками не метят и не вспоминают. Слаг берётся из отчётаsprint close, а не изSPRINT.md: сессия идёт после закрытия, а закрытие этот файл очищает; - дальше по залежалости —
list --stale; - по потребности — одна секция целиком, один тег (партия ревью), одна цель
(
--goal), список от пользователя.
- урожай спринта —
- Останавливайся на границе порции, даже если «ещё чуть-чуть осталось». Между порциями — промежуточный доклад.
Что делать с каждой задачей
Сперва то, что не требует ничьего решения:
- Проверь, не сделано ли уже. Задача, реализованная попутно в соседнем
изменении, — самая частая находка. Смотри код, документацию, историю коммитов
по ключевым словам. Удаление «как реализованной» деструктивно и без следа
(в
REJECTED.mdреализованные не пишутся), поэтому порог улики жёсткий:close <slug> --implementedтолько имея конкретный коммит или строку документа, закрывающие задачу, и ссылка идёт в доклад. Есть лишь косвенные признаки — не удаляй сам, вынеси в пачку вопросов. Сделана частично → задача сжимается до остатка: тело правишь редактором, заголовок и «зачем» — черезedit. - Проверь, не отменена ли решением. Документ, ADR или архивное изменение
мог закрыть вопрос иначе — тогда
close <slug> --reason "<ссылка на решение>". Задача закрывается не только коммитом. - Проверь пересечения. Две задачи об одном — содержимое в одну, вторую
close <slug> --reason "слита с <другой-слаг>". Смотри шире порции: интейк дедуплицирует новое против существующего, но никогда не пересматривает уже лежащее, и две задачи с одной причиной могут лежать рядом месяцами. - Пере-кластеризуй по общей причине. Несколько задач, оказавшихся симптомами одного дефекта, сливаются в одну — это находка, которую интейк дать не мог.
- Гигиена полей — протухшее «зачем», вопрос в прозе, снятый ответ, свойство
репозитория в рамках, предписание процесса в теле, тип, разошедшийся с
задачей, границы вместо реализации в разделе «Затрагивает». Список и правила —
в скилле
tasks. Переоценка — то самое место, где беклог добирает тип и разделы его схемы: требовать их на входе значило бы выгонять в заметки то, что должно лежать задачей, а к взятию в спринт они уже обязательны. Блок здоровьяcheckпечатает, сколько записей готово к взятию, — по этому числу и видно, добрала переоценка или нет.
Затем — то, что решает пользователь:
-
Жива ли она вообще. Контекст мог измениться: ушла зависимость, отпал сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
-
Та ли цель — и нужна ли она вообще. Приоритетов нет, и «повысить» нечего — вместо повышения задача меняет цель (
edit <slug> --goal <другой>) или входит в ближайший набор.feature, которой не находится цель, — кандидат на выход: новая возможность вне цели это возможность, которой никто не заказывал. Операционной задаче (fix,chore,research) цель не нужна, и выдумывать её здесь не надо.Отменяется и сама цель — когда замысел оказался неверен, а не когда задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую либо закрыть своей причиной, либо перевесить на другую цель, и только потом закрыть цель. Порядок и почему он такой — task-goal.md. Здесь этому и место: отмена цели это разбор её задач, а разбор задач — этот шаг.
-
Задача ли это по-прежнему. Не проходит тест «готова к взятию» →
edit <slug> --type researchи опустошённый раздел «Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач под той же целью, дальше декомпозиция. -
Переоценка по пройденному. Прошедший спринт показывает, чего на самом деле стоит такая работа. Это меняет цену других задач, и именно здесь применяется: задача, оказавшаяся заметно дороже, чем думалось, при прежней пользе — кандидат на выход. Судит человек по тому, что помнит о прошедшем спринте; замеров процесс не ведёт и оценок не хранит.
Храповик на залежавшихся
Сильно залежавшаяся задача — сигнал сама по себе: её либо ни разу не собирались
делать, либо нечем взять. Измеряй наблюдаемым — датой последней правки из git
(list --stale ставит такие первыми); счётчик «сколько сессий пережила» нигде
не хранится.
Задача из верхних строк --stale, которую и этот заход оставляет без изменений,
либо двигается (меняет цель, идёт в набор, уходит с причиной), либо остаётся с
явно записанной причиной, почему её держим (move <slug> --section <та же> --reason …). Молчаливое «оставить как есть» на давно неподвижной задаче — это
решение не принимать решение; запись причины превращает его в осознанное и не
даёт тому же вопросу всплыть на следующей сессии.
Интерактив
- Вопросы — через
AskUserQuestion, не больше трёх за раз. Порция в 5–8 задач обычно даёт больше трёх суждений — тогда веди несколько итераций по ≤3, а не по одному на задачу и не одним перегруженным запросом. - К каждому варианту — предварительное суждение, рекомендация первым вариантом: «предлагаю выкинуть, потому что …». Пользователю дешевле возразить, чем судить с нуля.
- Всё, что решается фактом (сделано / отменено / дублируется), решай сам и показывай списком в докладе, а не выноси в вопросы.
Пример одной итерации — три залежавшихся задачи, механику по ним уже разобрали:
Переоценка: 3 залежавшихся (порция по
--stale)
versii-kachestvo-repaki— версии и качество одного тайтла
- Выкинуть (рекомендую) — помечена «не боль», за полгода ни разу не возникла
- Оставить под целью
nadyozhnost-razdach- Перевести под цель
kachestvo-mediateki— там она первая в очередиbackup-sqlite— бэкап базы
- Оставить под текущей целью (рекомендую) — не сработала, но риск реальный
- Взять в ближайший набор — без бэкапа ретеншн опасен
- Выкинуть
guessit-sputnik— вынести распознавание в сервис-спутник
- Понизить до сырья (
--type research) (рекомендую) — не проходит тест «готова к взятию»- Оставить задачей
Каждый вариант несёт причину — ту самую, что уедет в --reason. Ответы применяй
сразу и, если в порции осталось ещё, следующей итерацией показывай следующие ≤3.
Шаг 4. Выбор цели и набор спринта
-
Покажи состояние проекта: секцию
Готово(что приложение уже умеет — это половина ответа на «где мы»), затемЗапланированос обоснованием очереди,Направления, и по каждой цели-кандидату — сколько под ней задач без открытых вопросов (list --goal <слаг>). Цель без готовых задач набором не станет: её сперва надо декомпозировать. -
Цель называет человек — либо называет, что цели не будет. Это продуктовое решение, а не механика: агент предлагает и объясняет, но не выбирает. Оба ответа законны, и «без цели» — такой же ответ, как слаг: спринт бывает под багфикс, под техдолг, под здоровье проекта. Спрашивается он так же, как цель, и в отдельный вопрос не выносится: это один и тот же вопрос «подо что набираем».
-
Набор собирает агент —
sprint start --goal <слаг>(илиsprint start --no-goal), затемsprint take …. Скрипт не даст взять цель, задачу с чужой целью, с открытым вопросом, без типа и без разделов, которых требует её тип (уfixэто в том числеВоспроизведение, уresearch—ВопросиКуда ляжет ответ, и сырьё поэтому не берётся вовсе). Задача без цели (fix,chore,research) берётся свободно — операционная работа входит в набор помимо его цели. В спринте без цели чужой цели нет вовсе: сверять не с чем, берётся что угодно готовое, и единственной защитой остаётся показ набора человеку. -
Набор показывается человеку до старта работ. Показ — это и есть момент заморозки: после него набор не двигается. В показе называется состав по типам — три
fixи ни однойfeatureпод целью развития это разговор про цель, а не про набор, и увидеть его надо до заморозки, а не в докладе по итогам. У набора без цели показ — единственная проверка состава: скрипту там отказывать не по чему, и «что угодно готовое» превращается в осмысленный набор только глазами человека.Здесь же последний дешёвый момент заметить разнородную задачу: раздел «Затрагивает» показывает границы до того, как заведено предложение об изменении. Строка, которая одна тянет задачу на метку выше остального перечня, — кандидат на разрез (шов — в
tasks,references/split.md). Замеченная здесь, она стоит одногоedit; замеченная на ревью — выброшенного предложения. -
Задача, которой для взятия не хватает только разделов её типа, дописывается здесь же — критерии с оракулами, перечень границ, шаги воспроизведения. Но если для этого нужен ответ человека, это вопрос, и задача в набор не идёт.
Размер — ориентир, а не закон: 5–8 задач. Можно взять больше, можно меньше — набор под цель важнее круглого числа; одна крупная задача спринтом тоже бывает.
Доклад сессии
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
- Разбор процесса: что записано и куда.
- Сверка документов с кодом: звался ли
doc-code-drift, что проверено из названного, что разошлось. - Изменения списком: удалено как реализованное (со ссылками), ушло без реализации (с причинами), понижено до сырья, слито, сменило тип или цель.
- Новый спринт: цель — или строка «без цели» с объяснением, почему (багфикс, техдолг, здоровье), — набор со слагами, дата, состав по типам.
- Границы покрытия: сколько задач не трогали и какие именно секции, теги или цели остались — иначе доклад читается как «беклог разобран».
tasks.py checkпосле правок — результат строкой.