спринт без цели: цель стала необязательной, но не молчаливой

Цель была обязательной во всех трёх местах сразу: sprint start требовал слаг,
check считал ошибкой набор без названной цели, sprint take отказывал задаче под
чужой целью. Модель описывала только спринт развития — а спринт бывает под
багфикс, под техдолг, под здоровье проекта: такой набор собран по
работоспособности, а не по направлению, и цели у него нет не по недосмотру.

Обходной путь существовал и был хуже прямого: завести цель-пустышку «Здоровье
проекта» и вешать под неё fix-ы. Тогда ROADMAP.md — документ про то, что
приложение умеет, — обрастает строками про то, что оно не ломается, а тег goal:
перестаёт значить направление.

Теперь sprint start принимает --goal <слаг> ИЛИ --no-goal, и голое отсутствие
обоих — отказ с объяснением. Причина в стимуле: цель называет человек, и это
единственный продуктовый вопрос всей сессии. Разреши мы заводить спринт просто
без флага — забытый флаг, лень спросить и осознанное решение стали бы
неотличимы на выходе, а дешевле всего из трёх агенту именно не спрашивать. Тот
же обход записан в «Стимулы» скилла session вместе с защитой.

В спринте без цели цель не проверяется вовсе: набор берёт что угодно готовое к
взятию, включая задачи под разными целями. Правило «набор служит одной цели» не
ослаблено, оно просто не применяется — целей там не больше одной, их ноль.
Взамен машинной проверки остаётся показ набора человеку до заморозки, и в
cadence.md сказано прямо: у бесцельного спринта это единственная проверка
состава. Доклад обязан называть спринт бесцельным и объяснять, чем он был.

Признак «спринт идёт» разъехался с целью и переехал на слаг: слаг есть у любого
спринта, потому что без него нечем проставить sprint:<слаг>, то есть нечем
собрать урожай. На sprint_started() переведены sprint take, блок здоровья check
и reopen. Поле «Цель» в шапке остаётся и у бесцельного набора — пишется прозой
без ссылки: «цели нет» и «цель потерялась» обязаны различаться.

Побочно выправлен reopen: он возвращает задачу в набор идущего спринта тем же
тестом, что и sprint take (мешает только чужая цель). Прежде тест был уже —
совпадение целей, — и задача без цели, реопенутая при спринте с целью, уезжала
в беклог вопреки прозе скилла.

Тема 39 в DECISIONS.md, следствия 145-147.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-08-07 09:19:29 +03:00
co-authored by Claude Opus 5
parent 4386eb3e1c
commit 37394444e8
7 changed files with 217 additions and 69 deletions
@@ -278,12 +278,16 @@
| --- | --- | --- |
| `ROADMAP.md` | что приложение уже умеет и чего ещё не умеет | канонические и в этом порядке: `Запланировано`, `Направления`, `Сопровождение`, `Готово` (англ. `Planned`, `Directions`, `Operations`, `Done`) |
| `BACKLOG.md` | что **можно взять** — только задачи | категории проекта (по умолчанию Ядро/Инфра) |
| `SPRINT.md` | какая цель и какой набор под неё | одна: «Набор» |
| `SPRINT.md` | какая цель (или что её нет) и какой набор заморожен | одна: «Набор» |
| `REJECTED.md` | что ушло без реализации и почему | — |
Шапку `SPRINT.md` пишет `sprint start` — **тем же мета-блоком, что у задачи**:
поле на строку, `- **Цель:** [Заголовок](items/slug.md)`, `- **Начат:**` датой,
`- **Спринт:**` слагом, которым метится урожай. Прежняя форма (три поля одной
`- **Спринт:**` слагом, которым метится урожай. У спринта без цели
(`sprint start --no-goal`) поле «Цель» остаётся на месте и пишется прозой без
ссылки — «не названа»: **«цели нет» и «цель потерялась» обязаны различаться**.
Поэтому и признак «спринт идёт» — слаг, а не цель: слаг есть у любого спринта,
без него нечем метить урожай. Прежняя форма (три поля одной
строкой через `·`) читается по-прежнему и уходит сама: файл переписывается на
следующем `sprint start` и очищается на `sprint close`.