# Решения по устройству процесса Журнал согласований: что решено, почему и что из этого следует. Пишется по ходу разбора тем, одна тема — один раздел. Причина обязательна: через месяц она забывается раньше факта. Незакрытые остатки прошлого захода — [REMAINING.md](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.yaml` → `context`, и планируется шестой — `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.yaml` → `context` держит только нужды генерации.** Язык, правила именования 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: 4. Правило «поведение — в спеку, устройство и границы — в архитектуру» становится контрактом плагина документов и правилом шага «синк документации» в `task-pipeline`. 5. healthlog чистится **не разом**: раздел вычищается той задачей, которая его касается. Нужен способ не потерять остаток — иначе 950 строк «Хранилища» останутся навсегда. 6. **Дыра, которую решение открывает:** «почему» после архивации. Сегодня `CLAUDE.md` healthlog велит писать причину решения в `architecture.md` — а мы её оттуда выселяем. Спеки нормативны и «почему» не держат; `design.md` живёт внутри change и уезжает в архив. Либо ADR (как у jellybit), либо явное правило «почему живёт в архивных change». **Первый вопрос следующей темы.** Из C: 7. `av-dev-pipeline` даёт образец `openspec/config.yaml` отдельным reference — он владеет связью с OpenSpec. Заполняется при старте проекта и при `adopt`. 8. У обоих проектов из `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 шагов) и `/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` растворяется в `/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//spec.md что система делает — нормативно changes/archive/ журнал изменений с design.md — сырьё для ADR ``` Слотов **нет** у: `docs/drafts/`, `docs/specs/`, `docs/plan.md`, `BRIEF.md`, `docs/backlog/`, `docs/review/journal.md`. ### Что из этого следует 9. **Переезд healthlog:** `architecture.md` 1611 → обзор (поведение уезжает в `openspec/specs` по разделу за задачу); `conventions.md` → `conventions/README.md`; `local-research.md` 1829 → `research/`; `plan.md` → `docs/tasks/PLAN.md`; `backlog/` → `docs/tasks/`; завести `docs/adr/`. 10. **Переезд jellybit:** `BRIEF.md` → `docs/passport.md` (заодно обновить — не трогался с 13 июня); `docs/specs/architecture.md` → `docs/architecture.md`; `docs/specs/database.md` → `docs/database.md`; `docs/specs/jellyfin-layout.md` → `docs/research/`; `docs/specs/{recognition,review-ux,workflow}.md` сверить с capability и удалить как дубли; `docs/review/journal.md` → `docs/review-journal.md`; `drafts/` растворить по H; `docs/backlog/` → `docs/tasks/`. 11. **`adopt` меняет природу** — теперь он переносит файлы, а не правит указатели. Разбирается в теме про старт проекта. 12. **Открыто до темы 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.md` → `docs/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//spec.md, changes/archive/ ``` Слотов **нет** у: `docs/review-brief.md`, `docs/drafts/`, `docs/specs/`, `docs/plan.md`, `BRIEF.md`, `docs/backlog/`, `docs/review-journal.md`. ### Что из этого следует 13. **Скилл `project-brief` растворяется.** Заведение недостающих документов канона — часть скилла старта/адаптации (тема 5, требование Т1). 14. **Девять charter'ов переписываются второй раз.** Сейчас каждый читает «из раздела `## X` брифа»; станет — из файла канона. **Цена названа вслух:** первая переписка (вынос в плагин) осталась незамеренной — `REMAINING.md`, пункт 1. Вторая делает замер по четырём реальным находкам healthlog **обязательным, а не желательным**: два неизмеренных изменения подряд в том самом месте, где присваивается severity. 15. **Теряется соседство фактов, и charter обязан сшивать.** Контракт настаивал, что замер становится находкой только рядом с настройкой: «768 МиБ пика» — аномалия, лишь если известно, что запись лежит сжатой и распаковывается целиком; «5.019 с удержания блокировки» — отказ соседа, лишь если известен таймаут занятости. Теперь это `research/` и `database.md`, и charter'ы `ops`, `adversary`, `reimpl` обязаны прямо говорить «собери из этих двух», иначе проход снимет верное число и честно понизит находку до гипотезы. 16. **Деградация становится поразрядной** — и это лучше прежнего «нет брифа → деградирует всё». Нет `security.md` — деградирует `adversary`; нет `research/` — числа неизвестны `ops`, `adversary` и `reimpl`; нет `passport.md` — архитектурный проход теряет границу домена. Каждый проход пишет свою строку в границы покрытия. 17. **Открытый вопрос из `REMAINING.md` закрыт:** раздел `## Триггеры` удаляется вместе с брифом. Правило выбора профиля остаётся в скилле конвейера; проектная конкретизация, если понадобится, — в `docs/review.md`. ## 4. Границы плагинов (2026-08-03) ### Что было Связь `tasks` ↔ `pipeline` уже сделана **ролями, а не именами**: скиллы говорят «пайплайн проекта», «владелец спринта», «тот, кто ведёт задачи». Жёсткая ссылка по имени ровно одна — `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` случайно. ### Что из этого следует 18. **Переименование `av-dev-tasks` → `av-dev-pm`** тянет `plugin.json`, `marketplace.json` и пространство имён скиллов: `av-dev-tasks:session` → `av-dev-pm:session`, включая ссылку из `task-pipeline:112`. 19. **Раздел «Стимулы, которые процесс создаёт» в `session` переписывается.** Снятая граница выбила механическую опору у трёх защит: «сжать задачу до остатка», «занизить урожай», «занизить критерии приёмки» — во всех трёх приёмщик и исполнитель теперь совпадают. Остаются: **отчёт триажа** в `openspec/changes//review/` (независимый артефакт, `task-batch` уже сверяет полноту ревью по нему, а не по прозе исполнителя), **`SPRINT.md` под git** с видимой историей и **`reopen --reason`** — закрытие не окончательно, приёмка человеком на сессии его отменяет. Раздел обязан назвать их поимённо, иначе обещает защиту, которой нет. 20. **Конфликт владения `docs/tasks/` снят** — канон и задачи теперь в одном плагине. 21. **Скилл `adopt` из `av-dev-tasks` поглощается** скиллом адаптации проекта уровня канона (требование Т1). Разбирается в теме 5. 22. **Состав `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/` выводятся из него. Они заводятся скелетом с честной строкой («архитектуры пока нет: кода нет, заводится первой задачей») и наполняются шагом синка документации. ### Что из этого следует 23. **`docs/.pm.json` поглощает `/.tasks.json`.** Меняется цепочка разрешения в `tasks.py` — сегодня он ищет `.tasks.json` вверх от текущего каталога. Нужен переходный период либо чтение обоих. 24. **`tasks.py adopt` становится шагом внутри `canon adopt`**, а не отдельной пользовательской операцией: `docs/tasks/` — часть той же раскладки. 25. **Версия канона — целое число**, не semver: у канона нет обратной совместимости, есть только «приведён» и «не приведён». 26. **Журнал изменений канона** живёт в плагине — `av-dev-pm/skills/canon/references/changelog.md`, запись на версию: что добавилось, что переехало, что удалено, что сделать проекту. 27. **Открыто до темы 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. Остаток чистки помечается маркером и считается числом.** Неразобранный раздел получает ``, `docs.py` считает маркеры и печатает остаток. Закрывается порциями, как переоценка задач. **Маркеры гейт не красят.** Это долг, а не отказ: покрасневший гейт на первом маркере сделал бы постепенный переезд невозможным, а разовый — обязательным. Число печатается и убывает на глазах. ### Что из этого следует 28. **Шаг 9 `task-pipeline` переписывается** из четырёх пунктов прозой в построчный доклад по документам канона. 29. **`promote.md`, шаг 3, переписывается:** «вычеркнуть пункт из брифа, правило переезжает в перечень механизированного в разделе `## Карта`» → перечень механизированного живёт в `conventions/README.md`. Брифа нет. 30. **`docs/.pm.json` держит не только версию канона**, но и пути, нужные проверкам: каталог миграций — как минимум. 31. **`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». Имя, которое нигде не работает, — украшение. ### Что из этого следует 32. **Решение P уточняется:** оркестратор закрывает задачи **вызовом скилла** `av-dev-pm:tasks`, а не запуском скрипта по пути. Плагина в проекте нет — вызов не разрешается, и пайплайн, как прежде, только докладывает исход. 33. **Слот исчезает из двух скиллов** — `tasks` (слот 6) и `session` (слот 7), — и из текстов `task-pipeline` и `task-batch`, которые на него ссылаются. 34. **`canon upgrade` отвечает за раскладку и версию в `docs/.pm.json`.** Скрипты обновляются обновлением маркетплейса, а не проектом. 35. **Скрипты живут в `av-dev-pm/skills/{tasks,canon}/scripts/`.** `docs.py` — в `canon`, потому что раскладку проверяет он. 36. **`canon check` сверяет версию канона проекта с версией установленного плагина** и говорит, кто отстал. Это нужно и без вендоринга: маркетплейс — git-клон, обновляется явно, и на момент разбора отставал на четыре коммита. 37. **Установленный маркетплейс требует обновления перед любой работой** — сейчас в нём нет ни `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 ``` ### Что из этого следует 38. **`REMAINING.md` частично устарел:** пункт 2 «Завести брифы» отменён темой 3; закрыты открытые вопросы про `## Триггеры`, `av-dev-backlog`, имя процесса и `AGENTIC-TASKS.md`. Пункт 1 (калибровка) стал обязательным, а не желательным. Пересобрать на шаге 1.7. 39. **Замер — единственный шаг, который нельзя переставить.** Всё остальное в порядке 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. `RUF001`–`RUF003` выключены.** Весь текст скриптов русский: сообщения, докстроки, комментарии. «Похожая на латиницу кириллица» здесь норма, а не опечатка, и три этих правила давали **311 срабатываний из 338** — шум, в котором тонут остальные 27. **HH. `av-dev-backlog` исключён из проверки.** Плагин помечен устаревшим и живёт до перевода последнего проекта, после чего удаляется целиком. Шесть его находок косметические (`os.replace`, `l` как имя), а правка замороженного кода без тестов — риск без выгоды. Исключение уходит вместе с плагином. **II. Голый `except Exception` разрешён только помеченный.** Правило `BLE` включено, а два места последнего рубежа (`main` обоих скриптов, код выхода 4 по словарю) несут `# noqa: BLE001` с причиной. Так третий такой except не появляется молча. ### Что из этого следует 40. **Найдено и починено 27 находок ruff и 14 pyrefly.** Содержательных две: мёртвая переменная `ques` в `check` (вычислялась и не использовалась — вопросы проверяет `questions_open`) и два места в `check --fix`, где `find_entry_index` может вернуть `None`, а результат идёт прямо в `list.pop` и в `range`. Оба сегодня недостижимы, и недостижимость держалась на рассуждении о вызывающем коде, а не на проверке. *Поправлено по ревью:* там стоит `raise`, а не `continue`. Тихий пропуск превратил бы сломанный инвариант в отчёт «индексы согласованы» — то есть в враньё; громкий отказ кодом 4 честнее. 41. **`os` из `tasks.py` ушёл целиком.** `os.replace` → `Path.replace`, `os.path.basename` → `Path.name`; импорт стал не нужен. 42. **`fail()` в `docs.py` объявлен `NoReturn`.** Без этого `read_config` выглядел как возвращающий неинициализированное значение — и это ровно то, что читатель кода тоже не мог знать наверняка. 43. **Проверка не входит ни в один гейт.** CI у репозитория нет, хука нет; запускается руками командой из README. Заводить хук ради двух скриптов, которые правятся раз в месяц, — плата ритуалом без выгоды. ## 10. Ревью готовых плагинов двумя проходами (2026-08-03) ### Что было Два независимых сабагента `fable` — по одному на `av-dev-pm` и `av-dev-pipeline`. **20 находок, из них две найдены обоими независимо.** Прошлые три круга ревью шли по одному проходу на всё; два прохода с разными предметами дали и больший урожай, и перекрёстное подтверждение самого дорогого дефекта. ### Что оказалось сломано по существу **JJ. Перестановка закрытия за коммит (решение из темы 8) сломала `reopen` и батч — и это нашли оба прохода.** `close --implemented` печатает «дорога назад: файл восстанавливается из git», а `reopen` искал **коммит удаления**, которого в новом порядке ещё нет: шаг 11 идёт последним, и учёт остаётся незакоммиченным. Проверено прогоном: `reopen` отказывал кодом 2 на свежезакрытой задаче — то есть в самом вероятном своём применении. Тем же грязным деревом ломался `task-batch`: `git rebase` и `git worktree remove` отказывают, и **каждая успешно закрывшая задачу ветка** уезжала бы в провалившиеся. Починено с обеих сторон: `reopen` берёт текст из `HEAD`, если коммита удаления нет, а шаг 11 обязан **коммитить учёт вторым коммитом** — иначе закрытие не доезжает до основной ветки и опора «`SPRINT.md` под git» остаётся словами. **KK. Канонический пример `docs/.pm.json` убивал `tasks.py`.** `canon.md`, `skeletons.md`, `tasks/SKILL.md` и `adopt.md` показывали ключ `tasks.sections`, которого скрипт не знает: `_validate_config` отвергает неизвестные ключи кодом 3 на **любой** команде. Проект, заведённый по канону дословно, остался бы без работы с задачами целиком — а `docs.py check` при этом печатал «канон соблюдён», потому что чужой код 3 уходит в «не проверялось». Секции живут в заголовках `##` индекса и второго дома не получают. ### Что из этого следует 44. **Класс находок тот же, что и в прошлые три круга: стыки.** Не новый код, а место, где один файл ссылается на другой. `sprint.md` в пункте «Сделана» всё ещё отсылал к порядку, который сам же тремя экранами ниже отменил; три остатка «шаг 9а» несли **предкоммитную** позицию закрытия; путь отчёта триажа не переживал `opsx:archive`, хотя по нему сверяют полноту ревью четверо. 45. **Инструкция, которую нельзя выполнить, выглядит как выполненная.** Ответ на вопрос по документированной процедуре (снять тег) оставлял задачу незабираемой, потому что судит **раздел**, а не тег; `canon adopt` требовал гнать `docs.py check` «до отсутствия дрейфа», недостижимого без нарушения запрета сочинять цели; урожай спринта, заведённый после `sprint close`, терял автотег молча. 46. **Два прохода по разным предметам дороже одного, но не вдвое.** Перекрытие оказалось ровно в одной находке из двадцати — той самой, что подтвердилась дважды. Практика остаётся: ревью на плагин, а не одно на репозиторий. ## 11. Зависимости между плагинами (2026-08-03) ### Целевая картина, которую проверяли `av-dev-git` ни от чего не зависит. `av-dev-pipeline` сам по себе: задача приходит **и обычным текстом**, и из `tasks`. `av-dev-pm` оперирует абстрактным «сделать задачу» и не знает, чем она выполняется. ### Что показала проверка **LL. Первые две цели выполняются, третья в исходной формулировке недостижима — и формулировку надо поправить, а не картину.** `av-dev-pm` **владеет конфигурационным файлом конвейера**: `docs/review.md` держит «Вопросы к проходам» и «Триггеры профиля», то есть перечисляет проходы поимённо, а скелет `review.md` несёт форму журнала дефектов. Кто-то этим словарём владеть обязан — канон и есть схема данных, которую конвейер читает. Честная формулировка цели: **`av-dev-pm` не зовёт пайплайн и не требует его наличия**. Она выполняется. **MM. Настоящая протечка была одна — необъявленная деградация опор приёмки.** «Стимулы» в `session` и приёмка в `sprint.md` держались на «сохранённом отчёте триажа» по конкретному OpenSpec-пути. В проекте без конвейера ревью защита от занижения урожая исчезала **молча**: сверять не с чем, а текст об этом не говорил. Теперь опора названа абстрактно («независимый отчёт ревью»), путь `av-dev-pipeline` дан как частный случай, а отсутствие конвейера обязано попадать строкой в доклад спринта. **NN. Ветка деградации шага 9 была неисполнима — ровно в том случае, ради которого написана.** «Плагина нет — открой `av-dev-pm/skills/canon/references/canon.md`»: путь в дерево маркетплейса, из проекта без установленного плагина не разрешается ниоткуда. Кросс-плагинные пути в дерево маркетплейса теперь не используются вообще: пайплайн ходит в **свой** `references/project-facts.md`, а ссылки в чужой плагин даются через `Skill <плагин>:<скилл>`. ### Что из этого следует 47. **Знаниевый цикл есть и он законен, но каждый его контракт обязан иметь единственный дом.** Пайплайн описывает раскладку `pm`, `pm` описывает артефакты пайплайна — пять симметричных контрактов, из них два уже разошлись: форма журнала дефектов (шесть полей против пяти, «Причина» потеряна) и список читателей `docs/research/` (`specs` выпал). Дома назначены: форма журнала — у конвейера, список читателей — у канона; в обеих копиях стоит явное указание на дом. 48. **Пайплайн больше не называет внутренние имена файлов `pm`.** `items/.md` и `SPRINT.md` в его тексте были вторым домом для раскладки, которую проект вправе переименовать через `docs/.pm.json`. 49. **Описания плагинов в манифестах врали умолчанием.** Ни `marketplace.json`, ни `plugin.json` не говорили, что `av-dev-pm` для конвейера **опционален**, а задача принимается текстом. Теперь говорят — это первое, что читает человек, выбирая, что подключать.