diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 21296f5..f158892 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -18,7 +18,7 @@ { "name": "av-dev-pipeline", "source": "./av-dev-pipeline", - "description": "Проведение задачи через цикл SDD и конвейер ревью с обязательным триажем, плюс прогон нескольких задач разом. Требует OpenSpec. Задача принимается и обычным текстом; плагины av-dev-docs и av-dev-tasks опциональны — первый даёт документы канона для проходов ревью, второй учёт задач, без них прогон деградирует поразрядно и говорит об этом." + "description": "Проведение задачи через цикл SDD и конвейер ревью с обязательным триажем. Требует OpenSpec. Задача принимается и обычным текстом; плагины av-dev-docs и av-dev-tasks опциональны — первый даёт документы канона для проходов ревью, второй учёт задач, без них прогон деградирует поразрядно и говорит об этом." }, { "name": "av-dev-git", diff --git a/README.md b/README.md index 134d41b..a989a22 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ инструмента). Каталог принадлежит конвейеру, а не канону: без конвейера он проекту не нужен, и `docs.py` о нём молчит; - `task-pipeline` — задача через полный цикл SDD, от постановки до коммита; - - `task-batch` — несколько задач разом, каждая в своём worktree; - `review-pipeline` — конвейер ревью **по темам**: документ проекта либо заводит тему проверки, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Разметка идёт **один раз на задачу**, сразу после @@ -55,9 +54,7 @@ flowchart TB subgraph pipe["av-dev-pipeline — исполнение, требует OpenSpec"] direction LR - batch["task-batch"] --> tp["task-pipeline"] - tp --> rp["review-pipeline
10 агентов-проходов"] - batch --> rp + tp["task-pipeline"] --> rp["review-pipeline
10 агентов-проходов"] osp["openspec
заводит и проверяет openspec/"] end subgraph docsp["av-dev-docs — документация, владеет docs/"] @@ -160,7 +157,7 @@ claude plugin install av-dev-git@av-dev-skills --scope project ``` **При установке в проект, где лежали проектные копии** скиллов и агентов -(`.claude/skills/` — голые `task-pipeline`, `review-pipeline`, `task-batch` +(`.claude/skills/` — голые `task-pipeline`, `review-pipeline` и с префиксом проекта `<проект>-task-pipeline`, `.claude/agents/<проект>-review-*.md`) — снеси их. Две копии одного скилла расходятся, и побеждает та, что короче названа. diff --git a/REMAINING.md b/REMAINING.md index 38ed191..16a69ab 100644 --- a/REMAINING.md +++ b/REMAINING.md @@ -57,7 +57,7 @@ severity. Пробы готовы и синтетических не нужно установке каждого плагина в одиночку. - **Проектные копии в healthlog и jellybit.** Два `.claude/skills/` и одиннадцать `.claude/agents/` старого поколения. У jellybit хуже: его скиллы названы - `task-pipeline`, `review-pipeline`, `task-batch` — **ровно как в плагине**. + `task-pipeline`, `review-pipeline` — **ровно как в плагине**. Claude Code не переопределяет их, а держит обе пары, так что короткое имя может увести в устаревшую копию, и молча. diff --git a/TODO.md b/TODO.md index 9523083..4397f9e 100644 --- a/TODO.md +++ b/TODO.md @@ -56,7 +56,7 @@ - [ ] то же, что у healthlog: плагины, проектные копии, `adopt`, каталог задач, гейт - [ ] проектные копии здесь опаснее: скиллы названы `task-pipeline`, - `review-pipeline`, `task-batch` — **ровно как в плагине**, и короткое имя + `review-pipeline` — **ровно как в плагине**, и короткое имя может увести в устаревшую копию молча (REMAINING) ## 2. Учёт работ без спринтов diff --git a/av-dev-docs/agents/doc-code-drift.md b/av-dev-docs/agents/doc-code-drift.md index 68616a5..54b87db 100644 --- a/av-dev-docs/agents/doc-code-drift.md +++ b/av-dev-docs/agents/doc-code-drift.md @@ -50,7 +50,7 @@ color: green проверить, — это **не находка, а строка в границах покрытия**. 1. **Имя основной ветки** (`CLAUDE.md`). От неё считается база диффа - (`git merge-base HEAD <ветка>`), в неё вливает батч, от неё ветвятся задачи. + (`git merge-base HEAD <ветка>`), в неё коммитит работу конвейер. Проверка: `git symbolic-ref refs/remotes/origin/HEAD` либо перечень веток. Угадывание между `master` и `main` ломает интеграцию целиком, и это самая дешёвая находка из всех. diff --git a/av-dev-docs/skills/canon/references/canon.md b/av-dev-docs/skills/canon/references/canon.md index 794fc14..3e124ed 100644 --- a/av-dev-docs/skills/canon/references/canon.md +++ b/av-dev-docs/skills/canon/references/canon.md @@ -415,7 +415,7 @@ kebab-case.** Причина не эстетическая: имя файла с Плюс то, что нужно git-операциям и проходам и не выводится ниоткуда: - **имя основной ветки** — от неё считается база диффа - (`git merge-base HEAD <ветка>`), в неё вливает батч, от неё ветвятся задачи. + (`git merge-base HEAD <ветка>`), в неё коммитит работу конвейер. Угадывание между `master` и `main` ломает интеграцию целиком; - **что запускать запрещено, с путями** — рабочая БД, боевой каталог данных, внешние сервисы. Запретом с путями, а не «будь осторожен»; diff --git a/av-dev-pipeline/.claude-plugin/plugin.json b/av-dev-pipeline/.claude-plugin/plugin.json index c228604..21c9b2b 100644 --- a/av-dev-pipeline/.claude-plugin/plugin.json +++ b/av-dev-pipeline/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "av-dev-pipeline", - "description": "Проведение задачи через полный цикл Spec Driven Development и конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом, независимой реализацией и обязательным триажем; плюс прогон нескольких задач разом по одной в изолированном worktree. Требует OpenSpec и сам его заводит скиллом openspec. Задача принимается и обычным текстом. Плагины av-dev-docs и av-dev-tasks опциональны: первый даёт документы канона, из которых проходы читают проектную конкретику, второй — учёт задач; без них прогон деградирует поразрядно и называет это строкой.", + "description": "Проведение задачи через полный цикл Spec Driven Development и конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом, независимой реализацией и обязательным триажем. Требует OpenSpec и сам его заводит скиллом openspec. Задача принимается и обычным текстом. Плагины av-dev-docs и av-dev-tasks опциональны: первый даёт документы канона, из которых проходы читают проектную конкретику, второй — учёт задач; без них прогон деградирует поразрядно и называет это строкой.", "author": { "name": "Anton Vakhrushev", "email": "anwinged@gmail.com" diff --git a/av-dev-pipeline/agents/review-specs.md b/av-dev-pipeline/agents/review-specs.md index 9d9c6cf..a62fb01 100644 --- a/av-dev-pipeline/agents/review-specs.md +++ b/av-dev-pipeline/agents/review-specs.md @@ -1,6 +1,6 @@ --- name: review-specs -description: "Сверка изменения с дельта-спеками в обе стороны — spec→code (каждое требование реализовано и подтверждено тестом) и, что важнее, code→spec (поведение, которое код имеет, а спека не заказывала: тихие ветки, самодеятельные дефолты, проглоченные ошибки, отброшенные поля, ретраи «на всякий случай»). Плюс границы спеки — что она не определяет и что пришлось домыслить. Работает в трёх режимах: дизайн/спеки ДО кода, код против спек ПОСЛЕ apply и стык после слияния нескольких задач, когда change уже заархивированы. Только чтение." +description: "Сверка изменения с дельта-спеками в обе стороны — spec→code (каждое требование реализовано и подтверждено тестом) и, что важнее, code→spec (поведение, которое код имеет, а спека не заказывала: тихие ветки, самодеятельные дефолты, проглоченные ошибки, отброшенные поля, ретраи «на всякий случай»). Плюс границы спеки — что она не определяет и что пришлось домыслить. Работает в двух режимах: дизайн/спеки ДО кода и код против спек ПОСЛЕ apply. Только чтение." tools: Read, Grep, Glob, Bash model: opus color: yellow @@ -63,11 +63,9 @@ Development на OpenSpec). Оптика — требования, а не ст намерение, а спека нормирует. Расхождение между proposal и дельтой — само по себе находка. -**Исключение — режим 3 (ниже): живого change нет.** Тогда источник требований — -**актуальные** `openspec/specs//spec.md`, а дельты поднимаются из -архива (`openspec/changes/archive//specs/`) как свидетельство о намерении -каждой слитой задачи. Задание обязано назвать этот режим явно; не названо — -работаешь по режиму 1 или 2 и говоришь в границах покрытия, что change не нашёл. +**Живого change нет — ты не запускаешься.** Оба режима стоят на дельта-спеке; без +неё сверять нечего, и это строка отказа, а не повод взять источником актуальные +спеки: они описывают, что система делает вообще, а не что заказало это изменение. Дополнительно поднимаешь **с метки `medium`**: `design.md` и `tasks.md` change, затронутые актуальные спеки. Инварианты из `CLAUDE.md` — при любой метке. Если тема ещё не перенесена в спеки и живёт только в @@ -144,27 +142,6 @@ change, затронутые актуальные спеки. Инвариант скажи об этом прямо, с последствием. Такая находка всегда `Действие: развилка`: менять спеку — решение человека. -## Режим 3 — стык после слияния нескольких задач - -Зовётся финальной сверкой `task-batch`: несколько задач влиты в основную ветку, -их change **уже заархивированы**, живой дельта-спеки не существует. Предмет — -**только то, что появилось от слияния**, а не capability целиком заново: каждая -задача уже проверена в своём worktree, и повторение даст те же находки дороже. - -Ищешь ровно три вещи: - -- **отменённое требование** — одна задача его выполнила, соседняя незаметно - сняла; в актуальной спеке требование есть, в интегрированном коде его больше - нет; -- **два описания одного поведения** — два архивных change по-разному нормировали - одно и то же, и актуальная спека собрала из них противоречие; -- **осиротевшее поведение** — код, пришедший от слияния (разрешение конфликта, - правка при rebase), которого не заказывал ни один из change. - -База — интегрированный дифф основной ветки против точки, с которой батч начался. -В границах покрытия скажи прямо: **capability целиком в этом режиме не -сверялась**, проверялись стыки. - ## Чего этот проход принципиально не может поймать - Качество формы решения: код может точно соответствовать спеке и быть плохим. diff --git a/av-dev-pipeline/agents/review-triage.md b/av-dev-pipeline/agents/review-triage.md index a12c556..5753c2f 100644 --- a/av-dev-pipeline/agents/review-triage.md +++ b/av-dev-pipeline/agents/review-triage.md @@ -29,12 +29,10 @@ color: yellow и метка с обоснованием. Он твой главный инструмент сверки: ты единственный, кто видит и то, что размечено, и то, что пришло. -**Плана нет — ты не запускаешься.** Сверка размеченного с пришедшим — твоя -единственная защита от молчащего пропуска, и без плана она не выполняется вовсе. -Отчёт, собранный без неё, выглядит полным ровно настолько же, насколько и -неполный. Исключение одно и объявленное: финальная сверка стыка в -`av-dev-pipeline:task-batch` — там разметчика нет по построению, и план тебе -собирает сам батч, коротким списком запущенного. +**Плана нет — ты не запускаешься, и исключений нет.** Сверка размеченного с +пришедшим — твоя единственная защита от молчащего пропуска, и без плана она не +выполняется вовсе. Отчёт, собранный без неё, выглядит полным ровно настолько же, +насколько и неполный. Из документов проекта тебе нужны: diff --git a/av-dev-pipeline/skills/review-pipeline/SKILL.md b/av-dev-pipeline/skills/review-pipeline/SKILL.md index 6fa2645..48dfca5 100644 --- a/av-dev-pipeline/skills/review-pipeline/SKILL.md +++ b/av-dev-pipeline/skills/review-pipeline/SKILL.md @@ -1,6 +1,6 @@ --- name: review-pipeline -description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Разметка задачи идёт один раз, после propose: агент review-scope выводит размер и сложность, из их максимума — метка, и раздаёт темы проходам обеих стадий. Метка правит и ревью дизайна (small — только specs; medium — плюс rubric; large — плюс architecture), и ревью кода (small — гейт, спеки, код, триаж; medium — плюс приёмник тем; large — плюс доказательство: враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе). Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона av-dev-docs. Вызывается из task-pipeline (чекпоинты ревью) и из task-batch (финальная сверка)." +description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Разметка задачи идёт один раз, после propose: агент review-scope выводит размер и сложность, из их максимума — метка, и раздаёт темы проходам обеих стадий. Метка правит и ревью дизайна (small — только specs; medium — плюс rubric; large — плюс architecture), и ревью кода (small — гейт, спеки, код, триаж; medium — плюс приёмник тем; large — плюс доказательство: враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе). Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона av-dev-docs. Вызывается из скилла resolve — двумя чекпоинтами: ревью дизайна до кода и ревью кода после apply." --- # Конвейер ревью @@ -58,7 +58,7 @@ description: "Конвейер ревью изменения, устроенны - **Документы канона** — см. следующий раздел. - **Проектные копии этих скиллов и агентов удаляются при установке.** Если в проекте уже лежат свои `.claude/skills/review-pipeline`, - `.claude/skills/task-pipeline`, `.claude/skills/task-batch` или + `.claude/skills/task-pipeline`, `.claude/skills/resolve` или `.claude/agents/<проект>-review-*.md` — снеси их. Иначе короткое имя разрешится в устаревшую проектную копию, молча и без признаков подмены. @@ -94,8 +94,8 @@ description: "Конвейер ревью изменения, устроенны Своих скиллов это касается ровно так же: `av-dev-pipeline:review-pipeline`, -`av-dev-pipeline:task-pipeline`, `av-dev-pipeline:task-batch` — подменяется -короткое имя, а не чужое. +`av-dev-pipeline:resolve`, `av-dev-pipeline:openspec` — подменяется короткое имя, +а не чужое. ## Темы, источники и процессные документы @@ -560,8 +560,7 @@ flowchart TD ничего не портит, он только дольше, и домысливать тут нечего; 2. **машина занята, и знает об этом вызывающий.** Рядом идёт другая задача, поднят сервис, гоняется дорогая проверка проекта. Сам конвейер занятости - машины не видит — её обязан назвать тот, кто запускает; так и делает - `av-dev-pipeline:task-batch`, когда ведёт задачи параллельно; + машины не видит — её обязан назвать тот, кто запускает; 3. **разбор самого конвейера** — когда выясняется, почему проход чего-то не нашёл, порядок и изоляция важнее скорости. @@ -956,8 +955,8 @@ flowchart TD Он единственное, по чему потом видно, что было найдено и что из этого не заведено: нулевой урожай при непустом отчёте виден сразу. **Вместе с изменением он и переезжает:** после `opsx:archive` его адрес — - `openspec/changes/archive//review/`. Кто ищет отчёт после архивации (батч - на финальной сверке, приёмщик на сессии), смотрит **оба** пути; «отчёта нет» + `openspec/changes/archive//review/`. Кто ищет отчёт после архивации + (приёмщик на сессии, разбор дефекта), смотрит **оба** пути; «отчёта нет» объявляется, только когда пуст и архивный, иначе самый дорогой сценарий «состав ревью неизвестен, гоняем заново» срабатывает на каждой доведённой задаче. diff --git a/av-dev-pipeline/skills/task-batch/SKILL.md b/av-dev-pipeline/skills/task-batch/SKILL.md deleted file mode 100644 index b035f55..0000000 --- a/av-dev-pipeline/skills/task-batch/SKILL.md +++ /dev/null @@ -1,518 +0,0 @@ ---- -name: task-batch -description: Проводит несколько задач разом — планирует порядок и пересечения, гонит каждую задачу отдельным сабагентом в своём git worktree через task-pipeline (по умолчанию по одной задаче за раз; параллельно по графу зависимостей — по явной просьбе), интегрирует по одной ветке через rebase + fast-forward (линейная история), проверяет полноту ревью каждой ветки и в конце сверяет стыки, возникшие от слияния. Набор задач приходит извне. Использовать, когда просят сделать несколько задач сразу. ---- - -# Батч задач - -Оркестратор **набора** задач. Планирует порядок, раскидывает задачи по -изолированным worktree, каждую проводит через полный цикл -`av-dev-pipeline:task-pipeline`, затем сводит в основную ветку линейной историей -и делает финальную сверку. Тонкая обёртка над пайплайном задачи — не -переизобретай её шаги, вызывай как есть. - -**По умолчанию задачи идут по одной**, в порядке зависимостей. Параллельно — по -явной просьбе, и тогда параллельность **по графу зависимостей**: одновременно -гонится только то, между чем нет ни зависимости, ни пересечения. Правило и его -цена — в шаге 4. - -Работай **максимально автономно**, по тому же принципу, что и одиночный пайплайн: -вопрос, который решать не тебе, записывается и не останавливает поток; спрашиваем -только про **необратимое** (деплой, выкладка наружу, удаление или перезапись -рабочих данных). Механику — планирование, worktree, rebase, интеграцию, чистку — -делаем без спроса. - -## Предпосылки - -- **OpenSpec и скиллы `opsx:*`** — на них стоит цикл внутри каждого сабагента и - проход `review-specs` финальной сверки. Проекта без OpenSpec это касается так - же, как одиночного пайплайна (см. его раздел «Предпосылки»). -- **Проектные копии этих скиллов и агентов при установке плагина удаляются.** - -### Обращение к соседним плагинам - -**Копия.** Дом — `shared/plugin-boundary.md` в репозитории плагинов. Правится -дом, а не этот файл. - - - -Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на -месте. - -**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, -`av-dev-tasks:tasks`, `av-dev-pipeline:review-pipeline`. Короткое имя может -разрешиться в устаревшую проектную копию из `.claude/skills/`, и подмены не будет -видно ни в докладе, ни в поведении. - -**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт -только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо -откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он -прочитает его сам. - -**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови -строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя, -пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже. - -**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня -установленных плагинов проект не ведёт — он разошёлся бы с действительностью -молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.pm.json` — -канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер. - - - -**У батча правило строже одним пунктом: полное имя обязательно и в charter'е -сабагента.** Сабагент твоего контекста не видит, короткое имя разрешает у себя, и -подмена на устаревшую проектную копию случится там, куда ты уже не смотришь. - -Перед стартом прочитай `CLAUDE.md` проекта: оттуда берутся **имя основной -ветки** (оно подставляется в каждую команду git ниже), команда и семантика -гейта, инварианты и что запускать запрещено. Раскладка нумерованных артефактов — -`docs/database.md` и `docs/.pm.json` (ключ `migrations`). - -**Документов канона нет — проект к нему не приведён.** Скажи это строкой и -предложи `av-dev-docs:canon` **до первой задачи**: иначе каждая задача батча -заплатит поразрядной деградацией ревью, а имя основной ветки придётся -угадывать. - -## Границы - -- **Набор задач приходит извне.** Батч его не формирует: не выбирает из беклога, - не приоритизирует, не решает, что важнее. Набор не задан — попроси его у - вызывающего и остановись. -- **Батч не владеет спринтом и целями.** Он сообщает исход по каждой задаче в тех - же трёх словах, что и `task-pipeline`: сделана / не доведена / оказалась крупнее - задачи. -- **Задачи закрывает пайплайн внутри каждого сабагента**, шагом 12 — после - коммита работы и **отдельным коммитом учёта**, вызовом Skill `av-dev-tasks:tasks`. - Батч сам записей учёта не трогает: он не знает, чем кончилась приёмка, и - дублировать закрытие ему незачем. Но грязное дерево после сабагента — **его** - проблема: на нём откажут и `rebase`, и `worktree remove` (см. шаг 6). Урожай ревью батч - отдаёт списком, а задачи из него заводит тот, кто ведёт задачи проекта. - -## Ключевое отличие от одиночного пайплайна - -`task-pipeline` коммитит **в текущую ветку**, и при ручном запуске это основная -ветка. Здесь так нельзя, поэтому батч — **осознанное исключение**: временные -ветки и worktree заводятся лишь как средство изоляции, а конечное состояние — та -же линейная история основной ветки через rebase + fast-forward. Ветки после -вливания удаляются. - -Изоляция нужна **в обоих режимах, а не только в параллельном**: батч не вливает -ветку, пока не проверил полноту её ревью (шаг 5), и упавшая задача обязана -остаться в своём worktree для ручного дожатия (шаг 6), не оставив следа в -основной ветке. В параллельном режиме к этому добавляется вторая причина — -задачи не должны видеть недоделанную работу друг друга. - -## Модель исполнения - -- Каждая задача = **один автономный сабагент** (`general-purpose`, чтобы иметь - доступ к Skill и Agent для вложенных чекпоинтов ревью), работающий **только в - своём worktree** и прогоняющий `task-pipeline` целиком на этой задаче. -- Оркестратор кода задач не пишет: он планирует, заводит worktree, запускает - сабагентов, проверяет полноту их ревью, интегрирует ветки и делает финальную - сверку. -- Стиль правок внутри — заточка под проект и конвенции, right-size, без - золочения. - -## Шаги - -### 1. Прочитать набор - -Набор задан списком (слаги, файлы, описания) — прочитай файл каждой задачи и -связанные спеки и черновики. Сырьё (в терминах `av-dev-tasks` — запись типа -`research` с пустым разделом «Вопрос») включается, но помни: сабагент проведёт -его сперва через `opsx:explore`, это тяжелее и чаще упирается в вопрос. - -### 2. Спланировать порядок и пересечения (автономно) - -Для каждой задачи определи: - -- **затронутые capability** — по её описанию и по каталогу актуальных спек - (`openspec/specs/`); -- **жёсткие зависимости**: задача B строится на результате A → A строго раньше B; -- **замеряющая задача** — та, чьё ревью будет доказывать находки **числами**. В - последовательном прогоне это ничего не меняет: она и так идёт одна. В - параллельном она гонится в волне **одна** (обоснование — ниже, в шаге 4). - Помечается здесь, на планировании, а не во время прогона, и **независимо от - режима**: состав волны определяется сейчас, режим может смениться просьбой уже - после плана, а метка ревью назовёт разметчик уже внутри пайплайна задачи, - после `propose`, — ключевать волну на ещё не сделанный выбор нельзя. Триггеры — по фактам о задаче, каждый - сам по себе достаточен: - - трогает схему хранилища, миграцию, формат на диске или объём хранимого; - - трогает конкурентность: транзакции, блокировки, фоновые циклы, общее - состояние; - - трогает размер тела, буфер, память, сжатие, ретеншен, темп потока; - - её тема названа в журнале `docs/review.md` как место, где уже ломалось. - - Ни один триггер не сработал — задача не замеряющая, даже если её ревью - окажется `large`. Метка про глубину проверки, замеряющая — про соревнование за - железо; это разные вопросы, и совпадают они не всегда. Обратное тоже бывает и - тоже законно: помеченная задача, чьё ревью пошло меткой `small` или - `medium`, машину не займёт вовсе — меряющие проходы живут только в `large`. - Пометка от этого не снимается: она ставится **до** разметки, и перестраховка - здесь стоит одной волны, а ошибка — испорченных чисел; -- **нумерованные артефакты — номера раздаёт оркестратор заранее.** Если проект - нумерует миграции (путь — `docs/.pm.json`, ключ `migrations`), посмотри последний - номер и **раздай номера всем задачам, которые, вероятно, их добавят**, до - запуска. Номер уходит в charter сабагента, и он берёт назначенный, а не - «следующий свободный». - - **Это отдельная механика от правила волны, и она ему не служит** — их раньше - путали, и они тянули в разные стороны. Правило волны отвечает на вопрос «кто с - кем гонится одновременно», предраздача — на вопрос «какой номер берёт задача». - Раздача нужна там, где **две задачи одной под-пачки** добавляют нумерованный - артефакт: каждая считает «следующий свободный» по основной ветке, которая ещё - не видела соседку, и обе берут один номер. Миграции под это почти не попадают — - миграция и так триггер замеряющей задачи, а замеряющая идёт одна; но - нумерованные артефакты бывают не только миграциями. Поэтому номера раздаются - **всем** задачам с таким артефактом, независимо от того, в какой волне они - окажутся: раздача ничего не стоит, а её отсутствие ловится только конфликтом на - интеграции. Между волнами проблемы нет — ветка следующей волны берётся от - вершины, уже включающей предыдущие; в последовательном прогоне проблемы нет по - той же причине, но номера всё равно раздай: режим может смениться просьбой, а - раздача бесплатна; -- **жёстко сериализуем** (не гоняем одновременно) настоящие пересечения: - - **одна capability на несколько задач** — две задачи, правящие одну спеку (тем - более одно и то же `### Requirement`), дают не текстовый, а **семантический** - конфликт при архивации; сериализуем по смыслу, а не только по файлам; - - пересечение по одним и тем же исходникам; -- **мягкие конфликты** сериализовать не надо: файлы-перечни, где каждая задача - правит **свою** строку (индексы, оглавления, списки записей), и спеки разных - capability — разные строки и файлы, сливаются сами. - -Собери план как **граф зависимостей**, а не как плоский список: рёбра — жёсткие -зависимости и сериализуемые пересечения. Дальше по режиму: - -- **последовательно (умолчание)** — линеаризуй граф в один порядок: - топологический, а там, где он оставляет свободу, — раньше то, от чего зависит - больше задач, и раньше то, что правит общие для набора места. Волн нет, - замеряющие задачи ничем не отличаются от прочих; -- **параллельно (по просьбе)** — нарежь граф на **волны**: в одну волну попадают - только задачи, между которыми нет ребра; замеряющие стоят отдельными волнами по - одной. - -Рёбра у графа двух видов, и путать их не надо: **зависимость** направлена (B без -результата A не делается), **пересечение** — нет (кто первый, неважно, лишь бы не -разом). Тот же словарь у графа проходов ревью — см. «Порядок прогона» в -`av-dev-pipeline:review-pipeline`. - -```mermaid -flowchart TD - A["A: схема хранилища
(замеряющая)"] - B["B: эндпоинт поверх A"] - C["C: формат лога"] - D["D: правит ту же capability, что C"] - - A -->|зависимость| B - C -. пересечение — одна capability .- D -``` - -Этот граф даёт: **последовательно** — `A → B → C → D` (или `A → C → B → D`, обе -линеаризации законны); **параллельно** — волна 1 `A` одна (замеряющая), волна 2 -`B` и `C`, волна 3 `D`. - -Схемы в этом скилле — **пример и сводка**, правила ставит текст: при расхождении -прав он. (В `av-dev-pipeline:review-pipeline` наоборот — там граф прогона и есть -алгоритм, и старший он.) - -Покажи план короткой репликой — режим, порядок или состав волн, какие задачи -признаны замеряющими и по какому триггеру, — и иди дальше. - -### 3. Свежая база - -Убедись, что рабочее дерево чистое и основная ветка свежая. Зафиксируй базовый -коммит. Новые ветки бери от свежей вершины; ветку следующей задачи (в -параллельном режиме — ветки следующей волны) — от вершины, уже включающей -результат предыдущих. - -### 4. Провести задачи - -**Умолчание — по одной задаче за раз, в порядке из шага 2.** Следующая стартует, -когда предыдущая вернула отчёт и (если она зелёная) влилась. Обосновывать это не -надо — обосновывается отступление. Причина умолчания в том, что задача батча -дороже прохода ревью: каждая тянет полный цикл пайплайна с гейтом, поднятием -сервиса и вложенным ревью, и две такие на одной машине дерутся за порты, рабочие -каталоги, СУБД и само железо. Последовательный прогон к тому же оставляет ревью -внутри задачи его собственное умолчание — **параллельные проходы**: машина -свободна, и выигрыш берётся там, где он ничего не стоит. - -**Параллельно — по явной просьбе, и параллельность идёт по графу зависимостей.** -Одновременно гонится только то, между чем на шаге 2 не нашлось ребра: ни жёсткой -зависимости, ни общей capability, ни общих исходников. «Гони параллельно» не -означает «гони всё разом» — граф остаётся в силе, просьба лишь разрешает -использовать его ширину. - -В параллельном режиме действуют два ограничения: - -- **потолок — 2–3 задачи одновременно.** Больше трёх разом душат машину и - провоцируют гонки. Волну шире трёх бей на под-пачки по ≤3 и **гони под-пачки - последовательно**: следующая стартует, когда предыдущая вернула отчёты. Иначе - потолок обходится тривиально — шесть задач, запущенных «двумя под-пачками» в - одном сообщении, это шесть задач разом; -- **волна из одной задачи обязательна для замеряющей.** Задача, признанная на - шаге 2 **замеряющей**, гонится одна: соседний прогон на той же машине портит - числа, а находка с испорченным оракулом хуже отсутствующей — она выглядит - доказанной. Если замеряющая задача всё же пошла в общей волне, её отчёт обязан - нести строку в границах покрытия: замеры сняты под соседней нагрузкой. - -Для каждой задачи (в параллельном режиме — для каждой задачи под-пачки): - -1. Заведи worktree и ветку от текущей вершины: - `git worktree add -b task/ <основная ветка>`. Путь — во временном - каталоге проекта (`./tmp`), не в системном `/tmp`. -2. Запусти сабагента, `subagent_type: general-purpose`: в последовательном режиме - — одного и дождись отчёта; в параллельном — **по одному на задачу под-пачки, - всех в одном сообщении**. Charter сабагента: - - работай **строго в своём worktree** ``; в другие каталоги и в основную - ветку не лезь; - - прогони Skill **`av-dev-pipeline:task-pipeline`** ровно на этой задаче, - полный цикл SDD с обоими чекпоинтами ревью; - - если задаче назначен **номер артефакта** — используй строго его; - - **метка ревью выбирает разметчик конвейера, а не ты и не сабагент.** Он - идёт внутри пайплайна задачи, шагом 4, сразу после `propose`, и его план - обслуживает оба чекпоинта. Метка в вызов не передаётся вовсе. Батч не - повод её понижать: «нас много и - мы спешим» — ровно тот стимул, из-за которого проходы пропускают, и он снят - тем, что регулятор не в руках у автора; - - **режим прогона проходов ревью — от режима батча**, и его называет charter, - а не сабагент: батч идёт по одной задаче → режим умолчательный, **`по - графу`** (машина свободна); батч идёт волнами → **`линейно`**, твой worktree - не один на машине, и этой причиной ты обязан объяснить режим в отчёте. - Внутренние рёбра графа — цепочку проходов, держащих машину — конвейер - соблюдает сам, в любом режиме; - - **если вложенные сабагенты недоступны** (движок не даёт запускать агентов из - агента) — не пропускай ревью и не понижай метка: проведи его **инлайн** по - тем же charter'ам `av-dev-pipeline`, сохранив обязательное — разметку первой - (план с темами и меткой), гейт до опиниативных проходов, состав по плану, - триаж последним. И **скажи в отчёте прямым текстом, что ревью шло инлайн**: - инлайновый проход видит контекст автора и потому разведён с ним слабее — а - инлайновая разметка вдобавок означает, что метка выбрал автор, и это - отдельная строка; - - **вернуть отчёт**, в котором обязательно: исход задачи одним из трёх слов; - **план прогона: метка с обоснованием, темы и их глубины**, и режим; что сделано; какие вопросы - записаны и куда; изменённые файлы; добавлялся ли нумерованный артефакт и с - каким номером; затронутые capability; состояние гейта; **перечень - тем с исходом по каждой**; **путь к - сохранённому отчёту триажа** (`openspec/changes//review/`, после - архивации — `openspec/changes/archive//review/`); шло ли ревью - инлайн; границы покрытия. - -**Шаги 5 и 6 отрабатываются на вернувшейся задаче до старта следующей** — в -параллельном режиме на вернувшейся волне до старта следующей. Иначе ветка -следующей возьмётся от вершины, не видевшей предыдущую работу, и весь смысл -порядка из шага 2 теряется. - -Сабагент, упершийся в вопрос, **не останавливает батч**: он записывает вопрос, -режет задачу до остатка и доводит остаток — либо, если остатка нет, возвращает -исход «не доведена». Оркестратор собирает такие вопросы и выносит их в финальный -доклад пачкой. - -### 5. Проверить полноту ревью — до интеграции - -**Ветка, чей отчёт не называет план прогона, не вливается.** Пропуск не отличим -от прохода без находок, и на уровне батча это ещё опаснее: отчётов много, каждый -выглядит полным, а сверять их некому, кроме тебя. - -Сверка идёт в три шага, и порядок важен: - -1. **Возьми план прогона** из отчёта задачи — таблицу «тема → дом → глубина → кто - закрывает» с меткой и обоснованием; он затем и заказан в обязательных полях - шага 4. Плана в отчёте нет — сверять не с чем; это само по себе основание не - вливать, пока сабагент не покажет план разметки задачи. -2. **Сверяй с независимым артефактом, а не с прозой отчёта.** Перечень проходов - бери из **сохранённого отчёта триажа** (`openspec/changes//review/` или - `openspec/changes/archive//review/` — задача доведена, change заархивирован) — - пайплайн обязан его туда положить. Проза сабагента написана тем же, кто мог - проход и пропустить: она подтверждает сама себя. Отчёта триажа на месте нет — - считай, что состав неизвестен, и дозапускай ревью целиком. -3. **Сверь план с исходом**: против каждой темы плана обязан стоять отчёт либо - названная причина его отсутствия. Раскладка «тема → кто закрывает с этой меткой» — в скилле `av-dev-pipeline:review-pipeline`. - -Расхождение — не повод отменять задачу: дозапусти недостающие проходы **на -ветке**, в её worktree, через `av-dev-pipeline:review-pipeline`, и только потом -интегрируй. - -**Передай в дозапуск тот же план.** Триаж требует его обязательным входом — без -плана он не может сверить, все ли размеченные темы вернули отчёт, а эта сверка и -есть то, ради чего дозапуск затевается. Плана не осталось (сабагент не сохранил -его в отчёте) — пусть повторит разметку задачи: это самый дешёвый проход -конвейера, и он дешевле, чем прогон, который нечем сверить. - -**Находки дозапуска — такие же находки, и зелёный гейт их не отменяет.** Правило -интеграции «вливаем только зелёные» смотрит на гейт, а дозапущенный `critical` -гейт не красит: он был бы пропущен молча, если это не сказать прямо. Поэтому: - -- `critical` или `major` из дозапуска — **вливание этой ветки останавливается**. - Помеченное `инлайн` чинится в её worktree, после починки — гейт, затем - интеграция. Помеченное `развилка` — вопрос в запись, задача режется до остатка - ровно так же, как это сделал бы пайплайн внутри; -- остатка нет — ветка не вливается и уходит в доклад как провалившаяся, со своим - worktree; -- `minor` и `nit` из дозапуска — в урожай доклада, вливанию не мешают. - -Отчёт дозапуска приложи к отчёту задачи и назови в докладе (шаг 9), почему он -понадобился: систематический пропуск одного и того же прохода — находка о самом -конвейере, а не о задаче. - -### 6. Интегрировать — rebase + fast-forward, по одной ветке - -Сводим ветки **строго последовательно** (линейная история), в порядке -зависимостей. Вливаем **только зелёные**. - -**Ветка задачи занята её worktree, и это определяет форму команд.** Пока worktree -жив (а удаляется он последним, после зелёного гейта), ветка `task/` -checkout'нута в нём, и `git rebase <основная> task/` из главного worktree -**падает**: `fatal: 'task/' is already used by worktree at …`. Поэтому -rebase делается **внутри worktree задачи**, а ff-слияние — из главного. - -Для каждой готовой ветки `task/`: - -- `git -C rebase <основная>` — перенос ветки задачи на текущую вершину, - выполняется в её собственном worktree; -- резолв конфликтов (их почти нет — конфликтоопасное сериализовано, номера - розданы заранее). Неавтоматический конфликт — **не форсируй**: прерви - (`git -C rebase --abort`), оставь ветку и worktree как есть, вынеси это - в доклад как нераспознанное пересечение; -- **дерево сабагента обязано быть чистым.** `git -C status --porcelain` - до `rebase`: непусто — значит сабагент не довёл шаг 12 до коммита учёта (или - оставил мусор). Не форсируй и не коммить за него: назови задачу в докладе - недоведённой и оставь ветку с worktree. Молчаливый `rebase` на грязном дереве - всё равно откажет, но с сообщением про unstaged changes — а причина другая; -- **ненулевой код `rebase` относится к этой ветке и только к ней.** Прерванный - rebase в чужом worktree не трогает ни главное дерево, ни остальные ветки: - проверь `git -C status` и `git status` — обе чистые. Уводить весь батч - в провалившиеся из-за одного ненулевого кода запрещено: это ложная причина, - из-за которой зелёные задачи не доедут до основной ветки. Провалилась одна — - провалилась одна; -- из главного worktree (он стоит на основной ветке — проверь - `git rev-parse --abbrev-ref HEAD`): `git merge --ff-only task/`. Ветку в - главном дереве **не переключай** — `git checkout task/` тоже упрётся в - занятость; -- после каждой интеграции — **гейт на основной ветке**. Красное — **откати эту - интеграцию** (`git reset --hard` на прошлую вершину), ветку с worktree сохрани, - задачу перечисли в докладе. Основная ветка **никогда** не остаётся - полузелёной; -- только после зелёного: `git worktree remove ` и - `git branch -d task/` — в этом порядке, иначе ветка снова занята. - -**Политика частичного провала.** Упавшая задача (исход «не доведена», красные -тесты в её worktree, конфликт при rebase, невлитая из-за находок дозапуска) -**не блокирует остальные**: интегрируем все зелёные, упавшую оставляем в её -worktree и ветке нетронутой — ничего не удаляем, — и перечисляем в докладе с -причиной, отчётом и путём к worktree. Причина называется **настоящая**: «конфликт -rebase в файле X», а не «нераспознанное пересечение» на всякий случай. - -### 7. Финальный гейт - -На основной ветке после всех интеграций — гейт целиком. Зелёное обязательно; пока -красное, шаг 8 не начинается. - -### 8. Финальная сверка — только то, чего не видел никто - -Каждая задача уже прошла полный конвейер в своём worktree. Повторять его на -интегрированном диффе бессмысленно: те же проходы на тех же файлах дадут те же -находки и удорожат триаж. Здесь проверяется **только то, что появилось от -слияния**: - -- запусти **по одному `review-specs` на каждую затронутую capability**, все - разом — граф здесь плоский: машину эти проходы не держат, ребра между ними нет, - а сама машина к этому моменту свободна (все сабагенты вернулись). Линейно — - только по причине из раздела «Порядок прогона» - `av-dev-pipeline:review-pipeline`, и причину назови; -- **задание у этих проходов особое, и это надо сказать прямо.** Живого change - здесь нет — все заархивированы, дельта-спек не существует. Источник требований - — **актуальные** `openspec/specs//spec.md`, а предмет — стык: - требование, которое одна задача выполнила, а соседняя незаметно отменила; два - архивных change, по-разному описавшие одно поведение. Скажи проходу это прямо, - иначе он пойдёт искать дельты и не найдёт ничего; -- если задачи пересекались по файлам, добавь один `review-architecture` на - интегрированный дифф с вопросом «не появился ли второй способ делать то, что - уже делается» — именно он возникает, когда две задачи независимо решали - похожее; -- **заверши триажем.** Он единственный, кто агрегирует, и без него у находок нет - ни оракула, ни пометки `инлайн`/`развилка` — а следующий абзац на неё - опирается. Прогон из двух проходов без триажа — это сырые находки, выданные за - разобранные; -- **план триажу собери сам, здесь, — разметчика на этой сверке нет.** Триаж - требует план обязательным входом: он сверяет размеченное с пришедшим, и без - плана эта сверка не выполняется вовсе. Разметка задачи сюда не годится — она - описывала одну задачу, а сверка идёт по интегрированной ветке. План здесь - короткий и составляется по факту запуска: - - ``` - метка: не применяется — сверка стыка, а не ревью изменения - тема дом глубина закрывает - requirements openspec/specs// разбор specs (стык) - requirements openspec/specs// разбор specs (стык) - architecture docs/architecture.md разбор architecture - + источник docs/passport.md - ``` - - Темы, которых в этом списке нет (`autotests`, `conventions`, `security`, - `operations`, свои темы проекта), назови строкой «не проверяется на сверке - стыка: закрыто прогонами отдельных задач». Это не формальность — без такой - строки отчёт сверки читается как полное ревью ветки. - -Граф этой сверки — веер в один сток, и он такой же, как у обычного прогона: - -```mermaid -flowchart TD - merged["основная ветка после всех интеграций
(финальный гейт зелёный)"] - s1["review-specs: capability 1
режим «стык после слияния»"] - s2["review-specs: capability 2
режим «стык после слияния»"] - arch["review-architecture на интегрированном диффе
(если задачи пересекались по файлам)"] - tri["review-triage — сток"] - - merged --> s1 --> tri - merged --> s2 --> tri - merged --> arch --> tri -``` - -Замечания отрабатывай как одиночный пайплайн: `инлайн` чини сам, `развилка` — -вопросом в запись; после правок — снова гейт. - -### 9. Прибраться и доложить - -- Убери worktree и ветки **только успешно влитых** задач, в конце - `git worktree prune`. Worktree и ветки **провалившихся** не трогай — они нужны - для ручного дожатия. -- **Записей учёта батч не трогает** — их правит пайплайн внутри сабагента на - шаге 12. Батч сообщает исход по каждой задаче; если какой-то сабагент дошёл до - коммита, но закрытия не сделал (плагина нет, вызов не разрешился), скажи это - строкой — иначе задача останется открытой молча. -- Доложи кратко: - - **исход по каждой задаче** одним из трёх слов, с хешем коммита; - - **режим прогона** — по одной или волнами, и если волнами, то по чьей просьбе; - порядок задач или состав волн, порядок интеграции, с пометкой, какие задачи - шли по одной как замеряющие; - - вопросы, записанные сабагентами, пачкой; - - что дозапускалось на шаге 5 и почему; шло ли где-то ревью инлайн; - - итог финальной сверки и ссылки на архивные change; - - **`Урожай`** — отложенные находки всех задач одним списком, с провенансом. - Задачи из него заводит тот, кто ведёт задачи проекта, а не батч; - - **отдельно — провалившиеся** задачи с настоящей причиной и путём к - оставленному worktree; - - **границы покрытия сводной строкой**, включая задачи, чьи замеры снимались в - общей волне, и ветки, где ревью шло инлайн. - -## Тонкости - -- **Изоляция параллельных тестов — цена параллельного режима, и проверяется она - до первой волны.** Прежде чем гнать несколько прогонов разом, убедись, что - тесты не делят фиксированный порт или файл БД (обычно берут временный каталог и - эфемерный порт — тогда ок). Делят — такие задачи гони по одной, даже если - просили параллельно, и скажи об этом строкой: просьба про параллельность, а не - про сломанные тесты. В умолчательном режиме вопрос не встаёт вовсе — это одна - из причин, по которым умолчание такое. -- Поведенческая верификация внутри сабагента поднимает изменение вживую: в - параллельном режиме следи, чтобы соседние worktree не дрались за порты и - рабочие каталоги. Если проект умеет поднимать только один экземпляр — такие - задачи в одну волну не ставь. -- Ревью выполненного — **до** интеграции; это забота - `av-dev-pipeline:task-pipeline` внутри каждого сабагента, дублировать не надо. -- `openspec validate --strict` тоже внутри пайплайна задачи — не пропускай его - своими правками на интеграции. -- Крупная переработка, предложенная ревью внутри задачи, — развилка: не вливай - молча, вынеси в доклад. -- Держи вызывающего в цикле короткими репликами на переходах фаз (план → прогон - задач → интеграция → финальная сверка), но не проси подтверждать механику.