канон версии 3: роадмап, род работы, границы задачи
Три изменения одной версией, потому что все три про одно — можно ли оценить задачу, не открывая код. PLAN.md → ROADMAP.md. Слово «план» значило в репозитории три разных вещи: оглавление целей, план реализации внутри задачи и PLAN.json разовой адаптации. Переименовано целиком — ключ конфига tasks.plan → tasks.roadmap, --index roadmap, --roadmap-sections, --roadmap. Старый ключ в docs/.pm.json не игнорируется молча: скрипт останавливается кодом 3 и называет переименование, иначе проект искал бы опечатку там, где на самом деле версия канона. Род работы — тег kind:feature|fix|chore|research, вторая ось поверх типа записи. В один префикс их не свести: идея бывает про функцию, эпик функцией и является. Дом — тег, потому что теги здесь единственный механизм разметки, а list --kind работает даром; цена принята — в строку индекса род не попадает. Словарь закрыт, иначе он разъедется на bug/bugfix/fix/defect. Отдельно легализован chore: у него «что станет наблюдаемо иначе» отвечается разработчику, а раньше такие задачи либо не заводились, либо придумывали себе пользовательскую пользу — и это второе хуже, оно проходит проверку. Раздел «Затрагивает» — границы, которых изменение касается: эндпоинт, таблица и миграция, формат на диске, публичный тип пакета. Без него задача оценивается по объёму текста, а не по объёму поверхности. Механизируется только наличие непустого раздела: полноту перечня машина не видит. Род и границы требуются к взятию в спринт, а не к заведению — тот же приём, что уже работает для критериев приёмки, и по той же причине. check о пропаже только напоминает: иначе два живых проекта покраснели бы на 98 задачах, заведённых до этого решения. Плюс правила языка задач: англицизм, у которого есть русское слово, заменяется; термин не из паспорта, архитектуры или конвенций вводится строкой или не употребляется; задача, которую не удаётся сказать просто, чаще всего не одна задача. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+119
-27
@@ -48,13 +48,13 @@ description: Ведение задач и целей как каталога mar
|
||||
```
|
||||
docs/tasks/
|
||||
items/ задачи и цели файлами, <slug>.md, слаги английские
|
||||
PLAN.md оглавление целей: порядок (значим) и темы (без порядка)
|
||||
ROADMAP.md оглавление целей: порядок (значим) и темы (без порядка)
|
||||
BACKLOG.md что можно взять — только задачи, целей здесь нет
|
||||
SPRINT.md текущий спринт: цель, набор, дата
|
||||
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
|
||||
```
|
||||
|
||||
Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `PLAN.md` — то,
|
||||
Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `ROADMAP.md` — то,
|
||||
подо что берут.** Цель в спринт взять нельзя, поэтому в списке берущихся ей не
|
||||
место.
|
||||
|
||||
@@ -82,7 +82,7 @@ docs/tasks/
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
state "BACKLOG.md — что берут" as B
|
||||
state "PLAN.md — подо что берут" as P
|
||||
state "ROADMAP.md — подо что берут" as P
|
||||
state "SPRINT.md — набор спринта" as S
|
||||
state "REJECTED.md — ушла без реализации" as R
|
||||
state "записи нет — реализована" as D
|
||||
@@ -110,7 +110,7 @@ stateDiagram-v2
|
||||
|
||||
## Цели
|
||||
|
||||
**Цель — такой же файл в `items/`, тип `[goal]`**, перечисленный в `PLAN.md`:
|
||||
**Цель — такой же файл в `items/`, тип `[goal]`**, перечисленный в `ROADMAP.md`:
|
||||
либо цель из секции **порядок** — там очередь значима и обоснована прозой, — либо
|
||||
**тематическая**, в порядок не встающая («прочность слияния», «журнал и
|
||||
пересборка»). Без второй секции половина целей была бы нигде не перечислена:
|
||||
@@ -134,6 +134,78 @@ stateDiagram-v2
|
||||
разбирают, и он исчезает. Два срока жизни под одним словом разъезжаются,
|
||||
поэтому слова два.
|
||||
|
||||
## Род работы
|
||||
|
||||
**Тип записи и род работы — две оси, и путать их нельзя.** Тип отвечает «что это
|
||||
за запись» (цель, идея, эпик, задача), род — «какого рода работа»: `feature`,
|
||||
`fix`, `chore`, `research`. Одним значением на оба вопроса не ответить: идея
|
||||
бывает *про* функцию, эпик функцией *и является*.
|
||||
|
||||
- **`feature`** — снаружи появляется или меняется то, чего раньше не было.
|
||||
- **`fix`** — поведение расходится с заявленным, и расхождение воспроизводится.
|
||||
Не воспроизводится — это `research`, а не `fix`.
|
||||
- **`chore`** — обслуживание: зависимости, сборка, перенос, чистка. Наблюдаемое
|
||||
поведение не меняется, и в этом всё дело: **у `chore` тест готовности слабее
|
||||
честно**, а не молча. «Что станет наблюдаемо иначе» здесь отвечается
|
||||
разработчику («перестанет собираться два раза», «уедет последний вызов
|
||||
устаревшего API»), а не пользователю. Пока рода не было, такие задачи либо не
|
||||
заводились, либо формулировались как выдуманная польза.
|
||||
- **`research`** — исход работы знание, а не изменение системы: ответ на вопрос,
|
||||
замер, разведка. Приёмка — записанный ответ (`docs/research/`, ADR, тело
|
||||
задачи), а не изменённый код.
|
||||
|
||||
Дом рода — **тег `kind:<род>`**, а не префикс заголовка и не поле меты: теги
|
||||
здесь единственный механизм разметки, и `list --kind fix` работает даром. Цена
|
||||
известна: в строку индекса род не попадает (индексы производны), и «в наборе одни
|
||||
починки» видно командой, а не глазами по `SPRINT.md`.
|
||||
|
||||
Словарь **закрыт**. Открытый разъедется на синонимах — `bug`, `bugfix`, `fix`,
|
||||
`defect`, — и отбор по роду перестанет отвечать на свой единственный вопрос. Ни
|
||||
один род не подходит — это сигнал, что в задаче их два и её надо разделить.
|
||||
|
||||
**Род обязателен у задачи, у цели запрещён, у идеи и эпика необязателен** — идея
|
||||
получает его, когда становится задачей, а эпик исчезает после разбора, и род
|
||||
несут его части. Требуется он там, где по нему принимают решение: `sprint take`
|
||||
без рода откажет. `check` о пропаже только **напоминает** — беклог, заведённый до
|
||||
появления рода, законен, и переоформлять его «заодно» здесь не просят.
|
||||
|
||||
**Род не выбирает профиль ревью и вообще ничего не предписывает пайплайну.**
|
||||
Профиль выбирается по факту изменения, а не по роду задачи: `chore` бывает
|
||||
миграцией схемы, `fix` — правкой публичного контракта. Правило «предписание
|
||||
процесса в теле задачи снимается» родом не отменяется, а подтверждается: он
|
||||
описывает работу, а не то, как её проверять.
|
||||
|
||||
## Как написана задача
|
||||
|
||||
Два требования к тексту, и оба про то, чтобы задачу можно было **оценить, не
|
||||
открывая код**.
|
||||
|
||||
**Функции и границы, а не намерения.** Задача называет, что система начнёт
|
||||
делать, и какие границы это трогает: эндпоинт или команду, таблицу и миграцию,
|
||||
формат на диске, публичный тип пакета, внешний сервис. Перечень живёт разделом
|
||||
«Затрагивает» (форма — [references/task-format.md](references/task-format.md)) и
|
||||
требуется к взятию в спринт. Без него задача оценивается по объёму текста, а не
|
||||
по объёму поверхности, — и оценка систематически занижена ровно там, где текст
|
||||
короткий, а границ много. Названы **границы**, а не то, как они изменятся: план
|
||||
реализации живёт в предложении об изменении, а не в задаче.
|
||||
|
||||
**Предметно, но без усложнения.** Текст задачи читает человек, который решает,
|
||||
брать её или нет, и делает это по строке индекса и одному экрану тела.
|
||||
|
||||
- **англицизм, у которого есть русское слово, — заменяется**: не «зафиксить
|
||||
флоу», а «починить порядок доставки»; не «отрефакторить», а «убрать второй
|
||||
путь приёма». Английские остаются там, где они и есть имя вещи: слаг,
|
||||
`capability`, имя пакета, команда, тип в коде.
|
||||
- **термин, которого нет в паспорте, архитектуре или конвенциях проекта, вводится
|
||||
одной строкой** или не употребляется. Свой словарь у задачи — самый дешёвый
|
||||
способ сделать беклог нечитаемым для того, кто вернётся к нему через квартал.
|
||||
- **сложность формулировки — не признак сложности работы.** Задачу, которую не
|
||||
удаётся сказать просто, чаще всего не удаётся и оценить: это либо две задачи,
|
||||
либо идея.
|
||||
|
||||
Эти правила — про **язык**, а не про объём: короткая задача без границ хуже
|
||||
длинной с ними.
|
||||
|
||||
## Инструмент (`tasks.py`)
|
||||
|
||||
Пусть `tk="$CLAUDE_PLUGIN_ROOT/skills/tasks/scripts/tasks.py"`, а `D` —
|
||||
@@ -143,15 +215,15 @@ stateDiagram-v2
|
||||
```
|
||||
python3 $tk check --dir D # согласованность индексов + здоровье
|
||||
python3 $tk check --dir D --fix # + починить дрейф (секция, заголовок, дубли, «зачем», форма меты)
|
||||
python3 $tk list --dir D [--stale] [--section S] [--type T] [--tag a,b] [--goal S] [--index …] [--questions]
|
||||
python3 $tk add --dir D --slug S --title T [--type goal|idea|epic] [--section S] [--goal G] [--why «зачем»] [--tag a,b]
|
||||
python3 $tk edit S --dir D [--title T] [--why «зачем»] [--type T] [--goal G] [--add-tag a,b] [--rm-tag c]
|
||||
python3 $tk list --dir D [--stale] [--section S] [--type T] [--kind K] [--tag a,b] [--goal S] [--index …] [--questions]
|
||||
python3 $tk add --dir D --slug S --title T [--type goal|idea|epic] [--section S] [--goal G] [--kind K] [--why «зачем»] [--tag a,b]
|
||||
python3 $tk edit S --dir D [--title T] [--why «зачем»] [--type T] [--goal G] [--kind K] [--add-tag a,b] [--rm-tag c]
|
||||
python3 $tk move S --dir D --section S [--reason R] [--after S | --first]
|
||||
python3 $tk close S --dir D --reason R # в REJECTED.md + удалить (ушла без реализации)
|
||||
python3 $tk close S --dir D --implemented # просто удалить (реализована и закоммичена)
|
||||
python3 $tk reopen S --dir D --reason R # вернуть закрытую: приёмка не сошлась
|
||||
python3 $tk sprint start --goal S --dir D | take S… | drop S… --reason R | close [--dissolve --reason R]
|
||||
python3 $tk init --dir D [--sections …] [--plan-sections …] [--items …] [--backlog …] …
|
||||
python3 $tk init --dir D [--sections …] [--roadmap-sections …] [--items …] [--backlog …] …
|
||||
python3 $tk adopt scan --from … | apply --plan … # разовая адаптация, references/adopt.md
|
||||
```
|
||||
|
||||
@@ -174,13 +246,14 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
|
||||
**Мутации правят файл и индексы заодно** — руками строку индекса или мету
|
||||
не пиши, зови `add`/`edit`/`move`/`close`/`sprint`. Смена заголовка, «зачем», типа,
|
||||
цели и **тегов** — это `edit`: он держит H1, мету и индекс в синхроне.
|
||||
цели, рода работы и **тегов** — это `edit`: он держит H1, мету и индекс в синхроне.
|
||||
Снятие тега — `--rm-tag` (после ответа на вопрос снимается `question`), смена
|
||||
цели — `--goal`, он заменяет прежний `goal:*`.
|
||||
цели — `--goal`, рода — `--kind`; оба заменяют прежнее значение, а не добавляют
|
||||
второе.
|
||||
|
||||
**Переезд между индексами — следствие смены типа, а не отдельная команда.**
|
||||
`edit <slug> --type goal --section <часть плана>` переносит строку из
|
||||
`BACKLOG.md` в `PLAN.md` (и обратно `--type task --section <секция беклога>`);
|
||||
`edit <slug> --type goal --section <часть роадмапа>` переносит строку из
|
||||
`BACKLOG.md` в `ROADMAP.md` (и обратно `--type task --section <секция беклога>`);
|
||||
`move` двигает только внутри одного индекса и пишет причину. `--section` у
|
||||
`edit` работает **только** при таком переезде — иначе он отсылает к `move`,
|
||||
потому что смена секции без причины и есть тот дрейф, который потом никто не
|
||||
@@ -206,16 +279,22 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
и цель, у которой есть задачи, получает тег `decomposed`. Оба случая печатаются
|
||||
поимённо.
|
||||
|
||||
**Что механизировано, а что нет.** Критерии приёмки проверяются у задачи, взятой
|
||||
в набор (`sprint take` и `check` по задачам спринта): число пунктов — жёстко
|
||||
(меньше двух — отказ, больше пяти — замечание), наличие оракула — **эвристикой**
|
||||
по слову «оракул» в пункте. Настоящий оракул от слова «оракул» машина не
|
||||
отличает, поэтому эвристика даёт только замечание, и в докладе это называется
|
||||
как есть: «проверено число пунктов, годность оракулов — глазами».
|
||||
**Что механизировано, а что нет.** У задачи, взятой в набор (`sprint take` и
|
||||
`check` по задачам спринта), проверяются три вещи, и у каждой своя глубина:
|
||||
|
||||
- **критерии приёмки** — число пунктов жёстко (меньше двух отказ, больше пяти
|
||||
замечание), наличие оракула **эвристикой** по слову «оракул» в пункте;
|
||||
- **род работы** — жёстко: назван и из закрытого словаря;
|
||||
- **раздел «Затрагивает»** — только **наличие непустого**. Полнота перечня машине
|
||||
не видна: границу, которую забыли назвать, она от отсутствующей не отличает.
|
||||
|
||||
Настоящий оракул от слова «оракул» машина тоже не отличает, поэтому эвристика
|
||||
даёт только замечание, и в докладе это называется как есть: «проверено число
|
||||
пунктов и наличие границ, годность оракулов и полнота границ — глазами».
|
||||
|
||||
Формат файла, меты, слага, индексов и `REJECTED.md` —
|
||||
[references/task-format.md](references/task-format.md). Там же тест «готова к
|
||||
взятию» и требования к критериям приёмки.
|
||||
взятию», требования к критериям приёмки и раздел «Затрагивает».
|
||||
|
||||
## Сценарии
|
||||
|
||||
@@ -239,12 +318,14 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
(`--type goal` в «темы»), либо это сигнал, что задача никому не служит и
|
||||
заводить её не надо. У идеи цели может не быть — она проставляется, когда
|
||||
идея становится задачей.
|
||||
5. `add …`, затем допиши тело редактором: одна фраза, критерии приёмки с
|
||||
оракулами, рамки. «Зачем» отвечает «зачем нужна эта задача» — состояние,
|
||||
остаток, боль, — а не пересказывает первый абзац, и пишется **для человека**:
|
||||
не «канонизация внутри транзакции», а «тело 40 МиБ держит блокировку 5 секунд,
|
||||
соседние доставки уходят в отказ».
|
||||
6. `check`.
|
||||
5. **Род работы** — `--kind feature|fix|chore|research` (см. «Род работы»). Не
|
||||
подходит ни один — задача не одна, разбирай.
|
||||
6. `add …`, затем допиши тело редактором: одна фраза, **затрагиваемые границы**,
|
||||
критерии приёмки с оракулами, рамки. «Зачем» отвечает «зачем нужна эта
|
||||
задача» — состояние, остаток, боль, — а не пересказывает первый абзац, и
|
||||
пишется **для человека**: не «канонизация внутри транзакции», а «тело 40 МиБ
|
||||
держит блокировку 5 секунд, соседние доставки уходят в отказ».
|
||||
7. `check`.
|
||||
|
||||
### Разобрать находки аудита или ревью
|
||||
|
||||
@@ -258,7 +339,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
### Прийти в репозиторий, где задачи уже как-то ведутся
|
||||
|
||||
Разовая операция: вывести каталог задач из старой раскладки беклога, `TODO.md`,
|
||||
заметок или списка шагов в плане — [references/adopt.md](references/adopt.md).
|
||||
заметок или списка шагов роадмапа — [references/adopt.md](references/adopt.md).
|
||||
Сюда же относится переименование транслитных слагов в английские: оно делается
|
||||
**одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу.
|
||||
|
||||
@@ -291,7 +372,18 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
снимок берётся при постановке, а не при заведении;
|
||||
- **предписание процесса в теле** — «делать таким-то профилем ревью», «взять
|
||||
такой-то агент»: это второй дом для правила выбора и путь понизить требования
|
||||
решением, принятым до проектирования. Снимается.
|
||||
решением, принятым до проектирования. Снимается;
|
||||
- **род, разошедшийся с задачей** — задача заводилась починкой, а после разбора
|
||||
оказалось, что поведение никогда и не было заявлено: это `feature`, а не `fix`.
|
||||
Правится `edit <slug> --kind …`; род, оставшийся от прошлой формулировки, врёт
|
||||
ровно там, где по нему отбирают;
|
||||
- **границы, названные вместо реализации** — «переписать хранилище на новый
|
||||
драйвер» в разделе «Затрагивает» это не граница, а замысел. Границы —
|
||||
`таблица points и её миграция`, `эндпоинт POST /ingest`, `формат отпечатка на
|
||||
диске`. Переписывается перечнем;
|
||||
- **англицизм и термин из ниоткуда** — правится по ходу той же операции, что
|
||||
касается задачи (см. «Как написана задача»). Именно по ходу: беклог не
|
||||
переписывают ради языка.
|
||||
|
||||
## Переносимость
|
||||
|
||||
|
||||
Reference in New Issue
Block a user