канон 4: тип записи стал единственной осью и задаёт схему

Осей было две — тип записи (goal/idea/task) и род работы (kind:<род>
тегом), — и ортогональность у них была фальшивой: из двенадцати клеток
произведения законны шесть. У цели род запрещён, у задачи обязателен, у
идеи пуст и на практике не ставится. Плюс «алгоритм работы над записью
такого типа» крепится не к task, а к fix и research, то есть к роду:
ось, к которой пишется алгоритм, и была настоящим типом. Схлопнуто в
одну ось из пяти значений: goal | feature | fix | chore | research.

Тип idea упразднён отдельно и по другой причине: он значил не род
работы, а незаполненность, а состояние типом быть не может — оно
меняется по мере того, как запись дописывают, а тип меняют командой.
Теперь состояние выводится из заполненности: research без раздела
«Вопрос» это сырьё. В спринт не берётся, как и прежняя идея, лежит в
конце категории, отбирается list --raw.

Дом типа — поле меты «Тип» первой строкой, эмодзи в H1 производна.
Прежнее «отдельного поля типа нет: два места для одного факта
разъезжаются» отменено собственным аргументом: он был против префикса
плюс поля, а при переносе дома место остаётся одно. Эмодзи стоит в H1,
а не в строке индекса, чтобы инвариант «заголовок в индексе дословно»
остался нетронутым.

Поле места названо по типу: «Секция» у цели (часть роадмапа, состояние
очереди), «Категория» у задачи (полка домена, куда её вернёт sprint
drop). Одинаковое переименование закрепило бы конфляцию; какое поле
обязательно, решает тип — то самое, ради чего затевалась правка.

Два новых обязательных раздела выросли из правил, которые были записаны
и которые нечем было проверить. «Не воспроизводится — это research, а не
fix» стояло в каноне: теперь есть раздел «Воспроизведение». Приёмка
разведки — «записанный ответ, а не изменённый код» — тоже стояла, но
sprint take требовал от research два-пять критериев с оракулами, и они
писались ради проверки; вместо них «Вопрос» и «Куда ляжет ответ».

Сортировка «по важности» из заметок не взята: она требует, чтобы кто-то
важность поддерживал, а это приоритет, от которого отказалось правило 4.
Взято только «сырьё в конец категории» — этот порядок выводится из типа
и заполненности, а не назначается человеком, и потому проверяется
машиной.

TYPE_SCHEMA кормит и body_template, и schema_verdict: иначе add кладёт
то, на чём sprint take потом откажет. check --fix мигрирует за один
проход — kind:/[goal]/[idea] в поле «Тип», эмодзи в заголовок, «Секция»
→ «Категория», сырьё в конец. Тип, которого неоткуда взять, не
угадывается: feature от chore машина не отличает, такие записи уходят в
НЕОДНОЗНАЧНО поимённо.

Попутно закрыт класс отказов в --fix: шагов, правящих мету, стало пять,
и второй, перечитавший файл с диска, стирал правку первого. Общий
stage() поверх отложенных правок; до этого корректность держалась на том,
что шагов было мало.

Устав на тип отдельным файлом — references/task-<тип>.md, пять штук:
схема, алгоритм, что видит машина и что человек. Агент task-form получил
правило «тип сходится с тем, что в записи написано» с проверяемыми
расхождениями.

Обкатано на демо-наборе из 13 записей: миграция за один проход, второй
прогон даёт ноль починок; fix без «Воспроизведения» и сырьё в спринт не
идут, годная feature берётся.

DECISIONS тема 27 (ААББ–ЛЛММ, следствия 101–104).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-08-05 10:00:05 +03:00
co-authored by Claude Opus 5
parent 069205ac69
commit 228b6c7eee
18 changed files with 1521 additions and 511 deletions
+153 -114
View File
@@ -1,19 +1,19 @@
---
name: tasks
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). Заведение задачи, идеи или цели из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, мозговой штурм идеи, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Ритуал между спринтами — скилл session. Не реализует задачи — этим занимается пайплайн проекта.
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Ритуал между спринтами — скилл session. Не реализует задачи — этим занимается пайплайн проекта.
---
# Задачи
Задачи — каталог markdown-файлов. Одна запись = один файл `items/<slug>.md` плюс
строка **ровно в одном** индексе. Скилл владеет **форматом и содержимым**:
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует идеи.
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
Чем он **не** владеет: ритуалом между спринтами (разбор вопросов → разбор
прошедшего спринта → переоценка → выбор цели и набор) — это скилл `session`; и
выполнением задачи — это пайплайн проекта.
## Пять правил, из которых всё следует
## Шесть правил, из которых всё следует
Ситуация не покрыта инструкцией — решай по ним.
@@ -43,10 +43,20 @@ description: Ведение задач и целей как каталога mar
4. **Порядка нет, есть цель — но цель есть не у всякой задачи.** Приоритетов,
«повысить» и «встать раньше» нет: «что делать дальше» отвечает набор спринта,
а между спринтами порядок не нужен никому. Цель обязательна там, где она и
есть содержание работы, — у **новой возможности** (`kind:feature`). Починка,
есть содержание работы, — у **новой возможности** (`feature`). Починка,
техдолг и разведка служат работоспособности, а не направлению, и живут без
цели законно; в набор спринта они входят помимо его цели. Придуманная им цель
— то же враньё, от которого спасает род работы.
— то же враньё, от которого спасает тип.
Единственный порядок, который в беклоге всё-таки есть, **производен от типа**,
а не назначен человеком: **сырьё** (`research` без раздела «Вопрос») стоит в
конце своей категории. Его не берут, и между берущимся оно каждый раз требует
открыть файл, чтобы это понять. Раз порядок выводится, его проверяет машина —
и приоритетом он не становится.
5. **Тип решает, что с записью можно делать.** Тип — единственная ось и первое
поле меты: от него зависят обязательные разделы тела, нужна ли цель, берётся
ли запись в спринт и в каком индексе живёт её строка. Словарь закрыт; ни один
тип не подошёл — значит, в записи их два, и её надо разделить.
## Раскладка
@@ -82,10 +92,12 @@ docs/tasks/
открывают чаще всего, — что делается сейчас и что дальше. Порядок проверяет
`check`, переставляет `check --fix`.
**Секции роадмапа канонические, секции беклога — нет**, и разница не в любви к
**Секции роадмапа канонические, категории беклога — нет**, и разница не в любви к
единообразию. У каждой секции роадмапа свой смысл, в достигнутое пишет сам `close`, и
роадмап, названный по-своему, читался бы только своим автором. Секции беклога
(`Ядро`, `Инфра`) смысла не несут — это полки, и остаются делом проекта.
роадмап, названный по-своему, читался бы только своим автором. Категории беклога
(`Ядро`, `Инфра`) смысла не несут — это полки домена, и остаются делом проекта.
Отсюда и разные имена поля меты: у цели **Секция** (часть роадмапа — состояние
очереди), у задачи **Категория** (полка, в которую она вернётся из спринта).
Отсюда четыре правила, которые проверяет `tasks.py check`: **состав закреплён**
(чужая секция — ошибка, а не вольность), **все четыре обязаны быть** (нет
@@ -93,7 +105,7 @@ docs/tasks/
канонический**. `--roadmap-sections` у `init` нет: выбирать нечего.
**Заголовок секции отбит пустой строкой с обеих сторон и написан с прописной.**
Во всех индексах одинаково, включая секции беклога, которые проект называет сам.
Во всех индексах одинаково, включая категории беклога, которые проект называет сам.
Написание канонических секций правит `check --fix` (заодно и ссылку на секцию в
мете файлов: имя секции принадлежит заголовку индекса, файл на неё только
ссылается); отбивку и порядок он правит везде.
@@ -143,10 +155,10 @@ stateDiagram-v2
state "записи нет — реализована" as D
state "ROADMAP.md, «умеет» — цель достигнута" as A
[*] --> B: add
[*] --> B: add --type feature|fix|chore|research
[*] --> P: add --type goal
B --> P: edit --type goal --section
P --> B: edit --type task --section
P --> B: edit --type feature|fix|chore|research --section
B --> S: sprint take
S --> B: sprint drop --reason
S --> D: close --implemented
@@ -169,7 +181,7 @@ stateDiagram-v2
## Цели
**Цель — возможность приложения.** Такой же файл в `items/`, тип `[goal]`,
**Цель — возможность приложения.** Такой же файл в `items/`, тип `goal` (🎯),
перечисленный в `ROADMAP.md`. Формулируется ответом на вопрос **«что приложение
будет уметь»**, а не названием области работ: не «Работа с чтением», а «Чтение
данных клиентами»; не «Рефакторинг слияния», а «Исход слияния не зависит от
@@ -226,52 +238,51 @@ stateDiagram-v2
проектов. Встретился в чужом беклоге — это цель либо набор задач, и `check`
назовёт его неизвестным типом.
## Род работы
## Тип записи
**Тип записи и род работы — две оси, и путать их нельзя.** Тип отвечает «что это
за запись» (цель, идея, задача), род — «какого рода работа»: `feature`, `fix`,
`chore`, `research`. Одним значением на оба вопроса не ответить: идея бывает
*про* функцию, а цель функцией *и является*.
**Тип — единственная ось, и он решает, что с записью можно делать.** Дом типа —
**поле меты `Тип` первой строкой**; эмодзи в заголовке H1 от него производна, её
ставит `add` и чинит `check --fix`.
- **`feature`** — снаружи появляется или меняется то, чего раньше не было.
- **`fix`** — поведение расходится с заявленным, и расхождение воспроизводится.
Не воспроизводится — это `research`, а не `fix`.
- **`chore`** — обслуживание: зависимости, сборка, перенос, чистка. Наблюдаемое
поведение не меняется, и в этом всё дело: **у `chore` тест готовности слабее
честно**, а не молча. «Что станет наблюдаемо иначе» здесь отвечается
разработчику («перестанет собираться два раза», «уедет последний вызов
устаревшего API»), а не пользователю. Пока рода не было, такие задачи либо не
заводились, либо формулировались как выдуманная польза.
- **`research`** — исход работы знание, а не изменение системы: ответ на вопрос,
замер, разведка. Приёмка — записанный ответ (`docs/research/`, ADR, тело
задачи), а не изменённый код.
| Тип | Обязательные разделы | Цель | В спринт | Устав |
| --- | --- | --- | --- | --- |
| 🎯 `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) |
Дом рода — **тег `kind:<род>`**, а не префикс заголовка и не поле меты: теги
здесь единственный механизм разметки, и `list --kind fix` работает даром. Цена
известна: в строку индекса род не попадает (индексы производны), и «в наборе одни
починки» видно командой, а не глазами по `SPRINT.md`.
Сверх обязательных у любой задачи допустимы `Рамки` и `Вопросы`. Раздел не из
схемы своего типа — **замечание, а не ошибка**: свой раздел законная вольность
проекта, но `Воспроизведение` у `chore` почти всегда значит, что тип проставлен
не тот, и сказать об этом стоит, не запрещая.
**Осей было две, и ортогональность у них была фальшивой.** Тип записи
(`goal`/`idea`/`task`) и род работы (`kind:<род>` тегом) давали двенадцать клеток
произведения, из которых законны были шесть: у цели род запрещён, у задачи
обязателен, у идеи пуст. Плюс алгоритм работы крепится не к `task`, а к `fix` и
`research` — то есть к роду. Оси схлопнуты, тег `kind:` упразднён.
**Тип `idea` упразднён вместе с ними.** Он значил не род работы, а **состояние
незаполненности** — «первый, второй или третий вопрос теста готовности не
отвечается», — а состояние типом быть не может: оно меняется по мере того, как
запись дописывают, а тип меняют командой. Теперь это состояние называется честно:
`research` без раздела «Вопрос» — **сырьё**. В спринт не берётся ровно как
прежняя идея, лежит в конце своей категории и отбирается `list --raw`.
Словарь **закрыт**. Открытый разъедется на синонимах — `bug`, `bugfix`, `fix`,
`defect`, — и отбор по роду перестанет отвечать на свой единственный вопрос. Ни
один род не подходит — это сигнал, что в задаче их два и её надо разделить.
`defect`, — и отбор по типу перестанет отвечать на свой единственный вопрос. Ни
один тип не подходит — это сигнал, что в задаче их два и её надо разделить.
**Род обязателен у задачи, у цели запрещён, у идеи необязателен** — идея получает
его, когда становится задачей. Требуется он там, где по нему принимают решение:
`sprint take` без рода откажет. `check` о пропаже только **напоминает** — беклог,
заведённый до появления рода, законен, и переоформлять его «заодно» здесь не
просят.
**Требуется тип там, где по нему принимают решение:** `sprint take` без типа
откажет, потому что не знает, каких разделов требовать. `check` о пропаже только
**напоминает** — беклог, заведённый до появления типа, законен, и переоформлять
его «заодно» здесь не просят.
**Род решает и то, обязательна ли цель.** `feature` без цели не бывает: новая
возможность и есть содержание цели, и если подходящей нет — либо она заводится,
либо это не `feature`. `fix`, `chore` и `research` живут без цели законно, и
`check` о них молчит: они служат работоспособности, а не направлению. Это
единственный случай, когда род что-то определяет за пределами отбора, — и
определяет он учёт, а не процесс проверки.
**Род не выбирает профиль ревью и вообще ничего не предписывает пайплайну.**
Профиль выбирается по факту изменения, а не по роду задачи: `chore` бывает
**Тип не выбирает профиль ревью и вообще ничего не предписывает пайплайну.**
Профиль выбирается по факту изменения, а не по типу задачи: `chore` бывает
миграцией схемы, `fix` — правкой публичного контракта. Правило «предписание
процесса в теле задачи снимается» родом не отменяется, а подтверждается: он
процесса в теле задачи снимается» типом не отменяется, а подтверждается: он
описывает работу, а не то, как её проверять.
## Как написана задача
@@ -283,17 +294,18 @@ stateDiagram-v2
| Тип | Отвечает на | Пример |
| --- | --- | --- |
| цель | что приложение будет уметь | Соперником может быть компьютер |
| задача | что нужно сделать | Печатать поле одним куском кода |
| идея | о чём она | Подсказка следующего хода |
| 🎯 `goal` | что приложение будет уметь | Соперником может быть компьютер |
| `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | Печатать поле одним куском кода |
| 🔬 `research` | о чём разведка | Подсказка следующего хода |
Задача — **глаголом в неопределённой форме**, перед ним допускается «не»: «Не
отбрасывать молча лишние символы в ходе», а не «Лишние символы молча
отбрасываются». Описательный заголовок называет **состояние**, а из состояния не
видно, чего от работы ждут: «Ничья объявляется, пока клетки есть» одинаково
читается и как жалоба, и как задание, — и в списке, где решают «брать или не
брать», это разные вещи. Идея формы действия не несёт **намеренно**: что делать,
ещё неизвестно, и заголовок-действие обещал бы решённость, которой нет.
брать», это разные вещи. `research` формы действия не несёт **намеренно**: её
исход знание, что делать — ещё неизвестно, и заголовок-действие обещал бы
решённость, которой нет.
Из этого же правила растёт разница индексов: роадмап — список возможностей,
беклог — список работ, и если заголовки перепутать формами, каждый из них
@@ -336,7 +348,7 @@ stateDiagram-v2
И одно требование, которое есть только у задачи: **сложность формулировки — не
признак сложности работы.** Задачу, которую не удаётся сказать просто, чаще
всего не удаётся и оценить: это либо две задачи, либо идея.
всего не удаётся и оценить: это либо две задачи, либо сырьё.
Эти правила — про **язык**, а не про объём: короткая задача без границ хуже
длинной с ними.
@@ -349,10 +361,10 @@ stateDiagram-v2
```
python3 $tk check --dir D # согласованность индексов + здоровье
python3 $tk check --dir D --fix # + починить дрейф (секция, заголовок, дубли, «зачем», форма меты)
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] [--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 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 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 # просто удалить (реализована и закоммичена)
@@ -375,20 +387,22 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
Различать 1 и 3 обязательно: «дрейф в беклоге» — рабочая ситуация, «каталога
нет» — нерабочая, и одинаковая реакция на них была бы неверна в обоих случаях.
Тип — английское ключевое слово `goal` / `idea` / `task` (как и прочие токены
команд); `task` префикса не несёт, остальные кодируются `[goal]`/`[idea]` в
заголовке. Текст задачи при этом русский.
Тип — английское ключевое слово `goal` / `feature` / `fix` / `chore` /
`research` (как и прочие токены команд), у `add` **обязательное**: без него
неизвестно, какой шаблон тела класть. Текст задачи при этом русский, а эмодзи в
заголовке ставит скрипт.
**Мутации правят файл и индексы заодно** — руками строку индекса или мету
не пиши, зови `add`/`edit`/`move`/`close`/`sprint`. Смена заголовка, «зачем», типа,
цели, рода работы и **тегов** — это `edit`: он держит H1, мету и индекс в синхроне.
Снятие тега — `--rm-tag` (после ответа на вопрос снимается `question`), смена
цели — `--goal`, рода`--kind`; оба заменяют прежнее значение, а не добавляют
второе.
цели и **тегов** — это `edit`: он держит H1 (вместе с эмодзи), мету и индекс в
синхроне. Снятие тега — `--rm-tag` (после ответа на вопрос снимается
`question`), смена цели — `--goal`, типа`--type`; оба заменяют прежнее
значение, а не добавляют второе.
**Переезд между индексами — следствие смены типа, а не отдельная команда.**
`edit <slug> --type goal --section <часть роадмапа>` переносит строку из
`BACKLOG.md` в `ROADMAP.md` (и обратно `--type task --section <секция беклога>`);
`BACKLOG.md` в `ROADMAP.md` (и обратно — задачным типом плюс
`--section <категория беклога>`);
`move` двигает только внутри одного индекса и пишет причину. `--section` у
`edit` работает **только** при таком переезде — иначе он отсылает к `move`,
потому что смена секции без причины и есть тот дрейф, который потом никто не
@@ -401,39 +415,54 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
`check` — единственный судья согласованности; что именно он ловит, скажет его
вывод, здесь не пересказываем. Гоняй его **в начале сессии** и **после каждой
правки**, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся
чини `check --fix` — он детерминированно правит то, где истина однозначна
(секция, заголовок, дубли, «зачем» из индекса в файл, старая форма меты,
пометка `decomposed` у цели с задачами), а неоднозначное (задача сразу в двух
индексах, нечего восстанавливать) печатает отдельной пометкой `НЕОДНОЗНАЧНО`
это тебе, и это идёт строкой доклада. **Ссылка на исчезнувший файл в пометку не
попадает:** `--fix` её просто не трогает, и она остаётся `ОШИБКА` обычного
`check` — то есть видна, но в докладе её надо назвать отдельно.
чини `check --fix` — он детерминированно правит то, где истина однозначна (тип в
своё поле, эмодзи заголовка, имя поля места, секция, заголовок, дубли, «зачем» из
индекса в файл, старая форма меты, пометка `decomposed` у цели с задачами, сырьё
в конец категории), а неоднозначное (задача сразу в двух индексах, нечего
восстанавливать, **тип, которого неоткуда взять**) печатает отдельной пометкой
`НЕОДНОЗНАЧНО` — это тебе, и это идёт строкой доклада. **Ссылка на исчезнувший
файл в пометку не попадает:** `--fix` её просто не трогает, и она остаётся
`ОШИБКА` обычного `check` — то есть видна, но в докладе её надо назвать отдельно.
`--fix` правит **и файлы**ровно в двух местах, где источник ровно один и
выбирать не из чего: «зачем», оставшееся только в индексе, переезжает в мету,
и цель, у которой есть задачи, получает тег `decomposed`. Оба случая печатаются
поимённо.
`--fix` правит **и файлы** — там, где источник ровно один и выбирать не из чего:
тип переезжает из прежнего дома (тег `kind:`, префикс `[goal]`/`[idea]`) в поле
меты, заголовок получает эмодзи, поле места — имя по типу, «зачем», оставшееся
только в индексе, переезжает в мету, цель с задачами получает `decomposed`.
Каждый случай печатается поимённо.
**Тип, который не выводится ниоткуда, `--fix` не угадывает.** `feature` от
`chore` машина не отличает, и подставленное наугад значение врало бы ровно там,
где по нему принимают решение. Такие записи идут в `НЕОДНОЗНАЧНО`, и тип им
проставляет человек — `edit <слаг> --type …`.
**Что механизировано, а что нет.** У задачи, взятой в набор (`sprint take` и
`check` по задачам спринта), проверяются три вещи, и у каждой своя глубина:
`check` по задачам спринта), проверяется схема её типа, и у каждой части своя
глубина:
- **критерии приёмки** — число пунктов жёстко (меньше двух отказ, больше пяти
замечание), наличие оракула **эвристикой** по слову «оракул» в пункте;
- **род работы** — жёстко: назван и из закрытого словаря;
- **раздел «Затрагивает»** — только **наличие непустого**. Полнота перечня машине
не видна: границу, которую забыли назвать, она от отсутствующей не отличает.
- **тип** — жёстко: назван и из закрытого словаря;
- **критерии приёмки** (`feature`, `fix`, `chore`) — число пунктов жёстко
(меньше двух отказ, больше пяти замечание), наличие оракула **эвристикой** по
слову «оракул» в пункте;
- **прочие разделы схемы** (`Затрагивает`, `Воспроизведение`, `Вопрос`,
`Куда ляжет ответ`, `Завершение`) — только **наличие непустого**. Содержимое
машине не видно: границу, которую забыли назвать, она от отсутствующей не
отличает, а шаги, по которым ничего не воспроизводится, — от годных.
Настоящий оракул от слова «оракул» машина тоже не отличает, поэтому эвристика
даёт только замечание, и в докладе это называется как есть: «проверено число
пунктов и наличие границ, годность оракулов и полнота границ — глазами».
даёт только замечание, и в докладе это называется как есть: «проверено наличие
разделов своего типа и число критериев, годность оракулов и полнота границ —
глазами».
Формат файла, меты, слага, индексов и `REJECTED.md`
[references/task-format.md](references/task-format.md). Там же тест «готова к
взятию», требования к критериям приёмки и раздел «Затрагивает».
Формат записи, меты, слага, индексов и `REJECTED.md`
[references/task-format.md](references/task-format.md); там же тест «готова к
взятию». Схема и алгоритм каждого типа — по файлу на тип:
[goal](references/task-goal.md) · [feature](references/task-feature.md) ·
[fix](references/task-fix.md) · [chore](references/task-chore.md) ·
[research](references/task-research.md).
## Сценарии
### Завести задачу, идею или цель из диалога
### Завести запись из диалога
1. **Фильтр.** Делаем прямо сейчас — не заводим. Не пожалеем о потере — не
заводим. Родилось три кандидата — покажи их и спроси, какие заводить: молча
@@ -444,30 +473,36 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
ту строку и что изменилось с момента отказа (`add` предупредит и сам, но
молча заводить нельзя). Две задачи об одном — самая дорогая находка
переоценки.
3. **Тип по тесту готовности** (см. task-format): проходит — задача, не
проходит — идея (`--type idea`). Не делается одним заходом — это не эпик, а
несколько задач под одной целью: дроби сразу. Возможность приложения, а не
шаг — цель (`--type goal`).
4. **Цель задачи — если род её требует.** У `feature` должен быть
`--goal <слаг>`: новая возможность и есть содержание цели. Подходящей нет —
либо она заводится (`--type goal`), либо перед тобой не `feature`. У `fix`,
`chore` и `research` цели может не быть вовсе, и придумывать её не надо. У
идеи цель проставляется, когда идея становится задачей.
5. **Род работы**`--kind feature|fix|chore|research` (см. «Род работы»). Не
подходит ни один — задача не одна, разбирай.
6. `add …`, затем допиши тело редактором: одна фраза, **затрагиваемые границы**,
критерии приёмки с оракулами, рамки. «Зачем» отвечает «зачем нужна эта
задача» — состояние, остаток, боль, — а не пересказывает первый абзац, и
пишется **для человека**: не «канонизация внутри транзакции», а «тело 40 МиБ
держит блокировку 5 секунд, соседние доставки уходят в отказ».
7. `check`.
3. **Тип**`--type` обязателен, и он же первое содержательное решение:
- возможность приложения, а не шаг к ней → `goal`;
- снаружи появляется то, чего не было → `feature`;
- поведение расходится с заявленным и **воспроизводится** `fix`
(не воспроизводится → `research`);
- обслуживание, наблюдаемое поведение не меняется → `chore`;
- исход — знание, а не изменение системы → `research`.
Не подходит ни один — в записи их два, разбирай. Не проходит тест готовности
(см. task-format) — это **сырьё**: `--type research`, раздел «Вопрос» пока
пуст, место в конце категории. Не делается одним заходом — это не эпик, а
несколько задач под одной целью: дроби сразу.
4. **Цель — если тип её требует.** У `feature` должен быть `--goal <слаг>`:
новая возможность и есть содержание цели. Подходящей нет — либо она
заводится (`--type goal`), либо перед тобой не `feature`. У `fix`, `chore` и
`research` цели может не быть вовсе, и придумывать её не надо.
5. `add …`, затем допиши тело редактором **по схеме своего типа** — шаблон её
уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем
нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый
абзац, и пишется **для человека**: не «канонизация внутри транзакции», а
«тело 40 МиБ держит блокировку 5 секунд, соседние доставки уходят в отказ».
6. `check`.
### Разобрать находки аудита или ревью
Ревью и аудиты — тоже источник задач, но с зеркальной диалогу опасностью: не
пять файлов из одной мысли, а сорок файлов из сорока сырых находок. Защита та
же, что в самом ревью: кластеризация по причине, дедуп против живых и
`REJECTED.md`, находка без свидетельства → идея, а не задача, и карта кластеров
`REJECTED.md`, находка без свидетельства → сырьё (`research`), а не задача, и карта кластеров
пользователю до создания файлов. Порядок, отображение серьёзности и привязка к
целям — [references/from-review.md](references/from-review.md).
@@ -481,7 +516,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
Если переводить надо не только задачи, а весь `docs/` — это скилл
`av-dev-pm:canon`, и он зовёт этот сценарий сам на своём шаге.
### Декомпозиция и штурм идеи
### Декомпозиция и штурм сырья
[references/split.md](references/split.md). Обе операции превращают одну запись в
несколько, и у обеих есть проверяемый тест: части должны **мерджиться порознь** и
@@ -551,10 +586,14 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
- **предписание процесса в теле** — «делать таким-то профилем ревью», «взять
такой-то агент»: это второй дом для правила выбора и путь понизить требования
решением, принятым до проектирования. Снимается;
- **род, разошедшийся с задачей** — задача заводилась починкой, а после разбора
- **тип, разошедшийся с задачей** — задача заводилась починкой, а после разбора
оказалось, что поведение никогда и не было заявлено: это `feature`, а не `fix`.
Правится `edit <slug> --kind`; род, оставшийся от прошлой формулировки, врёт
ровно там, где по нему отбирают;
Правится `edit <slug> --type`; тип, оставшийся от прошлой формулировки, врёт
ровно там, где по нему отбирают, **и требует не тех разделов**: у брошенного
`fix` останется «Воспроизведение», которого нечем заполнить;
- **сырьё, у которого появился вопрос** — разведка обросла формулировкой, но
раздел «Вопрос» так и пуст: она числится сырьём и в спринт не берётся.
Записывается вопрос, и `check --fix` поднимает строку из конца категории;
- **границы, названные вместо реализации** — «переписать хранилище на новый
драйвер» в разделе «Затрагивает» это не граница, а замысел. Границы —
`таблица points и её миграция`, `эндпоинт POST /ingest`, `формат отпечатка на
@@ -619,7 +658,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
- **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным
предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть,
под какую цель отнести, какая рамка идеи верна — решение пользователя. Слаг,
под какую цель отнести, какая рамка разведки верна — решение пользователя. Слаг,
формулировка, порядок строк в индексе — механика, делаем сами.
- **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа;
решений больше — веди **несколько итераций** диалога по ≤3, а не один
@@ -23,8 +23,11 @@
- **Находка со свидетельством**, отложенная к исполнению → **задача**.
Свидетельство и последствие переносим в тело — это её «почему», то самое, что
переживает запись.
- **Находка без свидетельства / низкой уверенности** → **идея** (`[idea]`), а не
задача. Её судьба — штурм, где либо найдётся подтверждение, либо она уедет в
- **Находка без свидетельства / низкой уверенности** → **сырьё**: `research`, у
которого раздел «Вопрос» и есть недостающее свидетельство («при каких условиях
это воспроизводится»). Не `fix`: без `Воспроизведения` его в спринт не
возьмут, и правильно — чинить нечего, пока непонятно, что ломается. Судьба
сырья — штурм, где либо найдётся подтверждение, либо оно уедет в
`REJECTED.md`.
- **Уже починено по ходу ревью** → **ничего**. Починенное не заводим.
- **Развилка, решённая при ревью** → ничего; решённая «потом» → задача с
@@ -44,13 +47,13 @@
4. **Разложи по целям — там, где цель нужна.** Большинство находок ревью это
`fix` и `chore`, и **цель им не требуется**: они служат работоспособности, а
не направлению, и в спринт входят помимо его цели. Придуманная им цель —
ровно то враньё, от которого спасает род работы.
ровно то враньё, от которого спасает тип.
Цель обязательна у находки, которая оказалась **новой возможностью**
(`kind:feature`): нашлось поведение, которого никто не заказывал, и его надо
(`feature`): нашлось поведение, которого никто не заказывал, и его надо
либо заказать целью, либо убрать. Подходящей цели нет — заведи её
(`add --type goal --section Направления`) в том же проходе.
5. **Покажи карту до создания файлов.** Кластер → задача / идея / строка в
5. **Покажи карту до создания файлов.** Кластер → задача / сырьё / строка в
пакетный файл / уже заведено / отброшено, и под какую цель — пачкой через
`AskUserQuestion`. Это тот же барьер, что и «три кандидата» в интейке из
диалога: массовое заведение файлов без подтверждения — ровно тот отказ, ради
@@ -60,11 +63,12 @@
6. **Заводи утверждённое** через `tasks.py add`, с тремя добавками:
- **тег партии** — `--tag review-ГГГГ-ММ-ДД` (или `audit-<тема>`), чтобы весь
заход разбора поднимался одной командой `list --tag …`;
- **род работы** — `--kind`. У находок ревью он **не по умолчанию `fix`**:
починкой считается расхождение с заявленным поведением, а находка «этого
свойства никто не заказывал» — это `feature`, находка «не знаем, как
поведёт себя драйвер» — `research`. Род, розданный оптом, врёт ровно там,
где по нему потом отбирают;
- **тип** — `--type`, и он **не по умолчанию `fix`**: починкой считается
расхождение с заявленным поведением, а находка «этого свойства никто не
заказывал» — это `feature`, находка «не знаем, как поведёт себя драйвер» —
`research`. Тип, розданный оптом, врёт ровно там, где по нему потом
отбирают, **и требует не тех разделов**: каждому `fix` придётся заполнить
`Воспроизведение`, а у находки без свидетельства его нет;
- **провенанс в теле** — кто нашёл, каким проходом, с каким свидетельством.
Без него через месяц не отличить проверенную находку от догадки.
7. `tasks.py check`.
+10 -5
View File
@@ -57,10 +57,10 @@
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
наследников, а не археологией git;
- родитель осмыслен как **возможность**, а не как шаг → это цель. Тип на месте
не меняется (цель живёт в другом индексе): заводится `[goal]` в `ROADMAP.md`,
не меняется (цель живёт в другом индексе): заводится `--type goal` в `ROADMAP.md`,
части получают `--goal <новый слаг>`, родитель закрывается с причиной-ссылкой.
**Промежуточного зонтика между целью и задачей нет.** Тип `[epic]` упразднён:
**Промежуточного зонтика между целью и задачей нет.** Тип `epic` упразднён:
роль зонтика играет цель, а слишком крупный шаг дробится на шаги помельче под
той же целью. Если частям нужен общий заголовок — значит у них общая
возможность, и её надо назвать целью, а не заводить временный тип.
@@ -73,10 +73,15 @@
спринт продолжается остальными. Части заводятся сразу под той же целью, но в
текущий набор **не добавляются** — набор заморожен.
## Мозговой штурм идеи
## Мозговой штурм сырья
Идея (`[idea]`) не проходит тест «готова к взятию»: неясно, что именно делаем.
Штурм проясняет — и это **generative-операция, а не applicative**.
Сырьё — запись типа `research`, у которой раздел «Вопрос» пуст: она не проходит
тест «готова к взятию», потому что неясно, что именно делаем. Штурм проясняет —
и это **generative-операция, а не applicative**.
Исход штурма и есть заполненный «Вопрос» (тогда разведку можно брать в спринт)
или набор задач с типами, которые из ответа следуют. Третий законный исход —
`close --reason`.
Applicative-штурм («перечисли задачи, следующие из идеи») выдаёт очевидное:
перечисляется то, что уже видно в формулировке. Ценное — на уровень выше.
@@ -0,0 +1,60 @@
# 🧹 `chore` — обслуживание, наблюдаемое поведение не меняется
Зависимости, сборка, перенос, чистка, оснастка. Отвечает на **«что нужно
сделать»**, глаголом в неопределённой форме.
Общая форма записи (мета, слаг, строка индекса) — [task-format.md](task-format.md).
Здесь только то, что у этого типа своё.
## Схема
| | |
| --- | --- |
| Заголовок отвечает на | что нужно сделать |
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
| Допустимые сверх того | `Рамки`, `Вопросы` |
| Поле места | **Категория** — полка домена беклога |
| Цель (`goal:<слаг>`) | нет: цель — это возможность, а здесь её не появляется |
| Индекс | `BACKLOG.md``SPRINT.md` |
| Берётся в спринт | да |
## Адресат — разработчик, и это законно
Тест готовности спрашивает «что станет наблюдаемо иначе». У `chore` ответ
адресован **разработчику**, а не пользователю: «перестанет собираться два раза»,
«уедет последний вызов устаревшего API», «проверки гоняются одной командой».
Это ответ, а не отговорка.
**У `chore` тест готовности слабее честно, а не молча.** Пока типа не было,
такие задачи либо не заводились вовсе, либо формулировались как выдуманная
пользовательская польза — и то и другое хуже, чем сказать прямо, для кого работа.
Отсюда же граница: если после задачи меняется то, что видит пользователь, — это
не `chore`. Тип, оставшийся от первой формулировки, врёт ровно там, где по нему
отбирают.
## Алгоритм
1. **Проверить, что поведение не меняется.** Меняется — это `feature` или `fix`,
и у неё другие требования (цель, воспроизведение).
2. **Назвать, что перестанет мешать** — одной фразой, адресуясь разработчику.
«Прибраться в модуле X» ответом не является: непонятно, что изменится.
3. **Назвать границы** в `Затрагивает`. У обслуживания они часто не в коде:
конфиг и его образцы, версия зависимости, команда сборки, файл CI. Границей
считается то, у чего есть внешняя сторона и цена изменения.
4. **Написать критерии приёмки** — 2–5 утверждений с оракулами. У `chore`
оракул обычно самый дешёвый из всех типов: команда, которая раньше падала
или требовала трёх шагов, теперь отрабатывает одним.
5. **Проверить, что это не «заодно».** Обслуживание любит склеиваться в пачку
(«обновить зависимости и переписать сборку и убрать мёртвый код»). Не
мерджится порознь — это несколько задач ([split.md](split.md)).
6. **Цель не проставлять.** `chore` служит работоспособности, а не направлению,
и в набор спринта входит помимо его цели. Работа по сопровождению проекта
при этом видна в роадмапе — секцией `Сопровождение`, но целью не становится.
## Что видит машина, а что человек
`check` и `sprint take` смотрят на **наличие непустого** `Затрагивает` и на
**число** критериев — ровно то же, что у `feature`. Разница между типами здесь не
в строгости проверки, а в том, **кому адресован ответ** на «что станет
наблюдаемо иначе», — и это судит человек.
@@ -0,0 +1,63 @@
# ✨ `feature` — снаружи появляется то, чего не было
Задача, после которой наблюдаемое поведение меняется в сторону новой
возможности. Отвечает на **«что нужно сделать»** и пишется глаголом в
неопределённой форме.
Общая форма записи (мета, слаг, строка индекса) — [task-format.md](task-format.md).
Здесь только то, что у этого типа своё.
## Схема
| | |
| --- | --- |
| Заголовок отвечает на | что нужно сделать («Печатать поле одним куском кода») |
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
| Допустимые сверх того | `Рамки`, `Вопросы` |
| Поле места | **Категория** — полка домена беклога |
| Цель (`goal:<слаг>`) | **обязательна** |
| Индекс | `BACKLOG.md``SPRINT.md` |
| Берётся в спринт | да |
**Цель обязательна, и это единственный тип, у которого так.** Новая возможность
и есть содержание цели: подходящей нет — либо она заводится, либо перед тобой не
`feature`. `sprint take` без цели откажет.
## Алгоритм
1. **Найти цель или завести её.** Задача без цели, названная функцией, — самый
частый способ пронести в беклог работу, которой никто не заказывал.
2. **Назвать границы** в разделе `Затрагивает`: эндпоинт или команда, таблица и
миграция, формат на диске, публичный тип пакета, внешний сервис. Названы
**границы, а не замысел**: «переписать хранилище на новый драйвер» — замысел,
`таблица points и её миграция` — граница. Проверяется вопросом «это можно
назвать до того, как решено *как* делать?».
3. **Написать критерии приёмки** — 2–5 проверяемых утверждений списком, у
каждого назван оракул. Не «работает корректно», а «повторный прогон даёт тот
же отпечаток — оракул: команда сверки».
4. **Сказать, какую строку «Завершения» цели задача двигает.** Одной строкой в
теле. Это защита от задачи «отрефакторить X»: она проваливается не потому,
что невидима снаружи, а потому, что не находит строки, к которой относится.
5. **Проверить, что задача одна.** Отвечается всё, но задача не делается одним
заходом и не мерджится целиком — это несколько задач под одной целью, дроби
сразу ([split.md](split.md)). Промежуточного зонтика между целью и задачей
нет.
6. **Реализация** — дело пайплайна проекта, не этого скилла. Закрывается
`close <слаг> --implemented`: файл и строка удаляются, суть переезжает в
`openspec/specs/` и документацию.
## Что видит машина, а что человек
`check` и `sprint take` смотрят на **наличие непустого** раздела `Затрагивает`,
на **число** критериев (меньше двух — отказ, больше пяти — замечание) и на цель.
Наличие оракула проверяется **эвристикой** — словом «оракул» в пункте.
Полнота перечня границ машине не видна: границу, которую забыли назвать, она от
отсутствующей не отличает. Настоящий оракул от слова «оракул» тоже не отличает.
Поэтому в докладе это называется как есть: «проверено число пунктов и наличие
границ, годность оракулов и полнота границ — глазами».
**Критерии — пол, но расхождение с ними есть дефект критериев.** Видишь, что
критерии закрыты, а суть задачи не достигнута — **правь критерии и возвращай
задачу**, а не держи невидимое сверх-требование: иначе исполнитель никогда не
знает, закончил ли, и мотивирован занижать критерии заранее.
@@ -0,0 +1,70 @@
# 🐞 `fix` — поведение расходится с заявленным
Задача о расхождении между тем, что система делает, и тем, что про неё заявлено
— в спеке, в инварианте `CLAUDE.md`, в критериях закрытой задачи. Отвечает на
**«что нужно сделать»**, глаголом в неопределённой форме, перед ним допускается
«не»: «Не отбрасывать молча лишние символы в ходе».
Общая форма записи (мета, слаг, строка индекса) — [task-format.md](task-format.md).
Здесь только то, что у этого типа своё.
## Схема
| | |
| --- | --- |
| Заголовок отвечает на | что нужно сделать |
| Обязательные разделы | **`Воспроизведение`**, `Затрагивает`, `Критерии приёмки` |
| Допустимые сверх того | `Рамки`, `Вопросы` |
| Поле места | **Категория** — полка домена беклога |
| Цель (`goal:<слаг>`) | необязательна |
| Индекс | `BACKLOG.md``SPRINT.md` |
| Берётся в спринт | да |
## `Воспроизведение` — раздел, которого нет у других типов
**Не воспроизводится — это `research`, а не `fix`.** Правило было записано и
раньше, но проверять его было нечем, и «починки» без единого шага повторения
уходили в спринт наравне с остальными. Раздел делает правило проверяемым: он
называет, **что сделать, чтобы расхождение проявилось, и что при этом видно
вместо ожидаемого**.
Пишется двумя частями, обе обязательны по смыслу:
- **шаги или вход** — команда, запрос, файл, последовательность действий;
- **что видно и что ожидалось** — «ввод `а1б2` ходит в `a1`, а должен быть
отвергнут с ошибкой».
Это не критерии приёмки и не дублирует их: воспроизведение описывает **сегодня**,
критерии — **завтра**. Пропущенное воспроизведение чаще всего означает одно из
двух: расхождение приняли на слово, или его вообще нет, а есть недовольство
поведением — и тогда это `feature`, а не `fix`.
## Алгоритм
1. **Воспроизвести.** Не удаётся — это `research`: заведи вопрос «при каких
условиях проявляется» и не притворяйся, что чинить есть что.
2. **Найти, чему поведение противоречит.** Спека, инвариант, критерий закрытой
задачи. Не противоречит ничему — это `feature`: поведение никогда и не было
заявлено, а тип, оставшийся от первой формулировки, врёт ровно там, где по
нему отбирают.
3. **Записать воспроизведение** — шаги и наблюдаемое против ожидаемого.
4. **Назвать границы** в `Затрагивает`: починка часто трогает больше, чем
кажется по объёму текста, и оценка систематически занижена именно здесь.
5. **Написать критерии приёмки** — 2–5 утверждений с оракулами. У починки
почти всегда есть парный критерий: **прежнее поведение не сломалось**
(«ввод `а1` принимается по-прежнему»). Без него починка чинит одно и ломает
соседнее.
6. **Цель не выдумывать.** `fix` служит работоспособности, а не направлению, и в
набор спринта входит помимо его цели. Придуманная цель — то же враньё, от
которого спасает тип.
7. **Записать дефект в журнал** `docs/review.md` с пометкой «проскочил / пойман
ревью». Проскочившие — эвал-сет для калибровки конвейера; пойманные с
оракулом — лучшая опора для прохода ревью: проектные, воспроизводимые,
однажды оказавшиеся правдой.
## Что видит машина, а что человек
`check` и `sprint take` смотрят на **наличие непустого** `Воспроизведения` и
`Затрагивает` и на **число** критериев. Годность воспроизведения — человеку:
шаги, по которым ничего не воспроизводится, машина от годных не отличает, и
делать вид, что проверено больше проверенного, хуже, чем не проверять вовсе.
+143 -85
View File
@@ -1,78 +1,125 @@
# Формат задач, целей и индексов
# Формат записей и индексов
Заголовок, мета-блок и строку индекса ставит `tasks.py add` — руками их не
пишут. Этот файл описывает, что именно скрипт создаёт и что проверяет `check`;
тело задачи (одну фразу, критерии, рамки, контекст) дописывает агент.
пишут. Этот файл описывает **общую форму** любой записи и то, что проверяет
`check`; тело дописывает агент.
## Файл задачи
Чем разделы тела отличаются от типа к типу, какой алгоритм у каждого типа и что
у него обязательно — **отдельным файлом на тип**:
| Тип | Файл | Одной строкой |
| --- | --- | --- |
| 🎯 `goal` | [task-goal.md](task-goal.md) | возможность приложения |
| ✨ `feature` | [task-feature.md](task-feature.md) | снаружи появляется то, чего не было |
| 🐞 `fix` | [task-fix.md](task-fix.md) | поведение расходится с заявленным |
| 🧹 `chore` | [task-chore.md](task-chore.md) | обслуживание, поведение не меняется |
| 🔬 `research` | [task-research.md](task-research.md) | исход — знание, а не изменение |
## Файл записи
`items/<slug>.md`:
```markdown
# Тай-брейк при равной полноте
# 🐞 Не отбрасывать молча лишние символы в ходе
- **Секция:** Ядро — вышла из спринта: остаток писал нерешённое в журнал
- **Зачем:** порядок канонических форм берёт меньшее в 96% случаев — для накопительных это систематический недосчёт
- **Теги:** goal:merge-robustness, kind:fix, sprint:2026-08-03
- **Тип:** fix
- **Категория:** Ядро — вышла из спринта: остаток писал нерешённое в журнал
- **Зачем:** ввод «а1б2» ходит в a1 — игрок не видит, что ошибся, и винит игру
- **Теги:** goal:merge-robustness, sprint:2026-08-03
При столкновении точек выигрывает более полная, но при равной полноте побеждает
последняя доставка — а она систематически беднее первой.
Разбор хода читает первые два символа и молча выбрасывает остаток строки.
## Воспроизведение
Ввести `а1б2` в свой ход: программа ходит в `a1` и ничего не сообщает.
Ожидалось — отказ с ошибкой разбора.
## Затрагивает
Таблица `points` и её миграция; правило слияния в приёме доставки; формат
отпечатка состояния на диске. Публичного контракта не трогает.
Разбор строки хода; текст ошибки в выводе партии. Формат сохранения партии
не трогается.
## Критерии приёмки
- повторный прогон свёртки даёт тот же отпечаток состояния — оракул: команда сверки
- накопительная метрика за сутки не уменьшается после повторной доставки — оракул: тест
- в логе видно, какая из двух точек выиграла и почему — оракул: глазами по логу прогона
- ввод «а1б2» отвергается с ошибкой — оракул: тест разбора
- ввод «а1» принимается по-прежнему — оракул: тест разбора
## Рамки
Схема не трогается; данные только читаются; перезапуск сервиса допустим.
Схема не трогается; данные только читаются; перезапуск допустим.
Связано: решение о канонической форме содержимого.
```
- **Заголовок H1** — он же заголовок строки в индексе, дословно. Тип кодируется
префиксом `[goal]` / `[idea]`; обычная задача — без префикса.
Отдельного поля типа **нет**: два места для одного факта разъезжаются, а
префикс виден прямо в индексе, где и принимается решение «брать или не брать».
- **Форма заголовка — по типу записи.** Задача отвечает на «что нужно сделать»
и пишется глаголом в неопределённой форме («Печатать поле одним куском кода»,
«Не отбрасывать молча лишние символы»); цель — на «что приложение будет
уметь»; идея просто называет, о чём она. Почему так — SKILL.md, «Как написана
- **Заголовок H1** — он же заголовок строки в индексе, дословно. Начинается
**эмодзи типа**, и она **производна**: её ставит `add` и чинит `check --fix`
по полю меты. Второго дома у типа нет — эмодзи это его отображение, как
строка индекса это отображение файла.
- **Форма заголовка — по типу.** Цель отвечает на «что приложение будет уметь»;
`feature`, `fix` и `chore` — на «что нужно сделать», глаголом в неопределённой
форме, перед ним допускается «не»; `research` называет предмет разведки и
формы действия **не несёт намеренно**. Почему так — SKILL.md, «Как написана
задача». `check` считает заголовки не в форме действия и печатает число в
здоровье; годность формулировки смотрит агент `task-form`.
- **Мета-блок** — список сразу после заголовка, **поле на строку**. Обязательна
секция, причина после тире желательна (именно она объясняет, почему задача
здесь оказалась — в том числе «вышла из спринта: …»), «зачем» и теги
опциональны. Порядок свободный, поле в одну строку. Нераспознанные поля
сохраняются: скрипт правит свои и не трогает чужие.
- **Мета-блок** — список сразу после заголовка, **поле на строку**. Обязательны
**тип** и **место**, причина после тире желательна (именно она объясняет,
почему задача здесь оказалась — в том числе «вышла из спринта: …»), «зачем» и
теги опциональны. Нераспознанные поля сохраняются: скрипт правит свои и не
трогает чужие.
- **Тип — первым полем.** Он решает, что у записи вообще может быть: какие
разделы обязательны, нужна ли цель, берётся ли она в спринт, — и читается
раньше всего остального. Словарь **закрыт**: `goal` | `feature` | `fix` |
`chore` | `research`. Не подходит ни один — это сигнал, что в записи их два и
её надо разделить.
- **«Зачем» отвечает на «зачем нужна эта задача»** — состояние, остаток, боль.
Не пересказ задачи: пересказ уже есть по ссылке. Живёт здесь, а не только в
индексе: строка индекса его повторяет и производна от него, `check` сверяет,
`check --fix` восстанавливает пропавшую строку **вместе с ним**. Пока поле
лежало только в индексе, штатная починка дрейфа теряла его молча и
навсегда — а это единственное, по чему задачу выбирают, не открывая.
- **Тело** — одна фраза «что станет наблюдаемо иначе», затрагиваемые границы,
критерии приёмки, рамки, контекст, ссылки. Пишется на языке документации
проекта: предметно, без англицизмов, у которых есть русское слово, и без
терминов, которых нет ни в паспорте, ни в архитектуре, ни в конвенциях
(правило и его причина — в SKILL.md, раздел «Как написана задача»).
Мета **одной строкой через `·`** — прежняя форма. Она читается по-прежнему,
`check` называет её дрейфом, `check --fix` переписывает списком; поле `Хук`
при этом становится `Зачем`. Причина отказа от строки простая: с тремя полями
и длинным «зачем» строка уезжала за экран, а `·` приходилось запрещать в тексте
причины и самого «зачем».
- **Тело** — одна фраза «что станет наблюдаемо иначе», дальше разделы по схеме
типа. Пишется на языке документации проекта: предметно, без англицизмов, у
которых есть русское слово, и без терминов, которых нет ни в паспорте, ни в
архитектуре, ни в конвенциях (правило и его причина — в SKILL.md, раздел «Как
написана задача»).
Тело — не план реализации и не спецификация: принятое и реализованное переезжает
в документацию проекта, а файл задачи удаляется.
### Поле места: «Категория» и «Секция»
Поле называет, **где числится строка**, и имя у него **зависит от типа**:
| Тип | Поле | Значения | Что это |
| --- | --- | --- | --- |
| `goal` | **Секция** | `Запланировано`, `Направления`, `Сопровождение` | часть роадмапа: состояние очереди |
| прочие | **Категория** | секции беклога проекта (`Ядро`, `Инфра`, …) | полка домена, в которую задача вернётся из спринта |
Разные имена потому, что это **разные вещи**. У задачи поле переживает спринт:
`sprint drop` возвращает её именно туда. У цели оно называет не полку, а место в
очереди работ. Одно имя на два смысла и было конфляцией; `check` называет
несовпадение дрейфом, `check --fix` переименовывает.
Имя самого места принадлежит **заголовку индекса** — файл на него лишь
ссылается, и принадлежность сверяется по нижнему регистру.
### Прежние формы, которые читаются, но не пишутся
Всё это `check` называет дрейфом, а `check --fix` переписывает:
| Было | Стало |
| --- | --- |
| префикс `[goal]` / `[idea]` в H1 | поле **Тип** + эмодзи в H1; `[idea]``research` |
| тег `kind:<род>` | поле **Тип** (род работы стал типом) |
| поле **Секция** у задачи | поле **Категория** |
| поле **Хук** | поле **Зачем** |
| мета одной строкой через `·` | мета списком, поле на строку |
Единственное, чего `--fix` не делает сам, — **проставить тип записи, у которой
его неоткуда взять**: `feature` от `chore` машина не отличает, и подставленное
наугад значение врало бы ровно там, где по нему принимают решение. Такие записи
он называет поимённо пометкой `НЕОДНОЗНАЧНО`.
### Затрагивает
Перечень **границ**, которых изменение касается. Границей считается то, у чего
@@ -100,8 +147,8 @@
забыли назвать, она от отсутствующей не отличает. Раздела нет — отказ во взятии:
оценивать нечем.
**У идей раздела нет**как и критериев: границы становятся известны, когда идея
превращается в задачу.
**У `goal` и `research` раздела нет**у первой границы называют её задачи, у
второй они становятся известны, когда из разведки родятся задачи.
### Критерии приёмки
@@ -118,7 +165,9 @@
замечание: настоящий оракул от слова «оракул» машина не отличает, и делать вид,
что проверено больше проверенного, хуже, чем не проверять вовсе.
**У идей критериев нет — именно поэтому они идеи.**
**У `research` критериев нет** — её приёмка это записанный ответ, и описывается
она разделами «Вопрос» и «Куда ляжет ответ». **У `goal` их заменяет
«Завершение».**
**Критерии — пол, но расхождение с ними есть дефект критериев.** Если приёмщик
видит, что критерии закрыты, а суть задачи не достигнута, он **правит критерии и
@@ -129,16 +178,21 @@
### Рамки
Одна строка: чего касаться нельзя, что перезапускается, что считается
необратимым, трогается ли схема данных. **Свойства репозитория сюда не пишутся**
— номер последней миграции, версия зависимости, хеш: в лежалой задаче они
протухают молча и становятся ложной рамкой. Снимок берётся при постановке, а не
при заведении.
необратимым, трогается ли схема данных. Раздел **допустим у любого типа задачи и
ни у одного не обязателен**. **Свойства репозитория сюда не пишутся** — номер
последней миграции, версия зависимости, хеш: в лежалой задаче они протухают
молча и становятся ложной рамкой. Снимок берётся при постановке, а не при
заведении.
### Вопросы
Неразобранное решение человека живёт разделом `## Вопросы` **плюс тегом
`question`**. Раздел без тега или тег без раздела — дрейф, `check` о нём скажет.
Раздел `Вопросы` (о решении человека) и раздел `Вопрос` у `research` (предмет
разведки) — **разные вещи и разные слова**: первый блокирует взятие, второй его
разрешает.
**Судит факт, а не метка.** Отказ во взятии даёт **непустой раздел «Вопросы»**,
независимо от того, стоит ли тег: иначе забывший тег проходил бы, а поставивший
спотыкался — стимул ровно обратный записанному правилу. Тег производен: он нужен
@@ -158,13 +212,12 @@
## Файл цели
**Заголовок цели отвечает на «что приложение будет уметь».** Не область работ и
не имя подсистемы: не «Работа со слиянием», а «Исход слияния не зависит от
порядка доставки». Свойство поведения — тоже возможность.
Форма та же, разделы и алгоритм — [task-goal.md](task-goal.md).
```markdown
# [goal] Исход слияния не зависит от порядка доставки
# 🎯 Исход слияния не зависит от порядка доставки
- **Тип:** goal
- **Секция:** Направления
- **Теги:** decomposed
@@ -181,10 +234,6 @@
- **Задачи цели здесь не перечисляются.** Перечень даёт
`tasks.py list --goal <слаг>`; хранимый список стал бы третьим индексом и
поехал бы на первой же закрытой задаче.
- **Раздел «Завершение» — списком, а не абзацем.** Это признаки того, что
приложение уже умеет; **на строку «Завершения» ссылается задача**, объясняя,
какую часть возможности она двигает (см. тест готовности). Абзацем такая
ссылка не берётся, поэтому список.
- **Тег `decomposed`** отличает «цель ещё не разобрана» от «все её задачи
закрыты» — два состояния, у которых снаружи один и тот же признак: задач нет.
Пометка именно **тегом**, а не строкой в теле: только так она проверяется.
@@ -217,16 +266,18 @@
Строка везде одной формы:
```markdown
- [Заголовок дословно](items/slug.md) — зачем
- [🐞 Заголовок дословно](items/slug.md) — зачем
```
«Зачем» отвечает на «зачем нужна эта задача» одним предложением: состояние,
остаток, боль. Пересказ первого абзаца бесполезен — он уже есть по ссылке.
Эмодзи внутри квадратных скобок не украшение: заголовок копируется **дословно**,
и тип виден там, где решают «брать или не брать».
| Файл | Что отвечает | Секции |
| --- | --- | --- |
| `ROADMAP.md` | что приложение уже умеет и чего ещё не умеет | канонические и в этом порядке: `Запланировано`, `Направления`, `Сопровождение`, `Готово` (англ. `Planned`, `Directions`, `Operations`, `Done`) |
| `BACKLOG.md` | что **можно взять** — только задачи | секции проекта (по умолчанию Ядро/Инфра) |
| `BACKLOG.md` | что **можно взять** — только задачи | категории проекта (по умолчанию Ядро/Инфра) |
| `SPRINT.md` | какая цель и какой набор под неё | одна: «Набор» |
| `REJECTED.md` | что ушло без реализации и почему | — |
@@ -237,8 +288,15 @@
следующем `sprint start` и очищается на `sprint close`.
Секции — **единственные заголовки `##` в индексе**: любой другой `##` в
преамбуле проверка сочтёт секцией. Внутри секции беклога порядок значения не
имеет — порядка в беклоге нет вовсе.
преамбуле проверка сочтёт секцией.
**Порядка «по важности» внутри секции беклога нет** — «что делать дальше»
отвечает набор спринта. Единственный порядок, который есть, **производен от типа
и заполненности**: **сырьё** (`research` без раздела «Вопрос») стоит в конце
своей секции, потому что его не берут, и между берущимся оно каждый раз требует
открыть файл, чтобы это понять. Проверяет `check`, переставляет `check --fix`,
и человек этот порядок не назначает — иначе он был бы приоритетом, которого
здесь нет.
**Секции «блокеры» среди них нет.** Блокер — состояние, а не полка: он живёт до
ответа человека, а следы остаются вопросами в файлах задач распущенного спринта.
@@ -251,15 +309,14 @@
`REJECTED.md`, и по той же причине (файла уже нет, ссылаться некуда).
**Секции роадмапа закреплены** — состав, полнота, единство языка и **порядок**
проверяются `check`; секции беклога проект называет сам. Почему так — SKILL.md.
Порядок закреплён потому, что `Готово` копится: стоя первым, достигнутое
отодвигает за экран то, ради чего роадмап открывают чаще всего.
проверяются `check`; категории беклога проект называет сам. Почему так —
SKILL.md. Порядок закреплён потому, что `Готово` копится: стоя первым,
достигнутое отодвигает за экран то, ради чего роадмап открывают чаще всего.
**Заголовок секции пишется с прописной и отбивается пустой строкой с обеих
сторон** — во всех индексах, включая секции беклога, имена которых выбирает проект. Написание
канонических секций и отбивку правит `check --fix`; он же сводит написание
секции в мете файла с заголовком индекса — **имя секции принадлежит заголовку**,
файл на неё лишь ссылается, и принадлежность сверяется по нижнему регистру.
сторон** — во всех индексах, включая категории беклога, имена которых выбирает
проект. Написание канонических секций и отбивку правит `check --fix`; он же
сводит написание места в мете файла с заголовком индекса.
Индексы **производны**: расходятся с файлом — правим индексы (`check --fix`).
Строку руками не пишут.
@@ -295,19 +352,13 @@
## Теги
Единственный механизм разметки, потому что `list --tag` уже умеет отбирать по
ним порцию разбора. Отдельных полей меты под это не заводим.
Разметка сверх типа. Тип полем, потому что он один и обязателен; теги — потому
что их много и `list --tag` уже умеет отбирать по ним порцию разбора.
- `goal:<слаг>` — цель, которой служит задача. Обязателен **у `kind:feature`**:
- `goal:<слаг>` — цель, которой служит задача. Обязателен **у `feature`**:
новая возможность и есть содержание цели. У `fix`, `chore` и `research` его
может не быть — они служат работоспособности, а не направлению, и в набор
спринта входят помимо его цели.
- `kind:<род>` — род работы: `feature` | `fix` | `chore` | `research`. Словарь
**закрыт**, значение ровно одно. Обязателен у задачи (без него `sprint take`
откажет), у цели запрещён, у идеи необязателен. Ставится
`add --kind` / `edit --kind`; `--kind` заменяет прежнее значение, а не
добавляет второе. Смысл рода и почему он тегом, а не префиксом — в SKILL.md,
раздел «Род работы».
- `question` — в файле есть неразобранный раздел «Вопросы».
- `sprint:<слаг>` — задача заведена в этом спринте; по нему отбирается первая
порция разбора («урожай спринта»). **Ставится сам**: слаг спринта заводит
@@ -317,6 +368,9 @@
разбора — урожай прошедшего спринта».
- `decomposed` — на цели: разложена на задачи (см. «Файл цели»).
Тега `kind:<род>` больше нет: род работы стал типом. Оставшийся в файле `check`
называет дрейфом, а `check --fix` снимает, перенеся значение в поле «Тип».
Отбор — `list --tag a,b`: перечисленные через запятую теги требуются **все
сразу** (это И, не ИЛИ). Тег, которого нет ни у одной задачи, `list` называет
вслух: молчаливый ноль читается как «таких задач нет», а чаще это опечатка.
@@ -327,17 +381,20 @@
## Тест «готова к взятию»
Задача готова, если из файла отвечаются четыре вопроса:
Задача готова, если из файла отвечаются четыре вопроса. Первый и четвёртый —
общие, второй и третий у каждого типа свои и перечислены в его файле.
1. **Что станет наблюдаемо иначе**, когда она сделана — снаружи: пользователю,
владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет
ломаться Y при Z» — ответ. **У `kind:chore` адресат — разработчик, и это
ломаться Y при Z» — ответ. **У `chore` адресат — разработчик, и это
законно**: «уедет последний вызов устаревшего API» — ответ, а не отговорка.
Род объявлен как раз затем, чтобы такие задачи не выдумывали себе
Тип объявлен как раз затем, чтобы такие задачи не выдумывали себе
пользовательскую пользу.
2. **Каких границ это касается** — раздел «Затрагивает». Без него задачу нельзя
оценить: остаётся судить по длине текста.
3. **По чему видно, что закончено** — критерии приёмки с оракулами.
2. **Что известно про сегодня** — то, что тип требует знать до работы:
у `fix` это `Воспроизведение`, у `research` — `Вопрос`, у `feature` и
`chore` — `Затрагивает`.
3. **По чему видно, что закончено** — критерии приёмки с оракулами;
у `research` вместо них `Куда ляжет ответ`.
4. **Какую часть «Завершения» своей цели она двигает** — у задачи с целью.
Строкой: «двигает пункт 2 «Завершения» — накопительная метрика перестаёт
уменьшаться». Это и есть защита от задачи «отрефакторить X»: она проваливает
@@ -349,9 +406,10 @@
**У задачи без цели** (`fix`, `chore`, `research`) вопрос не задаётся: они
служат работоспособности, а не направлению.
Не отвечается первый, второй или третий вопрос → это **идея** (`[idea]`), её
место в штурме. Не отвечается четвёртый у `feature` → либо цель есть и не
проставлена, либо это не новая возможность.
Не отвечается первый, второй или третий вопрос → это ещё не задача, а **сырьё**:
тип `research` без раздела «Вопрос», место — конец секции, работа над ним —
штурм. Не отвечается четвёртый у `feature` → либо цель есть и не проставлена,
либо это не новая возможность.
Отвечается всё, но задача не делается одним заходом и не мерджится целиком →
это **несколько задач под одной целью**, дроби сразу. Промежуточного зонтика
@@ -0,0 +1,70 @@
# 🎯 `goal` — возможность приложения
Цель отвечает на **«что приложение будет уметь»**. Не область работ и не имя
подсистемы: не «Работа со слиянием», а «Исход слияния не зависит от порядка
доставки». Свойство поведения — тоже возможность.
Общая форма записи (мета, слаг, строка индекса) — [task-format.md](task-format.md).
Здесь только то, что у этого типа своё.
## Схема
| | |
| --- | --- |
| Заголовок отвечает на | что приложение будет уметь |
| Обязательные разделы | `Завершение` |
| Допустимые сверх того | — |
| Поле места | **Секция** — часть роадмапа |
| Цель (`goal:<слаг>`) | запрещена: цель и есть цель |
| Индекс | `ROADMAP.md`, и никогда `BACKLOG.md` или `SPRINT.md` |
| Берётся в спринт | нет — берутся её задачи |
Поле места у цели называется **«Секция»**, а не «Категория», и это не разнобой:
у задачи оно называет полку домена, в которую она вернётся из спринта, а у цели
— часть роадмапа, то есть состояние очереди. Одно имя на два смысла и было
конфляцией.
## «Завершение» — списком, а не абзацем
Это признаки того, что приложение **уже умеет**, и на строки этого раздела
ссылаются задачи цели: «двигает пункт 2 «Завершения» — накопительная метрика
перестаёт уменьшаться». Абзацем такая ссылка не берётся, поэтому список.
Отсюда же читается обратное и более полезное: **строка «Завершения», к которой
не относится ни одна задача, — незакрытая часть возможности**. Достаточность
набора задач видна из самой цели, а не из чьей-то памяти.
## Алгоритм
1. **Проверить, что это возможность, а не работа.** Сборка, проверки, выкладка,
мониторинг, дежурство на вопрос «что приложение будет уметь» не отвечают. Им
отведена секция `Сопровождение` — там они видны в том же экране и не читаются
как обещание продукта. Граница проходит по тому, **кто наблюдает**:
«приложение сообщает о своём состоянии» — возможность, «дежурный видит
состояние на одном экране» — сопровождение.
2. **Выбрать секцию.** Очередь значима и обоснована прозой — `Запланировано`;
тянется долго и очереди не имеет — `Направления`; про то, чем держат проект,
`Сопровождение`. В `Готово` кладёт сам `close`.
3. **Написать «Завершение»** — 2–5 наблюдаемых признаков списком. Пишутся до
декомпозиции: иначе задачи придумают себе цель задним числом.
4. **Разложить на задачи** и проставить им `goal:<слаг>`. Перечень задач в теле
цели **не хранится** — он был бы третьим индексом и поехал бы на первой же
закрытой задаче; выводит `tasks.py list --goal <слаг>`.
5. **Пометить `decomposed`.** Тег отличает «ещё не разобрана» от «все задачи
закрыты» — два состояния с одним внешним признаком. `check --fix` ставит его
сам цели, у которой задачи есть.
6. **Закрыть достигнутой**`close <слаг> --implemented`, когда не осталось
открытых задач. Файл удаляется, строка с датой переезжает в `Готово`. Скрипт
откажет, если задачи ещё живы.
## Что видит машина, а что человек
`check` считает цели, различает разобранные и пустые, ставит `decomposed`,
запрещает закрыть цель с живыми задачами и держит `Секцию` в согласии с
заголовком роадмапа. **Годность формулировки — не машине**: «возможность это или
область работ» решает [агент вычитки](../SKILL.md#вычитка-два-прохода-а-не-один).
Достигнутая цель **не исчезает**: «что приложение умеет» — половина вопроса, ради
которого роадмап открывают. Вторым домом поведения роадмап при этом не
становится: нормативное поведение живёт в `openspec/specs/`, роадмап отвечает,
**когда и в каком порядке** оно появилось.
@@ -0,0 +1,83 @@
# 🔬 `research` — исход работы знание, а не изменение системы
Ответ на вопрос, замер, разведка, проработка сырой мысли. Приёмка — **записанный
ответ**, а не изменённый код.
Общая форма записи (мета, слаг, строка индекса) — [task-format.md](task-format.md).
Здесь только то, что у этого типа своё.
## Схема
| | |
| --- | --- |
| Заголовок отвечает на | о чём разведка (предмет, а не действие) |
| Обязательные разделы | `Вопрос`, `Куда ляжет ответ` |
| Допустимые сверх того | `Рамки`, `Вопросы` |
| Поле места | **Категория** — полка домена беклога |
| Цель (`goal:<слаг>`) | нет |
| Индекс | `BACKLOG.md``SPRINT.md` |
| Берётся в спринт | да — **но только с заполненным «Вопросом»** |
**Критериев приёмки у `research` нет, и это не поблажка.** Критерии в форме
«оракул: тест» разведке натянуты: проверять нечего, пока ответа нет. Её приёмка
описывается раздельно — вопрос, на который отвечаем, и место, куда ляжет ответ.
**Заголовок формы действия не несёт намеренно.** Что делать, ещё неизвестно, и
заголовок-действие обещал бы решённость, которой нет. «Подсказка следующего
хода», а не «Сделать подсказку следующего хода».
## Этот тип вобрал прежний `[idea]`
Тип `idea` упразднён. Он значил не род работы, а **состояние незаполненности**
«первый, второй или третий вопрос теста готовности не отвечается», — а состояние
типом быть не может: оно меняется по мере того, как запись дописывают, а тип
меняют командой.
Теперь это состояние называется честно: **`research` без раздела «Вопрос» — это
сырьё**.
| | сырьё | разведка |
| --- | --- | --- |
| Раздел `Вопрос` | пуст или отсутствует | заполнен |
| `sprint take` | отказ | берёт |
| Место в секции беклога | **конец**, `check --fix` сносит туда сам | среди прочих |
| `tasks.py list --raw` | показывает | нет |
Порядка «по важности» в беклоге по-прежнему нет. Этот порядок **производен от
типа и заполненности**, а не назначен человеком, — потому его и проверяет машина,
и потому он не противоречит правилу «порядка нет, есть цель».
Сырьём заводится и **сырая функция**: «Подсказка следующего хода» — ещё не
`feature`, потому что неизвестно, что именно делать. Работа над ней — думание, и
её исход — либо задачи, либо отказ.
## Алгоритм
1. **Записать вопрос одной фразой.** Не тему, а вопрос: не «Разобраться с
выводом в терминалах», а «Какими символами рамки печатаются одинаково в
Терминале, iTerm и `tmux`». Вопроса ещё нет — запись заводится сырьём и
лежит в конце секции, пока вопрос не появится.
2. **Назвать, куда ляжет ответ**: `docs/research/<тема>.md`, ADR, тело этой
задачи. Место называется **заранее**, иначе ответ остаётся в переписке, а
через квартал разведку заказывают заново.
3. **Ограничить рамками**, если разведка может утечь: сколько времени, какие
источники, что заведомо вне.
4. **Провести разведку** и **записать ответ по названному адресу**. Числа — с
провенансом: с командой или условиями, которыми получены. Число без источника
проход ревью обязан читать как условие, а не как замер.
5. **Разложить исход на задачи** — если он их родил. Разведка кончается одним из
трёх: заведены задачи, записано знание, отказ. **Отказ — полноправный исход**:
«проверили, не проблема» экономит спринт.
6. **Закрыть**`close <слаг> --implemented`, когда ответ записан. Файл
удаляется: запись ответа и есть след, второго не нужно. Ушла без ответа —
`close --reason`, и строка уезжает в `REJECTED.md`.
## Что видит машина, а что человек
`check` и `sprint take` смотрят на **наличие непустых** разделов `Вопрос` и
`Куда ляжет ответ`, считают сырьё отдельной строкой здоровья и держат его в конце
секции. Годность вопроса — человеку: «вопрос это или тема» машина не различает,
и `check` о годности молчит намеренно.
Штурм сырья, дробление исхода на задачи и тест «части мерджатся порознь» —
[split.md](split.md).
File diff suppressed because it is too large Load Diff