скилл tasks: приоритет стал порядком строк в беклоге

Правило 4 переписано целиком. Было «порядка нет, есть цель», и
обосновано это было тем, что на «что делать дальше» отвечает набор
спринта. Набора нет — вопрос остался, отвечать нечем.

Приоритет — свойство очереди, а не задачи, поэтому его дом индекс: то
же исключение из правила 2, что и «в каком индексе лежит запись».
Положи его в файл числом — два соседних файла смогли бы утверждать одно
место, а строка индекса противоречить обоим. Цель и приоритет —
независимые оси: очередь может идти поперёк целей.

Расстановка — это move --after и move --first, и только они: руками
поправленная строка не оставляет причины.

Место сырья в конце секции из очереди изъято: оно производно от типа и
заполненности, его назначает машина, приоритетом оно не становится.

Схема состояний потеряла SPRINT.md и четыре перехода; шесть уставов
типов, task-format, split, from-review и adopt переведены со «взятия в
спринт» на ready.
This commit is contained in:
av
2026-08-09 16:36:26 +03:00
parent a73eedb893
commit 3653c5cff5
11 changed files with 142 additions and 138 deletions
+70 -50
View File
@@ -1,6 +1,6 @@
---
name: tasks
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Ритуал между спринтами — скилл session. Не реализует задачи — этим занимается пайплайн проекта.
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Расстановка приоритетов и разбор накопившегося — скилл groom. Не реализует задачи — этим занимается скилл решения задачи.
---
# Задачи
@@ -9,9 +9,9 @@ description: Ведение задач и целей как каталога mar
строка **ровно в одном** индексе. Скилл владеет **форматом и содержимым**:
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
Чем он **не** владеет: ритуалом между спринтами (разбор вопросов → разбор
прошедшего спринта → переоценка → выбор цели и набор) — это скилл `session`; и
выполнением задачи — это пайплайн проекта.
Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть
важным, решает скилл `groom`, а этот скилл лишь даёт ему операции; и выполнением
задачи — это пайплайн проекта.
## Шесть правил, из которых всё следует
@@ -35,27 +35,38 @@ description: Ведение задач и целей как каталога mar
Поэтому **«зачем» живёт в мете файла**, а строка индекса его лишь
повторяет: пока поле лежало только в индексе, восстановление пропавшей
строки теряло его молча и навсегда. Единственное исключение намеренное: **в каком
индексе лежит задача, знают индексы** — «в спринте» это свойство спринта, а
не файла, поля-состояния нет.
индексе лежит запись, знают индексы** — поля-состояния в файле нет. И
**порядок строк в беклоге**: приоритет это свойство очереди, а не задачи, и в
файле ему места нет (правило 4).
3. **Причина переживает запись.** Выкинутая без причины задача вернётся через
квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не
оставляет ничего, поэтому у неё есть `REJECTED.md`.
4. **Порядка нет, есть цель — но цель есть не у всякой задачи.** Приоритетов,
«повысить» и «встать раньше» нет: «что делать дальше» отвечает набор спринта,
а между спринтами порядок не нужен никому. Цель обязательна там, где она и
есть содержание работы, — у **новой возможности** (`feature`). Починка,
техдолг и разведка служат работоспособности, а не направлению, и живут без
цели законно; в набор спринта они входят помимо его цели. Придуманная им цель
— то же враньё, от которого спасает тип.
4. **Приоритет — это порядок строк, а цель есть не у всякой задачи.** Очередь
внутри секции беклога значима: **первая строка — то, что делают следующим**.
Приоритет назначает человек на груминге, машина его не выводит и не угадывает.
Единственный порядок, который в беклоге всё-таки есть, **производен от типа**,
а не назначен человеком: **сырьё** (`research` без раздела «Вопрос») стоит в
конце своей категории. Его не берут, и между берущимся оно каждый раз требует
открыть файл, чтобы это понять. Раз порядок выводится, его проверяет машина —
и приоритетом он не становится.
Прежде здесь стояло «порядка нет, есть цель», и обосновано это было тем, что
на «что делать дальше» отвечает **набор спринта**. Набора больше нет, а
вопрос остался — и без порядка отвечать на него стало нечем.
**Дом приоритета — индекс, а не файл.** Это то же исключение из правила 2,
что и «в каком индексе лежит запись»: приоритет — свойство очереди. Положи он
в файл числом, и два соседних файла смогли бы утверждать одно и то же место,
а строка индекса — противоречить обоим.
Цель обязательна там, где она и есть содержание работы, — у **новой
возможности** (`feature`). Починка, техдолг и разведка служат
работоспособности, а не направлению, и живут без цели законно. Придуманная им
цель — то же враньё, от которого спасает тип. **Цель и приоритет —
независимые оси:** очередь может идти поперёк целей, и это законно.
Одно место в очереди назначено **не человеком, а типом**: **сырьё**
(`research` без раздела «Вопрос») стоит в конце своей категории. Его не берут,
и между берущимся оно каждый раз требует открыть файл, чтобы это понять. Раз
это выводится, проверяет и чинит это машина.
5. **Тип решает, что с записью можно делать.** Тип — единственная ось и первое
поле меты: от него зависит, какие разделы обязательны в теле, нужна ли цель,
берётся ли запись в спринт и в каком индексе живёт её строка. Словарь закрыт;
берётся ли запись в работу и в каком индексе живёт её строка. Словарь закрыт;
ни один тип не подошёл — значит, в записи их два, и её надо разделить.
## Раскладка
@@ -70,14 +81,14 @@ description: Ведение задач и целей как каталога mar
tasks/
items/ задачи и цели файлами, <slug>.md, слаги английские
ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет
BACKLOG.md что можно взять — только задачи, целей здесь нет
SPRINT.md текущий спринт: цель (или её отсутствие), набор, дата
BACKLOG.md что можно взять — только задачи, целей здесь нет.
Порядок строк в секции значим: это очередь
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
```
Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `ROADMAP.md` — то,
подо что берут.** Цель в спринт взять нельзя, поэтому в списке берущихся ей не
место.
подо что берут.** Цель в работу взять нельзя — берут её задачи, — поэтому в
списке берущихся ей не место.
**Четыре секции роадмапа, и последняя отвечает на половину вопроса:**
@@ -99,7 +110,7 @@ tasks/
роадмап, названный по-своему, читался бы только своим автором. Категории беклога
(`Ядро`, `Инфра`) смысла не несут — это полки домена, и остаются делом проекта.
Отсюда и разные имена поля меты: у цели **Секция** (часть роадмапа — состояние
очереди), у задачи **Категория** (полка, в которую она вернётся из спринта).
очереди), у задачи **Категория** (полка домена, на которой она лежит).
Отсюда четыре правила, которые проверяет `tasks.py check`: **состав закреплён**
(чужая секция — ошибка, а не вольность), **все четыре обязаны быть** (нет
@@ -118,24 +129,31 @@ tasks/
называла слишком много: роадмап **весь** про разработку, и секция с таким именем
не отличалась от остальных ничем.
**Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (спринт не может
**Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (работа не может
продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и
записи в такой секции не успевают жить. Следы блокера остаются вопросами в
файлах задач распущенного спринта. Постоянно пустая секция со старой семантикой
файлах задач. Постоянно пустая секция со старой семантикой
«разбираются пачками» противоречила бы правилу «блокер эскалируется немедленно»,
поэтому `init` её заводить отказывается, а `check` о ней говорит. **Проекту,
который переезжает с такой секцией, её надо удалить** — это единственное место,
где это сказано.
**Задача живёт в одном индексе за раз.** Взята в спринт — строка переезжает из
`BACKLOG.md` в `SPRINT.md`; вышла — обратно. Файл в `items/` при этом **не
двигается**: он и есть запись, индексы лишь показывают, где она числится.
**Запись живёт в одном индексе за раз.** Индексов два, и выбирает между ними
тип: цель в роадмапе, задача в беклоге. Сменился тип — строка переезжает
(`edit --type`). Файл в `items/` при этом **не двигается**: он и есть запись,
индексы лишь показывают, где она числится и в каком порядке стоит.
**Порядок строк в беклоге — приоритет**, и он единственное, чего в файле нет
(правило 4). Отсюда следствие для всякой машинной правки индекса:
восстановленная или перенесённая строка встаёт **в конец своей секции**, и
скрипт об этом говорит. Молчаливая вставка выдала бы машинную позицию за
решение человека — а решение это его.
**У сделанной задачи записи не остаётся** — файл и строка удаляются (`close
--implemented`). Ей хватает коммита и документации проекта; вторая запись была
бы вторым домом для того же факта. Вопрос «что было в спринте N» отвечается
даром: `SPRINT.md` лежит под git, `git log -p tasks/SPRINT.md` отдаёт историю
всех наборов без отдельного журнала.
бы вторым домом для того же факта. Вопрос «что было сделано и когда» отвечается
даром: индексы лежат под git, а закрытие коммитится отдельным коммитом учёта —
`git log -p tasks/BACKLOG.md` отдаёт историю без отдельного журнала.
**У достигнутой цели запись остаётся, и это единственное исключение.** Файл
удаляется так же, а строка переезжает в секцию `Готово` с датой. Причина в том,
@@ -152,7 +170,6 @@ tasks/
stateDiagram-v2
state "BACKLOG.md — что берут" as B
state "ROADMAP.md — подо что берут" as P
state "SPRINT.md — набор спринта" as S
state "REJECTED.md — ушла без реализации" as R
state "записи нет — реализована" as D
state "ROADMAP.md, «умеет» — цель достигнута" as A
@@ -161,12 +178,9 @@ stateDiagram-v2
[*] --> P: add --type goal
B --> P: edit --type goal --section
P --> B: edit --type feature|fix|chore|research --section
B --> S: sprint take
S --> B: sprint drop --reason
S --> D: close --implemented
B --> D: close --implemented
P --> A: close --implemented
B --> R: close --reason
S --> R: close --reason
P --> R: close --reason
D --> B: reopen --reason
R --> B: reopen --reason
@@ -247,7 +261,7 @@ stateDiagram-v2
**поле меты `Тип` первой строкой**; эмодзи в заголовке H1 от него производна, её
ставит `add` и чинит `check --fix`.
| Тип | Обязательные разделы | Цель | В спринт | Устав |
| Тип | Обязательные разделы | Цель | В работу | Устав |
| --- | --- | --- | --- | --- |
| 🎯 `goal` | `Завершение` | — | нет | [task-goal.md](references/task-goal.md) |
| ✨ `feature` | `Затрагивает`, `Критерии приёмки` | **обязательна** | да | [task-feature.md](references/task-feature.md) |
@@ -270,14 +284,14 @@ stateDiagram-v2
незаполненности** — «первый, второй или третий вопрос теста готовности не
отвечается», — а состояние типом быть не может: оно меняется по мере того, как
запись дописывают, а тип меняют командой. Теперь это состояние называется честно:
`research` без раздела «Вопрос» — **сырьё**. В спринт не берётся ровно как
`research` без раздела «Вопрос» — **сырьё**. В работу не берётся ровно как
прежняя идея, лежит в конце своей категории и отбирается `list --raw`.
Словарь **закрыт**. Открытый разъедется на синонимах — `bug`, `bugfix`, `fix`,
`defect`, — и отбор по типу перестанет отвечать на свой единственный вопрос. Ни
один тип не подходит — это сигнал, что в задаче их два и её надо разделить.
**Требуется тип там, где по нему принимают решение:** `sprint take` без типа
**Требуется тип там, где по нему принимают решение:** `ready` без типа
откажет, потому что не знает, каких разделов требовать. `check` о пропаже только
**напоминает** — беклог, заведённый до появления типа, законен, и переоформлять
его «заодно» здесь не просят.
@@ -324,7 +338,7 @@ stateDiagram-v2
делать, и какие границы это трогает: эндпоинт или команду, таблицу и миграцию,
формат на диске, публичный тип пакета, внешний сервис. Перечень живёт разделом
«Затрагивает» (форма — [references/task-format.md](references/task-format.md)) и
требуется к взятию в спринт. Без него задача оценивается по объёму текста, а не
требуется к взятию в работу. Без него задача оценивается по объёму текста, а не
по объёму поверхности, — и оценка систематически занижена ровно там, где текст
короткий, а границ много. Названы **границы**, а не то, как они изменятся: план
реализации живёт в предложении об изменении, а не в задаче.
@@ -373,7 +387,7 @@ 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 | --no-goal) --dir D | take S… | drop S… --reason R | close [--dissolve --reason R]
python3 $tk ready S… --dir D # схема типа выполнена — можно брать в работу
python3 $tk init --dir D [--sections …] [--items …] [--backlog …] …
python3 $tk adopt scan --from … | apply --plan … # разовая адаптация, references/adopt.md
```
@@ -397,7 +411,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
заголовке ставит скрипт.
**Мутации правят файл и индексы заодно** — руками строку индекса или мету
не пиши, зови `add`/`edit`/`move`/`close`/`sprint`. Смена заголовка, «зачем», типа,
не пиши, зови `add`/`edit`/`move`/`close`/`reopen`. Смена заголовка, «зачем», типа,
цели и **тегов** — это `edit`: он держит H1 (вместе с эмодзи), мету и индекс
согласованными. Снятие тега — `--rm-tag` (после ответа на вопрос снимается
`question`), смена цели — `--goal`, типа — `--type`; оба заменяют прежнее
@@ -410,7 +424,12 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
`move` двигает только внутри одного индекса и пишет причину. `--section` у
`edit` работает **только** при таком переезде — иначе он отсылает к `move`,
потому что смена секции без причины и есть тот дрейф, который потом никто не
объяснит. Задача в наборе спринта тип не меняет вовсе: сперва `sprint drop`.
объяснит.
**`move --after <слаг>` и `move --first` — это и есть расстановка приоритета.**
Порядок строк в секции значим (правило 4), и двигают его только этой командой:
руками поправленная строка не оставляет причины, а причина здесь и есть половина
решения.
Тело задачи скрипт не трогает:
`add` кладёт заголовок, мета-блок и шаблон с подсказками, тело дописываешь
@@ -439,9 +458,9 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
где по нему принимают решение. Такие записи идут в `НЕОДНОЗНАЧНО`, и тип им
проставляет человек — `edit <слаг> --type …`.
**Что механизировано, а что нет.** У задачи, взятой в набор (`sprint take` и
`check` по задачам спринта), проверяется схема её типа, и у каждой части своя
глубина:
**Что механизировано, а что нет.** Схему типа проверяет `ready` на входе в
работу — там, где по ней принимают решение; `check` о недостающем только
напоминает счётчиком «готово к взятию». У каждой части своя глубина:
- **тип** — жёстко: назван и из закрытого словаря;
- **критерии приёмки** (`feature`, `fix`, `chore`) — число пунктов жёстко
@@ -602,7 +621,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
ровно там, где по нему отбирают, **и требует не тех разделов**: у брошенного
`fix` останется «Воспроизведение», которого нечем заполнить;
- **сырьё, у которого появился вопрос** — разведка обросла формулировкой, но
раздел «Вопрос» так и пуст: она числится сырьём и в спринт не берётся.
раздел «Вопрос» так и пуст: она числится сырьём и в работу не берётся.
Записывается вопрос, и `check --fix` поднимает строку из конца категории;
- **границы, названные вместо реализации** — «переписать хранилище на новый
драйвер» в разделе «Затрагивает» это не граница, а замысел. Границы —
@@ -691,6 +710,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
## Чего этот скилл не делает
Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в
работу — этим занимается пайплайн проекта. Не ведёт спринт и не проводит сессию
между спринтами — это `session`. Не решает за пользователя, что важно. Не
работу — этим занимается пайплайн проекта. **Не ведёт очередь:** что делать
следующим и что перестало быть важным — скилл `groom`, а этот даёт ему операции.
Не решает за пользователя, что важно. Не
переоформляет существующие задачи «заодно»: правится то, чего касается операция.