Files
dev-skills/av-dev-pm/skills/tasks/references/task-format.md
T
avandClaude Opus 5 cbfae90f3f словарь: пять слов сняты, девять закрыты списком вместо оговорки «прижилось»
Проход упрощения уткнулся в один класс у всех пяти агентов: слово, живущее в
трёх-шести файлах разом. Правка в одном месте развела бы словарь, правка во
всех — уже не упрощение текста скилла. Каждый честно остановился и записал слово
в отчёт, и одни и те же слова всплыли в разных отчётах. Разобрано этим проходом.

Причина, по которой они вообще накопились, оказалась в самом уставе языка. Он
разрешал не переводить «термин, у которого нет точного русского эквивалента и
который в команде уже прижился». Проверить это нельзя: прижившимся выглядит
любое слово, встреченное трижды, — и ровно так рассудили пять агентов подряд,
каждый независимо. Оговорка заменена закрытым списком из девяти терминов с
колонкой «что называет»: интейк, триаж, провенанс, дедуп, чек-лист, дифф,
промпт, сущности OpenSpec, роды проходов ревью. Интейк оставлен потому, что
«заведение» называет создание файла, и слить их значит смешать две операции;
провенанс — потому что «источник» рядом называет саму запись, а не свойство
числа. Слово не из списка и не из таблицы имён вещей — находка, а не стиль.

Список заведён домом язык-словарь в language.md и копией в уставе doc-wording.
Копия обязательна: агент работает в репозитории проекта, где плагина может не
быть, и без списка предъявил бы интейк как англицизм.

Снято пять слов, 29 мест: конфляция → смешение, декорреляция → разведённость,
непоймание → почему не поймали, эвал-сет → проверочный набор, гайд →
руководство. Латинизм или калька при живом русском слове в каждом случае.

Разбор декорреляции показателен: проект уже владел нужным словом — «агенты
разведены по глубине», «разведены по охвату» — и держал рядом латинский синоним
того же понятия. Это не англицизм, а второй дом для слова.

Непоймание снято ещё и потому, что форма журнала дефектов, которую канон кладёт
в проекты, спрашивает «Почему не поймали», а проза рядом называла это «причиной
непоймания». Скелет и проза о скелете говорили разными словами.

Снятое записано вместе с оставленным, в одном списке и с заменой каждого. Иначе
слово возвращается: из текстов оно уходит, но ничто не мешает следующему проходу
завести его заново — оно ведь короткое и точное на вид.

Тема 32 в DECISIONS.md, следствия 124-126. Нумерация правил в уставе doc-wording
сдвинута: словарь встал шестым, жаргон и далее уехали на единицу.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:35:06 +03:00

34 KiB
Raw Blame History

Формат записей и индексов

Заголовок, мета-блок и строку индекса ставит tasks.py add — руками их не пишут. Этот файл описывает общую форму любой записи и то, что проверяет check; тело дописывает агент.

Чем разделы тела отличаются от типа к типу, какой алгоритм у каждого типа и что у него обязательно — отдельным файлом на тип:

Тип Файл Одной строкой
🎯 goal task-goal.md возможность приложения
feature task-feature.md снаружи появляется то, чего не было
🐞 fix task-fix.md поведение расходится с заявленным
🧹 chore task-chore.md обслуживание, поведение не меняется
🔬 research task-research.md исход — знание, а не изменение

Файл записи

items/<slug>.md:

# 🐞 Не отбрасывать молча лишние символы в ходе

- **Тип:** fix
- **Категория:** Ядро — вышла из спринта: остаток писал нерешённое в журнал
- **Зачем:** ввод «а1б2» ходит в a1 — игрок не видит, что ошибся, и винит игру
- **Теги:** goal:merge-robustness, sprint:2026-08-03

Разбор хода читает первые два символа и молча выбрасывает остаток строки.

## Воспроизведение

Ввести `а1б2` в свой ход: программа ходит в `a1` и ничего не сообщает.
Ожидалось — отказ с ошибкой разбора.

## Затрагивает

Разбор строки хода; текст ошибки в выводе партии. Формат сохранения партии
не трогается.

## Критерии приёмки

- ввод «а1б2» отвергается с ошибкой — оракул: тест разбора
- ввод «а1» принимается по-прежнему — оракул: тест разбора

## Рамки

Схема не трогается; данные только читаются; перезапуск допустим.

Связано: решение о канонической форме содержимого.
  • Заголовок H1 — он же заголовок строки в индексе, дословно. Начинается эмодзи типа, и она производна: её ставит add и чинит check --fix по полю меты. Второго дома у типа нет — эмодзи это его отображение, как строка индекса это отображение файла.
  • Форма заголовка — по типу. Цель отвечает на «что приложение будет уметь»; feature, fix и chore — на «что нужно сделать», глаголом в неопределённой форме, перед ним допускается «не»; research называет предмет разведки и формы действия не несёт намеренно. Почему так — SKILL.md, «Как написана задача». check считает заголовки не в форме действия и печатает число в здоровье; годность формулировки смотрит агент task-form.
  • Мета-блок — список сразу после заголовка, поле на строку. Обязательны тип и место, причина после тире желательна (именно она объясняет, почему задача здесь оказалась — в том числе «вышла из спринта: …»), «зачем» и теги необязательны. Нераспознанные поля сохраняются: скрипт правит свои и не трогает чужие.
  • Тип — первым полем. Он решает, что у записи вообще может быть: какие разделы обязательны, нужна ли цель, берётся ли она в спринт, — и читается раньше всего остального. Словарь закрыт: goal | feature | fix | chore | research. Не подходит ни один — это сигнал, что в записи их два и её надо разделить.
  • «Зачем» отвечает на «зачем нужна эта задача» — состояние, остаток, боль. Не пересказ задачи: пересказ уже есть по ссылке. Живёт здесь, а не только в индексе: строка индекса его повторяет и производна от него, check сверяет, check --fix восстанавливает пропавшую строку вместе с ним. Пока поле лежало только в индексе, штатная починка дрейфа теряла его молча и навсегда — а это единственное, по чему задачу выбирают, не открывая.
  • Тело — одна фраза «что станет наблюдаемо иначе», дальше разделы по схеме типа. Пишется на языке документации проекта: предметно, без англицизмов, у которых есть русское слово, и без терминов, которых нет ни в паспорте, ни в архитектуре, ни в конвенциях (правило и его причина — в SKILL.md, раздел «Как написана задача»).

Тело — не план реализации и не спецификация: принятое и реализованное переезжает в документацию проекта, а файл задачи удаляется.

Поле места: «Категория» и «Секция»

Поле называет, где числится строка, и имя у него зависит от типа:

Тип Поле Значения Что это
goal Секция Запланировано, Направления, Сопровождение часть роадмапа: состояние очереди
прочие Категория секции беклога проекта (Ядро, Инфра, …) полка домена, в которую задача вернётся из спринта

Разные имена потому, что это разные вещи. У задачи поле переживает спринт: sprint drop возвращает её именно туда. У цели оно называет не полку, а место в очереди работ. Одно имя на два смысла их и смешивало; check называет несовпадение дрейфом, check --fix переименовывает.

Имя самого места принадлежит заголовку индекса — файл на него лишь ссылается, и принадлежность сверяется по нижнему регистру.

Прежние формы, которые читаются, но не пишутся

Всё это check называет дрейфом, а check --fix переписывает:

Было Стало
префикс [goal] / [idea] в H1 поле Тип + эмодзи в H1; [idea]research
тег kind:<род> поле Тип (род работы стал типом)
поле Секция у задачи поле Категория
поле Хук поле Зачем
мета одной строкой через · мета списком, поле на строку

Единственное, чего --fix не делает сам, — проставить тип записи, у которой его неоткуда взять: feature от chore машина не отличает, и подставленное наугад значение врало бы ровно там, где по нему принимают решение. Такие записи он называет поимённо пометкой НЕОДНОЗНАЧНО.

Затрагивает

Перечень границ, которых изменение касается. Границей считается то, у чего есть внешняя сторона и цена изменения:

  • эндпоинт, команда, форма ответа, код ответа;
  • таблица, поле, миграция, формат на диске, формат сообщения в очереди;
  • публичный тип или функция пакета, конфиг и его образцы;
  • внешний сервис или библиотека, чьё поведение становится нужным.

Ничего из этого не трогается — так и пишется: «границ не трогает, изменение внутри одного узла». Это ответ, а не пустой раздел.

Границы, а не замысел. «Переписать хранилище на новый драйвер» — замысел; таблица points и её миграция, эндпоинт POST /ingest — границы. Разница проверяется вопросом «это можно назвать до того, как решено как делать?»: если нет, строка описывает реализацию, и её место в предложении об изменении.

Свойства репозитория сюда не пишутся — по той же причине, что и в рамки: имя таблицы стабильно, номер последней миграции протухает молча. Пишется таблица points и её миграция, а не миграция 0042.

Что из этого механизировано. check и sprint take смотрят только на наличие непустого раздела. Полнота перечня машине не видна: границу, которую забыли назвать, она от отсутствующей не отличает. Раздела нет — отказ во взятии: оценивать нечем.

У goal и research раздела нет — у первой границы называют её задачи, у второй они становятся известны, когда из разведки родятся задачи.

Критерии приёмки

2–5 проверяемых утверждений списком - …, у каждого назван оракул. Не «работает корректно», а «повторный прогон даёт тот же отпечаток — оракул: команда сверки». Это не второе определение готовности, а проектная конкретизация вопроса «по чему видно, что закончено» из теста готовности ниже: там сказано «признак завершённости», здесь — «признак плюс чем проверяется».

Что из этого механизировано. check и sprint take считают пункты: меньше двух — отказ («— работает» одной строкой больше не проходит), больше пяти — замечание, обычно это признак, что задача крупнее задачи. Наличие оракула проверяется эвристикой — словом «оракул» в пункте, — и потому даёт только замечание: настоящий оракул от слова «оракул» машина не отличает, и делать вид, что проверено больше проверенного, хуже, чем не проверять вовсе.

У research критериев нет — её приёмка это записанный ответ, и описывается она разделами «Вопрос» и «Куда ляжет ответ». У goal их заменяет «Завершение».

Критерии — пол, но расхождение с ними есть дефект критериев. Если приёмщик видит, что критерии закрыты, а суть задачи не достигнута, он правит критерии и возвращает задачу исполнителю, а не держит невидимое сверх-требование. Иначе исполнитель никогда не знает, закончил ли, и мотивирован занижать критерии заранее.

Рамки

Одна строка: чего касаться нельзя, что перезапускается, что считается необратимым, трогается ли схема данных. Раздел допустим у любого типа задачи и ни у одного не обязателен. Свойства репозитория сюда не пишутся — номер последней миграции, версия зависимости, хеш: в лежалой задаче они протухают молча и становятся ложной рамкой. Снимок берётся при постановке, а не при заведении.

Вопросы

Неразобранное решение человека живёт разделом ## Вопросы плюс тегом question. Раздел без тега или тег без раздела — дрейф, check о нём скажет.

Раздел Вопросы (о решении человека) и раздел Вопрос у research (предмет разведки) — разные вещи и разные слова: первый блокирует взятие, второй его разрешает.

Судит факт, а не метка. Отказ во взятии даёт непустой раздел «Вопросы», независимо от того, стоит ли тег: иначе забывший тег проходил бы, а поставивший спотыкался — стимул ровно обратный записанному правилу. Тег производен: он нужен отбору снаружи файла (list --questions, list --tag question), и его отсутствие при непустом разделе — замечание, а не лазейка. Тег без раздела тоже отказ, но с другим советом: либо вопрос записан не туда, либо тег пора снять.

Ответ на вопрос — три правки, и первая обязательна. Раздел «Вопросы» опустошается: ответ переезжает в тело решением, а не остаётся вопросом рядом с ответом. Затем снимается тег (edit <slug> --rm-tag question) и переписывается «зачем»: «Решено: …» на вопрос «зачем нужна эта задача» уже не отвечает.

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

Файл цели

Форма та же, разделы и алгоритм — task-goal.md.

# 🎯 Исход слияния не зависит от порядка доставки

- **Тип:** goal
- **Секция:** Направления
- **Теги:** decomposed

Ради чего: точки из разных доставок сходятся в один часовой объект, и сегодня
исход столкновения зависит от порядка доставки, а не от содержания.

## Завершение

- повторная доставка тех же точек в другом порядке даёт то же состояние;
- накопительная метрика за сутки не уменьшается после повторной доставки;
- в логе видно, какая из двух точек выиграла и почему.
  • Задачи цели здесь не перечисляются. Перечень даёт tasks.py list --goal <слаг>; хранимый список стал бы третьим индексом и поехал бы на первой же закрытой задаче.
  • Тег decomposed отличает «цель ещё не разобрана» от «все её задачи закрыты» — два состояния, у которых снаружи один и тот же признак: задач нет. Пометка именно тегом, а не строкой в теле: только так она проверяется. check напоминает о нём у цели без задач замечанием — неразобранная цель законна и зелёного прогона не ломает; check --fix сам ставит его цели, у которой задачи есть, а цель с тегом и без задач — прямое приглашение закрыть.
  • Цель живёт в ROADMAP.md и никогда — в BACKLOG.md или SPRINT.md.
  • Достигнутая цель не исчезает. close <слаг> --implemented удаляет файл и переносит строку в секцию Готово с датой: - 2026-08-04 \merge-order` — Исход слияния не зависит от порядка доставки. …Ссылки на файл в ней нет — файл удалён, а битая ссылка это ошибкаcheck`. Поведение живёт в спеках проекта; роадмап отвечает, когда и в каком порядке оно появилось.

Слаг

Латиница и цифры, kebab-case, без ведущих, хвостовых и двойных дефисов (foo-bar, не -foo, a--b). Именуется по сути, а не по текущей формулировке: заголовок будет переписан при переоценке, а слаг стоит в ссылках из других задач, коммитов и черновиков. Транслита не заводимtie-break-equal-completeness, а не taj-brejk-pri-ravnoj-polnote: транслит нечитаем для того, кто ищет по смыслу, и не сокращается.

Переименование слага — не правка, а перенос ссылок: делается одним атомарным проходом по всем местам, где слаг упомянут, иначе останутся битые ссылки, которых никто не проверяет.

Индексы

Строка везде одной формы:

- [🐞 Заголовок дословно](items/slug.md) — зачем

«Зачем» отвечает на «зачем нужна эта задача» одним предложением: состояние, остаток, боль. Пересказ первого абзаца бесполезен — он уже есть по ссылке. Эмодзи внутри квадратных скобок не украшение: заголовок копируется дословно, и тип виден там, где решают «брать или не брать».

Файл Что отвечает Секции
ROADMAP.md что приложение уже умеет и чего ещё не умеет канонические и в этом порядке: Запланировано, Направления, Сопровождение, Готово (англ. Planned, Directions, Operations, Done)
BACKLOG.md что можно взять — только задачи категории проекта (по умолчанию Ядро/Инфра)
SPRINT.md какая цель и какой набор под неё одна: «Набор»
REJECTED.md что ушло без реализации и почему

Шапку SPRINT.md пишет sprint startтем же мета-блоком, что у задачи: поле на строку, - **Цель:** [Заголовок](items/slug.md), - **Начат:** датой, - **Спринт:** слагом, которым метится урожай. Прежняя форма (три поля одной строкой через ·) читается по-прежнему и уходит сама: файл переписывается на следующем sprint start и очищается на sprint close.

Секции — единственные заголовки ## в индексе: любой другой ## в преамбуле проверка сочтёт секцией.

Порядка «по важности» внутри секции беклога нет — «что делать дальше» отвечает набор спринта. Единственный порядок, который есть, производен от типа и заполненности: сырьё (research без раздела «Вопрос») стоит в конце своей секции, потому что его не берут, и между берущимся оно каждый раз требует открыть файл, чтобы это понять. Проверяет check, переставляет check --fix, и человек этот порядок не назначает — иначе он был бы приоритетом, которого здесь нет.

Секции «блокеры» среди них нет. Блокер — состояние, а не полка: он живёт до ответа человека, а следы остаются вопросами в файлах задач распущенного спринта. Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила бы правилу «эскалируем немедленно», поэтому init её не заводит, а check говорит о ней в чужом беклоге. Переезжаешь с такой секцией — удали её. В секции Запланировано очередь значима и обосновывается прозой; двигают строку move <slug> --section Запланировано --after <другой>. В секции Готово строки не той формы, что у прочих индексов: дата, слаг, заголовок — как в REJECTED.md, и по той же причине (файла уже нет, ссылаться некуда).

Секции роадмапа закреплены — состав, полнота, единство языка и порядок проверяются check; категории беклога проект называет сам. Почему так — SKILL.md. Порядок закреплён потому, что Готово копится: стоя первым, достигнутое отодвигает за экран то, ради чего роадмап открывают чаще всего.

Заголовок секции пишется с прописной и отбивается пустой строкой с обеих сторон — во всех индексах, включая категории беклога, имена которых выбирает проект. Написание канонических секций и отбивку правит check --fix; он же сводит написание места в мете файла с заголовком индекса.

Индексы производны: расходятся с файлом — правим индексы (check --fix). Строку руками не пишут.

Отсюда же ответ на «а если оборвётся посередине». Мутация сперва проверяет всё и складывает правки, и только потом пишет: сначала все временные файлы, потом переименования подряд. Полной транзакции на несколько файлов файловая система не даёт, но окно сжато до цепочки переименований, а всё, что в нём может разъехаться, — производное: файлы целы, индексы восстанавливает check --fix. Поэтому отказ на второй задаче из пяти не оставляет первую переписанной при нетронутых индексах.

SPRINT.md и есть артефакт заморозки: без него набор существует только в контексте сессии, и нарушение заморозки ненаблюдаемо.

REJECTED.md

Туда уходит задача, покинувшая беклог без реализации. Строку пишет tasks.py close --reason, а check следит за форматом:

- 2026-07-23 `versii-kachestvo-repaki` — Версии и качество одного тайтла.
  Причина: калибровка болей — не боль, ни разу не возникло за полгода.
  Была секция: Инфра.

Реализованные сюда не попадают: у них остаётся коммит и документация. У выкинутой не остаётся ничего — и через квартал она возвращается тем же текстом. Это первое место, куда смотрит дедупликация при заведении.

Запись не запрещает завести задачу заново: изменился контекст — заводим и ссылаемся на строку, объясняя, что изменилось.

Теги

Разметка сверх типа. Тип полем, потому что он один и обязателен; теги — потому что их много и list --tag уже умеет отбирать по ним порцию разбора.

  • goal:<слаг> — цель, которой служит задача. Обязателен у feature: новая возможность и есть содержание цели. У fix, chore и research его может не быть — они служат работоспособности, а не направлению, и в набор спринта входят помимо его цели.
  • question — в файле есть неразобранный раздел «Вопросы».
  • sprint:<слаг> — задача заведена в этом спринте; по нему отбирается первая порция разбора («урожай спринта»). Ставится сам: слаг спринта заводит sprint start (по умолчанию — дата начала, он же пишется в SPRINT.md), и add при открытом спринте помечает заводимое. Тег, который надо помнить ставить руками, не ставится никогда — а на нём висит правило «первая порция разбора — урожай прошедшего спринта».
  • decomposed — на цели: разложена на задачи (см. «Файл цели»).

Тега kind:<род> больше нет: род работы стал типом. Оставшийся в файле check называет дрейфом, а check --fix снимает, перенеся значение в поле «Тип».

Отбор — list --tag a,b: перечисленные через запятую теги требуются все сразу (это И, не ИЛИ). Тег, которого нет ни у одной задачи, list называет вслух: молчаливый ноль читается как «таких задач нет», а чаще это опечатка.

Свои теги проект заводит свободно (партия ревью review-ГГГГ-ММ-ДД, тема, источник) — словарь не фиксирован. В индексы теги не выносим: индексы производны, отбор делает list --tag, а не глаза.

Тест «готова к взятию»

Задача готова, если из файла отвечаются четыре вопроса. Первый и четвёртый — общие, второй и третий у каждого типа свои и перечислены в его файле.

  1. Что станет наблюдаемо иначе, когда она сделана — снаружи: пользователю, владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет ломаться Y при Z» — ответ. У chore адресат — разработчик, и это законно: «уедет последний вызов устаревшего API» — ответ, а не отговорка. Тип объявлен как раз затем, чтобы такие задачи не выдумывали себе пользовательскую пользу.

  2. Что известно про сегодня — то, что тип требует знать до работы: у fix это Воспроизведение, у researchВопрос, у feature и choreЗатрагивает.

  3. По чему видно, что закончено — критерии приёмки с оракулами; у research вместо них Куда ляжет ответ.

  4. Какую часть «Завершения» своей цели она двигает — у задачи с целью. Строкой: «двигает пункт 2 «Завершения» — накопительная метрика перестаёт уменьшаться». Это и есть защита от задачи «отрефакторить X»: она проваливает тест не потому, что невидима снаружи, а потому, что не находит строки, к которой относится. Заодно видно обратное — достаточен ли набор задач для цели: строка «Завершения», к которой не относится ни одна задача, это незакрытая часть возможности.

    У задачи без цели (fix, chore, research) вопрос не задаётся: они служат работоспособности, а не направлению.

Не отвечается первый, второй или третий вопрос → это ещё не задача, а сырьё: тип research без раздела «Вопрос», место — конец секции, работа над ним — штурм. Не отвечается четвёртый у feature → либо цель есть и не проставлена, либо это не новая возможность.

Отвечается всё, но задача не делается одним заходом и не мерджится целиком → это несколько задач под одной целью, дроби сразу. Промежуточного зонтика между целью и задачей нет: тип [epic] упразднён, потому что зонтиком стала сама цель.

Тест применяется при заведении и при переоценке. К старым задачам, которых операция не касается, задним числом не применяется — беклог не переоформляют «заодно».