Files
dev-skills/av-dev-pm/skills/session/references/cadence.md
T
avandClaude Opus 5 d5bee11a6b классификация задачи: три категории документов и метка вместо ступени
Канон 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>
2026-08-07 10:57:15 +03:00

278 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Сессия: четыре шага
Одна сессия между спринтами. Порядок шагов — **зависимость, а не список**:
переоценивать задачи, не разобрав вопросы, значит переоценивать вслепую; набирать
спринт, не переоценив, значит набирать из протухшего.
Начинается сессия с `tasks.py check``check --fix`, если дрейф накопился) —
результат идёт строкой в доклад.
## Шаг 1. Разбор вопросов
`tasks.py list --questions` — всё, что накопилось. Вопрос это решение человека,
и разбирается он **пачкой**, а не по одному, как только возник: по одному —
это дёрганье, пачкой — это сессия.
Порядок по каждому вопросу:
1. **Проверь, не отвечен ли он уже** — решением, документом, соседним
изменением, самим ходом прошедшего спринта. Отвеченный вопрос не выносится
человеку: это самая частая находка и она не требует ничьего решения.
2. **Сформулируй развилку** с вариантами и последствием каждого, рекомендация —
первым вариантом.
3. **Вынеси пачкой** через `AskUserQuestion`, не больше трёх за раз.
4. **Запиши ответ в тело задачи, опустоши раздел «Вопросы»**, сними тег
(`edit <slug> --rm-tag question`), **перепиши «зачем»**: «Решено:
…» на вопрос «почему это лежит в беклоге» уже не отвечает. Опустошение
раздела — не уборка, а условие взятия: правило и причина в скилле `tasks`,
[references/task-format.md](../../tasks/references/task-format.md).
**Вопросы на задачах-кандидатах разбираются вне очереди порции** — здесь же, на
этой сессии, даже если сама задача в порцию переоценки не попала. Иначе правило
«задача с открытым вопросом в набор не берётся» создаёт стимул вопрос не
записывать, лишь бы не вычеркнуть задачу из ближайшего спринта.
## Шаг 2. Разбор прошедшего спринта — про процесс, а не про задачи
Не «что мы сделали» (это доклад спринта, он уже был), а:
- **что сломалось в процессе и почему не поймали** — промах, доехавший до конца;
- **что оказалось дороже, чем выглядело при заведении** — не число, а сам факт и
причина: чего не было видно в постановке;
- **какие правила не сработали или сработали не так** — в том числе правила
этого плагина.
Замеров процесс не ведёт намеренно: оценки в очках и velocity не взяты
(«[Почему не Scrum](../SKILL.md#почему-не-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 это две-три порции.
- **Отбор порций по порядку:**
1. **урожай спринта**`list --tag sprint:<слаг>`: свежезаведённое ещё не
проходило ни одной проверки на нужность. Тег на задачах проставлен
автоматически при заведении — руками не метят и не вспоминают. **Слаг
берётся из отчёта `sprint close`, а не из `SPRINT.md`:** сессия идёт после
закрытия, а закрытие этот файл очищает;
2. дальше **по залежалости**`list --stale`;
3. по потребности — одна секция целиком, один тег (партия ревью), одна цель
(`--goal`), список от пользователя.
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
Между порциями — промежуточный доклад.
### Что делать с каждой задачей
Сперва то, что не требует ничьего решения:
1. **Проверь, не сделано ли уже.** Задача, реализованная попутно в соседнем
изменении, — самая частая находка. Смотри код, документацию, историю коммитов
по ключевым словам. Удаление «как реализованной» деструктивно и без следа
`REJECTED.md` реализованные не пишутся), поэтому порог улики жёсткий:
`close <slug> --implemented` только имея **конкретный коммит или строку
документа**, закрывающие задачу, и ссылка идёт в доклад. Есть лишь косвенные
признаки — не удаляй сам, вынеси в пачку вопросов. Сделана частично → задача
сжимается до остатка: тело правишь редактором, заголовок и «зачем» — через
`edit`.
2. **Проверь, не отменена ли решением.** Документ, ADR или архивное изменение
мог закрыть вопрос иначе — тогда `close <slug> --reason "<ссылка на
решение>"`. Задача закрывается не только коммитом.
3. **Проверь пересечения.** Две задачи об одном — содержимое в одну, вторую
`close <slug> --reason "слита с <другой-слаг>"`. Смотри **шире порции**:
интейк дедуплицирует новое против существующего, но никогда не
пересматривает уже лежащее, и две задачи с одной причиной могут лежать рядом
месяцами.
4. **Пере-кластеризуй по общей причине.** Несколько задач, оказавшихся симптомами
одного дефекта, сливаются в одну — это находка, которую интейк дать не мог.
5. **Гигиена полей** — протухшее «зачем», вопрос в прозе, снятый ответ, свойство
репозитория в рамках, предписание процесса в теле, тип, разошедшийся с
задачей, границы вместо реализации в разделе «Затрагивает». Список и правила —
в скилле `tasks`. **Переоценка — то самое место, где беклог добирает тип и
разделы его схемы:** требовать их на входе значило бы выгонять в заметки то,
что должно лежать задачей, а к взятию в спринт они уже обязательны. Блок
здоровья `check` печатает, сколько записей готово к взятию, — по этому числу
и видно, добрала переоценка или нет.
Затем — то, что решает пользователь:
6. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
7. **Та ли цель — и нужна ли она вообще.** Приоритетов нет, и «повысить» нечего
— вместо повышения задача **меняет цель** (`edit <slug> --goal <другой>`) или
входит в ближайший набор. `feature`, которой не находится цель, — кандидат
на выход: новая возможность вне цели это возможность, которой никто не
заказывал. Операционной задаче (`fix`, `chore`, `research`) цель не нужна, и
выдумывать её здесь не надо.
**Отменяется и сама цель** — когда замысел оказался неверен, а не когда
задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую
либо закрыть своей причиной, либо перевесить на другую цель, и только потом
закрыть цель. Порядок и почему он такой —
[task-goal.md](../../tasks/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель).
Здесь этому и место: отмена цели это разбор её задач, а разбор задач — этот
шаг.
8. **Задача ли это по-прежнему.** Не проходит тест «готова к взятию» → `edit
<slug> --type research` и опустошённый раздел «Вопрос», то есть сырьё; дальше
штурм. Разрослась → это несколько задач под той же целью, дальше
декомпозиция.
9. **Переоценка по пройденному.** Прошедший спринт показывает, чего на самом
деле стоит такая работа. Это меняет цену **других** задач, и именно здесь
применяется: задача, оказавшаяся заметно дороже, чем думалось, при прежней
пользе — кандидат на выход. Судит человек по тому, что помнит о прошедшем
спринте; замеров процесс не ведёт и оценок не хранит.
### Храповик на залежавшихся
Сильно залежавшаяся задача — сигнал сама по себе: её либо ни разу не собирались
делать, либо нечем взять. Измеряй наблюдаемым — датой последней правки из git
(`list --stale` ставит такие первыми); счётчик «сколько сессий пережила» нигде
не хранится.
Задача из верхних строк `--stale`, которую и этот заход оставляет без изменений,
**либо двигается (меняет цель, идёт в набор, уходит с причиной), либо остаётся с
явно записанной причиной**, почему её держим (`move <slug> --section <та же>
--reason …`). Молчаливое «оставить как есть» на давно неподвижной задаче — это
решение не принимать решение; запись причины превращает его в осознанное и не
даёт тому же вопросу всплыть на следующей сессии.
### Интерактив
- Вопросы — через `AskUserQuestion`, **не больше трёх за раз**. Порция в 5–8
задач обычно даёт больше трёх суждений — тогда веди несколько итераций по ≤3,
а не по одному на задачу и не одним перегруженным запросом.
- К каждому варианту — **предварительное суждение, рекомендация первым
вариантом**: «предлагаю выкинуть, потому что …». Пользователю дешевле
возразить, чем судить с нуля.
- Всё, что решается фактом (сделано / отменено / дублируется), решай сам и
показывай списком в докладе, а не выноси в вопросы.
Пример одной итерации — три залежавшихся задачи, механику по ним уже разобрали:
> **Переоценка: 3 залежавшихся (порция по `--stale`)**
>
> 1. `versii-kachestvo-repaki` — версии и качество одного тайтла
> - Выкинуть *(рекомендую)* — помечена «не боль», за полгода ни разу не возникла
> - Оставить под целью `nadyozhnost-razdach`
> - Перевести под цель `kachestvo-mediateki` — там она первая в очереди
> 2. `backup-sqlite` — бэкап базы
> - Оставить под текущей целью *(рекомендую)* — не сработала, но риск реальный
> - Взять в ближайший набор — без бэкапа ретеншн опасен
> - Выкинуть
> 3. `guessit-sputnik` — вынести распознавание в сервис-спутник
> - Понизить до сырья (`--type research`) *(рекомендую)* — не проходит тест «готова к взятию»
> - Оставить задачей
Каждый вариант несёт причину — ту самую, что уедет в `--reason`. Ответы применяй
сразу и, если в порции осталось ещё, следующей итерацией показывай следующие ≤3.
## Шаг 4. Выбор цели и набор спринта
1. **Покажи состояние проекта**: секцию `Готово` (что приложение уже умеет —
это половина ответа на «где мы»), затем `Запланировано` с обоснованием
очереди, `Направления`, и
по каждой цели-кандидату — сколько под ней задач без открытых вопросов
(`list --goal <слаг>`). Цель без готовых задач набором не станет: её сперва
надо декомпозировать.
2. **Цель называет человек** — либо называет, что цели не будет. Это
продуктовое решение, а не механика: агент предлагает и объясняет, но не
выбирает. **Оба ответа законны**, и «без цели» — такой же ответ, как слаг:
спринт бывает под багфикс, под техдолг, под здоровье проекта. Спрашивается он
так же, как цель, и в отдельный вопрос не выносится: это один и тот же вопрос
«подо что набираем».
3. **Набор собирает агент** — `sprint start --goal <слаг>` (или `sprint start
--no-goal`), затем `sprint take …`. Скрипт не даст взять цель, задачу с чужой
целью, с открытым вопросом, без типа и **без разделов, которых требует её
тип** (у `fix` это в том числе `Воспроизведение`, у `research` — `Вопрос` и
`Куда ляжет ответ`, и сырьё поэтому не берётся вовсе). Задача без цели (`fix`,
`chore`, `research`) берётся свободно — операционная работа входит в набор
помимо его цели. **В спринте без цели чужой цели нет вовсе**: сверять не с
чем, берётся что угодно готовое, и единственной защитой остаётся показ набора
человеку.
4. **Набор показывается человеку до старта работ.** Показ — это и есть момент
заморозки: после него набор не двигается. **В показе называется состав по
типам** — три `fix` и ни одной `feature` под целью развития это разговор про
цель, а не про набор, и увидеть его надо до заморозки, а не в докладе по
итогам. **У набора без цели показ — единственная проверка состава**: скрипту
там отказывать не по чему, и «что угодно готовое» превращается в осмысленный
набор только глазами человека.
Здесь же последний дешёвый момент заметить **разнородную задачу**: раздел
«Затрагивает» показывает границы до того, как заведено предложение об
изменении. Строка, которая одна тянет задачу на метку выше остального
перечня, — кандидат на разрез (шов — в `tasks`, `references/split.md`).
Замеченная здесь, она стоит одного `edit`; замеченная на ревью — выброшенного
предложения.
5. Задача, которой для взятия не хватает только разделов её типа, дописывается
здесь же — критерии с оракулами, перечень границ, шаги воспроизведения. Но
если для этого нужен ответ человека, это вопрос, и задача в набор не идёт.
**Размер — ориентир, а не закон:** 5–8 задач. Можно взять больше, можно меньше —
набор под цель важнее круглого числа; одна крупная задача спринтом тоже бывает.
## Доклад сессии
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
- Разбор процесса: что записано и куда.
- Сверка документов с кодом: звался ли `doc-code-drift`, что проверено из
названного, что разошлось.
- Изменения списком: удалено как реализованное (со ссылками), ушло без
реализации (с причинами), понижено до сырья, слито, сменило тип или цель.
- Новый спринт: цель — **или строка «без цели» с объяснением, почему** (багфикс,
техдолг, здоровье), — набор со слагами, дата, состав по типам.
- **Границы покрытия**: сколько задач не трогали и какие именно секции, теги или
цели остались — иначе доклад читается как «беклог разобран».
- `tasks.py check` после правок — результат строкой.