backlog: переименовать плагин в av-dev-backlog, скилл — в backlog
Соглашение об именах: длинное имя плагина с префиксом av-dev- (уникально в маркетплейсе), короткие имена скилов внутри. Вызов — /av-dev-backlog:backlog, единообразно для будущих плагинов. Путь к backlog.py в SKILL.md обновлён под новую раскладку ($CLAUDE_PLUGIN_ROOT/skills/backlog/scripts/backlog.py). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# Задачи из аудита и ревью
|
||||
|
||||
Ревью и аудиты — код-ревью, архитектурный проход, аудит безопасности, любой
|
||||
разбор другим агентом — порождают находки, часть которых становится задачами
|
||||
беклога. Это отдельный интейк со своей опасностью, **зеркальной** интейку из
|
||||
диалога.
|
||||
|
||||
- Интейк из диалога грешит переполнением: из одной мысли рождается пять файлов.
|
||||
- Интейк из ревью грешит сваливанием: сорок сырых находок превращаются в сорок
|
||||
файлов. Беклог раздувается, а следующий груминг склеивает их обратно.
|
||||
|
||||
Защита от сваливания — та же, что в самом ревью: **кластеризация по причине, а не
|
||||
файл-на-находку.** Если у ревью был триаж — половина работы уже сделана, бери его
|
||||
выход. Если нет — триажируй сам, прежде чем заводить.
|
||||
|
||||
## Находка агента — не задача
|
||||
|
||||
Мнение агента — **гипотеза, пока у неё нет свидетельства** (падающий тест,
|
||||
воспроизводимый шаг, положение гайда). Согласие нескольких находок само по себе
|
||||
достоверность не повышает: это один источник, высказавшийся несколько раз.
|
||||
|
||||
Отсюда фильтр входа, поверх обычного «не делаем сейчас + пожалеем о потере»:
|
||||
|
||||
- **Находка со свидетельством**, отложенная к исполнению → **задача**.
|
||||
Свидетельство и последствие переносим в тело — это её «почему», то самое, что
|
||||
переживает запись.
|
||||
- **Находка без свидетельства / низкой уверенности** → **идея** (`[idea]`), а не
|
||||
задача. Она не заработала приоритизацию: сравнивать неподтверждённое не с чем.
|
||||
Её судьба — штурм, где либо найдётся подтверждение, либо она уедет на кладбище.
|
||||
- **Уже починено по ходу ревью** → **ничего**. Починенное не заводим.
|
||||
- **Развилка, решённая при ревью** → ничего; решённая «потом» → задача с
|
||||
зафиксированным вопросом.
|
||||
|
||||
## Порядок
|
||||
|
||||
1. **Возьми выход триажа, а не сырые находки.** Сырой отчёт — это симптомы до
|
||||
дедупликации; в нём одна причина размазана по нескольким строкам.
|
||||
2. **Кластеризуй по причине.** Пять находок об одном отсутствующем инварианте —
|
||||
одна задача, а не пять. Класс мелочи (nits, косметика) — **один пакетный файл**
|
||||
со списком пунктов, а не файл на каждую запятую.
|
||||
3. **Дедуп против беклога и кладбища.** Аудит переоткрывает уже заведённое и уже
|
||||
выкинутое. Нашлось в беклоге — дописываем находку в существующий файл. Нашлось
|
||||
на кладбище — это сигнал: причина отказа могла устареть, выноси пользователю, а
|
||||
не заводи молча заново.
|
||||
4. **Покажи карту до создания файлов.** Кластер → задача / идея / строка в
|
||||
пакетный файл / уже в беклоге / отброшено — пачкой через `AskUserQuestion`.
|
||||
Это тот же барьер, что и «три кандидата» в интейке из диалога: массовое
|
||||
заведение файлов без подтверждения — ровно тот отказ, ради которого интейк из
|
||||
ревью и выделен. Дешёвая мелочь по явному согласию может заводиться и без
|
||||
поштучного вопроса — но карта пользователю всё равно предъявляется.
|
||||
5. **Заводи утверждённое** через `backlog.py add`, с двумя добавками:
|
||||
- **тег партии** — `add … --tag review-ГГГГ-ММ-ДД` (или `audit-<тема>`), чтобы
|
||||
весь заход груминга поднимался одной командой `backlog.py list --tag …`;
|
||||
- **провенанс в теле** — кто нашёл, каким проходом, с каким свидетельством. Без
|
||||
него через месяц не отличить проверенную находку от догадки.
|
||||
6. `backlog.py check`.
|
||||
|
||||
## Отображение серьёзности на приоритет
|
||||
|
||||
Правило концептуальное, от полей конкретного отчёта не зависит:
|
||||
|
||||
- **выше серьёзность → выше приоритет.** Самый тяжёлый класс находок → верхняя
|
||||
секция индекса, следующий → следующая. Отображать словарь серьёзности отчёта на
|
||||
словарь приоритетов проекта точно нечем — при сомнении спрашивай пользователя.
|
||||
- **низкая уверенность или нет свидетельства → идея**, не задача.
|
||||
- **мелочь → строка в пакетный файл**, не отдельный.
|
||||
- **уже починено / развилка решена сейчас → ничего.**
|
||||
|
||||
Если у ревью структурированный отчёт с полями серьёзности, уверенности,
|
||||
свидетельства и предписанного действия (например, конвейер ревью jellybit даёт
|
||||
`Severity`/`Confidence`/`Оракул`/`Действие: инлайн|развилка`) — правило выше
|
||||
ложится на эти поля механически. Но это пример одного формата, а не требование к
|
||||
источнику: тот же фильтр применяется к находкам в свободной форме.
|
||||
|
||||
Границы покрытия отчёта — то, что ревью проверить **не смогло**, — не находки и в
|
||||
задачи не идут: у них нет предмета. Их место — в докладе, не в беклоге.
|
||||
|
||||
## Доклад
|
||||
|
||||
- Источник (какое ревью/аудит, сколько находок на входе).
|
||||
- Свёрнуто в задачи: N кластеров из M находок, со слагами и тегом партии.
|
||||
- Что не заведено и почему: починено инлайн, уже в беклоге, ушло в идеи, на
|
||||
кладбище.
|
||||
- `backlog.py check`.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Груминг беклога
|
||||
|
||||
Цель — выкинуть то, что перестало быть задачей, и вернуть остальному честное
|
||||
состояние. Не «пересмотреть всё», а «пересмотреть порцию до конца».
|
||||
|
||||
## Порция и правило остановки
|
||||
|
||||
Тридцать задач за один заход — это усталость и штамповка: последние десять
|
||||
получат «оставить» не потому, что живы, а потому, что сессия затянулась.
|
||||
|
||||
- **5–8 задач за сессию.** Больше — только если пользователь настаивает, и тогда
|
||||
разбей на явные порции с промежуточным докладом.
|
||||
- **Отбор порции** — один из:
|
||||
- `backlog.py list --stale` — самые залежавшиеся по дате последней правки в
|
||||
git; поле «дата касания» заводить не надо, git её уже хранит;
|
||||
- одна секция приоритета целиком;
|
||||
- один тег (`--tag`) — например, задачи, пришедшие из одного ревью;
|
||||
- список от пользователя.
|
||||
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
||||
|
||||
## Что делать с каждой задачей
|
||||
|
||||
Сперва то, что не требует ничьего решения:
|
||||
|
||||
1. **Проверь, не сделано ли уже.** Задача, реализованная попутно в соседнем
|
||||
изменении, — самая частая находка груминга. Смотри код, спеки, историю
|
||||
коммитов по ключевым словам задачи. Удаление задачи «как реализованной» —
|
||||
деструктивно и без следа (кладбище для реализованных не пишется), поэтому
|
||||
порог улики жёсткий: удаляем (`close <slug> --implemented`), только имея
|
||||
**конкретный коммит или строку спеки**, закрывающие задачу, и ссылка на них
|
||||
идёт в доклад. Есть лишь косвенные признаки — не удаляй сам, вынеси в пачку
|
||||
вопросов. Сделана частично → задача сжимается до остатка: тело правишь
|
||||
редактором, заголовок и хук — через `edit <slug> --title … --hook …`.
|
||||
2. **Проверь, не отменена ли решением.** ADR, спека или архивный change мог
|
||||
закрыть вопрос иначе. Тогда `close <slug> --reason "<ссылка на решение>"`.
|
||||
3. **Проверь пересечения внутри порции.** Две задачи об одном — содержимое в
|
||||
одну, вторую `close <slug> --reason "слита с <другой-slug>"`.
|
||||
|
||||
Затем — то, что решает пользователь:
|
||||
|
||||
4. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
|
||||
сценарий, обошли иначе. Здесь и звучит вопрос о выкидывании.
|
||||
5. **Тот ли приоритет** (тест и правила — в SKILL.md и task-format.md).
|
||||
6. **Задача ли это по-прежнему.** Не проходит тест «готова к взятию» → `edit
|
||||
<slug> --type idea`, и её дальнейшая судьба — штурм, а не приоритизация.
|
||||
Разрослась → `edit <slug> --type epic`, дальше декомпозиция.
|
||||
|
||||
## Храповик
|
||||
|
||||
Сильно залежавшаяся задача — сигнал сама по себе: её либо ни разу не собирались
|
||||
делать, либо нечем взять. Измеряй наблюдаемым — датой последней правки из git
|
||||
(`backlog.py list --stale` ставит такие первыми); счётчик «сколько грумингов
|
||||
пережила» нигде не хранится, поэтому на него не опирайся.
|
||||
|
||||
Задача из верхних строк `--stale`, которую и этот заход оставляет без изменений,
|
||||
**либо двигается (вверх или на кладбище), либо остаётся с явно записанной
|
||||
причиной**, почему её держим (`move <slug> --priority <тот же> --reason …`).
|
||||
Молчаливое «оставить как есть» на давно неподвижной задаче — это решение не
|
||||
принимать решение; запись причины превращает его в осознанное и не даёт тому же
|
||||
вопросу всплыть на следующем груминге. В примере ниже вариант «оставить» именно
|
||||
такой — с названной причиной, а не по умолчанию.
|
||||
|
||||
## Интерактив
|
||||
|
||||
- Вопросы — через `AskUserQuestion`, **не больше трёх за раз**. Порция в 5–8
|
||||
задач обычно даёт больше трёх суждений — тогда веди несколько итераций по ≤3, а
|
||||
не по одному на задачу и не одним перегруженным запросом.
|
||||
- К каждому варианту — **предварительное суждение**, рекомендация первым
|
||||
вариантом: «предлагаю выкинуть, потому что …». Пользователю дешевле возразить,
|
||||
чем судить с нуля.
|
||||
- Всё, что решается фактом (сделано / отменено / дублируется), решай сам и
|
||||
показывай списком в докладе, а не выноси в вопросы.
|
||||
|
||||
Пример одной итерации — три залежавшихся задачи, механику по ним уже разобрали:
|
||||
|
||||
> **Груминг: 3 залежавшихся (порция по `--stale`)**
|
||||
>
|
||||
> 1. `versii-kachestvo-repaki` — репаки, апгрейд 1080p→2160p
|
||||
> - Выкинуть на кладбище *(рекомендую)* — помечена «не боль», за полгода ни разу не возникла
|
||||
> - Оставить в низком
|
||||
> - Поднять в средний
|
||||
> 2. `backup-sqlite` — бэкап SQLite
|
||||
> - Оставить в среднем *(рекомендую)* — не сработала, но риск реальный
|
||||
> - Поднять в высокий — обгоняет `retention-ochistka-bd`: без бэкапа ретеншн опасен
|
||||
> - Выкинуть
|
||||
> 3. `guessit-sputnik` — guessit как сервис-спутник
|
||||
> - Понизить до `[idea]` *(рекомендую)* — не проходит тест «готова к взятию»
|
||||
> - Оставить задачей в низком
|
||||
|
||||
Каждый вариант несёт причину — ту самую, что уедет в `move --reason` или
|
||||
`close --reason`. Ответы применяй сразу и, если в порции осталось ещё, следующей
|
||||
итерацией показывай следующие ≤3.
|
||||
|
||||
## Доклад
|
||||
|
||||
- Что просмотрено: N из M, по какому признаку отобрана порция.
|
||||
- Изменения списком: удалено (реализовано), на кладбище (с причинами), понижено
|
||||
до идей, слито, переприоритизировано.
|
||||
- **Границы покрытия**: сколько задач не трогали и какие именно секции или теги
|
||||
остались — иначе доклад читается как «беклог разобран».
|
||||
- `backlog.py check` после правок; результат — строкой в докладе.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Декомпозиция и мозговой штурм
|
||||
|
||||
Обе операции превращают одну запись беклога в несколько (или в ноль). Разница в
|
||||
входе: декомпозиция дробит **готовую задачу**, штурм прорабатывает **идею**,
|
||||
которая ещё не задача.
|
||||
|
||||
## Тест декомпозиции
|
||||
|
||||
Задачу можно дробить, только если части удовлетворяют **обоим** условиям:
|
||||
|
||||
1. **Мерджатся независимо.** Часть Б не требует, чтобы часть А была уже влита.
|
||||
Есть порядок «сперва А, потом Б, иначе не собрать» → это не декомпозиция, а
|
||||
план реализации: шаги остаются **внутри одного файла** в разделе «Шаги».
|
||||
2. **Каждая даёт видимую пользу.** Часть, полезная только в комплекте с другой, —
|
||||
не самостоятельная задача. Пользу проверяй тем же тестом «готова к взятию»
|
||||
(task-format): что станет наблюдаемо иначе именно от этой части.
|
||||
|
||||
Не проходит хотя бы одно — **не дроби**. Ложная декомпозиция плодит файлы,
|
||||
которые нельзя взять поодиночке, и груминг потом их склеивает обратно.
|
||||
|
||||
## Что делать с родителем
|
||||
|
||||
После разделения родитель **не остаётся** третьей висящей строкой:
|
||||
|
||||
- части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
|
||||
Кладбище здесь — не «выкинули», а именно тот след, что переживает запись:
|
||||
через квартал вопрос «куда делась задача X» отвечается строкой кладбища со
|
||||
ссылками на наследников, а не археологией git;
|
||||
- родитель осмыслен как зонтик → `edit <slug> --type epic`, тело — ссылки на
|
||||
задачи-части, своих шагов у него нет.
|
||||
|
||||
Одно и то же не должно лежать и в родителе, и в части. Задвоение — то же
|
||||
расхождение, что ловит `check`, только внутри тел.
|
||||
|
||||
## Мозговой штурм идеи
|
||||
|
||||
Идея (`[idea]`) не проходит тест «готова к взятию»: неясно, что именно делаем.
|
||||
Штурм проясняет — и это **generative-операция, а не applicative**.
|
||||
|
||||
Applicative-штурм («перечисли задачи, следующие из идеи») выдаёт очевидное:
|
||||
перечисляется то, что уже видно в формулировке. Ценное — на уровень выше.
|
||||
|
||||
1. **Сперва — формы, а не задачи.** Предложи **три разные постановки** идеи и
|
||||
назови **компромисс каждой**: что она даёт, чем платит, что оставляет за
|
||||
бортом. Если получилась одна постановка — штурм не состоялся, это applicative.
|
||||
2. **Вынеси формы пользователю** через `AskUserQuestion` с компромиссами. Рамку
|
||||
выбирает он: это продуктовое решение, не механика.
|
||||
3. **Только выбранную форму** дроби по тесту декомпозиции выше.
|
||||
|
||||
**«Выкинуть» — полноправный исход штурма, а не его неудача.** Проработка, честно
|
||||
показавшая, что пользы нет или она несоразмерна цене, — это результат: идея
|
||||
уезжает на кладбище с этой самой причиной, и та причина гасит её повторное
|
||||
появление.
|
||||
|
||||
## Доклад
|
||||
|
||||
- Идея/задача на входе, выбранная рамка (для штурма), задачи-наследники со
|
||||
слагами и приоритетами.
|
||||
- Судьба родителя: удалён / стал эпиком / выкинут.
|
||||
- `backlog.py check` после правок.
|
||||
- Границы покрытия: какие постановки рассмотрены и какие сознательно отброшены —
|
||||
чтобы штурм не пришлось повторять с нуля.
|
||||
@@ -0,0 +1,104 @@
|
||||
# Формат беклога
|
||||
|
||||
Заголовок, мета-строку и строку индекса ставит `backlog.py add` — руками их не
|
||||
пишут. Этот файл описывает, что именно скрипт создаёт и что проверяет `check`;
|
||||
тело задачи (контекст, шаги, ссылки) дописывает агент.
|
||||
|
||||
## Файл задачи
|
||||
|
||||
`<slug>.md` в каталоге беклога:
|
||||
|
||||
```markdown
|
||||
# Раздачи с докачиванием (merge при повторном добавлении)
|
||||
|
||||
**Приоритет:** высокий — блокирует типовой сценарий свежих сериалов · **Теги:** layout, ingest
|
||||
|
||||
Свежий сериал раздают по мере выхода: торрент с 5 из 10 эпизодов позже
|
||||
перезаливают целиком, пользователь добавляет раздачу повторно. …
|
||||
|
||||
Шаги:
|
||||
- в плане раскладки отличать «путь занят живой ссылкой того же матча» от коллизии
|
||||
- merge-раскладка: существующее пропустить, недостающее доложить
|
||||
|
||||
Зависит от правила сходимости. Связано: drafts/logical-title-model.md §6.2.
|
||||
```
|
||||
|
||||
- **Заголовок H1** — он же заголовок строки в индексе, дословно. Тип кодируется
|
||||
префиксом `[idea]` / `[epic]`; обычная задача без префикса. Отдельного поля
|
||||
типа **нет**: два места для одного факта разъезжаются, а префикс виден прямо в
|
||||
индексе, где и принимается решение «брать или не брать».
|
||||
- **Мета-строка** — первая непустая строка после заголовка. Обязателен приоритет,
|
||||
причина после тире желательна, теги опциональны. Поля разделяются ` · `, их
|
||||
порядок свободный. `·` — служебный разделитель: в тексте причины его быть не
|
||||
должно, иначе причина обрежется по нему.
|
||||
- **Тело** — контекст (почему это вообще задача), принятые решения, шаги,
|
||||
ссылки на спеки, ADR, черновики, прошлые ревью. Пишется на языке документации
|
||||
проекта.
|
||||
|
||||
Тело — не план реализации и не спецификация: принятое и реализованное переезжает
|
||||
в документацию проекта, а файл задачи удаляется.
|
||||
|
||||
## Слаг
|
||||
|
||||
Латиница и цифры, kebab-case, без ведущих, хвостовых и двойных дефисов
|
||||
(`foo-bar`, не `-foo`, `a--b`). Именуется **по сути задачи, а не по текущей
|
||||
формулировке**: заголовок будет переписан на груминге, а слаг стоит в ссылках из
|
||||
других задач, коммитов и черновиков. Транслит русского названия допустим, если
|
||||
суть иначе не выражается коротко.
|
||||
|
||||
## Индекс
|
||||
|
||||
`README.md` в том же каталоге: преамбула, затем секции по приоритетам, в каждой —
|
||||
строки вида
|
||||
|
||||
```markdown
|
||||
- [Заголовок задачи дословно](slug.md) — хук
|
||||
```
|
||||
|
||||
Хук отвечает на «почему это лежит в беклоге» одним предложением: состояние,
|
||||
остаток, боль. Пересказ первого абзаца бесполезен — он уже есть по ссылке.
|
||||
|
||||
Порядок секций задаёт порядок приоритетов, их названия — единственный словарь
|
||||
уровней. Внутри секции порядок значения не имеет. Секции приоритетов — **единственные
|
||||
заголовки `##` в индексе**: любой другой `##` в преамбуле проверка сочтёт уровнем
|
||||
приоритета.
|
||||
|
||||
Индекс **производен**: расходится с файлом — правим индекс. Строку индекса руками
|
||||
не пишут — её ставит `backlog.py add` в секцию приоритета и двигает `move`.
|
||||
|
||||
## Кладбище — `CLOSED.md`
|
||||
|
||||
Туда уходит задача, покинувшая беклог **без реализации**. Строку пишет
|
||||
`backlog.py close --reason`, а `check` следит за её форматом:
|
||||
|
||||
```markdown
|
||||
- 2026-07-23 `versii-kachestvo-repaki` — Версии/качество одного тайтла (репаки,
|
||||
апгрейд 1080p → 2160p). Причина: калибровка болей — не боль, ни разу не
|
||||
возникло за полгода. Был приоритет: низкий.
|
||||
```
|
||||
|
||||
Реализованные сюда не попадают: у них остаётся коммит, спека, ADR. У выкинутой не
|
||||
остаётся ничего — и через квартал она возвращается тем же текстом через инбокс.
|
||||
Кладбище — первое место, куда смотрит дедупликация при заведении.
|
||||
|
||||
Запись на кладбище не запрещает завести задачу заново: изменился контекст —
|
||||
заводим и ссылаемся на строку кладбища, объясняя, что изменилось.
|
||||
|
||||
## Тест «готова к взятию»
|
||||
|
||||
Задача готова, если из файла отвечаются три вопроса:
|
||||
|
||||
1. **Что станет наблюдаемо иначе**, когда она сделана — снаружи: пользователю,
|
||||
владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет
|
||||
ломаться Y при Z» — ответ.
|
||||
2. **По чему видно, что закончено.** Признак завершённости, а не список работ.
|
||||
3. **Почему приоритет такой** — одна строка.
|
||||
|
||||
Не отвечается первый или второй вопрос → это **идея**, её место в штурме, а не в
|
||||
приоритизации. Приоритизировать идеи бессмысленно: сравнивается неизвестно что.
|
||||
|
||||
Отвечается всё, но задача не делается одним заходом и не мерджится целиком →
|
||||
**эпик**, сперва декомпозиция.
|
||||
|
||||
Тест применяется при заведении и на груминге. К старым задачам, которых операция
|
||||
не касается, задним числом не применяется — беклог не переоформляют «заодно».
|
||||
Reference in New Issue
Block a user