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

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

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

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

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

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

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

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

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

204 KiB
Raw Permalink 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. (заменено на тему 30: плагин удалён раньше этого срока — условие пережило свою причину.) Описание переписывается так, чтобы не ловить триггер «добавь задачу в беклог» — иначе агент выбирает между ним и 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 исключён из проверки. (исчерпано темой 30: плагин удалён, исключение снято из pyproject.toml и copies.py.) Плагин помечен устаревшим и живёт до перевода последнего проекта, после чего удаляется целиком. Шесть его находок косметические (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. Заводить хук ради двух скриптов, которые правятся раз в месяц, — плата ритуалом без выгоды.

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 уходит в «не проверялось». Секции живут в заголовках ## индекса и второго дома не получают.

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

  1. Класс находок тот же, что и в прошлые три круга: стыки. Не новый код, а место, где один файл ссылается на другой. sprint.md в пункте «Сделана» всё ещё отсылал к порядку, который сам же тремя экранами ниже отменил; три остатка «шаг 9а» несли предкоммитную позицию закрытия; путь отчёта триажа не переживал opsx:archive, хотя по нему сверяют полноту ревью четверо.
  2. Инструкция, которую нельзя выполнить, выглядит как выполненная. Ответ на вопрос по документированной процедуре (снять тег) оставлял задачу незабираемой, потому что судит раздел, а не тег; canon adopt требовал гнать docs.py check «до отсутствия дрейфа», недостижимого без нарушения запрета сочинять цели; урожай спринта, заведённый после sprint close, терял автотег молча.
  3. Два прохода по разным предметам дороже одного, но не вдвое. Перекрытие оказалось ровно в одной находке из двадцати — той самой, что подтвердилась дважды. Практика остаётся: ревью на плагин, а не одно на репозиторий.

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 <плагин>:<скилл>.

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

  1. Знаниевый цикл есть и он законен, но каждый его контракт обязан иметь единственный дом. Пайплайн описывает раскладку pm, pm описывает артефакты пайплайна — пять симметричных контрактов, из них два уже разошлись: форма журнала дефектов (шесть полей против пяти, «Причина» потеряна) и список читателей docs/research/ (specs выпал). Дома назначены: форма журнала — у конвейера, список читателей — у канона; в обеих копиях стоит явное указание на дом.
  2. Пайплайн больше не называет внутренние имена файлов pm. items/<slug>.md и SPRINT.md в его тексте были вторым домом для раскладки, которую проект вправе переименовать через docs/.pm.json.
  3. Описания плагинов в манифестах врали умолчанием. Ни marketplace.json, ни plugin.json не говорили, что av-dev-pm для конвейера опционален, а задача принимается текстом. Теперь говорят — это первое, что читает человек, выбирая, что подключать.

12. Механическая проверка копий (2026-08-03)

Что было

Разделение плагинов оставлено (тема 11), но цена его названа: пять симметричных контрактов в двух домах, два уже разошлись — форма журнала дефектов потеряла в копии поле «Причина», список читателей docs/research/ потерял specs. Оба раза копия выглядела актуальной, и оба раза расхождение прошло мимо трёх ревью подряд.

Решено

OO. Копия допустима, но обязана быть дословной и помеченной. Разметка — HTML-комментарии, невидимые в отрендеренном markdown: <!-- дом: <id> --><!-- /дом: <id> --> и <!-- копия: <id> из <путь> --><!-- /копия: <id> -->. scripts/copies.py требует побайтового совпадения текста между маркерами.

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

PP. Идентификатор строгий — буквы, цифры, дефис — и повторяется в закрывающем маркере. Иначе документация о самом механизме объявляет дом и роняет проверку: это случилось на первом же прогоне, README.md объявил дом примером. Теперь пример пишется <id>, угловые скобки под шаблон не подходят.

QQ. Ограда блока кода в сверку не входит. В доме текст обрамлён своей ```, а в скелете тот же текст лежит внутри чужой, объемлющей ограды. Сверяется содержимое, а не разметка вокруг него.

RR. Коды выхода — общий словарь (0 сошлось, 1 расхождение, 2 разметка, 3 не тот каталог, 4 сбой). Третий скрипт репозитория, и третий по тем же кодам.

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

  1. Помечены два контракта: форма записи журнала дефектов (дом — конвейер ревью, копия — скелет канона; это кросс-плагинная пара) и «когда заводить ADR» (дом — канон, копия — его же скелет). Второй пришлось сперва сделать дословным: копия говорила «обязателен статус», дом — «обязателен статус „заменено на"», и это ровно тот класс, который и ищется.
  2. Чего проверка не ловит — копию, которую забыли пометить. Помечать остаётся решением человека, и это названо в README.md вслух: иначе зелёный прогон читался бы как «копий больше нет».
  3. Дом без копий — расхождение, а не замечание. Маркер, обещающий дисциплину, за которой не за чем следить, — такая же ложная запись, как разошедшаяся копия.
  4. Запись в журнал версий канона проверка не заменяет. Она видит, что копия отстала, но не видит, что проект уже унёс старую версию к себе. Это остаётся на человеке и сказано в обоих домах.

13. Секции PLAN.md переименованы (2026-08-03)

Что было

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

Решено

SS. «порядок» и «темы». Заголовок называет ровно то свойство, которым секции различаются: в первой очередь значима и обоснована прозой, во второй порядка нет вовсе. Расшифровывать нечего — правило написано в самом имени.

TT. Записи в журнал версий канона не требуется — канон этих имён не знает. canon.md называет файл docs/tasks/PLAN.md и ничего не говорит о его секциях: их дом — заголовки ## индекса, а умолчание живёт в tasks.py. Версия канона поэтому не меняется, и проект вправе называть секции по-своему. Причина названа вслух, потому что соблазн повысить версию «на всякий случай» здесь сильный, а повышение обязало бы каждый проект что-то делать — при том что делать нечего.

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

  1. Умолчание одно и живёт в DEFAULT_PLAN_SECTIONS. Имена секций по-прежнему настраиваются --plan-sections, а домом остаются заголовки ## индекса — переименование не трогает механику, только умолчание и тексты.
  2. Метафора — плохое имя для секции индекса. Секция читается человеком без контекста, часто из вывода list, и второго шанса объяснить себя у неё нет.

14. Умолчания режимов прогона перевёрнуты (2026-08-03)

Что было

Оба скилла держали одно и то же умолчание — «по очереди», — хотя цена очереди у них разная. review-pipeline гнал проходы последовательно и требовал для параллельности двух условий (явная просьба и поимённо названный набор). task-batch, наоборот, планировал волны параллельных задач с потолком 2–3 и считал параллельность нормой прогона.

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

Решено

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

VV. Меряющая пара — правило стадии, а не решение прогона. adversary и ops идут по очереди всегда: оба доказывают находки числами и оба меряют одно железо, а испорченный оракул хуже отсутствующего. Общее «гони параллельно» этого не отменяет; отменяет только прямое слово оператора про эту пару, и тогда в границы покрытия идёт строка про замеры под соседней нагрузкой.

WW. В батче умолчание — по одной задаче, параллельность — по графу зависимостей. План собирается как граф (рёбра — жёсткие зависимости и сериализуемые пересечения) и в умолчании линеаризуется в один порядок. Просьба «гони параллельно» разрешает использовать ширину графа, а не гнать всё разом: потолок 2–3, замеряющая задача — волной по одной. Прежние правила волн сохранены целиком, они просто перестали быть умолчанием.

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

  1. Режим батча задаёт режим ревью внутри задачи, и его называет charter. Батч идёт по одной — машина свободна, сабагент гонит проходы параллельно; батч идёт волнами — сабагенту предписан последовательный режим с этой самой причиной. Сабагент своего соседа не видит, поэтому решать это ему нельзя.
  2. Ранний выход из ревью переехал на границу стадии. Стадии идут по порядку в любом режиме, так что остановиться между ними можно всегда; остановка внутри стадии осталась побочной выгодой последовательного режима — но не поводом его выбирать.
  3. Цена параллельного батча проверяется до первой волны. Тесты, делящие фиксированный порт или файл БД, и проект, умеющий поднимать один экземпляр, — основание гнать по одной даже после просьбы, сказанное строкой: просьба была про параллельность, а не про сломанные тесты.

15. Порядок проходов ревью — граф зависимостей (2026-08-03)

Что было

Решение 14 перевернуло умолчание, но оставило порядок в прежней форме: «стадии идут по порядку номеров, параллельность — только внутри стадии». Номер стадии при этом ничего не означает: между стадиями 1–4 ни один проход не читает вывод другого, так что очередь между ними была платой ни за что. А правило про замеры держалось на двух именахadversary и ops, — и рассыпалось бы в тот день, когда мерить начнёт третий проход или проект добавит свой.

Решено

XX. Порядок задаёт граф; стадии остаются единицей состава. Профиль по-прежнему набирается стадиями, но запускается всё, у чего закрыты входящие рёбра. Рёбер три вида, и смешивать их нельзя: зависимость (гейт → все опиниативные, все проходы → триаж), конфликт за ресурс (ненаправленный, между теми, кто держит машину), барьер стоимости (только deep).

YY. Сериализует ресурс, а не имена. Пометка «держит машину» — таблицей в скилле: gate, adversary, ops, triage; читают и рассуждают — specs, code, reimpl, architecture, rubric. Проект вправе пометить свой проход в docs/review.md; снимать пометку с перечисленных нельзя. Правило теперь самораспространяется: начнёт проход мерить — попадёт в цепочку по факту, а не по поправке.

ZZ. Ранний выход заменён барьером стоимости. Он стоит там, где ранний выход зарабатывал: перед reimpl (пишет реализацию целиком) и architecture. В quick/standard барьера нет — стадий 3–4 там не бывает; в design нет по другой причине — предметом там и является форма, защищать нечего.

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

BBB. Диаграммы в скиллах — mermaid. Граф, описанный прозой, читается как инструкция и теряет форму; диаграмма показывает её целиком. В конвейере четыре: общий граф прогона, граф профиля design, пример графа задач батча, веер финальной сверки.

Критерий, где диаграмма уместна: структура — граф или автомат, и проза вынуждена его пересказывать. По этому критерию диаграммы заведены ещё в шести местах: жизненный цикл записи по индексам (tasks), четыре шага сессии с причинами на рёбрах (session), исходы задачи в спринте (sprint.md), одиннадцать шагов пайплайна с развилкой «тривиальная» (task-pipeline), храповик промоута с обратным ребром (promote.md), счётчик retune до drop (calibration.md) и граф вызовов между плагинами (README.md). Где структура — таблица соответствий (чек-лист синка в docs, профили ревью, коды выхода), диаграмма не заводится: она бы дублировала таблицу и разошлась с ней. Все диаграммы прогоняются через mermaid-cli перед коммитом — синтаксическая ошибка в блоке не видна при чтении и молча ломает рендер.

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

  1. Триаж — сток по определению, а не «стадия 5». Отсюда без отдельного обоснования следует правило, которое раньше приходилось защищать: на неполном графе триаж не запускается, потому что агрегировал бы половину и выглядел бы полным.
  2. Словарь рёбер общий у ревью и батча. «Жёсткая зависимость» и «сериализуемое пересечение» в task-batch — те же два вида рёбер; формулировки сведены, и в обоих скиллах стоит ссылка на другой.
  3. Значения режима стали по графу и линейно. Прежние «параллельно» и «последовательно» описывали способ запуска, а не структуру; линеаризация осталась отступлением с тремя причинами (оператор, занятая машина, разбор самого конвейера).
  4. Проход, держащий машину, знает об этом из своего charter'а. adversary и ops получили по абзацу: цепочка гарантирует им чистое железо, значит их число — оракул, и шум в нём объясняется замером, а не соседом.
  5. У каждой диаграммы объявлено старшинство — это цена второго дома. Схема и проза вокруг неё описывают один факт, и разойтись они могут молча: то самое, против чего написан copies.py. Механической сверки здесь нет — дословного соответствия между текстом и графом не существует, — поэтому работает объявление: в review-pipeline старший граф (он и есть алгоритм планировщика, проза объясняет рёбра), в остальных местах старшая проза (диаграмма там сводка). Для агента это не философия: без объявления он идёт за тем, что конкретнее, то есть чаще за схемой.
  6. Рендер диаграмм проверяется скриптом, а не памятью автора. scripts/diagrams.py вынимает все блоки mermaid и гонит их через mmdc или npx @mermaid-js/mermaid-cli; коды выхода — общий словарь, нет рендерера — код 3, а не молчаливый успех. Причина та же, что у остальных проверок репозитория: ошибка в блоке не видна при чтении — текст правдоподобен, дифф разумен, падает только рендер. Расхождение с прозой скрипт не ловит и не притворяется, что ловит: это работа правила 63.

16. Каталог вместо файла в docs/ — отложено до переезда healthlog (2026-08-04)

Что было

Вопрос: разрешить документам в корне docs/ быть не только файлом, но и каталогом — когда документ описывает несколько принципиальных решений или перерастает 400–500 строк. Паспорт остаётся файлом в любом случае: компактность и есть его функция.

Механизм в каноне уже работает — conventions/, research/, adr/ каталоги с обязательным README.md-индексом, — так что вопрос не «можно ли», а «от чего лечим».

Решено

CCC. Порог в строках триггером не становится. Замер по проектам: у порога ровно один документ — healthlog/docs/architecture.md, 1662 строки. В нём десять маркеров долга, а разделы — «Слои гранулярности» (211 строк), «Тренировки и прочие секции» (222), «Условный запрос», «Свёртка и размер ответа», «Форма ответа». Это поведение, чей нормативный дом openspec/specs/, где у проекта уже лежат пять capability. Остальные документы 108–438 строк, jellybit — 169. Порог сработал бы ровно там, где надо не разносить, а доводить переезд, и дал бы долгу постоянное жильё: разложить 1662 строки по файлам дешевле, чем вынести их в спеки, а после раскладки давление исчезнет и второй дом поведения останется навсегда.

DDD. Шов выноса — другой читатель или другой срок жизни, а не размер. По этому критерию кандидатов два. review.md — сильнее прочих: у него уже записаны два раздела с разными сроками жизни, настройка конвейера стабильна и читается проходами, а журнал дефектов растёт неограниченно. architecture.md — по шву «окружение, деплой, наблюдатель», у которого отдельный читатель ops. А вот расщепление архитектуры по принципиальным решениям отвергнуто: у факта «почему решено так» дом adr/, и вынесенные разделы немедленно станут его вторым домом.

EEE. security.md и passport.md каталогом не становятся. У security.md ценность именно в цельности: периметр первой строкой и «что вне модели» читаются враждебным проходом за один раз, а разнесённые — расходятся первыми. У database.md механизм заводить не под что: 241 и 211 строк.

FFF. Если вводить — точка входа остаётся одна. docs/architecture.md упомянут в репозитории 66 раз: девять charter'ов, карта project-facts.md, docs.py, скелеты. Развилка «файл или каталог» размножится на девять «прочитай либо обойди». Поэтому форма жёсткая: каталог легален только при <имя>/README.md, и он и есть прежний документ — обзор целиком со ссылками на вынесенное, а не оглавление к нему. Каждый файл каталога обязан быть достижим ссылкой из README.md; это проверяется сегодняшним механизмом ссылок docs.py и ловит файл-сироту. Вынеся раздел, README.md на него ссылается, а не пересказывает — тот же приём, которым в архитектуре уже описаны компоненты со ссылкой на capability.

GGG. Решение отложено до конца переезда healthlog (шаг 2 TODO). Порядок: довести поведение в спеки, замерить остаток. Жмёт после этого — вводить каноном версии 3, и сразу для review.md и architecture.md, а не для всех документов корня скопом.

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

  1. Цена изменения — версия канона, а не правка одного файла. Обратной совместимости у канона нет, поэтому в счёт входят: docs.py (check_stray с его ALLOWED_FILES/ALLOWED_DIRS, check_required — обязательный путь становится развилкой, check_capabilities — сегодня читает ровно один файл), skeletons.md, project-facts.md, девять charter'ов, запись в changelog.md канона и ветка upgrade в скилле canon.
  2. Раздутый документ канона — сначала подозреваемый, потом кандидат на вынос. Диагностика перед раскладкой — счёт маркеров долга (grep -c "<!-- канон:") и вопрос, не поведение ли это. Разложить дрейф по файлам значит перестать его видеть.
  3. Материал для решения даёт healthlog, а не jellybit. У второго 169 строк архитектуры — там вопрос не стоит вовсе, и принимать по нему решение значит принимать его без предмета.

17. Разбор заметок: ступень ревью, род работы, роадмап (2026-08-04)

Что было

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

Решено

HHH. Цвет charter'а кодирует модель, а не роль прохода. Раскладка sonnet → green, opus → yellow, fable → red. Роль прохода видна из имени, а стоимость прогона — ниоткуда; цвет, розданный по ролям, не отвечает ни на один вопрос, который задают во время прогона. Дом раскладки — таблица «Модель по проходу» в review-pipeline/SKILL.md.

III. Фронтматтеры проверяются машиной, а не вниманием. Три описания из четырнадцати содержали : в незакавыченном значении — для YAML это вложенное отображение, то есть синтаксическая ошибка, которую нельзя увидеть чтением: текст читается правильно. Тот же класс, что у mermaid-диаграмм, и лечится тем же способом — scripts/frontmatter.py. Он же держит раскладку цветов (HHH) и сверку name с именем каталога.

JJJ. Между standard и deep заведена ступень wide. (содержание триггеров пересмотрено темой 18, TTT: миграция схемы и публичный контракт ступень не поднимают.) Прыжок стоил самого дорогого прохода конвейера, а платить приходилось за одну архитектурную находку: изменений, которые трогают публичный контракт, но не вводят нового правила слияния, — большинство. wide — это standard плюс architecture (вход шире диффа, отсюда имя), семь проходов против восьми у deep.

KKK. Триггер независимой реализации стал триггером профиля. Раньше условие «изменение вводит новое правило идентичности, слияния или разбора» стояло внутри deep, и профиль означал то семь проходов, то восемь. Реестр состава, который «сверяется взглядом до коммита», проверять было нечем: у профиля не было одного правильного ответа. Теперь условие выбирает профиль, а reimpl в deep безусловен — и он единственное, чем deep отличается от wide.

LLL. Барьер стоимости остался только в deep. В wide за ним стоял бы один дешёвый проход с потолком в 3 находки, а барьер не бесплатен — он сериализует то, что могло идти разом. Вторая причина помельче: барьер спрашивает «выживает ли форма изменения», а architecture — как раз тот, кто на этот вопрос отвечает.

MMM. Род работы — вторая ось типа, и живёт тегом. Тип записи (goal/idea/epic/task) отвечает «что это за запись», род (feature/fix/chore/research) — «какого рода работа». В один префикс их не свести: идея бывает про функцию, эпик функцией и является. Дом — тег kind:<род>, потому что теги здесь и есть единственный механизм разметки, а list --kind работает даром. Принятая цена: в строку индекса род не попадает (индексы производны), и состав набора по роду виден командой, а не глазами. Словарь закрыт — открытый разъехался бы на синонимах bug/bugfix/fix.

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

OOO. Задача называет границы, а не намерения. Раздел «Затрагивает» — эндпоинт, таблица и миграция, формат на диске, публичный тип пакета. Без него задача оценивается по объёму текста, а не по объёму поверхности, и оценка систематически занижена ровно там, где текст короткий, а границ много. Механизм проверяет наличие непустого раздела: полноту перечня машина не видит, и делать вид, что видит, хуже, чем не проверять.

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

QQQ. PLAN.mdROADMAP.md, вместе с ключом конфига и токенами команд. Слово «план» в репозитории значит три разных вещи — оглавление целей, план реализации внутри задачи и PLAN.json разовой адаптации. Переименовано всё: tasks.plantasks.roadmap, --index plan--index roadmap, --plan-sections--roadmap-sections. Старый ключ в docs/.pm.json не игнорируется молча — скрипт останавливается и называет переименование.

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

  1. Версия канона 3 занята этим изменением. Отложенное решение темы 16 (каталог вместо файла в docs/) вводится теперь версией 4, а не 3.
  2. Род работы ничего не предписывает конвейеру. Профиль ревью выбирается по факту изменения: chore бывает миграцией схемы, fix — правкой публичного контракта. Правило «предписание процесса в теле задачи снимается» родом не отменяется, а подтверждается.
  3. Проверка фронтматтеров — третья проверка репозитория того же класса. Копии, диаграммы, фронтматтеры: всё это ошибки, невидимые при чтении. Класс опознаётся по признаку «диff выглядит разумно, а результат ломается», и каждый его представитель получает скрипт, а не пункт чек-листа.
  4. Ступеней профиля четыре, и правило выбора читается сверху вниз. Первое сработавшее условие и есть ответ: правило слияния → deep, контракт или схема → wide, видимое снаружи поведение → standard, иначе quick.

18. Ступень поднимает проход, а не риск (2026-08-04)

Что было

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

Разбор показал, что размер задач — только половина причины, и не главная.

Решено

RRR. Профиль — максимум по поверхности, а не средневзвешенное. Условия читаются сверху вниз, первое подошедшее отвечает за весь дифф. Значит цена ревью растёт быстрее размера задачи: на крупной задаче верхний профиль оплачивается в том числе за ту её часть, которая сама по себе была бы quick. Это и есть механизм, ради которого выбран путь мелких задач.

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

Триггер Кто закрывает Где этот проход
миграция схемы gate (шаг миграций), ops (миграция под потоком, откат при двух версиях) уже в standard
публичный контракт specs, направление code → spec во всех профилях
инвариант проекта основание для critical у любого прохода во всех
новый пакет, новое понятие architecture только wide
новое правило слияния reimpl только deep

Три верхних триггера не добавляли ни одного прохода — они поднимали ступень «на всякий случай». На проекте с базой и эндпоинтами это делало верхнюю ступень умолчанием, то есть правило объявляло исключением то, что происходит всегда.

TTT. Миграция схемы, публичный контракт и инвариант уехали в standard. wide теперь означает ровно одно: изменение вводит новое понятие или структурную единицу — новый пакет или слой, новая точка входа, второй способ делать то, что уже делается, перенос ответственности между узлами. Добавленное поле в существующем ответе концептом не является. Это отменяет часть JJJ темы 17: ступень wide остаётся, её содержание меняется. Проект, где изменение контракта и правда архитектурное (публичный SDK, чужие потребители), поднимает его сам в docs/review.md — уточнением, а не возвратом прежнего умолчания.

UUU. Чекпоинт design получил то же условие. review-specs в режиме «дизайн ДО кода» идёт всегда — это самый дешёвый чекпоинт конвейера. review-rubric и review-architecture — только при новом понятии. Причина арифметическая: чекпоинт стоит на каждой задаче, поэтому при мелкой нарезке три прохода умножаются на число задач и становятся самой большой статьёй. Причина по существу та же, что в SSS: рубрика на узел без нового понятия порождает свойства уже существующего рода, записанные конвенциями и спеками.

VVV. Шов нарезки — граница, за которой падает ступень. Тест декомпозиции отвечает, допустим ли разрез; шов отвечает, где его провести. Раздел «Затрагивает» перечисляет границы; строка, поднимающая ступень выше остальных, и есть кандидат на отдельную задачу.

WWW. Костяк из четырёх проходов платится за каждую задачу. Гейт, спеки, код, триаж несокращаемы, поэтому разрез, после которого обе половины остаются в одной ступени, делает ревью дороже: тот же объём тем же составом, но костяк оплачен дважды. Резать — когда разрез снимает дорогой проход с большей части диффа.

XXX. Верхняя ступень задана тестом, а не списком. «Идентичность, слияние, разбор» — формулировка, пришедшая из одного проекта, и в общем виде она не читалась: вопрос «как это применить к моему проекту» не имел ответа в тексте. Теперь класс задан тремя условиями, независимыми от домена и языка: вариантов несколько и оба защитимы; спека между ними не выбирает; неверный выбор не падает, а молча меняет смысл данных. Отрицательный тест сильнее положительных — то, что красит гейт или роняет запрос, в класс не входит. Три слова остались как три места, где такие правила водятся (граница входа данных и место их встречи), а проект перечисляет свои места в docs/review.md — перечень производен от теста и не расширяет класс.

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

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

  1. Порога в числе границ не заводится. Тот же принцип, что в теме 16 (CCC): размер не триггер. Шов проходит по скачку ступени, а не по длине перечня.
  2. Ступень — признак для планирования, но не запись в задаче. Строка «делать профилем standard» в теле — тот самый второй дом правила выбора, который снимает гигиена полей. Профиль выбирает тот, кто видит изменение.
  3. Дешёвое место заметить разнородную задачу — показ набора спринта. Там «Затрагивает» уже написан, а предложение об изменении ещё не заведено: разрез стоит одного edit вместо выброшенного предложения.
  4. Замер остаётся за обкаткой. Правило выведено из состава проходов, а не из статистики прогонов: считать, какая доля задач попадает в каждую ступень, можно только на спринтах нового процесса (TODO шаг 4).
  5. Отсутствие верхней ступени — законное состояние проекта. Бывают проекты, где данные приходят нормализованными, ничего ни с чем не сливается, а внешних форматов нет: deep там не срабатывает никогда, и придумывать ему повод не надо. Раньше это читалось как недонастройка.
  6. Ступень определяет класс правила, а не вид работы. Миграция схемы — standard, но миграция, переносящая данные по правилу («сложить дубли», «привести к одному виду перед сравнением»), несёт правило идентичности и потому deep. Одно слово в описании задачи попадает в разные ступени — это не противоречие, смотрят не на слово.

19. Роадмап — состояние проекта, а не очередь работ (2026-08-04)

Что было

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

Разбор показал, что инструмент отвечал ровно на половину этого вопроса.

Решено

YYY. Достигнутая цель из роадмапа не исчезает. close --implemented удалял у цели и файл, и строку — роадмап по построению показывал только «что осталось». Свидетельство нашлось в самом роадмапе healthlog: там руками заведена секция «Что уже пройдено» на двадцать строк прозы, и заканчивается она фразой «Эти звенья целями не заведены: закрытая цель записи не оставляет, ей хватает коммита и спеки». Обходной путь и его причина записаны рукой владельца. Теперь строка с датой переезжает в секцию достигнутого; файл удаляется по-прежнему.

Вторым домом поведения это не делает: нормативное поведение живёт в openspec/specs/, роадмап отвечает когда и в каком порядке оно появилось — другой вопрос. Ссылки на файл в строке нет намеренно: файла больше нет, а битая ссылка это законная ошибка check. Форма строки — как в REJECTED.md, и по той же причине.

ZZZ. Цель — возможность приложения, задача — шаг к ней. Заголовок цели отвечает на «что приложение будет уметь»: не «Работа со слиянием», а «Исход слияния не зависит от порядка доставки». Свойство поведения — тоже возможность: «сообщает о своём состоянии», «исход не зависит от порядка» — законные цели, переформулировки в функцию не требуют. Единственный настоящий чужак — работа над инструментом и процессом: на вопрос «что приложение будет уметь» она не отвечает и живёт в отдельной секции роадмапа.

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

БББ. Цель обязательна не у всякой задачи. Прежнее правило — «у каждой задачи должен быть goal:, иначе она не попадёт ни в один спринт» — было угрозой, а не аргументом, и заставляло операционную работу выдумывать себе направление. Граница проходит по роду работы: feature без цели не бывает (новая возможность и есть содержание цели), fix, chore и research живут без цели законно и входят в набор спринта помимо его цели. Это второй раз, когда род работы окупается, — и первый, когда он что-то определяет за пределами отбора.

ВВВ. Тип [epic] упразднён. Зонтик между целью и задачами не нужен: зонтиком стала цель, а слишком крупный шаг дробится на шаги помельче под ней. Замер: ноль употреблений на 97 записей двух живых проектов, при том что тип занимал место в словаре, тесте готовности, автомате переходов, split.md и трёх местах tasks.py.

ГГГ. Имена секций роадмапа — Готово / Запланировано / Направления / Разработка. Первый набор (умеет / строим / станок) прожил один заход и был признан неудачным. Из четырёх предложенных имён отвергнуто одно, и по проверяемой причине: окружение уже занято — в architecture.md это боевое окружение приложения, «где работает, что рядом, кто перезапускает», и одно слово в двух смыслах развело бы документы канона. Взято Разработка.

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

ДДД. Секции роадмапа канонические, секции беклога — нет. Разница выведена, а не назначена: у секций роадмапа есть семантика (достигнутое, очередь, долгое, не про продукт), в первую пишет сам close, и роадмап, названный по-своему, читался бы только своим автором. Секции беклога (Ядро, Инфра) семантики не несут — это полки. Поэтому check проверяет у роадмапа три вещи: состав закреплён (чужая секция — ошибка), все четыре обязаны быть, язык один на весь индекс; --roadmap-sections у init упразднён. Английский набор — Done | Planned | Directions | Tooling.

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

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

  1. Ключа tasks.achieved_section не появилось. Секция достигнутого опознаётся по каноническому имени в любом из двух языков, и лишний knob не заводится: канонический состав отвечает на тот же вопрос надёжнее конфига.
  2. reopen цели снимает строку достигнутого. Иначе роадмап продолжает утверждать, что приложение умеет то, что вернулось в работу.
  3. Прозаический раздел в индексе — дрейф. Любой ## проверка считает секцией, поэтому «Что уже пройдено» и «Почему в таком порядке» в healthlog формально были двумя лишними секциями, куда могла уехать задача. При повышении они разбираются: звенья — строками в Готово, обоснование очереди — прозой внутри Запланировано.
  4. Правил стало пять, и нулевое — про смысл, а не про механику. «Цель — возможность, задача — шаг к ней» стоит перед правилами о гниении беклога и производности индексов, потому что из него следует, зачем эти механики нужны.

20. Форма записи: заголовок, секции, вычитка (2026-08-04)

Что было

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

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

Решено

ЕЕЕ. Заголовок отвечает на вопрос своего типа, и форм три. Цель — утверждение о возможности («Соперником может быть компьютер»); задача — глагол в неопределённой форме, допускается «не» перед ним («Не отбрасывать молча лишние символы в ходе»); идея — назывное, без обещания. Причина не стилистическая: описательный заголовок называет состояние, а из состояния не видно, чего от работы ждут — «Ничья объявляется, пока клетки есть» одинаково читается как жалоба и как задание. В списке, где решают «брать или не брать», это разные вещи.

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

ЖЖЖ. Механизировано ровно то, что механизируется, — счётчиком, а не замечанием. check считает заголовки, у которых первое слово не оканчивается на -ть/-ти/-чь (перед ним допускается «не»), и печатает число в блоке здоровья. Замечанием на файл этого делать нельзя: проверка эвристическая, а беклог, заведённый до правила, переоформляют не «заодно» — десятки одинаковых строк научили бы пропускать весь блок.

ЗЗЗ. Годность формулировки судит отдельный агент doc-wording, а не чек-лист в скилле. Самопроверка текста слабее всего там, где формулировка казалась удачной при написании, — а пишет и проверяет иначе один и тот же агент в одном контексте. Агент читает пачку записей и возвращает готовые формулировки на замену, ничего не правя сам; заголовок и «зачем» подставляются командой и показываются человеку, потому что именно по ним задачу выбирают. Он намеренно не проверяет ничего из того, что ловит tasks.py check: повторить машинную проверку словами значит завести правилу второй дом.

ИИИ. Заголовок секции — с прописной, после него пустая строка. Во всех индексах, включая секции беклога, имена которых выбирает проект: правило про оформление, а не про имя. Канонические имена стали писаться с прописной (Готово | Запланировано | Направления | Разработка, англ. Done | Planned | Directions | Tooling), сверка везде идёт по нижнему регистру, так что старые индексы читаются по-прежнему и поднимаются check --fix.

ККК. Имя секции принадлежит заголовку индекса, файл на неё только ссылается. Это разрешает единственную неоднозначность починки: расхождение файла и заголовка в одном регистре правится в пользу заголовка. Без этого шага переезд на канон оставил бы Готово в роадмапе и готово в каждом файле цели — расхождение безвредное, но вечное, потому что свести его было бы некому.

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

  1. Отбивка живёт на записи, а не на вставке. spaced_sections вызывается в Plan.index, через который проходит каждая запись индекса. Чинить отбивку в каждом месте вставки значило бы полагаться на то, что ни одного не забыли, — а мест вставки три (--first, --after, в конец).
  2. Обкатка нашла два дефекта, которых не нашли ни линтеры, ни свои проверки. Вставка в пустую секцию съедала отбивку перед следующим заголовком; мета, разорванная пустой строкой, теряла поля молча, а check видел только следствие («без рода работы») и советовал edit --kind, который дописывал второе такое же поле. Оба класса теперь названы: пропуск пустых строк идёт только до первой непустой, а поле меты в теле — ошибка с названной причиной, которую --fix намеренно не чинит.
  3. Пустой проект показывает форму хуже живого. Чтобы увидеть достигнутую цель, отказ, спринт и все четыре рода работы, проект пришлось поставить на середину пути. Это довод в пользу того, чтобы обкатку вести на состоянии, а не на старте: у старта половина формы не наблюдаема.
  4. Мелкая цель даёт две задачи, и это не повод её укрупнять. У цели «Соперником может быть компьютер» третья задача напрашивалась (выбор уровня соперника), но не мерджится порознь: без сильного соперника выбирать не из чего. Тест декомпозиции её отбил — частичный ответ на вопрос «не выродились ли цели в ярлыки тем».

21. Язык проектных текстов — информационный стиль (2026-08-04)

Что было

Языковые правила лежали внутри скилла tasks, в разделе «Как написана задача»: англицизмы, неизвестные термины, «сложность формулировки — не признак сложности работы». Три пункта, выведенные из практики, без общей опоры и без ответа на вопрос «а что ещё сюда относится».

Дал ссылку на чужой скилл prepare-jira-text — там раздел «Язык» с информационным стилем, таблицей англицизмов-калек и таблицей жаргона. Заодно попросил найти справку об информационном стиле Максима Ильяхова и адаптировать его.

Решено

ЛЛЛ. У языка появился один дом — canon/references/language.md. Не в tasks, хотя пришёл он оттуда: правила относятся к документам канона, решениям ADR, запискам разведки и сообщениям коммитов в той же мере, что к задачам, а каталог задач и сам часть docs/. Раскладка отвечает, где текст лежит; этот файл — каким он должен быть. tasks/SKILL.md оставил у себя четыре правила, которые нарушаются чаще прочих, и ссылку.

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

Раздел «Что отброшено намеренно» написан не для полноты. Без него правило читается как «пиши короче», и первый же агент начинает резать «поэтому» и «иначе» — то есть ровно то, ради чего текст и писался.

ННН. «Снять корону» переведено на здешнего читателя. У Ильяхова это «надеть корону на клиента». Здесь клиент — ты сам через квартал и тот, кто возьмёт задачу. Отсюда конкретное требование: называть состояние и остаток, а не пересказывать, как было интересно разбираться.

ООО. Таблицы англицизмов и жаргона уехали в агента помеченной копией. Устав агента обязан быть самодостаточным — он не разрешает пути плагина и не ходит по ссылкам, — а два дома у одного правила уже трижды расходились. Механизм для этого в репозитории есть (scripts/copies.py), и это ровно его случай: копия дословная и помеченная, проверка ловит расхождение.

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

  1. У агента вычитки правил стало двенадцать, и они разделены на две группы. «Форма записи» верна только для каталога задач, «язык» — для любого проектного текста. Разделение не косметическое: находки докладываются группами и в этом порядке, потому что форма меняет решение «брать или не брать», а язык — только цену чтения.
  2. Порог правки записан дважды и одинаково — в language.md и в уставе агента: правка без нарушенного правила не делается. Это единственная защита от списка, в котором половина замечаний вкусовые: такой список перестают читать целиком, и настоящие находки пропадают вместе с ним.
  3. Переезд на канон 3 языком ничего не требует. Шаг в changelog так и записан: прочитать и ничего не переписывать задним числом. Сплошная вычитка старых документов стоит дороже, чем даёт, а правила применяются к тому, что правится сейчас.

22. Обкатка агента вычитки: имя, охват и «так везде» (2026-08-04)

Что было

Агента вычитки прогнали по тестовому набору — 13 записей выдуманного проекта. Устав он читал сам, как обычный подрядчик.

Решено

ППП. Агент называется doc-wording, а не task-wording. Имя пришло из задач, но правила языка относятся ко всем проектным текстам: документам канона, решениям ADR, запискам разведки. Форма записи — вторая половина устава — верна только для файлов docs/tasks/items/, и теперь это сказано заголовком раздела, а не подразумевается. Вход агента расширен: список файлов или каталог, вперемешку тоже.

РРР. «Так сделано везде» — не оправдание, а признак. Агент нашёл, что раздел «Затрагивает» в нескольких записях называет не только границу, но и её будущее состояние («источник хода становится двумя»), — и промолчал, объяснив это принятым стилем каталога. Записи писал один агент за один заход: систематичность здесь значит ровно обратное — правило не применялось вовсе.

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

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

  1. Находка агента попала в слово из собственного скилла. «Цель про станок, а не про игру» — метафора, которую я перенёс в тестовую запись из tasks/SKILL.md. Проверка показала худшее: станок в каноне уже занят — «общий станок» это красная проверка, врывающаяся в замороженный спринт (canon.md, session/SKILL.md). Одно слово в двух смыслах, тот же класс, что и окружение в теме 19. В tasks/SKILL.md заменено на «работа над инструментом и процессом» — как названа и секция роадмапа.
  2. Одна находка на 13 записей — не провал вычитки. Тексты писались сразу по правилам, и находить в них было почти нечего. Показательно другое: агент удержал порог (вкусовых правок не предложил) и явно сказал, по чему проверял термины, — то есть отработали обе защиты, а не только та, что ищет.

23. Вычитка разделена на два прохода (2026-08-04)

Что было

В уставе агента вычитки стоял заголовок «Форма записи — только для docs/tasks/items/». Условная половина устава: на документе канона она молчит, на задаче включается.

Решено

ССС. Проходов два: task-form и doc-wording. Разделены не по охвату — по глубине. Язык проверяется по словам и фразам, поштучно, и это подметание: залог, оценки, стоп-слова, англицизмы, жаргон. Форма записи требует понять, что задача делает, и открыть файл цели, на которую она ссылается, чтобы сверить, какую строку «Завершения» задача двигает. Слитый проход одну половину делает дорогой, а вторую — поверхностной.

Отсюда и разные модели: doc-wording — sonnet, task-form — opus. Первый подметает, второй судит смысл, и ровно на суждении обкатка показала провал — агент сам себе объяснил находку «принятым стилем каталога» (тема 22).

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

УУУ. Каждый устав отказывается от чужой половины прямо. «Увидел не по своей части — скажи строкой в границах покрытия, не находкой». Без такого отказа две проверки одного места расходятся и начинают спорить, а разнимать их потом дороже, чем не сводить. Исключение ровно одно и названо: неудачное слово в заголовке судит task-form, потому что заголовок целиком его.

ФФФ. Порог правки переехал в дом и копируется в оба устава. Он теперь в language.md помеченным домом порог-правки: правка без нарушенного правила не делается, систематичность нарушения — не довод в его пользу. Дублировать его руками в двух уставах значило бы получить два разных порога через месяц.

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

  1. Шестое правило task-form — единственное, что читает больше одного файла. Оно же единственное, что смотрит набор, а не запись: строка «Завершения», к которой не относится ни одна поданная задача, докладывается отдельным блоком. Это граница между вычиткой и разбором, и она проведена внутри правила, а не между агентами.
  2. Порядок вызова — сперва task-form. Его находки меняют решение «брать или не брать», а язык — только цену чтения; и переписанный заголовок бессмысленно вычитывать до того, как он переписан.
  3. Помеченных копий стало шесть при пяти домах. Механизм scripts/copies.py впервые используется не для скелетов канона, а чтобы удержать одно правило в двух уставах подрядчиков. Случай тот же: текст обязан быть на месте, потому что подрядчик по ссылкам не ходит.

24. Обкатка двух проходов: два дефекта в собственных правилах (2026-08-04)

Что было

Оба прохода запущены на тестовом наборе из 13 записей. task-form дал три находки и блок «строки Завершения», doc-wording — пять находок. Разделение окупилось сразу: task-form поймал ровно тот класс, на котором слитый агент промолчал (границы, названные будущим состоянием, — тема 22, РРР).

Но два его правила разошлись с остальным каноном.

Решено

ХХХ. «Одна мысль — одно предложение» не распространяется на поля меты. doc-wording предложил разбить «зачем» надвое — а task-format.md требует от «зачем» одного предложения: оно повторяется строкой индекса, и второму там не поместиться. Агент честно выполнил тот документ, который читал; виноват не он, а правило без оговорки. Оговорка записана и в доме (language.md), и в уставе: тесно — сокращай, но не дели.

ЦЦЦ. «Не своё» бывает двух родов, и поступают с ними по-разному. Чужому подрядчику — строкой в границах покрытия, чтобы находка не пропала. Машинной проверке — вообще ничего, даже строкой: это не потерянная находка, а уже проверенное. doc-wording отправил в «замечено не по моей части» открытый вопрос в задаче — а его ловит tasks.py check, и строка получилась шумом, который выглядит как работа.

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

  1. Шестое правило нашло то, чего не искали. Три строки «Завершения» оказались закрыты критериями задач, но не заявлены самими задачами, а одна строка цели (checks-one-command, «названа в README и в описании работы над проектом») — закрыта наполовину. Агент назвал оба толкования и выбирать не стал, как и велено. Выбрано сужение цели: описания работы над проектом у выдуманной игры нет вовсе, и строка обещала то, чего негде исполнить.
  2. Спорные находки полезны тем, что показывают спор правил, а не вкуса. Из пяти языковых находок три приняты сразу, две отклонены — и обе отклонённые указывали на одно и то же место канона (правило 4 без оговорки). Вкусовых находок не было ни одной: порог держится.

25. Секция Сопровождение и общий словарь трёх мест (2026-08-04)

Что было

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

Решено

ЧЧЧ. Секция называется Сопровождение / Operations, и её смысл расширен. Было «инструмент и процесс», стало «чем держат проект: инструмент, процесс, эксплуатация». Расширение не косметическое: английское Operations при узком смысле обещало бы эксплуатацию, а внутри лежал бы линтер. Либо слово, либо смысл — сошлись на смысле, потому что метрики, логи, инфраструктура и выкладка в эту секцию просятся и так.

ШШШ. Версия канона не менялась, и это законно. Ни один проект на каноне 3 не стоит: healthlog и jellybit держат канон 2, повышение только предстоит.

Отменено в тот же день (тема 26). Посылка была ложной: healthlog уже переехал на канон 3, и правка записи версии 3 задним числом переписывала то, по чему он ехал. Правило осталось верным, применение — нет: черновиком запись версии является ровно до того, как первый проект по ней поехал.

ЩЩЩ. Сопровождение и эксплуатация — целое и часть, и словарь у трёх мест общий. Тема живёт в трёх документах, и раньше каждое место говорило своим словом. Теперь: сопровождение — всё, чем держат проект (инструмент, процесс, выкладка, метрики, логи, инфраструктура, дежурство); эксплуатация — его часть, работа системы на проде.

Место Уровень Что там
ROADMAP.md, секция Сопровождение план работы, которые собираемся делать
architecture.md, раздел «Эксплуатация» состояние как устроено сейчас
эксплуатационный проход ревью оптика «это упало через неделю на проде»

Сливать три места в одно слово было бы ошибкой: они отвечают на разные вопросы — план, состояние, проверка. Синхронизирован словарь, а не границы; дом словаря — canon.md.

Слово «поддержка» запрещено вовсе: в нём слышится помощь пользователю, а это третья работа, к этим двум не относящаяся.

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

  1. Граница с возможностями проходит по тому, кто наблюдает. «Приложение сообщает о своём состоянии» — возможность (наблюдает пользователь сервиса); «дежурный видит состояние на одном экране» — сопровождение (наблюдаем мы). Одни и те же метрики попадают в разные секции роадмапа, и это верно.
  2. check --fix чужую секцию не переименовывает — и правильно. На переименовании РазработкаСопровождение проверка назвала секцию роадмапа чужой и остановилась: регистр она правит сама, смысл — нет. Ровно то поведение, которое нужно проекту при повышении канона.

26. Канон 4: правка задним числом отменена (2026-08-04)

Что было

Секцию Разработка переименовали в Сопровождение без повышения версии канона — на посылке «ни один проект на каноне 3 не стоит» (тема 25, ШШШ). Посылка оказалась ложной: healthlog уже переехал, docs/.pm.json держит "canon": 3, а роадмап — секцию Разработка с прописной. Правка записи версии 3 переписывала то, по чему он ехал.

Решено

ЭЭЭ. Запись версии — черновик ровно до первого переехавшего проекта. После этого она история, и любое изменение канона заводит новую версию, даже если меняется одно слово. Проверять это дёшево: grep '"canon"' */docs/.pm.json по живым проектам. Дорого — обратное: проект, повышенный по тексту, которого больше не существует, невоспроизводим.

Запись версии 3 восстановлена дословно (Разработка | Tooling), переименование уехало в версию 4. jellybit, стоящий на каноне 2, прочтёт обе записи подряд и заведёт Разработка, чтобы через шаг переименовать; в шаг версии 3 добавлена оговорка «едешь сразу на 4 — заводи Готово последней и не переставляй дважды». Лишний шаг — плата за честную историю, и она мала.

ЮЮЮ. Готово переехало вниз, и порядок секций стал каноническим. Достигнутое копится: через год этой секции больше, чем всех остальных вместе, — и стоя первой она отодвигает за экран ровно то, ради чего роадмап открывают чаще всего. Порядок теперь проверяется (roadmap_lint) и правится (check --fix переставляет секции вместе с содержимым): без проверки порядок разъедется молча, а переставлять секцию с десятком строк руками — работа, на которой ошибаются.

ЯЯЯ. Индексы позиций считаются из самого кортежа. ACHIEVED был 0 и стал 3; хардкод индексов пережил бы перестановку молча и сломал бы close. Теперь PLANNED, DIRECTIONS, OPERATIONS, ACHIEVED = range(len(ROADMAP_SECTIONS)) — переставили секцию, индексы переехали сами.

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

  1. Отбивка нужна и перед заголовком. Перестановка блоков ставит два заголовка вплотную — spaced_sections правил только строку после. Дефект нашёлся сразу же, на первой перестановке демо-набора: класс правки, существующий только потому, что появилась другая правка.
  2. check --fix переставляет, но не переименовывает. Чужую секцию он оставляет ошибкой, и на переименовании РазработкаСопровождение останавливается: имя — решение человека, порядок — механика. Тот же разрез, что между регистром (правит) и составом (не трогает).
  3. Версия канона отделяет состояния проектов, а не редакции текста — и ровно поэтому её нельзя не поднять, когда состояние хоть одного проекта уже зафиксировано.

27. Тип записи стал единственной осью и задаёт схему (2026-08-05)

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

ААББ. Осей было две, и ортогональность была фальшивой. Тип записи (goal/idea/task) и род работы (kind:<род> тегом) давали двенадцать клеток произведения, из которых законны шесть: у цели род запрещён, у задачи обязателен, у идеи пуст и на практике не ставится. Плюс «алгоритм работы над записью такого типа» крепится не к task, а к fix и research — то есть к роду. Ось, к которой пишется алгоритм, и была настоящим типом. Оси схлопнуты в одну из пяти значений: goal | feature | fix | chore | research.

ВВГГ. Тип idea упразднён: состояние не может быть типом. Он значил не род работы, а незаполненность — «первый, второй или третий вопрос теста готовности не отвечается». Состояние меняется по мере того, как запись дописывают, а тип меняют командой, и на этом расхождении idea и жила: её приходилось «понижать» и «повышать» вручную. Теперь состояние выводится из заполненности — research без раздела «Вопрос» это сырьё, — и различие держит та же машина, что и всё остальное.

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

ДДЕЕ. Дом типа — поле меты, эмодзи производна. Прежнее правило «отдельного поля типа нет: два места для одного факта разъезжаются» отменено не потому, что разонравилось, а потому, что его аргумент был против префикса плюс поля. При переносе дома в мету дом остаётся один; из индекса тип при этом пропадал бы — там, где принимают решение «брать или не брать», — и это чинит эмодзи. Она стоит в H1, а не в строке индекса, чтобы инвариант «заголовок в индексе дословно» остался нетронутым: одна проверка вместо двух.

ЖЖЗЗ. Поле места назвали по типу, а не одним словом на всех. «Категория» вместо «Секции» — просьба заметки, но одинаковое переименование закрепило бы смешение: у задачи поле называет полку домена, в которую она вернётся из спринта, у цели — часть роадмапа, то есть состояние очереди. Разные имена (Категория / Секция) выбраны именно потому, что какое поле обязательно, решает тип — то самое, ради чего затевалась вся правка.

ИИКК. Два новых обязательных раздела появились из уже записанных правил, которые нечем было проверить. «Не воспроизводится — это research, а не fix» стояло в каноне и не проверялось: раздел Воспроизведение делает его проверяемым. Приёмка разведки — «записанный ответ, а не изменённый код» — тоже стояла, но sprint take требовал от research два-пять критериев с оракулами, и они писались ради проверки; вместо них Вопрос и Куда ляжет ответ.

ЛЛММ. Сортировка «по важности» отклонена, «сырьё в конец» взято. Первая требует, чтобы кто-то важность поддерживал, — это ровно тот приоритет, от которого правило 4 отказалось сознательно. Вторая выводится из типа и заполненности, а не назначается человеком, и потому проверяется машиной и приоритетом не становится. Разрез прошёл по признаку «кто источник порядка», а не по признаку «полезно ли».

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

  1. Правило можно отменять его собственным аргументом. «Отдельного поля типа нет» держалось на «два места для одного факта»; перенос дома оставил одно место, и правило перестало применяться. Проверять надо не запись правила, а то, выполняется ли ещё его посылка.
  2. Схема, шаблон и проверка растут из одной таблицы. TYPE_SCHEMA кормит и body_template, и schema_verdict: иначе add кладёт то, на чём sprint take потом откажет. Тот же приём, что нормализатор spaced_sections для оформления индексов.
  3. --fix не угадывает того, чего нет. Тип переносится из тега kind: и префикса [goal]/[idea] детерминированно, но записи, заведённые до появления рода работы, не несут ни того ни другого — feature от chore машина не отличает. Они уходят в НЕОДНОЗНАЧНО поимённо, а не получают значение по умолчанию, которое врало бы ровно там, где по нему принимают решение.
  4. Мигрирующие шаги обязаны читать отложенный текст, а не диск. Шагов, правящих мету, стало пять, и второй, перечитавший файл, стёр бы правку первого. Общий stage() поверх files снял целый класс отказов, который до этого держался на том, что шагов было мало.

28. Слаг подкреплён проверкой, обещанный судья заведён (2026-08-05)

Два пункта заметок, оба про одно: правило было записано и никем не исполнялось.

ННОО. Правило про английские слаги существовало и не проверялось ничем. canon.md говорил «слаги файлов, capability и задач — английские, kebab-case» одной строкой в хвосте раскладки; docs.py имён файлов не смотрел вовсе. Итог предсказуем и нашёлся в самом плагине: единственный пример ADR в скилле docs назывался ADR-2026-08-03-ochered-tablicej. Раскладка канона при этом приглашала к нарушению — в схеме стояли плейсхолдеры <тема>.md, то есть слово «тема» по-русски там, где надо было писать <slug>.

Разрез проверки — по тому, что машина знает точно: кириллица в имени и не-kebab-case жёстко, форма ADR-ГГГГ-ММ-ДД-slug.md жёстко, транслит эвристикой, то есть замечанием. Набор маркеров транслита подобран так, чтобы ложных срабатываний не было вовсе: выброшены ost (ловит post, cost), sch (schema), ya (yaml), nost (nostalgia), хвост ii (radii). Цена названа: sostoyanie-partii проходит мимо. Правило, краснеющее на правде, приучает пролистывать весь блок — это дороже пропуска.

ППРР. Канон три версии обещал судью, которого не было. В canon.md есть таблица «Что проверяет машина, а что человек», и её правая колонка — смысловой дубль, поведение в architecture.md, протухший факт, достаточность честной строки — описывала работу, которую никто не делал: скилл canon предлагал агенту судить об этом самому, то есть проверять то, что он же и писал. Заведены doc-consistency и doc-code-drift, а колонка получила третий столбец с именем судьи: обещание без адресата и есть тот способ, которым правило перестаёт исполняться.

ССТТ. Агентов двое, разрез по глубине, а не по охвату. Тот же довод, что развёл task-form и doc-wording: сверка текста с текстом дёшева и зовётся на каждом синке документации, сверка с кодом требует читать репозиторий и зовётся раз в спринт. Слитый агент делает дешёвую половину редкой либо дорогую — поверхностной.

УУФФ. Перечень фактов, сверяемых с кодом, закрыт. Имя основной ветки, команды, пути, зависимости поимённо, настройки с числовым значением, единые точки проекта, capability, проверяемые инварианты. «Сверить архитектуру с кодом» — задача без дна, и агент, которому её поставили, выдаёт правдоподобную труху вместо находок. Отсюда и форма доклада doc-code-drift: он начинается таблицей проверенного, а не находками, — по ней видно, чего он не смотрел.

ХХЦЦ. Карта домов уехала в устав агента помеченной копией. Устав ссылался на файл плагина, а агент работает в репозитории проекта, где плагина может не быть. Копия дословная, под маркерами дом/копия, и copies.py теперь её сторожит — механизм для этого в репозитории уже был.

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

  1. Записанное правило без проверки не исполняется даже автором. Слаг ADR нарушен в единственном примере, который плагин показывает как образец. Тот же класс, что «прозаический триггер ADR дал 6 записей на 43 изменения»: умолчание становится отличимым только когда его проверяют.
  2. Плейсхолдер — часть правила. <тема>.md в схеме раскладки перевешивал строку правила, стоявшую двумя абзацами ниже: образец читают вместо текста.
  3. Эвристика настраивается по ложным срабатываниям, а не по полноте. Ноль ложных при одном пропуске лучше, чем наоборот: пропуск стоит одной ненайденной находки, ложное срабатывание — доверия ко всему блоку.
  4. Докстрока разошлась с кодом ровно там, где её читают. copies.py показывал закрывающие маркеры как <!-- /дом -->, а требовал <!-- /дом: <id> -->; нашлось это первой же попыткой ими воспользоваться. Пример в докстроке — тот же образец, что плейсхолдер в схеме.

29. Обкатка doc-consistency на самом dev-skills (2026-08-05)

Первый прогон агента — по репозиторию, который его же и содержит. Два прохода (av-dev-pm; пайплайн плюс верхний уровень), 17 находок, все подтверждены по файлам.

ЧЧШШ. Агент нашёл ровно тот класс, ради которого заводился, и в свежей работе. Пять находок — остатки прежней модели типов в файлах, которые я не дошёл поправить двумя коммитами раньше: adopt.md держал имена секций канона 2, from-review.md и TODO.md — упразднённый [idea], task-batch в другом плагине — «задачи-идеи». Правку модели я вёл от документов, которые менял, а не от документов, которые на них ссылаются, — и обратный обход не сделал ни разу.

ЩЩЪЪ. Самая дорогая находка была моей и свежей. Таблица типов в canon.md объявляла цель у fix запрещённой, а tasks/SKILL.md и task-fix.md — необязательной; код на стороне вторых. Копия разошлась с домом за один день — я написал обе половины в одном коммите. Это и есть цена второго дома в чистом виде: не «когда-нибудь разойдётся», а «разошлось прежде, чем высохли чернила».

Исход не «поправить значение», а убрать причину: canon.md дважды объявлял, что фиксирует только словарь типов, — значит колонкам «разделы» и «цель» в нём не место. Осталась таблица из двух колонок и ссылка на дом схемы.

ЫЫЬЬ. Копии перечня «чем держат проект» разъехались втроём. canon.md, tasks/SKILL.md и task-goal.md пересказывали его своими словами: «метрики и логи» против «мониторинга», «проверки» есть в двух из трёх. При этом tasks/SKILL.md ссылался на дом рядом с собственным пересказом — ссылка не мешает копии разойтись, если копия всё равно стоит.

ЭЭЮЮ. Находка про коммиты снята как неверная, и это дефект самого агента. Он прочитал av-dev-git/skills/commit/SKILL.md («без Co-Authored-By») как описание практики этого репозитория и предъявил 38 коммитов с трейлером. Но dev-skills — маркетплейс плагинов: скилл коммита здесь продукт, уезжающий в чужие проекты, а не правило, которому подчиняется сам репозиторий. Устав агента не различает «документ описывает этот репозиторий» и «документ описывает то, что репозиторий производит».

ЮЮЯЯ. Счётчики в документах отменены как класс. REMAINING.md держал «после разбора двенадцати тем и 16 коммитов» (стало 28 и 52) и «три неизмеренных изменения подряд» (стало больше). Оба числа обязан двигать человек, и оба отстали молча. Заменены на формулировки, которые не надо поддерживать, и в шапку записана причина.

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

  1. Правка модели идёт по обратным ссылкам, а не по изменённым файлам. Меняешь дом — обойди тех, кто на него ссылается: grep по упразднённому слову дал бы все пять остатков за минуту. Это дешевле любого агента и должно идти до него.
  2. Ссылка на дом не отменяет копию, стоящую рядом. Проверять надо не «есть ли ссылка», а «есть ли пересказ»; tasks/SKILL.md имел и то и другое.
  3. Копия расходится с домом в пределах одного коммита. Прежняя оценка («разойдётся на первой правке») занижена: расхождение возникает при написании, если оба места пишет один проход.
  4. Агент, читающий репозиторий-продукт, обязан различать «про нас» и «про то, что мы производим». Иначе он предъявляет продукту практику его потребителя. Устав doc-consistency этого различения не содержит — остаток записан в REMAINING.
  5. Число в документе — обязанность, которую никто не берёт. Счётчик тем, коммитов, правок протухает молча; формулировка без числа дешевле его сопровождения.

30. av-dev-backlog удалён (2026-08-05)

Плагин был помечен устаревшим решением Q и жил до перевода jellybit. Удалён раньше этого срока.

ААББВВ. Замороженный плагин стоит дороже, чем кажется. Он не менялся, но платил собой в каждой проверке репозитория: exclude в pyproject.toml, SKIP_DIRS в copies.py, два абзаца README, оговорка в описании маркетплейса, чтобы не ловить триггер «добавь задачу в беклог». Пять исключений ради кода, который никто не читает, — и каждое надо было объяснять всякий раз, когда кто-нибудь спрашивал, почему проверка обходит каталог.

ААББГГ. Понимание старой раскладки уехало из плагина раньше самого плагина. docs/backlog/ читает не backlog.py, а av-dev-pm:tasksadopt.md и адаптер в tasks.py держат ту же раскладку как вход миграции. Плагин перестал быть единственным, кто её знает, ещё когда писался adopt; условие «живёт до перевода последнего проекта» с тех пор охраняло пустоту.

ААББДД. Опасение про порядок снятия не подтвердилось. Удаление опередило снятие: на jellybit плагин оставался включённым, когда записи в маркетплейсе уже не было, и ожидалась ручная чистка enabledPlugins и installed_plugins.json. claude plugin uninstall отработал штатно — он идёт по реестру, а не по манифесту маркетплейса, и отсутствие записи там ему безразлично. Предупреждение из README снято, вместо него записан проверенный факт.

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

  1. Устаревшее удаляют, а не замораживают. Заморозка выглядит бесплатной, но растекается исключениями по конфигам и требует объяснения в каждом месте, куда попала. Если удалять пока рано — назвать условие и срок; условие без срока переживает свою причину.
  2. Условие «живёт до X» проверяют на живость, а не на X. Здесь X (перевод jellybit) не наступил, но причина условия отпала раньше: знание раскладки переехало в adopt. Перепроверять надо основание, иначе условие держит само себя.
  3. Порядок снятия и удаления из маркетплейса свободный. uninstall живёт реестром, манифест ему не нужен. Правило записано после проверки, а не из осторожности, — и осторожность здесь стоила бы лишнего абзаца в README про починку, которой не бывает.

31. Ревизия покрытия av-dev-pm продакт-оптикой (2026-08-05)

Сабагент в роли продакт-менеджера оценил покрытие жизненного цикла личного проекта (один человек, недели-месяцы) скиллами и агентами av-dev-pm. Скоуп сужен по ходу разбора: деплой и разбор инцидентов на проде делаются вручную, скиллов под них не заводим. Осталось планирование, разработка и доработка.

ААББЕЕ. Шаг 2 сессии требовал чисел, которых процесс отказался собирать решением. cadence.md делал обязанностью пересмотр «ориентира по размеру спринта, прироста беклога на закрытую задачу, времени на задачу» и «сколько заняли задачи против ожидания» — с обоснованием «иначе обязанность висит ничья». Данных под это нет: у записи нет дат заведения, взятия и закрытия, close --implemented удаляет файл, sprint close очищает SPRINT.md. Хуже того, «против ожидания» и «время на задачу» требуют оценки и тайм-бокса, а session/SKILL.md в «Почему не Scrum» их прямо не берёт: пункт противоречил решению, стоящему через файл от него.

Исход — выкинуть, а не подпереть данными. На практике числа не пересматривались ни разу, и заводить под них учёт дат значило бы обслуживать обязанность, которой никто не брал. Осталось качественное: что сломалось в процессе, что оказалось дороже, чем выглядело при заведении, какие правила не сработали. Шаг 3 пункт 9 переименован из «переоценки по измеренному» в «переоценку по пройденному», судит человек по памяти о спринте. Рядом записано, что замеров нет намеренно — иначе следующий читатель заведёт их обратно как недостающие.

ААББЖЖ. doc-consistency переехал с каждого синка на сессию, к doc-code-drift. Агент на opus зовётся шагом 9 пайплайна, то есть на каждой задаче: 5–8 opus-проходов за спринт по документам, которые за спринт меняются на несколько абзацев. Обоснование в каноне («сверка текста с текстом дёшева») верно относительно второго агента, но не в абсолюте на одиночке.

Довод сильнее денег: расхождение между двумя документами по определению требует двух документов, а на большинстве задач синк правит один. И пачка, отбираемая работой, не видит того, чего работа не касалась, — а расхождение живёт ровно там: правка отменяет решение в одном документе, парный статус нужен в другом. Это был открытый вопрос REMAINING про охват ADR при пересмотре; переезд его закрыл. Цена — потеря привязки находки к задаче, которая её породила: по теме 29 именно эта привязка дала пять самых точных находок. Принято сознательно.

ААББЗЗ. Отмена цели получила порядок, но не флаг. close запрещал закрыть цель с живыми задачами, а что делать с этими задачами, не говорил нигде: шаг 3 сессии знал только «та ли цель», task-goal.md описывал одно достижение, а session/SKILL.md вдобавок утверждал «цель постоянна». Человек получал отказ с перечнем и никакой подсказки.

Порядок записан: сперва задачи поштучно (close --reason своей причиной либо edit --goal на другую цель), потом сама цель через close --reason в REJECTED.md, а не в Готово — отменённая цель не умеет ничего. Флаг --cascade отвергнут: отмена цели редка и дорога, и поштучный разбор здесь не церемония, а единственный момент, когда видно, что из задач переживёт цель. Каскад превратил бы его в один Enter. Причина у каждой задачи своя: «цель отменена» это пересказ команды, в REJECTED.md от него нет пользы через квартал.

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

ААББИИ. У брошенного спринта появился второй законный исход, без порога. --dissolve во всех текстах был привязан к блокеру, и скрипт отказывал словами «роспуск объясняется блокером». Вернувшийся к набору, который стоял месяц, не имел законного хода: двигать нельзя (заморозка), распускать не по чему. Теперь роспуск объясняется блокером или тем, что набор протух.

Порога в неделях сознательно нет — это тот же класс, что выкинутые числа шага 2: счётчик простоя пришлось бы вести руками, а решает всё равно человек. Признак не срок, а что набор перестал быть твоим: перечитываешь, зачем эти задачи вместе — он протух. Туда же добавлена точка входа «вернулся, а спринт открыт»: check, SPRINT.md, развилка продолжать/распустить. Середины у развилки нет намеренно — «доделаю пару штук и решу» это работа по набору, которого ты не понимаешь.

ААББКК. Журнал канона прогоняется как есть, а проверка исхода поручена судьям. Схлопнуть записи 3 и 4 в один переход «с 2 на 4» отвергнуто: журнал описывает не только что сделать, но и порядок, в котором это делалось, и слитая запись экономит один проход ценой невоспроизводимости остальных. Оба живых проекта пройдут 2→3→4 по записям.

Взамен появилась проверка исхода: шагом 6 adopt и шагом 6 upgrade зовутся оба судьи документов. Это прямой ответ на открытый вопрос REMAINING «как проверять, что канон не разошёлся с проектами после upgrade»: check сверяет число в .pm.json с версией скрипта и про существо записи не знает ничего. Проект несёт "canon": 4 и может не иметь того, чего требовала любая из пройденных версий — записи применяются руками, а ручной проход по трём записям подряд ровно то место, где половина шага делается и забывается.

У adopt добавка другого рода: там судьи ловят не недоделанную миграцию, а последствия переноса — факт, растащенный по двум домам, поведение, осевшее в architecture.md, ADR, оторванный от своего design.md. Им передаётся объявленное переходное состояние из шага 5, иначе честная строка в незаполненном слоте вернётся находкой.

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

  1. Обязанность без источника данных отменяют, а не механизируют. Первый позыв — дать шагу данные (дописать даты, сводку спринта). Но обязанность, не исполнявшуюся ни разу, дешевле снять: механизация под неё производит учёт, который надо вести, ради разбора, который не делается.
  2. Требование, противоречащее решению через файл от него, — не мелочь, а признак копии. «Против ожидания» пережило решение «не берём оценки», потому что стояло в другом документе. Обратный обход по решению «что мы не берём» нашёл бы это сразу — тот же приём, что и следствие 109.
  3. Частота вызова агента выводится из того, что он ищет. Судья расхождений между документами бессмысленен там, где документ один; значит его место не на задаче, а на наборе задач. Цена вызова подтвердила вывод, но не она его дала.
  4. Запрет обязан называть выход. close верно не давал осиротить задачи, но текст отказа перечислял препятствия и молчал о ходе. Проверка без названного следующего шага — половина работы: она защищает данные и бросает человека.
  5. Признак вместо порога там, где счётчик пришлось бы вести руками. «Набор перестал быть твоим» проверяется в момент вопроса и ничего не требует хранить; «прошло N недель» требует учёта, который никто не ведёт, и всё равно кончается решением человека.
  6. Версионирование без единого переехавшего проекта — не журнал миграций, а история правок. Довод за схлопывание был верен по факту и отвергнут по принципу: обкатка на живых проектах и проверяет, работает ли механизм. Схлопнуть значило бы не прогнать его ни разу и оставить вопрос открытым.
  7. Проверка версии не есть проверка миграции. Число в .pm.json двигает тот же проход, что делал шаги, — и двигает независимо от того, все ли сделаны. Механической проверки существа нет; там, где её нет, ставится судья, а не отметка.

32. Сквозной проход по словарю: пять слов сняты, девять закрыты списком (2026-08-05)

Проход упрощения (тема 31) уткнулся в один и тот же класс у всех пяти агентов: слово, живущее в трёх-шести файлах разом. Правка в одном месте развела бы словарь, правка во всех — уже не упрощение текста скилла. Каждый агент честно остановился и записал слово в свой отчёт, и одни и те же слова всплыли в разных отчётах. Разобрано отдельным проходом.

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

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

ААББММ. Пять слов сняты, и все пятеро выглядели словарём, не будучи им. конфляция → смешение (4 места), декорреляция → разведённость (6), непоймание → почему не поймали (9), эвал-сет → проверочный набор (4), гайд → руководство (6). Латинизм или калька при живом русском слове в каждом случае.

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

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

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

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

  1. Escape hatch без перечня — это разрешение, а не исключение. «Термин, который прижился» освобождает от правила любое слово: проверка «прижился ли» возвращает «да» всякий раз, когда слово встретилось. Исключение из правила обязано быть списком, иначе оно съедает правило.
  2. Слово, от которого агент отказался править, — материал для отдельного прохода, а не мусор отчёта. Пять независимых агентов сошлись на одном наборе слов, ни разу друг друга не видя. Список «что не тронул» оказался полезнее списка правок именно этим.
  3. Снятое слово называется вместе с заменой и остаётся записанным. Убрать из текстов недостаточно: без записи «это снято и вот чем заменено» слово возвращается первым же, кто найдёт его удачным.