av-dev-pm расколот на av-dev-docs и av-dev-tasks

Плагин владел двумя разными вещами сразу — документацией проекта и учётом работ,
— и это мешало обеим. Канон нельзя было поставить без задач, задачи без канона, а
язык проектных текстов лежал внутри скилла canon и потому принадлежал половине.
Теперь плагина два, каждый ставится сам по себе.

av-dev-docs: скиллы canon, docs, init; агенты doc-consistency, doc-code-drift,
doc-wording; скрипт docs.py. av-dev-tasks: скиллы tasks, session; агенты
task-form, task-wording; скрипт tasks.py.

Между собой они зовутся через пространство имён, а не по пути в чужое дерево.
Все относительные ссылки, пересекшие границу плагина, сняты: tasks больше не
указывает в canon, canon не указывает в tasks. Вместо ссылки — имя скилла и
оговорка, что вызов может не разрешиться, и это исход, а не поломка.

То, что нужно обоим дословно, стало вторым общим домом. Словарь «Сопровождение и
эксплуатация» назван в трёх местах трёх плагинов — секция роадмапа, раздел
«Эксплуатация» в architecture.md, тема ревью operations — и ни один из трёх им не
владеет; он уехал в shared/operations.md, а canon.md и скилл задач везут копии.
Три перечня «чем держат проект» уже разъезжались на «метриках и логах» против
«мониторинга», так что ссылка тут не годится: плагин, поставленный в одиночку,
получил бы указатель в никуда. Тем же способом язык: у av-dev-tasks появилась
своя копия language.md.

Копий стало 18 при 8 домах.

Переименования разведены по смыслу, а не заменой строки: где речь о каноне —
av-dev-docs, где об учёте задач — av-dev-tasks. В пайплайне таких мест
одиннадцать, и оба адресата там встречаются вперемешку.

Журналы (DECISIONS, TODO, HISTORY) намеренно не тронуты: они описывают состояние
на момент записи. По той же причине оставлена наблюдённая строка в комментарии
docs.py — она цитирует конфиг живого проекта, а не называет плагин.

Не входит в этот заход и названо отдельно: слияние canon и docs в один скилл,
разделение docs/.pm.json на два конфига и переезд openspec в пайплайн.

Гейт зелёный: копии, фронтматтеры, диаграммы, json. Оба скрипта прогнаны после
переезда — docs.py version и tasks.py check на фикстуре.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-08-09 14:06:26 +03:00
co-authored by Claude Opus 5
parent 86e22d932c
commit 00ddfb0dde
42 changed files with 417 additions and 97 deletions
@@ -0,0 +1,115 @@
# Задачи из аудита и ревью
Ревью и аудиты — код-ревью, архитектурный проход, аудит безопасности, любой
разбор другим агентом — порождают находки, часть которых становится задачами.
Это отдельный интейк со своей опасностью, **зеркальной** интейку из диалога.
- Интейк из диалога грешит переполнением: из одной мысли рождается пять файлов.
- Интейк из ревью грешит сваливанием: сорок сырых находок превращаются в сорок
файлов. Беклог раздувается, а следующая переоценка склеивает их обратно.
Защита от сваливания — та же, что в самом ревью: **кластеризация по причине, а
не файл-на-находку.** Если у ревью был триаж — половина работы уже сделана, бери
его выход. Если нет — триажируй сам, прежде чем заводить.
## Находка агента — не задача
Мнение агента — **гипотеза, пока у неё нет свидетельства** (падающий тест,
воспроизводимый шаг, положение руководства). Согласие нескольких находок само по себе
достоверность не повышает: это один источник, высказавшийся несколько раз.
Отсюда фильтр входа, поверх обычного «не делаем сейчас + пожалеем о потере»:
- **Находка со свидетельством**, отложенная к исполнению → **задача**.
Свидетельство и последствие переносим в тело — это её «почему», то самое, что
переживает запись.
- **Находка без свидетельства / низкой уверенности** → **сырьё**: `research`, у
которого раздел «Вопрос» и есть недостающее свидетельство («при каких условиях
это воспроизводится»). Не `fix`: без `Воспроизведения` его в спринт не
возьмут, и правильно — чинить нечего, пока непонятно, что ломается. Судьба
сырья — штурм, где либо найдётся подтверждение, либо оно уедет в
`REJECTED.md`.
- **Уже починено по ходу ревью** → **ничего**. Починенное не заводим.
- **Развилка, решённая при ревью** → ничего; решённая «потом» → задача с
вопросом в разделе «Вопросы» и тегом `question`.
## Порядок
1. **Возьми выход триажа, а не сырые находки.** Сырой отчёт — это симптомы до
дедупликации; в нём одна причина размазана по нескольким строкам.
2. **Кластеризуй по причине.** Пять находок об одном отсутствующем инварианте —
одна задача, а не пять. Класс мелочи (nits, косметика) — **один пакетный
файл** со списком пунктов, а не файл на каждую запятую.
3. **Дедуп против живых задач и `REJECTED.md`.** Аудит переоткрывает уже
заведённое и уже выкинутое. Нашлось среди живых — дописываем находку в
существующий файл. Нашлось в `REJECTED.md` — это сигнал: причина отказа могла
устареть, выноси пользователю, а не заводи молча заново.
4. **Разложи по целям — там, где цель нужна.** Большинство находок ревью это
`fix` и `chore`, и **цель им не требуется**: они служат работоспособности, а
не направлению, и в спринт входят помимо его цели. Придуманная им цель —
ровно то враньё, от которого спасает тип.
Цель обязательна у находки, которая оказалась **новой возможностью**
(`feature`): нашлось поведение, которого никто не заказывал, и его надо
либо заказать целью, либо убрать. Подходящей цели нет — заведи её
(`add --type goal --section Направления`) в том же проходе.
5. **Покажи карту до создания файлов.** Кластер → задача / сырьё / строка в
пакетный файл / уже заведено / отброшено, и под какую цель — пачкой через
`AskUserQuestion`. Это тот же барьер, что и «три кандидата» в интейке из
диалога: массовое заведение файлов без подтверждения — ровно тот отказ, ради
которого интейк из ревью и выделен. Дешёвая мелочь по явному согласию может
заводиться и без поштучного вопроса — но карта пользователю предъявляется
всё равно.
6. **Заводи утверждённое** через `tasks.py add`, с тремя добавками:
- **тег партии** — `--tag review-ГГГГ-ММ-ДД` (или `audit-<slug>`), чтобы весь
заход разбора поднимался одной командой `list --tag …`;
- **тип** — `--type`, и он **не по умолчанию `fix`**: починкой считается
расхождение с заявленным поведением, а находка «этого свойства никто не
заказывал» — это `feature`, находка «не знаем, как поведёт себя драйвер» —
`research`. Тип, розданный оптом, врёт ровно там, где по нему потом
отбирают, **и требует не тех разделов**: каждому `fix` придётся заполнить
`Воспроизведение`, а у находки без свидетельства его нет;
- **провенанс в теле** — кто нашёл, каким проходом, с каким свидетельством.
Без него через месяц не отличить проверенную находку от догадки.
7. `tasks.py check`.
## Куда девается серьёзность, если приоритетов нет
Приоритетов нет, и отображать серьёзность некуда — но **выкидывать её нельзя**.
Правило замены:
- **тяжёлая находка со свидетельством** → задача под ту цель, которой она
угрожает, и **кандидат в ближайший набор**: серьёзность здесь превращается в
довод при выборе цели следующего спринта, а не в уровень в файле. Довод
записывается причиной в мете (`--reason`), иначе к моменту набора его
никто не вспомнит;
- **находка, ломающая уже идущий спринт**, — не интейк вовсе: см. правило
вторжения в скилле `session`. В беклог она падает, только если врываться не
положено;
- **низкая уверенность или нет свидетельства** → сырьё (`research` с пустым
разделом «Вопрос»);
- **мелочь** → строка в пакетный файл;
- **уже починено / развилка решена сейчас** → ничего.
Словарей серьёзности много, и отображать их механически не на что: при сомнении
— вопрос пользователю, а не догадка.
## Поимённая сверка
Интейк считается выполненным, только если **каждая** находка триажа получила
исход: слаг заведённой задачи, ссылку на существующую, строку пакетного файла
или запись «не заведена: причина». Нулевой урожай при непустом отчёте триажа
виден сразу — и это единственный способ отличить «находок не было» от «не стал
заводить». Список составляет не тот, кто отчитывается о заведении.
Границы покрытия отчёта — то, что ревью проверить **не смогло**, — не находки и
в задачи не идут: у них нет предмета. Их место в докладе, не в беклоге.
## Доклад
- Источник (какое ревью/аудит, сколько находок на входе).
- Свёрнуто в задачи: N кластеров из M находок, со слагами, целями и тегом партии.
- Что не заведено и почему: починено инлайн, уже заведено, стало сырьём, ушло в
`REJECTED.md`.
- Поимённая сверка: находок на входе N, исход есть у N.
- `tasks.py check`.