канон версии 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:
@@ -13,11 +13,16 @@
|
||||
|
||||
- **Секция:** ядро — вышла из спринта: остаток писал нерешённое в журнал
|
||||
- **Зачем:** порядок канонических форм берёт меньшее в 96% случаев — для накопительных это систематический недосчёт
|
||||
- **Теги:** goal:merge-robustness, sprint:2026-08-03
|
||||
- **Теги:** goal:merge-robustness, kind:fix, sprint:2026-08-03
|
||||
|
||||
При столкновении точек выигрывает более полная, но при равной полноте побеждает
|
||||
последняя доставка — а она систематически беднее первой.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
Таблица `points` и её миграция; правило слияния в приёме доставки; формат
|
||||
отпечатка состояния на диске. Публичного контракта не трогает.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- повторный прогон свёртки даёт тот же отпечаток состояния — оракул: команда сверки
|
||||
@@ -47,8 +52,11 @@
|
||||
лежало только в индексе, штатная починка дрейфа теряла его молча и
|
||||
навсегда — а это единственное, по чему задачу выбирают, не открывая.
|
||||
|
||||
- **Тело** — одна фраза «что станет наблюдаемо иначе», критерии приёмки, рамки,
|
||||
контекст, ссылки. Пишется на языке документации проекта.
|
||||
- **Тело** — одна фраза «что станет наблюдаемо иначе», затрагиваемые границы,
|
||||
критерии приёмки, рамки, контекст, ссылки. Пишется на языке документации
|
||||
проекта: предметно, без англицизмов, у которых есть русское слово, и без
|
||||
терминов, которых нет ни в паспорте, ни в архитектуре, ни в конвенциях
|
||||
(правило и его причина — в SKILL.md, раздел «Как написана задача»).
|
||||
|
||||
Мета **одной строкой через `·`** — прежняя форма. Она читается по-прежнему,
|
||||
`check` называет её дрейфом, `check --fix` переписывает списком; поле `Хук`
|
||||
@@ -59,6 +67,36 @@
|
||||
Тело — не план реализации и не спецификация: принятое и реализованное переезжает
|
||||
в документацию проекта, а файл задачи удаляется.
|
||||
|
||||
### Затрагивает
|
||||
|
||||
Перечень **границ**, которых изменение касается. Границей считается то, у чего
|
||||
есть внешняя сторона и цена изменения:
|
||||
|
||||
- эндпоинт, команда, форма ответа, код ответа;
|
||||
- таблица, поле, миграция, формат на диске, формат сообщения в очереди;
|
||||
- публичный тип или функция пакета, конфиг и его образцы;
|
||||
- внешний сервис или библиотека, чьё поведение становится нужным.
|
||||
|
||||
Ничего из этого не трогается — так и пишется: «границ не трогает, изменение
|
||||
внутри одного узла». Это ответ, а не пустой раздел.
|
||||
|
||||
**Границы, а не замысел.** «Переписать хранилище на новый драйвер» — замысел;
|
||||
`таблица points и её миграция`, `эндпоинт POST /ingest` — границы. Разница
|
||||
проверяется вопросом «это можно назвать до того, как решено *как* делать?»: если
|
||||
нет, строка описывает реализацию, и её место в предложении об изменении.
|
||||
|
||||
**Свойства репозитория сюда не пишутся** — по той же причине, что и в рамки:
|
||||
имя таблицы стабильно, номер последней миграции протухает молча. Пишется
|
||||
`таблица points и её миграция`, а не `миграция 0042`.
|
||||
|
||||
**Что из этого механизировано.** `check` и `sprint take` смотрят только на
|
||||
**наличие непустого раздела**. Полнота перечня машине не видна: границу, которую
|
||||
забыли назвать, она от отсутствующей не отличает. Раздела нет — отказ во взятии:
|
||||
оценивать нечем.
|
||||
|
||||
**У идей раздела нет** — как и критериев: границы становятся известны, когда идея
|
||||
превращается в задачу.
|
||||
|
||||
### Критерии приёмки
|
||||
|
||||
2–5 проверяемых утверждений **списком** `- …`, **у каждого назван оракул**. Не
|
||||
@@ -139,7 +177,7 @@
|
||||
`check` напоминает о нём у цели без задач замечанием — неразобранная цель
|
||||
законна и зелёного прогона не ломает; `check --fix` сам ставит его цели, у
|
||||
которой задачи есть, а цель с тегом и без задач — прямое приглашение закрыть.
|
||||
- Цель живёт в `PLAN.md` и **никогда** — в `BACKLOG.md` или `SPRINT.md`.
|
||||
- Цель живёт в `ROADMAP.md` и **никогда** — в `BACKLOG.md` или `SPRINT.md`.
|
||||
|
||||
## Слаг
|
||||
|
||||
@@ -167,7 +205,7 @@
|
||||
|
||||
| Файл | Что отвечает | Секции |
|
||||
| --- | --- | --- |
|
||||
| `PLAN.md` | какие есть цели, в какой очереди идут и почему | порядок (очередь значима) и темы (порядка нет) |
|
||||
| `ROADMAP.md` | какие есть цели, в какой очереди идут и почему | порядок (очередь значима) и темы (порядка нет) |
|
||||
| `BACKLOG.md` | что **можно взять** — только задачи | секции проекта (по умолчанию ядро/инфра) |
|
||||
| `SPRINT.md` | какая цель и какой набор под неё | одна: «Набор» |
|
||||
| `REJECTED.md` | что ушло без реализации и почему | — |
|
||||
@@ -229,6 +267,12 @@
|
||||
|
||||
- `goal:<слаг>` — цель, которой служит задача. Обязателен: задача без цели не
|
||||
попадёт ни в один спринт.
|
||||
- `kind:<род>` — род работы: `feature` | `fix` | `chore` | `research`. Словарь
|
||||
**закрыт**, значение ровно одно. Обязателен у задачи (без него `sprint take`
|
||||
откажет), у цели запрещён, у идеи и эпика необязателен. Ставится
|
||||
`add --kind` / `edit --kind`; `--kind` заменяет прежнее значение, а не
|
||||
добавляет второе. Смысл рода и почему он тегом, а не префиксом — в SKILL.md,
|
||||
раздел «Род работы».
|
||||
- `question` — в файле есть неразобранный раздел «Вопросы».
|
||||
- `sprint:<слаг>` — задача заведена в этом спринте; по нему отбирается первая
|
||||
порция разбора («урожай спринта»). **Ставится сам**: слаг спринта заводит
|
||||
@@ -248,17 +292,22 @@
|
||||
|
||||
## Тест «готова к взятию»
|
||||
|
||||
Задача готова, если из файла отвечаются три вопроса:
|
||||
Задача готова, если из файла отвечаются четыре вопроса:
|
||||
|
||||
1. **Что станет наблюдаемо иначе**, когда она сделана — снаружи: пользователю,
|
||||
владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет
|
||||
ломаться Y при Z» — ответ.
|
||||
2. **По чему видно, что закончено** — критерии приёмки с оракулами.
|
||||
3. **Какой цели она служит** — тег `goal:` и одна строка «почему именно этой».
|
||||
ломаться Y при Z» — ответ. **У `kind:chore` адресат — разработчик, и это
|
||||
законно**: «уедет последний вызов устаревшего API» — ответ, а не отговорка.
|
||||
Род объявлен как раз затем, чтобы такие задачи не выдумывали себе
|
||||
пользовательскую пользу.
|
||||
2. **Каких границ это касается** — раздел «Затрагивает». Без него задачу нельзя
|
||||
оценить: остаётся судить по длине текста.
|
||||
3. **По чему видно, что закончено** — критерии приёмки с оракулами.
|
||||
4. **Какой цели она служит** — тег `goal:` и одна строка «почему именно этой».
|
||||
|
||||
Не отвечается первый или второй вопрос → это **идея** (`[idea]`), её место в
|
||||
штурме. Не отвечается третий → либо цель есть и не проставлена, либо задача не
|
||||
служит ничему — тогда её не надо заводить.
|
||||
Не отвечается первый, второй или третий вопрос → это **идея** (`[idea]`), её
|
||||
место в штурме. Не отвечается четвёртый → либо цель есть и не проставлена, либо
|
||||
задача не служит ничему — тогда её не надо заводить.
|
||||
|
||||
Отвечается всё, но задача не делается одним заходом и не мерджится целиком →
|
||||
**эпик** (`[epic]`), сперва декомпозиция. Эпик временен и исчезает после
|
||||
|
||||
Reference in New Issue
Block a user