задачи: цель упразднена, у проекта появилась стадия

Тип goal и индекс ROADMAP.md убраны: цель — зонтик над параллельными
направлениями, а у проекта на одного человека список работ линеен. Роадмап
при этом наполовину дублировал беклог, а «что уже умеет» отвечают спеки и
git log индекса. Секция «Готово» удалена, а не перенесена.

Вместо цели — ось «стадия проекта»: build (беклог это план стройки, порядок
строк значит зависимость, секция одна) и support (очередь правок, порядок
значит важность, секции — полки домена). Стадия объявляется ключом
[tasks] stage, меняется командой stage, без неё check отказывает: порядок
строк нечем прочитать.

Ушли теги goal:/decomposed, поле «Секция», раздел «Завершение», флаги
--goal и edit --section. Версия раскладки 2 → 3, перевод проекта расписан
записью журнала.
This commit is contained in:
av
2026-08-13 14:26:21 +03:00
parent 0627199a1a
commit 3849f084be
26 changed files with 1128 additions and 1485 deletions
+195 -244
View File
@@ -1,13 +1,13 @@
---
name: task-track
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Каталог отстал от версии раскладки — это скажет tasks.py check, а повышает проект скилл av-dev:canon по общему журналу версий. Расстановка приоритетов и разбор накопившегося — скилл av-dev:task-groom. Не реализует задачи — этим занимается скилл решения задачи.
description: Ведение задач как каталога markdown-файлов (одна задача = один файл в items/ + строка в BACKLOG.md). У каждой задачи есть тип (feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. У проекта есть стадия (build — беклог это план стройки, порядок строк значит зависимость; support — очередь правок, порядок значит важность). Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей, смена стадии и проверка согласованности индекса. Использовать, когда просят добавить задачу или идею, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат, объявить стадию или проверить беклог. Каталог отстал от версии раскладки — это скажет tasks.py check, а повышает проект скилл av-dev:canon по общему журналу версий. Расстановка приоритетов и разбор накопившегося — скилл av-dev:task-groom. Не реализует задачи — этим занимается скилл решения задачи.
---
# Задачи
Задачи — каталог markdown-файлов. Одна запись = один файл `items/<slug>.md` плюс
строка **ровно в одном** индексе. Скилл владеет **форматом и содержимым**:
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
Задачи — каталог markdown-файлов. Одна задача = один файл `items/<slug>.md` плюс
строка в `BACKLOG.md`. Скилл владеет **форматом и содержимым**: заводит,
редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть
важным, решает скилл `task-groom`, а этот скилл лишь даёт ему операции; и выполнением
@@ -17,57 +17,53 @@ description: Ведение задач и целей как каталога mar
Ситуация не покрыта инструкцией — решай по ним.
0. **Цель — возможность приложения, задача — шаг к ней.** Цель отвечает на «что
приложение будет уметь», её «Завершение» — наблюдаемый признак того, что уже
умеет; задача отвечает на «что для этого нужно сделать». Оценивается проект
по **поведению**, а не по внутреннему устройству, поэтому и роадмап отвечает
не «сколько работ осталось», а «что уже умеет и чего ещё не умеет».
Свойство поведения — тоже возможность: «сообщает о своём состоянии»,
«исход слияния не зависит от порядка доставки» — законные цели.
1. **Беклог гниёт с той стороны, где его пополняют.** Заведение — самая частая
операция и с худшим отказом: из одного разговора рождается пять файлов, а
переоценка потом разгребает то, чего не надо было заводить. Дедупликация и
фильтр на входе дешевле любой чистки. Заводим только то, что **не делаем
сейчас** и о потере чего пожалеем.
2. **Файл — источник истины, индексы производны.** Разошлись — неправы индексы.
0. **Стадия решает, что значит порядок строк.** Проект живёт в одной из двух
стадий, и обе ведут один и тот же беклог, но читают его по-разному.
На **стройке** (`build`) беклог это план от базы к деталям: порядок —
зависимость, «раньше нельзя». На **доработке** (`support`) беклог это очередь
правок: порядок — важность, «раньше лучше». Из этого следует остальное —
сколько у беклога секций, как его пополняют, что значит его опустошение и
нужен ли груминг. Стадия объявлена ключом `[tasks] stage`; молчание ответом
не считается, и `check` без неё отказывает.
1. **Беклог гниёт с той стороны, где его пополняют — на доработке.** Заведение
там самая частая операция и с худшим отказом: из одного разговора рождается
пять файлов, а переоценка потом разгребает то, чего не надо было заводить.
Дедупликация и фильтр на входе дешевле любой чистки: заводим только то, что
**не делаем сейчас** и о потере чего пожалеем.
**На стройке правило не применяется**, и это не послабление. Список стройки
пишется вперёд целиком — он и есть замысел, — а фильтр «не заводи то, чего не
делаешь сейчас» запретил бы написать план дальше первого шага. Дедуп остаётся
в обеих стадиях: две записи об одном плохи всегда.
2. **Файл — источник истины, индекс производен.** Разошлись — неправ индекс.
Согласованность механизируема и проверяется командой, а не вниманием: всё,
что ловит `tasks.py check`, не должно попадать ни в чек-лист, ни в промпт.
Поэтому **«зачем» живёт в мете файла**, а строка индекса его лишь
повторяет: пока поле лежало только в индексе, восстановление пропавшей
строки теряло его молча и навсегда. Единственное исключение намеренное: **в каком
индексе лежит запись, знают индексы** — поля-состояния в файле нет. И
**порядок строк в беклоге**: приоритет это свойство очереди, а не задачи, и в
файле ему места нет (правило 4).
строки теряло его молча и навсегда. Единственное исключение намеренное:
**порядок строк в беклоге**он свойство списка, а не задачи, и в файле ему
места нет (правило 4).
3. **Причина переживает запись.** Выкинутая без причины задача вернётся через
квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не
оставляет ничего, поэтому у неё есть `REJECTED.md`.
4. **Приоритет — это порядок строк, а цель есть не у всякой задачи.** Очередь
внутри секции беклога значима: **первая строка — то, что делают следующим**.
Приоритет назначает человек на груминге, машина его не выводит и не угадывает.
4. **Порядок строк — единственное, чего в файле нет.** Он значим в обеих
стадиях, и назначает его человек: на стройке — раскладывая шаги по
зависимости, на доработке — на груминге. Машина порядок не выводит и не
угадывает; всё, что она делает сама, — ставит машинную позицию в **конец**
секции и говорит об этом вслух.
Прежде здесь стояло «порядка нет, есть цель», и обосновано это было тем, что
на «что делать дальше» отвечает **набор спринта**. Набора больше нет, а
вопрос остался — и без порядка отвечать на него стало нечем.
**Дом приоритета — индекс, а не файл.** Это то же исключение из правила 2,
что и «в каком индексе лежит запись»: приоритет — свойство очереди. Положи он
в файл числом, и два соседних файла смогли бы утверждать одно и то же место,
а строка индекса — противоречить обоим.
Цель обязательна там, где она и есть содержание работы, — у **новой
возможности** (`feature`). Починка, техдолг и разведка служат
работоспособности, а не направлению, и живут без цели законно. Придуманная им
цель — то же враньё, от которого спасает тип. **Цель и приоритет —
независимые оси:** очередь может идти поперёк целей, и это законно.
**Дом порядка — индекс, а не файл.** Положи его в файл числом, и два соседних
файла смогли бы утверждать одно и то же место, а строка индекса —
противоречить обоим.
Одно место в очереди назначено **не человеком, а типом**: **сырьё**
(`research` без раздела «Вопрос») стоит в конце своей категории. Его не берут,
(`research` без раздела «Вопрос») стоит в конце своей секции. Его не берут,
и между берущимся оно каждый раз требует открыть файл, чтобы это понять. Раз
это выводится, проверяет и чинит это машина.
5. **Тип решает, что с записью можно делать.** Тип — единственная ось и первое
поле меты: от него зависит, какие разделы обязательны в теле, нужна ли цель,
берётся ли запись в работу и в каком индексе живёт её строка. Словарь закрыт;
ни один тип не подошёл — значит, в записи их два, и её надо разделить.
5. **Тип решает, что с задачей можно делать.** Тип — вторая ось и первое
поле меты: от него зависит, какие разделы обязательны в теле и берётся ли
запись в работу. Словарь закрыт; ни один тип не подошёл — значит, в записи их
два, и её надо разделить.
## Раскладка
@@ -79,55 +75,23 @@ description: Ведение задач и целей как каталога mar
```
tasks/
items/ задачи и цели файлами, <slug>.md, слаги английские
ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет
BACKLOG.md что можно взять — только задачи, целей здесь нет.
Порядок строк в секции значим: это очередь
items/ задачи файлами, <slug>.md, слаги английские
BACKLOG.md что можно взять. Порядок строк в секции значим,
и значит он разное на разных стадиях
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
```
Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `ROADMAP.md` — то,
подо что берут.** Цель в работу взять нельзя — берут её задачи, — поэтому в
списке берущихся ей не место.
**Индекс один.** `REJECTED.md` индексом не считается: он не говорит, где запись
числится, — это кладбище ушедшего.
**Четыре секции роадмапа, и последняя отвечает на половину вопроса:**
**Секции беклога называет проект**, и `check` проверяет у них ровно две вещи:
что секция есть хоть одна и что на стройке она **одна**. Смысла секции не несут
— это полки домена (`Ядро`, `Инфра`), — и подгонять их имена под свой вкус
скрипт права не имеет. Поле меты, называющее полку, зовётся **Категория**.
| Секция | Англ. | Что в ней |
| --- | --- | --- |
| `Запланировано` | `Planned` | очередь значима и обосновывается прозой рядом |
| `Направления` | `Directions` | очереди нет, тянутся долго |
| `Сопровождение` | `Operations` | чем держат проект: инструмент, процесс, эксплуатация — не возможности приложения, и потому отдельно |
| `Готово` | `Done` | достигнутые цели — строкой с датой, **без ссылки на файл**: файл удалён, поведение живёт в спеках |
**Порядок тоже канонический, и `Готово` стоит последним не из скромности.**
Достигнутое **копится**: через год этой секции больше, чем всех остальных
вместе. Стоя первой, она отодвигает за экран ровно то, ради чего роадмап
открывают чаще всего, — что делается сейчас и что дальше. Порядок проверяет
`check`, переставляет `check --fix`.
**Секции роадмапа канонические, категории беклога — нет**, и разница не в любви к
единообразию. У каждой секции роадмапа свой смысл, в достигнутое пишет сам `close`, и
роадмап, названный по-своему, читался бы только своим автором. Категории беклога
(`Ядро`, `Инфра`) смысла не несут — это полки домена, и остаются делом проекта.
Отсюда и разные имена поля меты: у цели **Секция** (часть роадмапа — состояние
очереди), у задачи **Категория** (полка домена, на которой она лежит).
Отсюда четыре правила, которые проверяет `tasks.py check`: **состав закреплён**
(чужая секция — ошибка, а не вольность), **все четыре обязаны быть** (нет
секции — нет ответа на её часть вопроса), **язык один на весь индекс**, **порядок
канонический**. `--roadmap-sections` у `init` нет: выбирать нечего.
**Заголовок секции отбит пустой строкой с обеих сторон и написан с прописной.**
Во всех индексах одинаково, включая категории беклога, которые проект называет сам.
Написание канонических секций правит `check --fix` (заодно и ссылку на секцию в
мете файлов: имя секции принадлежит заголовку индекса, файл на неё только
ссылается); отбивку и порядок он правит везде.
Оговорка про `Сопровождение`: слово `окружение` сюда не годится — в
`architecture.md` оно уже значит боевое окружение приложения, и одно слово в двух
смыслах развело бы документы канона. А `Разработка`, стоявшая тут раньше,
называла слишком много: роадмап **весь** про разработку, и секция с таким именем
не отличалась от остальных ничем.
**Заголовок секции отбит пустой строкой с обеих сторон и написан с прописной**;
отбивку правит `check --fix`. Написание секции в мете файлов он тоже правит: имя
секции принадлежит заголовку индекса, файл на неё только ссылается.
**Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (работа не может
продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и
@@ -138,53 +102,31 @@ tasks/
который переезжает с такой секцией, её надо удалить** — это единственное место,
где это сказано.
**Запись живёт в одном индексе за раз.** Индексов два, и выбирает между ними
тип: цель в роадмапе, задача в беклоге. Сменился тип — строка переезжает
(`edit --type`). Файл в `items/` при этом **не двигается**: он и есть запись,
индексы лишь показывают, где она числится и в каком порядке стоит.
**Порядок строк в беклоге — приоритет**, и он единственное, чего в файле нет
(правило 4). Отсюда следствие для всякой машинной правки индекса:
восстановленная или перенесённая строка встаёт **в конец своей секции**, и
скрипт об этом говорит. Молчаливая вставка выдала бы машинную позицию за
решение человека — а решение это его.
**Порядок строк — единственное, чего в файле нет** (правило 4). Отсюда следствие
для всякой машинной правки индекса: восстановленная или перенесённая строка
встаёт **в конец своей секции**, и скрипт об этом говорит. Молчаливая вставка
выдала бы машинную позицию за решение человека — а решение это его.
**У сделанной задачи записи не остаётся** — файл и строка удаляются (`close
--implemented`). Ей хватает коммита и документации проекта; вторая запись была
бы вторым домом для того же факта. Вопрос «что было сделано и когда» отвечается
даром: индексы лежат под git, а закрытие коммитится отдельным коммитом учёта —
даром: индекс лежит под git, а закрытие коммитится отдельным коммитом учёта —
`git log -p tasks/BACKLOG.md` отдаёт историю без отдельного журнала.
**У достигнутой цели запись остаётся, и это единственное исключение.** Файл
удаляется так же, а строка переезжает в секцию `Готово` с датой. Причина в том,
что цель — не работа, а **возможность**: «что приложение умеет» это половина
вопроса, ради которого роадмап и открывают, и стирать её вместе с файлом значит
оставить инструмент, отвечающий только «что осталось». Вторым домом это не
становится: поведение живёт в `openspec/specs/`, а роадмап отвечает **когда и в
каком порядке оно появилось** — другой вопрос. Ссылки на файл в строке нет
намеренно: файл удалён, а битая ссылка — законная ошибка `check`.
Куда запись может переехать и какой командой — весь набор переходов:
```mermaid
stateDiagram-v2
state "BACKLOG.md — что берут" as B
state "ROADMAP.md — подо что берут" as P
state "REJECTED.md — ушла без реализации" as R
state "записи нет — реализована" as D
state "ROADMAP.md, «умеет» — цель достигнута" as A
[*] --> B: add --type feature|fix|chore|research
[*] --> P: add --type goal
B --> P: edit --type goal --section
P --> B: edit --type feature|fix|chore|research --section
B --> B: move --after | --first | --section
B --> D: close --implemented
P --> A: close --implemented
B --> R: close --reason
P --> R: close --reason
D --> B: reopen --reason
R --> B: reopen --reason
A --> P: reopen --reason
```
Состояния здесь — **где числится строка**, а не где лежит файл: файл
@@ -195,87 +137,90 @@ stateDiagram-v2
Схема — **сводка**: условия и оговорки живут в тексте разделов, и при
расхождении прав текст.
## Цели
## Две стадии
**Цель — возможность приложения.** Такой же файл в `items/`, тип `goal` (🎯),
перечисленный в `ROADMAP.md`. Формулируется ответом на вопрос **«что приложение
будет уметь»**, а не названием области работ: не «Работа с чтением», а «Чтение
данных клиентами»; не «Рефакторинг слияния», а «Исход слияния не зависит от
порядка доставки».
**Стадия проекта — ось, и решает она, что значит порядок строк беклога.**
Значения два, дом — ключ `[tasks] stage` в `.av-dev.toml`.
**Свойство поведения — тоже возможность.** «Наблюдаемость» это «приложение
сообщает о своём состоянии»; «прочность слияния» это «исход не зависит от
порядка». Такие цели законны и переформулировки в функцию не требуют — требуют
только, чтобы формулировка отвечала на «что приложение делает», а не на «какую
часть кода мы трогаем».
| | `build` — стройка | `support` — доработка |
| --- | --- | --- |
| Порядок строк | зависимость: раньше **нельзя** | важность: раньше **лучше** |
| Секции | ровно одна: список от базы к деталям | полки домена, сколько нужно |
| Заведение | список пишется вперёд целиком | по одной, по мере появления |
| Пустой беклог | план исчерпан, стройка окончена | нормальное состояние |
| Груминг | не применяется; замысел сменился — план пересматривается целиком | основная гигиена, порциями по 5–8 |
| Залежалость | не считается: шаг ждёт своей очереди законно | считается, `list --stale` |
**Целью не становится работа, которой держат проект.** Состав перечислен
[в словаре сопровождения](../../shared/operations.md);
на вопрос «что приложение будет уметь» ничто из него не отвечает. Им отведена отдельная секция роадмапа,
чтобы они были видны в том же экране и при этом не читались как возможности
продукта.
**Стадия называется явно, и молчание ответом не считается.** Без неё порядок
строк нечем прочитать: переставить строку значит на стройке сломать план, а на
доработке — принять решение о важности, и это разные действия. `init --stage`
обязателен, `check` без ключа отказывает, `check --fix` его не подставляет:
какая стадия у проекта, знает человек, а подставленное умолчание соврало бы ровно
там, где по нему принимают решение.
**Граница проходит по тому, кто наблюдает, а не по теме.** «Приложение сообщает
о своём состоянии» — возможность: наблюдает пользователь сервиса, и цели место
среди прочих. «Дежурный видит состояние на одном экране» — сопровождение:
наблюдаем мы. Одна и та же наблюдаемость попадает в разные секции, и это верно —
секции отвечают на разные вопросы.
**Секций на стройке одна, и это не педантизм.** Порядок там — зависимость, и
разложенный по полкам список перестаёт быть планом: два шага из разных секций
уже не сравнить. На доработке полки законны — правки независимы, и очередь
внутри полки самостоятельна.
**Сопровождение и эксплуатация — целое и часть**, а не синонимы, и та же тема
живёт ещё в двух местах: разделе «Эксплуатация» в `architecture.md` и теме ревью
`operations`. Словарь у всех трёх общий, и дом у него один:
[shared/operations.md](../../shared/operations.md) — читается по ссылке.
Пересказывать его своими словами нельзя: три перечня «чем держат проект» уже
разъезжались на «метриках и логах» против «мониторинга».
**Переход — событие, а не настройка.** `tasks.py stage support` переносит остаток
беклога в первую новую секцию, правит «Категорию» в файлах и говорит, что порядок
с этого момента значит другое. Датой ему служит коммит: отдельного журнала ради
одной строки не заводится. Признак созревания наблюдаемый — беклог стройки
исчерпан, и `check` об этом напоминает; **запретить переход раньше скрипт не
берётся**: «приложение построено» решает человек, а не счётчик строк.
Секция выбирается так: очередь значима и обоснована прозой — `Запланировано`;
тянется долго и очереди не имеет — `Направления`; не про приложение, а про то,
чем его держат, — `Сопровождение`; в `Готово` кладёт сам `close`.
Обратный переход (`stage build`) разрешён и устроен так же. Он редок — проект
уходит на стройку заново разве что при переделке замысла целиком, — но
запрещать его было бы запретом на то, что иногда и правда случается.
- **Список задач цели выводится, а не хранится.** В теле цели — зачем она и что
считается её завершением; перечня задач там нет. Он был бы третьим индексом и
поехал бы на первой же закрытой задаче, а `check` про него не знает. Связь
однонаправленна: задача несёт тег `goal:<слаг>`, перечень даёт
`tasks.py list --goal <слаг>`.
- **Статус цели выводится.** Цель достигнута, когда у неё не осталось открытых
задач; `[x]`/`[~]` руками не ведутся, а `close` цели с живыми задачами
скрипт запретит. Достижение — `close <цель> --implemented`: файл удаляется,
строка с датой переезжает в `Готово`. Ошиблись — `reopen` вернёт файл и
**снимет строку достигнутого**, чтобы роадмап не утверждал того, чего нет. Единственная оговорка: цель без задач неотличима — «ещё не
разобрана» или «всё закрыто». Различает **тег `decomposed`** в мете
цели: он ставится, когда цель разложена на задачи. Тег, а не строка в теле —
потому что проверяется механически: `check` **напоминает** о нём у пустой цели
(замечанием, не ошибкой — неразобранная цель это законное состояние), а `check
--fix` сам проставляет его цели, у которой задачи есть.
- **Тип `[epic]` упразднён.** Он был зонтиком между целью и задачами — «задача,
которая не мерджится целиком». Зонтик теперь цель, а слишком крупный шаг просто
дробится на шаги помельче под той же целью, и промежуточному типу места не
осталось. Замер подтвердил: ноль употреблений на 97 записей двух живых
проектов. Встретился в чужом беклоге — это цель либо набор задач, и `check`
назовёт его неизвестным типом.
## Чего у задач больше нет
**Тип `goal` и `ROADMAP.md` упразднены.** Цель была зонтиком над параллельными
направлениями: она нужна там, где список работ нельзя выстроить в один порядок,
и очередь идёт поперёк направлений. У проекта, который ведёт один человек, такого
не бывает — на стройке список линеен по зависимости, на доработке правки
независимы, — и зонтик не стоял ни над чем.
Роадмап при этом отвечал на свой вопрос наполовину: «чего ещё не умеет» — это
«что осталось в беклоге», то есть пересказ второго индекса. Вторая половина, «что
уже умеет», живёт в двух домах и без него: нормативное поведение — в
`openspec/specs/`, а когда и в каком порядке оно появилось — в `git log` индекса
и коммитах задач.
Вместе с целью ушли: секция `Готово` (её ответ дают спеки и история), теги
`goal:<слаг>` и `decomposed`, поле меты `Секция` (осталась `Категория`), раздел
`Завершение` и команды `list --goal`, `edit --goal`. Встретились в проекте —
`check` назовёт их поимённо, `check --fix` снимет теги, а запись типа `goal`
оставит человеку: во что она превращается — в задачу или в ничто, — машина не
решает.
**Тип `[epic]` упразднён раньше и не вернулся.** Слишком крупный шаг дробится на
шаги помельче, стоящие в списке подряд.
## Тип записи
**Тип — единственная ось этого скилла, и он решает, что с записью можно делать.**
**Тип решает, что с задачей можно делать.** Вторая ось скилла — первая стадия.
Перечень осей всего процесса и того, чего каждая **не** решает, —
[shared/axes.md](../../shared/axes.md). Дом типа —
**поле меты `Тип` первой строкой**; эмодзи в заголовке H1 от него производна, её
ставит `add` и чинит `check --fix`.
| Тип | Обязательные разделы | Цель | В работу | Устав |
| --- | --- | --- | --- | --- |
| 🎯 `goal` | `Завершение` | — | нет | [task-goal.md](references/task-goal.md) |
| `feature` | `Затрагивает`, `Критерии приёмки` | **обязательна** | да | [task-feature.md](references/task-feature.md) |
| 🐞 `fix` | `Воспроизведение`, `Затрагивает`, `Критерии приёмки` | необязательна | да | [task-fix.md](references/task-fix.md) |
| 🧹 `chore` | `Затрагивает`, `Критерии приёмки` | нет | да | [task-chore.md](references/task-chore.md) |
| 🔬 `research` | `Вопрос`, `Куда ляжет ответ` | нет | да | [task-research.md](references/task-research.md) |
| Тип | Обязательные разделы | Устав |
| --- | --- | --- |
| `feature` | `Затрагивает`, `Критерии приёмки` | [task-feature.md](references/task-feature.md) |
| 🐞 `fix` | `Воспроизведение`, `Затрагивает`, `Критерии приёмки` | [task-fix.md](references/task-fix.md) |
| 🧹 `chore` | `Затрагивает`, `Критерии приёмки` | [task-chore.md](references/task-chore.md) |
| 🔬 `research` | `Вопрос`, `Куда ляжет ответ` | [task-research.md](references/task-research.md) |
Берутся в работу все четыре: записи, которую нельзя взять, больше не существует.
Сверх обязательных у любой задачи допустимы `Рамки` и `Вопросы`. Раздел не из
схемы своего типа — **замечание, а не ошибка**: свой раздел законная вольность
проекта, но `Воспроизведение` у `chore` почти всегда значит, что тип проставлен
не тот, и сказать об этом стоит, не запрещая.
**Осей было две, и ортогональность у них была фальшивой.** Тип записи
**Прежних осей было две, и ортогональность у них была фальшивой.** Тип записи
(`goal`/`idea`/`task`) и род работы (`kind:<род>` тегом) давали двенадцать клеток
произведения, из которых законны были шесть: у цели род запрещён, у задачи
обязателен, у идеи пуст. Плюс алгоритм работы крепится не к `task`, а к `fix` и
@@ -301,7 +246,8 @@ stateDiagram-v2
изменения, а не по типу задачи: `chore` бывает миграцией схемы, `fix` — правкой
публичного контракта. Правило «предписание процесса в теле задачи снимается»
типом не отменяется, а подтверждается: он описывает работу, а не то, как её
проверять.
проверять. **Стадия проекта их тоже не выбирает**: изменение на стройке ничем не
проще того же изменения на доработке, и метку ему по-прежнему назначает разметка.
**Одно исполнителю тип всё же говорит — каким сценарием работу вести, и то не
один.** Скилл `av-dev:code-resolve` выбирает сценарий связкой из двух
@@ -316,11 +262,10 @@ stateDiagram-v2
Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы
задачу можно было **оценить, не открывая код**.
**Заголовок отвечает на вопрос своего типа.** Вопросов три, поэтому и форм три:
**Заголовок отвечает на вопрос своего типа.** Вопросов два, поэтому и форм две:
| Тип | Отвечает на | Пример |
| --- | --- | --- |
| 🎯 `goal` | что приложение будет уметь | Соперником может быть компьютер |
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | Печатать поле одним куском кода |
| 🔬 `research` | о чём разведка | Подсказка следующего хода |
@@ -333,10 +278,6 @@ stateDiagram-v2
исход знание, а что делать — ещё неизвестно, и заголовок-действие обещал бы
решённость, которой нет.
Из этого же правила растёт разница индексов: роадмап — список возможностей,
беклог — список работ, и если заголовки перепутать формами, каждый из них
начинает читаться как другой.
`check` считает заголовки не в форме действия и печатает **число** в блоке
здоровья, не замечанием на файл: проверка эвристическая (первое слово на
`-ть`/`-ти`/`-чь`), а беклог, заведённый до правила, не переоформляют «заодно».
@@ -386,18 +327,20 @@ stateDiagram-v2
подкаталога — обычное дело.
```
python3 $tk check --dir D # согласованность индексов + здоровье
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] [--raw] [--index …] [--questions]
python3 $tk add --dir D --slug S --title T --type goal|feature|fix|chore|research [--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] [--tag a,b] [--raw] [--questions]
python3 $tk add --dir D --slug S --title T --type feature|fix|chore|research [--section S] [--why «зачем»] [--tag a,b]
python3 $tk edit S --dir D [--title T] [--why «зачем»] [--type T] [--add-tag a,b] [--rm-tag c]
python3 $tk move S --dir D [--section S] [--reason R] [--after S | --first] # без --section — текущая секция
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 ready S… --dir D # схема типа выполнена — можно брать в работу
python3 $tk init --dir D [--sections …] [--items …] [--backlog …] …
python3 $tk adopt scan --from … | apply --plan … # разовая адаптация, references/adopt.md
python3 $tk stage --dir D # показать стадию
python3 $tk stage support --dir D [--sections] # сменить стадию: секции и смысл порядка
python3 $tk init --dir D --stage build|support [--sections …] [--items …] …
python3 $tk adopt scan --from … --stage S | apply --plan … # разовая адаптация, references/adopt.md
```
**Копия.** Дом словаря — `shared/axes.md` в репозитории плагина: словарь общий
@@ -422,35 +365,32 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
<!-- /копия: коды-выхода -->
Здесь это значит: код 1 приходит **только от `check`** — найден дрейф индексов и
Здесь это значит: код 1 приходит **только от `check`** — найден дрейф индекса и
файлов, чинится `check --fix`, остаток разбирается руками. Код 3 — каталог не
найден, конфиг битый или мимо диска: чинится путём или `.av-dev.toml` в корне.
Тип — английское ключевое слово `goal` / `feature` / `fix` / `chore` /
`research` (как и прочие токены команд), у `add` **обязательное**: без него
неизвестно, какой шаблон тела класть. Текст задачи при этом русский, а эмодзи в
заголовке ставит скрипт.
Тип — английское ключевое слово `feature` / `fix` / `chore` / `research` (как и
прочие токены команд), у `add` **обязательное**: без него неизвестно, какой
шаблон тела класть. Стадия — такое же слово, `build` / `support`, и у `init` она
обязательна по той же причине: без неё неизвестно, что писать в шапке беклога и
сколько заводить секций. Текст задачи при этом русский, а эмодзи в заголовке
ставит скрипт.
**Мутации правят файл и индексы заодно** — руками строку индекса или мету
не пиши, зови `add`/`edit`/`move`/`close`/`reopen`. Смена заголовка, «зачем», типа,
цели и **тегов** — это `edit`: он держит H1 (вместе с эмодзи), мету и индекс
**Мутации правят файл и индекс заодно** — руками строку индекса или мету
не пиши, зови `add`/`edit`/`move`/`close`/`reopen`. Смена заголовка, «зачем», типа
и **тегов** — это `edit`: он держит H1 (вместе с эмодзи), мету и индекс
согласованными. Снятие тега — `--rm-tag` (после ответа на вопрос снимается
`question`), смена цели — `--goal`, типа — `--type`; оба заменяют прежнее
значение, а не добавляют второе.
`question`), смена типа — `--type`; оба заменяют прежнее значение, а не
добавляют второе.
**Переезд между индексами — следствие смены типа, а не отдельная команда.**
`edit <slug> --type goal --section <часть роадмапа>` переносит строку из
`BACKLOG.md` в `ROADMAP.md` (и обратно — задачным типом плюс
`--section <категория беклога>`);
`move` двигает только внутри одного индекса и пишет причину. `--section` у
`edit` работает **только** при таком переезде — иначе он отсылает к `move`,
потому что смена секции без причины и есть тот дрейф, который потом никто не
объяснит.
**Секцию меняет только `move`, и он пишет причину**: смена полки без причины и
есть тот дрейф, который потом никто не объяснит.
**`move --after <слаг>` и `move --first` — это и есть расстановка приоритета.**
**`move --after <слаг>` и `move --first` — это и есть расстановка порядка.**
Порядок строк в секции значим (правило 4), и двигают его только этой командой:
руками поправленная строка не оставляет причины, а причина здесь и есть половина
решения.
решения. Что именно этот порядок значит, говорит стадия: на стройке `--after`
называет зависимость, на доработке — приоритет.
Тело задачи скрипт не трогает:
`add` кладёт заголовок, мета-блок и шаблон с подсказками, тело дописываешь
@@ -461,18 +401,23 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
правки**, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся
чини `check --fix` — он детерминированно правит то, где истина однозначна (тип в
своё поле, эмодзи заголовка, имя поля места, секция, заголовок, дубли, «зачем» из
индекса в файл, старая форма меты, пометка `decomposed` у цели с задачами, сырьё
в конец категории), а неоднозначное (задача сразу в двух индексах, нечего
восстанавливать, **тип, которого неоткуда взять**) печатает отдельной пометкой
индекса в файл, старая форма меты, снятые теги упразднённых целей, сырьё
в конец секции), а неоднозначное (нечего восстанавливать, **тип, которого
неоткуда взять**, запись типа `goal`) печатает отдельной пометкой
`НЕОДНОЗНАЧНО` — это тебе, и это идёт строкой доклада. **Ссылка на исчезнувший
файл в пометку не попадает:** `--fix` её просто не трогает, и она остаётся
`ОШИБКА` обычного `check` — то есть видна, но в докладе её надо назвать отдельно.
`--fix` правит **и файлы** — там, где источник ровно один и выбирать не из чего:
тип переезжает из прежнего дома (тег `kind:`, префикс `[goal]`/`[idea]`) в поле
меты, заголовок получает эмодзи, поле места — имя по типу, «зачем», оставшееся
только в индексе, переезжает в мету, цель с задачами получает `decomposed`.
Каждый случай печатается поимённо.
меты, заголовок получает эмодзи, поле места зовётся «Категория», «зачем»,
оставшееся только в индексе, переезжает в мету, теги `goal:` и `decomposed`
снимаются. Каждый случай печатается поимённо.
**Ни стадию, ни состав секций `--fix` не трогает.** Стадию он подставить не
может — это решение человека; секции стройки не сливает — в каком порядке пойдут
строки слитых полок, знает тоже только человек, а порядок здесь и есть
содержание.
**Тип, который не выводится ниоткуда, `--fix` не угадывает.** `feature` от
`chore` машина не отличает, и подставленное наугад значение врало бы ровно там,
@@ -483,7 +428,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
работу — там, где по ней принимают решение; `check` поимённо о ней не говорит, а
считает: строка здоровья **«схема типа не выполнена: N из M»** называет число и
первые слаги, строка **«готово к взятию»** — сколько задач беклога пройдут
`ready` целиком (схема плюс цель плюс отсутствие открытого вопроса). Это две
`ready` целиком (схема плюс отсутствие открытого вопроса). Это две
разные строки, и совпадение их чисел — совпадение. У каждой части своя глубина:
- **тип** — жёстко: назван и из закрытого словаря;
@@ -491,7 +436,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
(меньше двух отказ, больше пяти замечание), наличие оракула **эвристикой** по
слову «оракул» в пункте;
- **прочие разделы схемы** (`Затрагивает`, `Воспроизведение`, `Вопрос`,
`Куда ляжет ответ`, `Завершение`) — только **наличие непустого**. Содержимое
`Куда ляжет ответ`) — только **наличие непустого**. Содержимое
машине не видно: границу, которую забыли назвать, она от отсутствующей не
отличает, а шаги, по которым ничего не воспроизводится, — от годных.
@@ -500,10 +445,10 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
разделов своего типа и число критериев, годность оракулов и полнота границ —
глазами».
Формат записи, меты, слага, индексов и `REJECTED.md`
Формат записи, меты, слага, индекса и `REJECTED.md`
[references/task-format.md](references/task-format.md); там же тест «готова к
взятию». Схема и алгоритм каждого типа — по файлу на тип:
[goal](references/task-goal.md) · [feature](references/task-feature.md) ·
[feature](references/task-feature.md) ·
[fix](references/task-fix.md) · [chore](references/task-chore.md) ·
[research](references/task-research.md).
@@ -530,9 +475,13 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
### Завести запись из диалога
1. **Фильтр.** Делаем прямо сейчас — не заводим. Не пожалеем о потере — не
заводим. Родилось три кандидата — покажи их и спроси, какие заводить: молча
заведённая пачка и есть тот самый отказ из правила 1.
0. **Посмотри стадию**`stage`. От неё зависят шаг 1 и место новой строки: на
доработке беклог пополняют по одной и с фильтром, на стройке пишут планом.
1. **Фильтр — на доработке.** Делаем прямо сейчас — не заводим. Не пожалеем о
потере — не заводим. Родилось три кандидата — покажи их и спроси, какие
заводить: молча заведённая пачка и есть тот самый отказ из правила 1.
**На стройке фильтра нет**: план пишется вперёд целиком, и «этого мы сейчас
не делаем» — не довод против шага, а описание всякого шага, кроме первого.
2. **Дедуп.** `list` плюс поиск по слагам, полю «зачем» и телам (`grep -ril`),
**включая `REJECTED.md`**. Нашлось среди живых — **дописываем в существующий
файл**, а не заводим соседний. Нашлось в `REJECTED.md` — покажи пользователю
@@ -541,7 +490,6 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
переоценки.
3. **Тип**`--type` обязателен, и он же первое содержательное решение:
- возможность приложения, а не шаг к ней → `goal`;
- снаружи появляется то, чего не было → `feature`;
- поведение расходится с заявленным и **воспроизводится**`fix`
(не воспроизводится → `research`);
@@ -550,12 +498,12 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
Не подходит ни один — в записи их два, разбирай. Не проходит тест готовности
(см. task-format) — это **сырьё**: `--type research`, раздел «Вопрос» пока
пуст, место в конце категории. Не делается одним заходом — это не эпик, а
несколько задач под одной целью: дроби сразу.
4. **Цель — если тип её требует.** У `feature` должен быть `--goal <слаг>`:
новая возможность и есть содержание цели. Подходящей нет — либо она
заводится (`--type goal`), либо перед тобой не `feature`. У `fix`, `chore` и
`research` цели может не быть вовсе, и придумывать её не надо.
пуст, место в конце секции. Не делается одним заходом — дроби на шаги
помельче и ставь их в списке подряд.
4. **Место в списке.** `add` кладёт строку в конец секции всегда. На стройке это
почти наверняка не то место: порядок там зависимость, и новый шаг чаще всего
встаёт в середину — `move <слаг> --after <слаг>`. На доработке конец списка
законен: место в очереди назначает груминг, а не заведение.
5. `add …`, затем допиши тело редактором **по схеме своего типа** — шаблон её
уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем
нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый
@@ -569,13 +517,13 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
пять файлов из одной мысли, а сорок файлов из сорока сырых находок. Защита та
же, что в самом ревью: кластеризация по причине, дедуп против живых и
`REJECTED.md`, находка без свидетельства → сырьё (`research`), а не задача, и карта кластеров
пользователю до создания файлов. Порядок, отображение серьёзности и привязка к
целям — [references/from-review.md](references/from-review.md).
пользователю до создания файлов. Порядок и отображение серьёзности
[references/from-review.md](references/from-review.md).
### Прийти в репозиторий, где задачи уже как-то ведутся
Разовая операция: вывести каталог задач из старой раскладки беклога, `TODO.md`,
заметок или списка шагов роадмапа — [references/adopt.md](references/adopt.md).
заметок или списка шагов плана — [references/adopt.md](references/adopt.md).
Сюда же относится переименование транслитных слагов в английские: оно делается
**одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу.
@@ -601,13 +549,12 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
| Проход | Что смотрит | Над чем работает |
| --- | --- | --- |
| `task-form` | заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса, связь со строкой «Завершения» цели | только `items/`, **открывает файл цели** |
| `task-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индексов; документы проекта — только как словарь |
| `task-form` | заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса | только `items/` |
| `task-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индекса; документы проекта — только как словарь |
Разделены они не по охвату, а **по глубине**. Язык проверяется по словам и
фразам, поштучно; форма записи требует понять, что задача делает, и открыть
цель, на которую она ссылается. Слитый проход одну половину делает дорогой, а
вторую — поверхностной.
фразам, поштучно; форма записи требует понять, что задача делает. Слитый проход
одну половину делает дорогой, а вторую — поверхностной.
Модель у обоих одна, `sonnet`, и это не отменяет разреза. Оба судят по
**записанному правилу** — семь пунктов формы против правил языка, — а их находка
@@ -690,18 +637,20 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
- **Версия и настройки живут в `.av-dev.toml` в корне репозитория** — версия
ключом `version`, настройки каталога секцией `[tasks]`: `dir` — где каталог
лежит, плюс **имена** файлов и заголовков, и последние только если отличаются
от умолчания. Неизвестный ключ в секции — код 3 на любой команде, так что
лишнее слово останавливает работу с задачами целиком.
лежит, `stage` — стадия проекта, плюс **имена** файлов и заголовков, и
последние только если отличаются от умолчания. Неизвестный ключ в секции — код
3 на любой команде, так что лишнее слово останавливает работу с задачами
целиком.
Дом в корне, а не внутри каталога задач, по двум причинам: настройка, лежащая
внутри настраиваемого каталога, не смогла бы сказать, **где он**; и версия
одна на весь плагин, а корень есть и у проекта без `docs/`. Прежние
`<каталог задач>/.tasks.json` и `docs/.docs.json` не читаются — увидев их,
скрипт говорит «прежняя раскладка» и зовёт `upgrade`.
- **Секции беклога** берутся из заголовков `##` индекса как есть; их количество
и названия — дело проекта (умолчание `Ядро` / `Инфра`). **В конфиге их нет** —
второй список разошёлся бы с заголовками молча.
- **Секции беклога** берутся из заголовков `##` индекса как есть; их названия —
дело проекта (умолчание `План` на стройке, `Ядро` / `Инфра` на доработке), а
количество ограничено стадией: на стройке секция одна. **В конфиге секций
нет** — второй список разошёлся бы с заголовками молча.
### Вызов из другого плагина
@@ -738,8 +687,10 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
- **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным
предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть,
под какую цель отнести, какая рамка разведки верна — решение пользователя. Слаг,
формулировка, порядок строк в индексе — механика, делаем сами.
какая рамка разведки верна, пора ли менять стадию — решение пользователя. Слаг
и формулировка — механика, делаем сами. **Порядок строк механикой не
считается** ни на одной стадии: на стройке он зависимость, на доработке
приоритет, и оба называет человек.
- **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа;
решений больше — веди **несколько итераций** диалога по ≤3, а не один
перегруженный запрос. Между итерациями применяй уже решённое.