Files
dev-skills/av-dev-pm/skills/tasks/references/split.md
T
avandClaude Opus 5 228b6c7eee канон 4: тип записи стал единственной осью и задаёт схему
Осей было две — тип записи (goal/idea/task) и род работы (kind:<род>
тегом), — и ортогональность у них была фальшивой: из двенадцати клеток
произведения законны шесть. У цели род запрещён, у задачи обязателен, у
идеи пуст и на практике не ставится. Плюс «алгоритм работы над записью
такого типа» крепится не к task, а к fix и research, то есть к роду:
ось, к которой пишется алгоритм, и была настоящим типом. Схлопнуто в
одну ось из пяти значений: goal | feature | fix | chore | research.

Тип idea упразднён отдельно и по другой причине: он значил не род
работы, а незаполненность, а состояние типом быть не может — оно
меняется по мере того, как запись дописывают, а тип меняют командой.
Теперь состояние выводится из заполненности: research без раздела
«Вопрос» это сырьё. В спринт не берётся, как и прежняя идея, лежит в
конце категории, отбирается list --raw.

Дом типа — поле меты «Тип» первой строкой, эмодзи в H1 производна.
Прежнее «отдельного поля типа нет: два места для одного факта
разъезжаются» отменено собственным аргументом: он был против префикса
плюс поля, а при переносе дома место остаётся одно. Эмодзи стоит в H1,
а не в строке индекса, чтобы инвариант «заголовок в индексе дословно»
остался нетронутым.

Поле места названо по типу: «Секция» у цели (часть роадмапа, состояние
очереди), «Категория» у задачи (полка домена, куда её вернёт sprint
drop). Одинаковое переименование закрепило бы конфляцию; какое поле
обязательно, решает тип — то самое, ради чего затевалась правка.

Два новых обязательных раздела выросли из правил, которые были записаны
и которые нечем было проверить. «Не воспроизводится — это research, а не
fix» стояло в каноне: теперь есть раздел «Воспроизведение». Приёмка
разведки — «записанный ответ, а не изменённый код» — тоже стояла, но
sprint take требовал от research два-пять критериев с оракулами, и они
писались ради проверки; вместо них «Вопрос» и «Куда ляжет ответ».

Сортировка «по важности» из заметок не взята: она требует, чтобы кто-то
важность поддерживал, а это приоритет, от которого отказалось правило 4.
Взято только «сырьё в конец категории» — этот порядок выводится из типа
и заполненности, а не назначается человеком, и потому проверяется
машиной.

TYPE_SCHEMA кормит и body_template, и schema_verdict: иначе add кладёт
то, на чём sprint take потом откажет. check --fix мигрирует за один
проход — kind:/[goal]/[idea] в поле «Тип», эмодзи в заголовок, «Секция»
→ «Категория», сырьё в конец. Тип, которого неоткуда взять, не
угадывается: feature от chore машина не отличает, такие записи уходят в
НЕОДНОЗНАЧНО поимённо.

Попутно закрыт класс отказов в --fix: шагов, правящих мету, стало пять,
и второй, перечитавший файл с диска, стирал правку первого. Общий
stage() поверх отложенных правок; до этого корректность держалась на том,
что шагов было мало.

Устав на тип отдельным файлом — references/task-<тип>.md, пять штук:
схема, алгоритм, что видит машина и что человек. Агент task-form получил
правило «тип сходится с тем, что в записи написано» с проверяемыми
расхождениями.

Обкатано на демо-наборе из 13 записей: миграция за один проход, второй
прогон даёт ноль починок; fix без «Воспроизведения» и сырьё в спринт не
идут, годная feature берётся.

DECISIONS тема 27 (ААББ–ЛЛММ, следствия 101–104).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 10:00:05 +03:00

113 lines
9.8 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.
# Декомпозиция и мозговой штурм
Обе операции превращают одну запись в несколько (или в ноль). Разница во входе:
декомпозиция дробит **слишком крупную задачу**, штурм прорабатывает **идею**,
которая ещё не задача.
## Тест декомпозиции
Задачу можно дробить, только если части удовлетворяют **обоим** условиям:
1. **Мерджатся независимо.** Часть Б не требует, чтобы часть А была уже влита.
Есть порядок «сперва А, потом Б, иначе не собрать» → это не декомпозиция, а
план реализации: шаги остаются **внутри одного файла**.
2. **Каждая — самостоятельный шаг.** Часть, осмысленная только в комплекте с
другой, — не задача. Проверяй тестом «готова к взятию» (task-format): какую
строку «Завершения» цели двигает **именно эта часть** и какие у неё
собственные критерии приёмки. У операционных частей (`fix`, `chore`,
`research`) цели может не быть — тогда достаточно собственных критериев.
Не проходит хотя бы одно — **не дроби**. Ложная декомпозиция плодит файлы,
которые нельзя взять поодиночке, и переоценка потом склеивает их обратно.
## Где резать, если резать можно
Тест выше говорит, **допустим** ли разрез. Где его провести из нескольких
допустимых мест — отвечает шов.
**Шов — там, где падает ступень ревью.** Раздел «Затрагивает» перечисляет
границы; если одна строка перечня поднимает ступень выше остальных, эта часть и
режется отдельно. Пример: задача вводит новый пакет и заодно добавляет два поля в
существующий ответ. Целиком это `wide` — семь проходов по всему диффу. Разрезанная
по шву, она даёт `wide` на маленьком новом пакете и `standard` на остатке.
**Считай костяк, а не файлы.** У каждой задачи есть несокращаемые четыре прохода
(гейт, спеки, код, триаж), и они платятся за каждую. Разрез, после которого обе
половины остаются в одной ступени, делает ревью **дороже**: тот же объём
проверяется тем же составом, но костяк оплачен дважды. Отсюда правило: **резать,
когда разрез снимает дорогой проход с большей части диффа**, и не резать, когда
он просто делает файлы мельче.
**Это планирование, а не предписание процесса.** Ступень ревью выбирается по
факту изменения — тем, кто его видит, — и в тело задачи не пишется: строка
«делать профилем standard» это ровно тот второй дом правила выбора, который
гигиена полей снимает. Шов пользуется ступенью как **признаком**, что в задаче
две разнородные работы; решение о профиле остаётся за конвейером.
**Цель наследуется.** Все части несут `goal:` родителя: декомпозиция не меняет
того, чему работа служит. Если у части цель другая — это признак, что дробили не
по той границе, либо что часть вообще из другой работы.
## Что делать с родителем
После разделения родитель **не остаётся** третьей висящей строкой:
- части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
`REJECTED.md` здесь — не «выкинули», а именно тот след, что переживает запись:
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
наследников, а не археологией git;
- родитель осмыслен как **возможность**, а не как шаг → это цель. Тип на месте
не меняется (цель живёт в другом индексе): заводится `--type goal` в `ROADMAP.md`,
части получают `--goal <новый слаг>`, родитель закрывается с причиной-ссылкой.
**Промежуточного зонтика между целью и задачей нет.** Тип `epic` упразднён:
роль зонтика играет цель, а слишком крупный шаг дробится на шаги помельче под
той же целью. Если частям нужен общий заголовок — значит у них общая
возможность, и её надо назвать целью, а не заводить временный тип.
## Когда декомпозиция случается посреди спринта
Задача, которая **оказалась крупнее задачи**, распознаётся до того, как под неё
заведено предложение об изменении: иначе его придётся выбрасывать. Она выходит
из набора (`sprint drop … --reason "крупнее задачи"`), уходит на декомпозицию, а
спринт продолжается остальными. Части заводятся сразу под той же целью, но в
текущий набор **не добавляются** — набор заморожен.
## Мозговой штурм сырья
Сырьё — запись типа `research`, у которой раздел «Вопрос» пуст: она не проходит
тест «готова к взятию», потому что неясно, что именно делаем. Штурм проясняет —
и это **generative-операция, а не applicative**.
Исход штурма и есть заполненный «Вопрос» (тогда разведку можно брать в спринт)
или набор задач с типами, которые из ответа следуют. Третий законный исход —
`close --reason`.
Applicative-штурм («перечисли задачи, следующие из идеи») выдаёт очевидное:
перечисляется то, что уже видно в формулировке. Ценное — на уровень выше.
1. **Сперва — формы, а не задачи.** Предложи **три разные постановки** идеи и
назови **компромисс каждой**: что она даёт, чем платит, что оставляет за
бортом. Если получилась одна постановка — штурм не состоялся, это
applicative.
2. **Вынеси формы пользователю** через `AskUserQuestion` с компромиссами. Рамку
выбирает он: это продуктовое решение, не механика.
3. **Назови цель.** Выбранная форма служит какой-то цели — существующей или
новой. Идея, для которой цель не находится, скорее всего уезжает в
`REJECTED.md`, а не заводится задачей.
4. **Только выбранную форму** дроби по тесту декомпозиции выше и проставь
критерии приёмки: без них наследники останутся идеями под другим именем.
**«Выкинуть» — полноправный исход штурма, а не его неудача.** Проработка, честно
показавшая, что пользы нет или она несоразмерна цене, — это результат: идея
уезжает с этой самой причиной, и та причина гасит её повторное появление.
## Доклад
- Идея/задача на входе, выбранная рамка (для штурма), задачи-наследники со
слагами, целями и секциями.
- Судьба родителя: удалён / стал целью / выкинут с причиной.
- `tasks.py check` после правок.
- Границы покрытия: какие постановки рассмотрены и какие сознательно отброшены —
чтобы штурм не пришлось повторять с нуля.