Files
dev-skills/DECISIONS.md
T
avandClaude Opus 5 c692436b91 линтеры скриптов: ruff и pyrefly через uv, починены 41 находка
Скрипты остаются на голом python 3.12 без зависимостей: pyproject.toml живёт
только в этом репозитории и держит линтеры, а не зависимости скриптов.

Ноль зависимостей охраняется дважды: banned-api у ruff ловит частые соблазны по
имени, pyrefly видит окружение без ничего и не разрешает любой сторонний импорт.

Версии прибиты точно, uv.lock под git: обновление линтера меняет набор находок,
а находки правятся руками в скриптах, которые уезжают в чужие проекты.

RUF001–003 выключены — весь текст русский, 311 срабатываний из 338 шум.
av-dev-backlog исключён: заморожен до удаления, правка без выгоды.

Из 41 находки содержательных две: мёртвая ques в check и два места, где
find_entry_index может вернуть None прямо в list.pop и range. По ревью там
стоит raise, а не continue: тихий пропуск превратил бы сломанный инвариант в
отчёт «индексы согласованы».

os из tasks.py ушёл целиком, fail() в docs.py объявлен NoReturn.

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

61 KiB
Raw Blame History

Решения по устройству процесса

Журнал согласований: что решено, почему и что из этого следует. Пишется по ходу разбора тем, одна тема — один раздел. Причина обязательна: через месяц она забывается раньше факта.

Незакрытые остатки прошлого захода — REMAINING.md.

Требования, зафиксированные по ходу

Не решения — вход, который обязан быть удовлетворён и разбирается в названной теме.

Т1. Адаптация и проверка проекта под канон — обязательный скилл. Нужно уметь прийти в любой старый проект и перевести его на текущие рельсы. Канон при этом сам будет меняться, поэтому уже приведённые проекты тоже должны повышаться до новых версий. Разбирается в теме 5 (старт и жизненный цикл проекта).

Следствия, которые из этого уже видны:

  • У канона обязана быть версия, а у проекта — отметка, под какую он приведён. Иначе «соответствует канону» не имеет определённого ответа: сравнение идёт с тем, что модель помнит сейчас, а это и есть дрейф.
  • Журнал изменений канона — как миграции. Каждое повышение версии несёт запись «что добавилось, что переехало, что удалено, что сделать проекту». Без него адаптация переизобретается на каждом проекте.
  • Отметка версии машиночитаема. .docs.json отвергнут как указатель путей (решение F), но отметка версии — другое: её читает скрипт, и разбирать прозу CLAUDE.md для этого не нужно. Прецедент — .tasks.json.
  • Операций три: check (соответствие текущему канону), adopt (перевод чужой раскладки), upgrade (повышение с версии N до M по журналу). Первая и третья — одно сравнение с разными исходами.
  • Механизируемое и суждение не смешивать. Скрипт проверяет пути, лишние файлы, битые ссылки, версию. Агент судит о смысловых дублях (docs/specs/ recognition.md против capability recognition) и об оставшемся поведении в architecture.md. Скрипт, отчитавшийся «канон соблюдён» на проекте с тремя лишними файлами, хуже отсутствующего.
  • Границы плагинов: docs/tasks/ — часть канона документов, но владеет им av-dev-tasks со своим tasks.py adopt. Два плагина сходятся на одном каталоге. Тема 7.

1. Статус OpenSpec (2026-08-03)

Что было

OpenSpec несёт оба проекта: healthlog — 5 capability, 3530 строк спек, 9 архивных change за две недели; jellybit — 11 capability, 3895 строк, 43 архивных change. При этом в трёх местах плагина написана ветка «проект без OpenSpec» (task-pipeline предпосылки, review-pipeline предпосылки, task-batch предпосылки) — и не исполнялась ни разу.

Проектные факты живут в пяти домах: CLAUDE.md, docs/architecture.md, openspec/specs/, openspec/config.yamlcontext, и планируется шестой — docs/review-brief.md.

Расхождение измерено: у healthlog раздел «Хранилище» в docs/architecture.md — 950 строк (377–1328) против openspec/specs/storage/spec.md на 1337 строк. Два описания одного поведения, никем не сверяемые. У jellybit того же нет: docs/specs/architecture.md — 300 строк обзора, детали в 11 спеках. Проект с 43 изменениями держит архитектуру втрое короче проекта с 9.

Решено

A. OpenSpec — жёсткая предпосылка av-dev-pipeline. Ветки деградации удаляются, вместо них объявленная зависимость и проверка на старте. Зависимость на уровне плагина, а не процесса: av-dev-tasks, av-dev-git и будущий плагин документов от OpenSpec не зависят и работают на python/ansible-проектах.

Причина: непроверенная ветка деградации хуже честной строки «требуется OpenSpec» — она даёт ложную уверенность, что проект без спек поедет.

B. Нормативный дом поведения — openspec/specs/. architecture.md переопределяется как обзор: принципы, компоненты со ссылками на capability, внешние форматы данных, раскладка, деплой, открытые вопросы. Поведения он не описывает.

Причина: opsx:archive вливает дельты именно в openspec/specs/ — любой другой нормативный дом обязан синхронизироваться руками и разойдётся. Форма jellybit это уже подтвердила на 43 изменениях.

C. openspec/config.yamlcontext держит только нужды генерации. Язык, правила именования capability, придирки валидатора RFC 2119 — и ссылки. Правило ревью, пересказ конвенций и инварианты оттуда вычищаются: у них есть свои дома.

Причина: блок «Ревью (процесс, не артефакт)» в обоих config.yaml дословно повторяет шаги 4 и 7 task-pipeline. Это второй дом для правила, которым владеет плагин, и он разойдётся на первой же правке.

Что из этого следует

Из A:

  1. Три места с веткой деградации переписываются на объявленную предпосылку плюс проверку на старте (есть openspec/, разрешаются opsx:*) и внятный отказ: task-pipeline предпосылки, review-pipeline предпосылки, task-batch предпосылки.
  2. Описание av-dev-pipeline в маркетплейсе получает строку «требует OpenSpec».
  3. Факт для темы «объединять ли tasks и pipeline»: объединение потянуло бы зависимость от OpenSpec на управление задачами, которой там сейчас нет.

Из B:

  1. Правило «поведение — в спеку, устройство и границы — в архитектуру» становится контрактом плагина документов и правилом шага «синк документации» в task-pipeline.
  2. healthlog чистится не разом: раздел вычищается той задачей, которая его касается. Нужен способ не потерять остаток — иначе 950 строк «Хранилища» останутся навсегда.
  3. Дыра, которую решение открывает: «почему» после архивации. Сегодня CLAUDE.md healthlog велит писать причину решения в architecture.md — а мы её оттуда выселяем. Спеки нормативны и «почему» не держат; design.md живёт внутри change и уезжает в архив. Либо ADR (как у jellybit), либо явное правило «почему живёт в архивных change». Первый вопрос следующей темы.

Из C:

  1. av-dev-pipeline даёт образец openspec/config.yaml отдельным reference — он владеет связью с OpenSpec. Заполняется при старте проекта и при adopt.
  2. У обоих проектов из config.yaml вычищается блок «Ревью (процесс, не артефакт)», пересказ конвенций и инвариантов.

2. Канон документов проекта (2026-08-03)

Что было

Измерено по обоим проектам:

  • «Почему» не теряется — оно не находится. design.md пишется почти всегда (jellybit 39 из 43 архивных change, healthlog 9 из 9 — ≈285 КБ за две недели) и имеет секции Context / Goals / Non-Goals / Decisions / Risks / Trade-offs, то есть является ADR по структуре. Против этого ADR руками: 6 записей у jellybit, четыре из них 13 июня — в день старта; между 15 июня и 23 июля прошло ~40 изменений и ноль ADR. У healthlog ADR нет вовсе, а настоящее ADR-рассуждение (отказ от DuckDB) лежит в разделе «Открытые вопросы» файла architecture.md, потому что больше некуда.
  • Два плана. docs/plan.md healthlog («порядок и его обоснование», 11 шагов) и <tasks>/PLAN.md из av-dev-tasks («линия целей с обоснованием порядка прозой») — один артефакт под двумя именами.
  • Дубли спек у jellybit. Из шести файлов docs/specs/ три (recognition, review-ux, workflow) описывают поведение, уже покрытое capability в openspec/specs/.
  • docs/drafts/ раскладывается без остатка: roadmap.md → линия целей, conventions-backlog.md → задачи [idea], logical-title-model.md (293 строки, итог «сущность title не вводим») → намеренный отказ, то есть ADR.

Решено

D. «Почему» — ADR как промоут поверх архива. Обоснование по-прежнему пишет design.md; ADR — короткая запись, цитирующая решение и ссылающаяся на архивный design.md. Заводит её шаг «синк документации» пайплайна по названному триггеру (дорогой откат / намеренный отказ от очевидного / пересмотр прежнего решения), а не человек по вдохновению.

Причина: ручной ритуал эмпирически не выжил — 6 записей на 52 изменения. Автоматический (opsx:propose пишет design.md всегда) работает и производит на порядок больше. Чинить надо не дом, а индекс и критерий промоута.

E. docs/plan.md растворяется в <tasks>/PLAN.md. Файл удаляется, 11 шагов становятся линией целей, ссылки в CLAUDE.md и паспорте переводятся.

F. Пути жёсткие, оба проекта приводятся к одному виду. Плагин знает раскладку поимённо; указателя вида .docs.json нет.

Причина (словами владельца): «так проще ориентироваться во множестве проектов, а не видеть слегка похожую, но разную структуру в каждом. Все проекты малого и среднего размера, проще подогнать их под одну структуру. Кроме того, у OpenSpec тоже структура строгая». Цена принята сознательно: плагин перестаёт быть переносимым на чужой репозиторий, а adopt из «поправь указатели» превращается в «перенеси файлы».

G. Конвенции и разведка — каталогами с README-индексом. docs/conventions/ и docs/research/: путь жёсткий, нарезка внутри свободна. Схема хранилища — отдельный docs/database.md (своя каденция: меняется миграцией, а не архитектурным решением; гейт healthlog уже сверяет миграции с документацией). Конвенции идентификаторов и именования — не схема, они в conventions/.

H. Слота для черновиков нет. Идея → задача [idea]; намеренный отказ → ADR; порядок работ → PLAN.md; незрелое размышление → opsx:explore внутри change.

Канон

CLAUDE.md                      памятка агенту: что это, стек, инварианты, команды, слоты
docs/
  passport.md                  зачем и для кого; чем НЕ является; сценарии; референсы
  architecture.md              как сложено — обзор: принципы, компоненты со ссылками
                               на capability, внешние границы, раскладка, деплой
  database.md                  схема хранилища (там, где есть БД)
  conventions/README.md + <тема>.md      как пишем код; README держит правило промоута
  research/README.md + <тема>.md         что показала реальность: чужие форматы, живые данные
  adr/README.md + template.md + ADR-*.md почему — промоут поверх архивных design.md
  review-journal.md            промахи конвейера ревью        ← уточнено в теме 3
  review-brief.md              предмет ревью — см. тему 3      ← отменено в теме 3
  tasks/                       av-dev-tasks: items/, PLAN.md, BACKLOG.md, SPRINT.md, REJECTED.md
openspec/
  config.yaml                  только нужды генерации + ссылки
  specs/<capability>/spec.md   что система делает — нормативно
  changes/archive/             журнал изменений с design.md — сырьё для ADR

Слотов нет у: docs/drafts/, docs/specs/, docs/plan.md, BRIEF.md, docs/backlog/, docs/review/journal.md.

Что из этого следует

  1. Переезд healthlog: architecture.md 1611 → обзор (поведение уезжает в openspec/specs по разделу за задачу); conventions.mdconventions/README.md; local-research.md 1829 → research/; plan.mddocs/tasks/PLAN.md; backlog/docs/tasks/; завести docs/adr/.
  2. Переезд jellybit: BRIEF.mddocs/passport.md (заодно обновить — не трогался с 13 июня); docs/specs/architecture.mddocs/architecture.md; docs/specs/database.mddocs/database.md; docs/specs/jellyfin-layout.mddocs/research/; docs/specs/{recognition,review-ux,workflow}.md сверить с capability и удалить как дубли; docs/review/journal.mddocs/review-journal.md; drafts/ растворить по H; docs/backlog/docs/tasks/.
  3. adopt меняет природу — теперь он переносит файлы, а не правит указатели. Разбирается в теме про старт проекта.
  4. Открыто до темы 6 (поддержание): точная формулировка триггера промоута в ADR; нужен ли механический check раскладки документов, раз пути жёсткие; как не потерять остаток при постепенной чистке architecture.md.

3. Брифа ревью нет — бриф это и есть канон (2026-08-03)

Что было

Контракт брифа — 413 строк, 13 разделов, отдельный файл docs/review-brief.md, который каждый проход читает как истину. Заполнение на обоих проектах дало 841 и 734 строки, и REMAINING.md уже отметил, что часть разделов вырождается в пересказ.

Разбор по разделам после решения F (жёсткие пути) показал: посредник между агентом и файлом не нужен, когда путь известен. Восемь из тринадцати разделов дублируют канон или снимаются жёсткими путями.

Решено

I. Отдельного файла-брифа нет. Проектную конкретику проходам дают документы канона напрямую, по жёстким путям. Формулировка владельца: «артефакты в docs и должны стать частями брифа, а для ревью достаточно дать ссылки на эти артефакты».

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

J. Заводится docs/security.md. Периметр первой строкой (целевой и сегодняшний, если контур не развёрнут), недоверенный вход и его каналы, из чего строятся пути и ключи, что разграничивает доступ, что чувствительнее чего, что вне модели. Материал уже есть, но рассыпан: у healthlog — раздел «Аутентификация» в architecture.md и строка про секреты в CLAUDE.md, у jellybit — секреты в conventions/config.md. Периметра нет ни у одного, а без него враждебный проход не выбирает между «открыт наружу» и «контур доверенный».

K. review-journal.mddocs/review.md: журнал дефектов плюс настройка конвейера под проект. Туда садится остаток брифа, который фактом о проекте не является — типовые узлы, типовые ложноположительные, вопросы к проходам, недоступно проверке.

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

L. Журнал расширяется до всех воспроизведённых дефектов с пометкой «проскочил / пойман ревью». Эвал-сет для калибровки — выборка по пометке.

Причина: пойманные дефекты с оракулом (768 МиБ пика, 5.019 с удержания блокировки) сегодня не сохраняются нигде, кроме отчётов триажа в архиве change, а они и есть лучшая опора для прохода — проектные, воспроизводимые, однажды оказавшиеся правдой.

M. Семантика гейта — в CLAUDE.md, расширением раздела «Команды». Чем краснеет безусловно и почему, где логи, что означает исход, чего в гейте намеренно нет, кто и когда обязан гонять дорогое вне гейта, что запускать запрещено (с путями). Гейт краснеет не только в ревью — это факт о проекте.

N. Severity инвариантов дописывается в CLAUDE.md рядом с формулировкой. Контракт брифа сам называл это лучшим исходом; жёсткие пути делают возможным. Оговорка «выведена по обратимости» исчезает вместе с пересказом.

Канон после темы 3

CLAUDE.md                      что это, стек, инварианты с severity, команды,
                               семантика гейта, запреты, слоты
docs/
  passport.md                  зачем и для кого; чем НЕ является; сценарии; референсы
  architecture.md              как сложено — обзор; окружение, внешние зависимости,
                               наблюдатель, характер потока
  database.md                  схема хранилища; представление данных и настройки
                               с числовым значением (таймаут занятости, лимит тела,
                               режим журналирования, ретеншен)
  security.md                  периметр первой строкой; недоверенный вход; из чего
                               строятся пути и ключи; разграничение; что вне модели
  conventions/README.md + <тема>.md
  research/README.md + <тема>.md         наблюдения и измеренные числа с провенансом
  adr/README.md + template.md + ADR-*.md
  review.md                    настройка конвейера под проект + журнал дефектов
  tasks/                       av-dev-tasks
openspec/
  config.yaml, specs/<capability>/spec.md, changes/archive/

Слотов нет у: docs/review-brief.md, docs/drafts/, docs/specs/, docs/plan.md, BRIEF.md, docs/backlog/, docs/review-journal.md.

Что из этого следует

  1. Скилл project-brief растворяется. Заведение недостающих документов канона — часть скилла старта/адаптации (тема 5, требование Т1).
  2. Девять charter'ов переписываются второй раз. Сейчас каждый читает «из раздела ## X брифа»; станет — из файла канона. Цена названа вслух: первая переписка (вынос в плагин) осталась незамеренной — REMAINING.md, пункт 1. Вторая делает замер по четырём реальным находкам healthlog обязательным, а не желательным: два неизмеренных изменения подряд в том самом месте, где присваивается severity.
  3. Теряется соседство фактов, и charter обязан сшивать. Контракт настаивал, что замер становится находкой только рядом с настройкой: «768 МиБ пика» — аномалия, лишь если известно, что запись лежит сжатой и распаковывается целиком; «5.019 с удержания блокировки» — отказ соседа, лишь если известен таймаут занятости. Теперь это research/ и database.md, и charter'ы ops, adversary, reimpl обязаны прямо говорить «собери из этих двух», иначе проход снимет верное число и честно понизит находку до гипотезы.
  4. Деградация становится поразрядной — и это лучше прежнего «нет брифа → деградирует всё». Нет security.md — деградирует adversary; нет research/ — числа неизвестны ops, adversary и reimpl; нет passport.md — архитектурный проход теряет границу домена. Каждый проход пишет свою строку в границы покрытия.
  5. Открытый вопрос из REMAINING.md закрыт: раздел ## Триггеры удаляется вместе с брифом. Правило выбора профиля остаётся в скилле конвейера; проектная конкретизация, если понадобится, — в docs/review.md.

4. Границы плагинов (2026-08-03)

Что было

Связь taskspipeline уже сделана ролями, а не именами: скиллы говорят «пайплайн проекта», «владелец спринта», «тот, кто ведёт задачи». Жёсткая ссылка по имени ровно одна — task-pipeline:112 на канонический текст правила про остаток внутри session, и рядом обработан случай «плагин не подключён».

Слоты CLAUDE.md при этом дублировались уже внутри одного плагина: шесть у tasks, семь у session, три пары — одно и то же. Темы 2–3 растворили ещё часть: «куда переезжает суть» отвечает канон, «оракулы» — семантика гейта (решение M), «где живёт разбор процесса» — docs/review.md (решение K). Из тринадцати остаётся около четырёх.

Решено

O. Три плагина: av-dev-pm, av-dev-pipeline, av-dev-git.

  • av-dev-pm (бывший av-dev-tasks) — управление продуктом: канон документов, задачи, цели, спринты, старт и адаптация проекта. Владеет всем docs/, включая docs/tasks/.
  • av-dev-pipeline — исполнение: SDD-цикл, конвейер ревью, девять агентов.
  • av-dev-git — стиль коммитов; работает в любом репозитории.

Причина (словами владельца): «пайплайн можно и переиспользовать в других проектах с более простым подходом к управлению». Это подтверждается разбором: пайплайн зависит от файлов канона и от OpenSpec, а не от плагина av-dev-pm. В чужом проекте нужных файлов нет — включается поразрядная деградация (следствие 16), и это штатный режим, а не поломка.

Имя: pm = product management, «объединение всех операций по управлению продуктом», и согласуется с av-dev-git.

P. Граница «пайплайн не закрывает задачу» снимается. Закрывает задачу и двигает строки между SPRINT.md / BACKLOG.md / REJECTED.md агент- оркестраторtask-pipeline и task-batch, а не сабагенты внутри них. Зовёт он tasks.py через слот «Команда учёта задач» в CLAUDE.md.

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

Q. av-dev-backlog помечается устаревшим и остаётся до перевода jellybit. Описание переписывается так, чтобы не ловить триггер «добавь задачу в беклог» — иначе агент выбирает между ним и av-dev-pm случайно.

Что из этого следует

  1. Переименование av-dev-tasksav-dev-pm тянет plugin.json, marketplace.json и пространство имён скиллов: av-dev-tasks:sessionav-dev-pm:session, включая ссылку из task-pipeline:112.
  2. Раздел «Стимулы, которые процесс создаёт» в session переписывается. Снятая граница выбила механическую опору у трёх защит: «сжать задачу до остатка», «занизить урожай», «занизить критерии приёмки» — во всех трёх приёмщик и исполнитель теперь совпадают. Остаются: отчёт триажа в openspec/changes/<id>/review/ (независимый артефакт, task-batch уже сверяет полноту ревью по нему, а не по прозе исполнителя), SPRINT.md под git с видимой историей и reopen <slug> --reason — закрытие не окончательно, приёмка человеком на сессии его отменяет. Раздел обязан назвать их поимённо, иначе обещает защиту, которой нет.
  3. Конфликт владения docs/tasks/ снят — канон и задачи теперь в одном плагине.
  4. Скилл adopt из av-dev-tasks поглощается скиллом адаптации проекта уровня канона (требование Т1). Разбирается в теме 5.
  5. Состав av-dev-pm: tasks, session (есть), docs — ведение канона, project — старт, adopt, check, upgrade (тема 5).

5. Старт проекта и жизненный цикл под каноном (2026-08-03)

Что было

Требование Т1: прийти в любой старый проект и перевести на текущие рельсы; канон сам меняется, значит уже приведённые проекты тоже повышаются.

Существующий adopt (уровень задач) даёт готовую форму: scan — только чтение, карта → суждение человека → apply — запись одним проходом, с отказом до первой записи при неверной карте и с обязательным разделом «не разложилось» поимённо. Форма переносится на уровень канона как есть.

Четыре операции различаются не поровну: adopt, check и upgrade — одна машина сравнения с разными исходами, а init — принципиально другой режим, разговор, а не сверка.

Решено

R. Два скилла: av-dev-pm:init и av-dev-pm:canon. init — интервью по входному брифу для нового проекта. canon — привести к канону: check, adopt, upgrade одной машиной.

S. Скелет канона заводится целиком, незаполненное называется пустым. Все файлы канона есть с первого дня, но незаполненный держит одну честную информативную строку: «наблюдений на живых данных нет — внешний источник один, формат документирован», «прецедентов не накоплено», «внешних зависимостей нет, смотри на диск и на СУБД».

Причина: это тот же принцип, что был в контракте брифа, поднятый на уровень файлов. Проход читает такую строку как факт, а не как пробел, и не тратит обязательный вопрос впустую. Отсутствие файла он прочитать не может никак.

Защита от вырождения в заглушки берётся у tasks.py: пока на месте стоит плейсхолдер шаблона, check о нём напоминает. Строка «TBD» — это не «пустое названо пустым», и check обязан их различать.

T. Скрипт docs.py плюс версия канона в docs/.pm.json. Отдельный скрипт, не расширение tasks.py: рефакторинг 2421 работающей строки ради удобства вызова не окупается. docs.py check зовёт tasks.py check для своей части.

Граница механизируемого объявляется вслух — иначе check соврёт.

Проверяет docs.py Судит агент
отсутствующие пути канона смысловой дубль (docs/specs/recognition.md против capability)
файлы в docs/ вне канона поведение, оставшееся в architecture.md
битые относительные ссылки протухший факт, разошедшийся с кодом
версия канона и её отставание достаточность честной строки в пустом слоте
нетронутый плейсхолдер шаблона

check, отчитавшийся «канон соблюдён» на проекте, где из шести файлов три лишние, хуже отсутствующего.

Порядок интервью init — зависимость, а не удобство

Цель и потребители → чем это не является и мера успеха → периметр и что недоверенное → стек, хранилище, необратимое → чем краснеет гейт → первые цели в PLAN.md. Каждый блок опирается на ответ предыдущего.

Вход — свободный текст «что мне нужно и почему» (образец формы: BRIEF.md jellybit, 6 КБ). После init его дом — passport.md; отдельным файлом он не остаётся.

init физически не производит полный канон. В новом репозитории нет кода, а architecture.md, database.md, conventions/ и research/ выводятся из него. Они заводятся скелетом с честной строкой («архитектуры пока нет: кода нет, заводится первой задачей») и наполняются шагом синка документации.

Что из этого следует

  1. docs/.pm.json поглощает <tasks>/.tasks.json. Меняется цепочка разрешения в tasks.py — сегодня он ищет .tasks.json вверх от текущего каталога. Нужен переходный период либо чтение обоих.
  2. tasks.py adopt становится шагом внутри canon adopt, а не отдельной пользовательской операцией: docs/tasks/ — часть той же раскладки.
  3. Версия канона — целое число, не semver: у канона нет обратной совместимости, есть только «приведён» и «не приведён».
  4. Журнал изменений канона живёт в плагине — av-dev-pm/skills/canon/references/changelog.md, запись на версию: что добавилось, что переехало, что удалено, что сделать проекту.
  5. Открыто до темы 6: звать ли docs.py check из гейта проекта. У healthlog task gate уже сверяет миграции с документацией, так что место есть; но гейт принадлежит проекту, и плагин может только рекомендовать строкой в отчёте.

6. Поддержание документов по ходу разработки (2026-08-03)

Что было

Гейт healthlog уже изобрёл нужный механизм для одного документа — scripts/gate.py:177-181: миграция изменена, а docs/database.md нет → FAIL. Документ канона сверяется с кодом красным гейтом, а не напоминанием.

Против этого — прямое доказательство, что́ не работает: у adr/ был список триггеров прозой («выбор технологии, структурные решения, дорогой откат, намеренный отказ»), и он дал 6 записей на 43 изменения. Прозаический триггер, который некому проверить, не срабатывает.

Механизируемы три документа из десяти: database.md (миграция), architecture.md (capability в openspec/specs/ без упоминания в обзоре), tasks/ (tasks.py check). Плюс openspec/specs/ вливает opsx:archive.

Решено

U. Принуждённое отрицание в докладе шага синка. Шаг обязан назвать каждый документ канона: обновлён — чем, либо «не требуется, потому что…». Нетронутые группируются одной строкой с общей причиной.

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

Триггер ADR, окончательная формулировка. Запись заводится, когда верно одно из трёх: дорогой откат (переделка стоит дороже переписывания одного файла); намеренный отказ от очевидного подхода; пересмотр прежнего решения — тогда у старой записи обязателен статус «заменено на». Не заводится для рутины и для того, что видно из кода и git log. Источник — архивный design.md: ADR его цитирует и на него ссылается, а не пересказывает.

V. docs.py check обязателен в гейте проекта. Скилл canon при адаптации добавляет шаг и печатает это в отчёте.

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

Сверка «миграция изменена — database.md нет» обобщается: путь миграций проекта записывается в docs/.pm.json рядом с версией канона, и docs.py делает эту проверку сам, а не каждый проект заново.

W. Остаток чистки помечается маркером и считается числом. Неразобранный раздел получает <!-- канон: поведение → openspec/specs/<capability> -->, docs.py считает маркеры и печатает остаток. Закрывается порциями, как переоценка задач.

Маркеры гейт не красят. Это долг, а не отказ: покрасневший гейт на первом маркере сделал бы постепенный переезд невозможным, а разовый — обязательным. Число печатается и убывает на глазах.

Что из этого следует

  1. Шаг 9 task-pipeline переписывается из четырёх пунктов прозой в построчный доклад по документам канона.
  2. promote.md, шаг 3, переписывается: «вычеркнуть пункт из брифа, правило переезжает в перечень механизированного в разделе ## Карта» → перечень механизированного живёт в conventions/README.md. Брифа нет.
  3. docs/.pm.json держит не только версию канона, но и пути, нужные проверкам: каталог миграций — как минимум.
  4. docs.py check получает две сверки с кодом, а не только раскладку: миграции ↔ database.md, capability ↔ упоминание в architecture.md.

7. Раскладка скиллов и доставка скриптов (2026-08-03)

Решено

X. Пять скиллов в av-dev-pm.

av-dev-pm/skills/
  init/      интервью по брифу → канон нового проекта
  canon/     раскладка: check / adopt / upgrade
  docs/      содержимое канона: ADR из архивного design.md, промоут конвенций,
             запись в research/ и review.md, чистка architecture.md
  tasks/     формат и содержимое задач
  session/   ритуал спринта

Причина отдельного docs: правила ведения содержимого канона обязаны жить у владельца канона, а не в шаге синка чужого плагина — иначе проект без пайплайна документацию вести не может. Это работает потому, что вызов скилла через пространство имён между плагинами возможен, в отличие от $CLAUDE_PLUGIN_ROOT: task-pipeline уже зовёт opsx:propose и av-dev-pipeline:review-pipeline. Шаг синка зовёт av-dev-pm:docs, а в чужом проекте деградирует до прозаического списка.

Симметрия, по которой резалось: раскладка и содержимое разделены и для документов, и для задачcanon / docs, tasks / session.

Y. Скрипты не копируются — живут вместе со скиллами. Три вызывающих, три способа дотянуться:

Кто зовёт Как
скиллы tasks, canon, docs $CLAUDE_PLUGIN_ROOT — свой плагин, работает всегда
task-pipeline, task-batch вызов скилла av-dev-pm:tasks, а не путь
гейт проекта путь переменной с умолчанием на канонический путь маркетплейса; пишет canon adopt, внятный красный отказ, если не найден

Слот «Команда учёта задач» всё равно исчезает — но снимает его не копия, а вызов скилла через пространство имён. Тот же приём, которым шаг синка зовёт av-dev-pm:docs (решение X): чужой плагин зовёт скилл, скилл разрешает свой $CLAUDE_PLUGIN_ROOT сам. Путь наружу не выносится вовсе.

Первоначально здесь было решено вендорить scripts/tasks.py и scripts/docs.py в проект. Отменено после проверки фактов:

  • CI нет ни в одном проекте (ни .github, ни woodpecker, ни drone). Pre-commit есть только у jellybit — lefthook с gofmt/vet/lint/test/gitleaks — и гоняется на той же машине, где установлен плагин. Довод «не работает в CI и у человека без Claude Code» оказался гипотетическим.
  • Пара «источник — копия» существует и без вендоринга. Установленный маркетплейс — git-клон; на момент разбора он стоял на 092d07c, на четыре коммита позади master, и av-dev-tasks с av-dev-pipeline в нём отсутствовали вовсе. Довод «вендоринг создаёт вторую копию» был слабее, чем подан.
  • Обновление маркетплейса — одна точка на все проекты. При вендоринге каждый проект повышается отдельно, и проекты расходятся друг с другом — ровно та разнородность, против которой принято решение F.

Z. Имени у процесса нет — процесс это av-dev. Маркетплейс уже av-dev-skills, плагины av-dev-*; в CLAUDE.md проекта пишется «процесс av-dev, канон версии N». Имя, которое нигде не работает, — украшение.

Что из этого следует

  1. Решение P уточняется: оркестратор закрывает задачи вызовом скилла av-dev-pm:tasks, а не запуском скрипта по пути. Плагина в проекте нет — вызов не разрешается, и пайплайн, как прежде, только докладывает исход.
  2. Слот исчезает из двух скилловtasks (слот 6) и session (слот 7), — и из текстов task-pipeline и task-batch, которые на него ссылаются.
  3. canon upgrade отвечает за раскладку и версию в docs/.pm.json. Скрипты обновляются обновлением маркетплейса, а не проектом.
  4. Скрипты живут в av-dev-pm/skills/{tasks,canon}/scripts/. docs.py — в canon, потому что раскладку проверяет он.
  5. canon check сверяет версию канона проекта с версией установленного плагина и говорит, кто отстал. Это нужно и без вендоринга: маркетплейс — git-клон, обновляется явно, и на момент разбора отставал на четыре коммита.
  6. Установленный маркетплейс требует обновления перед любой работой — сейчас в нём нет ни av-dev-tasks, ни av-dev-pipeline. Это первый шаг выката (тема 8), иначе проверять будет нечего.

8. Порядок выката (2026-08-03)

Объём

Ссылок на бриф — 168 строк в 19 файлах av-dev-pipeline, из них ~48 уходят вместе с удаляемыми project-brief/SKILL.md, references/project-brief.md и references/brief-template.md. Остальное переписывается на пути канона.

Решено

AA. Инструмент строится целиком, потом проверяется. Не пилот руками.

Риск принят сознательно: если замер покажет деградацию severity, чинить придётся канон, зашитый к тому моменту в три скилла, скрипт и мигрированные файлы healthlog.

Удешевление, которое обязано быть заложено сразу: определение канона живёт в единственном reference-файле, который читают init, canon и docs, а не повторяется в каждом. Правка канона — одно место плюс запись в журнал версий.

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

BB. Работа ведётся в docs/tasks/ самого dev-skills. Скилл tasks не требует ни OpenSpec, ни языка — задачи для него просто каталог markdown. Цели — крупные куски, задачи — следствия. Заодно первая боевая обкатка собственного инструмента.

CC. AGENTIC-TASKS.md сжимается до истории решений и переезжает в dev-skills отдельным HISTORY.md: почему не Scrum, числа первого замера, что отвергнуто и почему. Он описывает процесс, а процесс живёт здесь, не в healthlog. Остальное содержимое уже в плагинах, и второй дом для тех же правил — ровно то, против чего документ сам и написан.

Порядок

0.  обновить установленный маркетплейс           предусловие всего
0.5 завести docs/tasks в dev-skills, разложить 37 следствий по целям

1.  РЕПОЗИТОРИЙ ПЛАГИНОВ
    1.1 av-dev-tasks → av-dev-pm, пространство имён
    1.2 канон одним reference-файлом — единственный дом определения
    1.3 правки tasks и session: слоты, «Стимулы», .pm.json
    1.4 новые init, canon, docs + docs.py
    1.5 av-dev-pipeline: удалить project-brief, снять ветки деградации OpenSpec,
        переписать шаг 9, девять charter'ов, promote.md, убрать слот
    1.6 av-dev-backlog устаревшим; README; журнал канона v1; HISTORY.md
    1.7 REMAINING.md пересобрать — часть его вопросов закрыта этим разбором

2.  HEALTHLOG — первая боевая проверка инструмента
    canon adopt, заполнение канона, security.md, review.md, ADR,
    маркеры в architecture.md, docs.py check в гейте

3.  КАЛИБРОВКА на четырёх находках healthlog        БЛОКИРУЕТ шаг 5

4.  один-два спринта healthlog на новом процессе

5.  JELLYBIT — переезд, удаление дублей specs, растворение drafts

Что из этого следует

  1. REMAINING.md частично устарел: пункт 2 «Завести брифы» отменён темой 3; закрыты открытые вопросы про ## Триггеры, av-dev-backlog, имя процесса и AGENTIC-TASKS.md. Пункт 1 (калибровка) стал обязательным, а не желательным. Пересобрать на шаге 1.7.
  2. Замер — единственный шаг, который нельзя переставить. Всё остальное в порядке 1–5 можно тасовать; шаг 3 стоит перед шагом 5 жёстко.

9. Линтеры скриптов (2026-08-03)

Что было

Три скрипта на python, 3600 строк, ни одной проверки. tasks.py — 2450 строк, которые ходят по файловой системе, переименовывают и удаляют файлы задач. Требование к самим скриптам прежнее и не обсуждается: голый python3 3.12, ноль внешних зависимостей — они лежат рядом со скиллами и запускаются в чужом проекте, где ничего ставить нельзя.

Решено

DD. pyproject.toml в корне dev-skills, зависимости через uv. Файл живёт только здесь и не уезжает никуда: он держит линтеры, а не зависимости скриптов. Скрипты остаются запускаемыми любым python3 — это проверено прогоном всех операций через /usr/bin/python3, а не через .venv.

EE. Ноль зависимостей охраняется двумя способами, и главный — второй. banned-api у ruff ловит частые соблазны по имени (requests, yaml, pydantic, click, rich) — список заведомо неполный. Настоящий страж — pyrefly: в окружении нет ничего, кроме линтеров, поэтому любой сторонний импорт у него не разрешается. Первый способ даёт понятное сообщение, второй — полноту.

FF. Версии линтеров прибиты точно (ruff==0.16.1, pyrefly==1.2.0) плюс uv.lock в git. Обновление линтера меняет набор находок, а находки правятся руками в скриптах, которые уезжают в чужие проекты. Обновление обязано быть отдельной осознанной правкой, а не побочным эффектом uv sync.

GG. RUF001RUF003 выключены. Весь текст скриптов русский: сообщения, докстроки, комментарии. «Похожая на латиницу кириллица» здесь норма, а не опечатка, и три этих правила давали 311 срабатываний из 338 — шум, в котором тонут остальные 27.

HH. av-dev-backlog исключён из проверки. Плагин помечен устаревшим и живёт до перевода последнего проекта, после чего удаляется целиком. Шесть его находок косметические (os.replace, l как имя), а правка замороженного кода без тестов — риск без выгоды. Исключение уходит вместе с плагином.

II. Голый except Exception разрешён только помеченный. Правило BLE включено, а два места последнего рубежа (main обоих скриптов, код выхода 4 по словарю) несут # noqa: BLE001 с причиной. Так третий такой except не появляется молча.

Что из этого следует

  1. Найдено и починено 27 находок ruff и 14 pyrefly. Содержательных две: мёртвая переменная ques в check (вычислялась и не использовалась — вопросы проверяет questions_open) и два места в check --fix, где find_entry_index может вернуть None, а результат идёт прямо в list.pop и в range. Оба сегодня недостижимы, и недостижимость держалась на рассуждении о вызывающем коде, а не на проверке. Поправлено по ревью: там стоит raise, а не continue. Тихий пропуск превратил бы сломанный инвариант в отчёт «индексы согласованы» — то есть в враньё; громкий отказ кодом 4 честнее.
  2. os из tasks.py ушёл целиком. os.replacePath.replace, os.path.basenamePath.name; импорт стал не нужен.
  3. fail() в docs.py объявлен NoReturn. Без этого read_config выглядел как возвращающий неинициализированное значение — и это ровно то, что читатель кода тоже не мог знать наверняка.
  4. Проверка не входит ни в один гейт. CI у репозитория нет, хука нет; запускается руками командой из README. Заводить хук ради двух скриптов, которые правятся раз в месяц, — плата ритуалом без выгоды.