«Идентичность, слияние, разбор» пришли из одного проекта, и в общем виде
формулировка не читалась: вопрос «как применить это к моему проекту» не имел
ответа в тексте. Теперь класс задан тремя условиями, не зависящими ни от
домена, ни от языка: вариантов несколько и оба защитимы; спека между ними не
выбирает; неверный выбор не падает, а даёт правдоподобный результат и молча
меняет смысл данных.
Отрицательный тест сильнее трёх положительных: то, что красит гейт, роняет
запрос или ломает тест, в класс не входит — это ловят проходы дешевле. Отсюда
же и причина, по которой класс достался самому дорогому проходу: независимая
реализация выберет другой вариант, и дифф между вариантами и есть находка; там,
где вариант один, она совпадёт с существующей.
Три слова остались как три места, где такие правила водятся — граница, где
данные входят или встречаются: состав ключа и нормализация перед сравнением;
победитель конфликта и тай-брейк при равенстве; границы токенов и неоднозначный
вход. Проект перечисляет свои места в docs/review.md, и перечень производен от
теста, а не заменяет его.
Две оговорки, без которых правило вырождается:
- триггер — новое или изменённое по существу правило, а не код рядом с ним;
иначе проект, чей домен и состоит из таких правил, всегда в deep;
- проект, где такого класса нет вовсе, deep не запускает никогда, и это
законное состояние, а не недонастройка.
review-reimpl получил тот же тест и право сказать первой строкой, что позвали
не на его класс, — строкой в границы покрытия, а не отказом работать.
DECISIONS 18, XXX и следствия 76–77.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Полный набор гонялся чаще, чем оправдано, и размер задач тут вторая причина,
не первая. Первая — триггеры: миграция схемы, публичный контракт и инвариант
поднимали ступень, не добавляя ни одного прохода. Миграцию гоняет gate шагом
миграций и разбирает ops, контракт сверяет specs направлением code→spec,
инвариант даёт основание для critical любому проходу — все трое уже в
standard. На проекте с базой и эндпоинтами верхняя ступень оказывалась не
исключением, а умолчанием: правило объявляло исключением то, что происходит
всегда.
Теперь ступень поднимает то, что даёт работу новому проходу. wide означает
ровно одно — изменение вводит новое понятие или структурную единицу; добавить
поле в существующий ответ это не концепт. standard стал рабочим умолчанием.
Проект, где изменение контракта и правда архитектурное, поднимает его сам в
docs/review.md — уточнением, а не возвратом прежнего умолчания.
Чекпоинт design получил то же условие: specs идёт всегда, rubric и
architecture — только при новом понятии. Он стоит на каждой задаче, поэтому
при мелкой нарезке три прохода умножаются на число задач.
Со стороны задач — шов нарезки: тест декомпозиции отвечает, допустим ли
разрез, шов отвечает, где его провести. Резать по границе, за которой падает
ступень; не резать, когда обе половины остаются в одной — костяк из четырёх
проходов платится за каждую задачу, и такой разрез делает ревью дороже.
Порога в числе границ нет по тому же принципу, что в теме 16: размер не
триггер. Дешёвое место заметить разнородную задачу — показ набора спринта,
там «Затрагивает» уже написан, а предложение ещё не заведено.
Правило выведено из состава проходов, а не из статистики прогонов — замер
остаётся за обкаткой. DECISIONS 18, RRR–WWW и следствия 72–75; JJJ темы 17
помечен как пересмотренный. Шаг про «Триггеры профиля» дописан в ещё не
выкаченную версию 3 канона, а не отдельной версией.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Прыжок standard → deep стоил самого дорогого прохода конвейера, а платить
приходилось за одну архитектурную находку: изменений, которые трогают
публичный контракт, но не вводят нового правила слияния, — большинство.
Ступень wide это standard плюс architecture (вход шире диффа, отсюда имя),
семь проходов против восьми.
Заодно вычистилась давняя неровность: триггер reimpl стоял внутри deep, и
профиль означал то семь проходов, то восемь — реестр состава, который
«сверяется взглядом до коммита», проверять было нечем. Теперь условие
«новое правило идентичности, слияния или разбора» выбирает профиль, reimpl
в deep безусловен и есть единственное отличие от wide. Барьер стоимости
остался только в deep: в wide за ним стоял бы один дешёвый проход с
потолком в 3 находки, а барьер сериализует то, что могло идти разом.
Цвет charter'а теперь кодирует модель, а не роль: sonnet → green,
opus → yellow, fable → red. Роль видна из имени, стоимость прогона —
ниоткуда, а список агентов читается взглядом.
scripts/frontmatter.py ловит три класса ошибок, невидимых при чтении:
- двоеточие с пробелом в незакавыченном описании — для YAML это вложенное
отображение, а не текст. Так было написано три описания из четырнадцати,
и читались они правильно;
- name, разошедшееся с именем каталога скилла или файла charter'а;
- цвет, не отвечающий модели: он ставится один раз при заведении charter'а,
а модель потом двигает калибровка.
Обе ветки проверены, коды выхода — общий словарь. Триггеры профиля в
canon.md и skeletons.md подтянуты под wide.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Номер стадии не означал зависимости: между стадиями 1–4 ни один проход
не читает вывод другого, и очередь между ними была платой ни за что.
А правило про замеры держалось на двух именах и рассыпалось бы в день,
когда мерить начнёт третий проход.
Рёбер три вида, и они разной природы: зависимость (гейт → опиниативные,
все проходы → триаж), конфликт за ресурс (ненаправленный) и барьер
стоимости. Стадии остаются единицей состава, порядок задаёт граф:
уходит всё, у чего входящие рёбра закрыты.
Сериализует ресурс, а не имена: пометка «держит машину» — gate,
adversary, ops, triage; остальные читают и рассуждают. Проект вправе
пометить свой проход, снять пометку с перечисленных — нет.
Ранний выход заменён барьером стоимости и стоит там, где выход
зарабатывал: перед reimpl и architecture, то есть только в deep.
Отдельным абзацем — что ребро значит порядок и никогда не данные:
графовый словарь провоцирует обратное прочтение, а проход, увидевший
чужие находки, соглашается с ними. Исключение одно и оно же сток.
Диаграммы — mermaid, прогнаны через mermaid-cli. Режим прогона теперь
«по графу» / «линейно»; task-batch и task-pipeline подтянуты под общий
словарь, adversary и ops знают о пометке из своих charter'ов.
DECISIONS 15.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Два независимых сабагента на av-dev-pm и av-dev-pipeline. Две находки нашли оба.
Главная — моя же перестановка закрытия за коммит сломала reopen и батч. close
печатал «дорога назад из git», а reopen искал коммит удаления, которого в новом
порядке ещё нет: шаг 11 последний, учёт остаётся незакоммиченным. Проверено
прогоном — отказ кодом 2 на свежезакрытой задаче. Тем же грязным деревом
ломались rebase и worktree remove в батче: каждая закрывшая задачу ветка уехала
бы в провалившиеся.
Починено с обеих сторон: reopen берёт текст из HEAD, если коммита удаления нет,
а шаг 11 коммитит учёт вторым коммитом.
Вторая — канонический пример docs/.pm.json убивал tasks.py. Четыре документа
показывали ключ tasks.sections, которого скрипт не знает: неизвестный ключ это
код 3 на любой команде. Проект, заведённый по канону дословно, остался бы без
работы с задачами, а docs.py при этом печатал «канон соблюдён». Секции живут в
заголовках индекса и второго дома не получают.
Остальные восемнадцать: init писал конфиг в упразднённый .tasks.json;
looks_like_tasks не видел переименованный индекс; урожай спринта терял автотег
после sprint close; ответ на вопрос по инструкции оставлял задачу незабираемой;
adopt требовал недостижимого зелёного; путь отчёта триажа не переживал archive;
review-specs не имел режима для стыка после слияния; три остатка «шаг 9а» несли
предкоммитную позицию закрытия; sprint.md отрицал сам себя в пункте «Сделана».
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- task-pipeline и task-batch запрещали шаг, который сами же добавили: раздел
границ и финальный доклад переписаны под снятую границу приёмки
- «триаж сводит строки в одну» противоречило «не сливает» — деградация снова
поразрядная во всех трёх местах
- канон не требовал в CLAUDE.md имени основной ветки, testdata и запретов, а
скелета CLAUDE.md не было вовсе — заведён
- обратимость жила в двух домах, читатели ходили в пустой; единственный дом
теперь CLAUDE.md
- заведены слоты «Единые точки проекта» и «Триггеры профиля», куда charter'ы
слали, а канон их не создавал
- блок «Вопросы к проходам» стал частью задания прохода: за ним ходили двое
из девяти
- чек-лист синка и правило «замер + настройка» сведены к одному дому;
plugin.json больше не про бриф
- удалены скилл project-brief и контракт брифа; вместо них references/
project-facts.md — карта «что нужно проходу → где лежит» и таблица
поразрядной деградации по документам
- девять charter'ов, review-pipeline, task-pipeline и task-batch переписаны
на пути канона; OpenSpec стал объявленной предпосылкой без ветки деградации
- шаг синка документации переписан в построчный доклад, закрытие задачи —
вызовом скилла av-dev-pm:tasks вместо строки-слота из CLAUDE.md
- по находкам ревью: docs.py звал tasks.py из чужого каталога и выдавал его
отказ окружения за дрейф; сверка миграций не видела рабочее дерево;
плейсхолдер краснел вместо замечания; сверка capability проходила по
совпадению с именем пакета; tasks.py не читал docs/.pm.json; скилл docs
пересказывал канон в пяти местах
- скилл project-brief: бриф собирается из CLAUDE.md, архитектуры, Taskfile
и конвенций и показывается человеку. Раньше единственная инструкция по
его созданию лежала внутри шаблона, поэтому деградированный режим был не
аварийным, а единственным: critical по основанию «нарушен инвариант»
недостижим ни на одной задаче
- rebase перенесён внутрь worktree задачи: прежняя форма падала на занятой
ветке, и агент уводил весь батч в провалившиеся с ложной причиной
- контракт брифа дополнен восемью слотами; проверен заполнением на обоих
проектах, незаполнимых нет. Прецедент healthlog вынут из общего charter'а
в бриф — там он вмёрз вместе с числами
- шов: пайплайн задачу не закрывает и записи учёта не трогает, урожай
отдаёт списком, правило остатка — ссылкой на av-dev-tasks
- деградированный абзац во всех девяти проходах, вопрос 9 в ops,
пространство имён в вызовах, раздел предпосылок
Пара плагинов с намеренно проведённой границей: av-dev-tasks отвечает
за то, что делаем и в каком порядке, av-dev-pipeline — за то, как ведём
одну задачу. Зависимости между ними нет: управление задачами работает и
с ручным исполнением, пайплайн — на проекте с любым учётом задач.
- av-dev-tasks — преемник av-dev-backlog: цели вместо приоритетов,
спринт под одну цель с заморозкой набора, различение вопроса и
блокера, каденция «вопросы — разбор — переоценка — набор».
Раскладка docs/tasks с items/, PLAN.md, BACKLOG.md, SPRINT.md,
REJECTED.md; проверенное из av-dev-backlog перенесено, не переписано.
- av-dev-pipeline — вынос того, что лежало копиями в healthlog и
jellybit (3628 строк) и уже разошлось: цикл SDD, конвейер ревью с
обязательным триажем, прогон нескольких задач разом. Проектная
специфика вынесена в файл-бриф, charter'ы несут метод.
Коммит фиксирует состояние на момент ревью: три прохода нашли
блокирующие дефекты (нет шага, заводящего бриф; git rebase на занятой
worktree ветке; sprint drop пишет наполовину) — они чинятся следующими
коммитами. Сохранено как база, от которой видно правки.