From 63ba36d71dcfc2959e83f89c482fcaad11640fb9 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 9 Aug 2026 18:22:35 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B3=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B=20?= =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D0=B3=D0=B8=D0=BD=D0=BE=D0=B2:=20=D0=BF?= =?UTF-8?q?=D1=83=D1=82=D1=8C=20=D0=B2=20=D1=87=D1=83=D0=B6=D0=BE=D0=B5=20?= =?UTF-8?q?=D0=B4=D0=B5=D1=80=D0=B5=D0=B2=D0=BE,=20=D0=B1=D0=B5=D0=B7?= =?UTF-8?q?=D1=8B=D0=BC=D1=8F=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=81=D1=82=D1=8B?= =?UTF-8?q?=D0=BA=D0=B8,=20=D0=B7=D0=B2=D0=BE=D0=BD=D1=8F=D1=89=D0=B8?= =?UTF-8?q?=D0=B9=20=D1=83=20=D0=B2=D1=8B=D1=87=D0=B8=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Правило границы моё, копий восемь — и нарушал его я же. - путь в дерево чужого плагина снят из пяти мест; маркер копии, уезжающий в проект скелетом, оставлен, но сказано, что сама пара маркеров не едет - короткое имя чужого скилла в четырёх местах стало полным - стык «урожай ревью → задачи» не был назван ни с одной стороны, хотя механика написана с обеих; теперь назван, с веткой «плагина нет» - resolve звал av-dev-git:commit без строки доклада и пересказывал формат коммита, нарушая собственное «ссылайся, не пересказывай» - doc-wording обещал момент вызова, которого не исполнял никто. Правило: звонящий — тот, кто только что писал текст. Вызов появился шагом в docs, init, adopt и upgrade; healthcheck по-прежнему его не зовёт - openspec.py искал SHALL по всему файлу, а образец даёт его в context — проверка молчала ровно в том случае, ради которого написана - фаза 2 review-rubric была недостижима; проход стал судить задуманное, а не код, и это сходится с тем, что о нём говорит конвейер - rules.tasks в образце конфига, ветка «записи задачи нет» у review-scope, возвраты на чекпоинт в схеме resolve, старшинство правила дельта-спек Co-Authored-By: Claude Opus 5 (1M context) --- av-dev-code/agents/review-adversary.md | 2 +- av-dev-code/agents/review-autotests.md | 2 +- av-dev-code/agents/review-ops.md | 2 +- av-dev-code/agents/review-rubric.md | 62 +++++++++---------- av-dev-code/agents/review-scope.md | 9 +++ av-dev-code/skills/openspec/SKILL.md | 14 +++-- .../openspec/references/config-skeleton.md | 13 +++- .../skills/openspec/scripts/openspec.py | 33 +++++++++- av-dev-code/skills/resolve/SKILL.md | 43 +++++++++---- av-dev-code/skills/review/SKILL.md | 22 ++++--- .../skills/review/references/project-facts.md | 5 +- .../review/references/review-journal.md | 3 +- .../skills/review/references/review-levels.md | 6 +- av-dev-docs/agents/doc-wording.md | 2 +- av-dev-docs/skills/canon/SKILL.md | 18 +++++- av-dev-docs/skills/canon/references/canon.md | 7 ++- .../skills/canon/references/skeletons.md | 12 +++- av-dev-docs/skills/docs/SKILL.md | 13 ++++ av-dev-docs/skills/init/SKILL.md | 23 +++++-- .../skills/tasks/references/from-review.md | 6 ++ 20 files changed, 212 insertions(+), 85 deletions(-) diff --git a/av-dev-code/agents/review-adversary.md b/av-dev-code/agents/review-adversary.md index b14b881..7226111 100644 --- a/av-dev-code/agents/review-adversary.md +++ b/av-dev-code/agents/review-adversary.md @@ -1,6 +1,6 @@ --- name: review-adversary -description: "Враждебный проход ревью — не проверяет свойства, а строит путь: «ты контролируешь вход целиком — выведи запись за пределы песочницы»; «ты шлёшь запрос и хочешь, чтобы данные не доехали или испортились — построй такой вход»; «ты можешь повторить и переставить любую операцию — что ломается»; «доведи чувствительное до места, где его быть не должно». Находка — построенный путь с шагами, а не наблюдение. Свойства без пути идут в отдельную секцию и не получают critical. Модель угроз берётся из docs/security.md проекта. Только чтение." +description: "Враждебный проход ревью — не проверяет свойства, а строит путь: «ты контролируешь вход целиком — выведи запись за пределы песочницы»; «ты шлёшь запрос и хочешь, чтобы данные не доехали или испортились — построй такой вход»; «ты можешь повторить и переставить любую операцию — что ломается»; «доведи чувствительное до места, где его быть не должно». Находка — построенный путь с шагами, а не наблюдение. Свойства без пути идут в отдельную секцию и не получают critical. Модель угроз берётся из docs/security.md проекта. Запускается только с меткой large — на изменении, которое не крупное и не незнакомое, построенного пути он не находит, а стоит дорого. Только чтение." tools: Read, Grep, Glob, Bash model: opus color: yellow diff --git a/av-dev-code/agents/review-autotests.md b/av-dev-code/agents/review-autotests.md index 7e1377c..6daf3eb 100644 --- a/av-dev-code/agents/review-autotests.md +++ b/av-dev-code/agents/review-autotests.md @@ -96,7 +96,7 @@ color: green просило: она может стоить минут и трогать данные. - **Правило есть в конвенциях, но не в линтере.** Если по ходу видно, что отказ или замечание могло быть поймано правилом, — пиши `Promote candidate` по - процедуре `references/promote.md`. + процедуре `${CLAUDE_PLUGIN_ROOT}/skills/review/references/promote.md`. ## Что читать не нужно diff --git a/av-dev-code/agents/review-ops.md b/av-dev-code/agents/review-ops.md index db040a2..85fc6bd 100644 --- a/av-dev-code/agents/review-ops.md +++ b/av-dev-code/agents/review-ops.md @@ -1,6 +1,6 @@ --- name: review-ops -description: "Эксплуатационный проход ревью — пишет постмортем «это упало через неделю на проде» от симптома у владельца сервиса к строке кода. Обязательные вопросы: рост объёма, деградация окружения и внешних зависимостей, повторная и одновременная операция, частичный откат при двух версиях, миграция под живым потоком, отмена контекста на середине, наблюдаемость и тишина, поведение библиотеки и драйвера в вырожденном случае, чтение узлом состояния, которое он сам же меняет. Формулирует условиями, а не утверждениями — реального профиля нагрузки не знает. Только чтение." +description: "Эксплуатационный проход ревью — пишет постмортем «это упало через неделю на проде» от симптома у владельца сервиса к строке кода. Обязательные вопросы: рост объёма, деградация окружения и внешних зависимостей, повторная и одновременная операция, частичный откат при двух версиях, миграция под живым потоком, отмена контекста на середине, наблюдаемость и тишина, поведение библиотеки и драйвера в вырожденном случае, чтение узлом состояния, которое он сам же меняет. Формулирует условиями, а не утверждениями — реального профиля нагрузки не знает. Запускается только с меткой large: постмортем на малом знакомом изменении пишется по общей практике, а не по этому проекту. Только чтение." tools: Read, Grep, Glob, Bash model: sonnet color: green diff --git a/av-dev-code/agents/review-rubric.md b/av-dev-code/agents/review-rubric.md index 2c41a07..1f240b4 100644 --- a/av-dev-code/agents/review-rubric.md +++ b/av-dev-code/agents/review-rubric.md @@ -1,6 +1,6 @@ --- name: review-rubric -description: "Generative-проход ревью — сперва, НЕ ВИДЯ КОДА, порождает 8–12 проверяемых свойств, по которым сильный инженер судит узел такого назначения (парсер входного формата, HTTP-обработчик, репозиторий, воркер, клиент внешнего сервиса, CLI-команда, файловое хранилище), и только потом читает код и оценивает по этой рубрике. Достаёт слой, которого нет ни в одной конвенции. Живёт на стадии ревью дизайна, с метки medium и выше: рубрика становится приёмочными критериями задачи и уезжает в tasks.md. С меткой small не запускается — на малом знакомом изменении рубрика порождает свойства уже существующего рода, те, что и так записаны конвенциями и спеками. Только чтение." +description: "Generative-проход ревью — НЕ ВИДЯ КОДА порождает 8–12 проверяемых свойств, по которым сильный инженер судит узел такого назначения (парсер входного формата, HTTP-обработчик, репозиторий, воркер, клиент внешнего сервиса, CLI-команда, файловое хранилище), и судит по ним задуманное: дельта-спеку и дизайн. Достаёт слой, которого нет ни в одной конвенции. Живёт на стадии ревью дизайна, с метки medium и выше: рубрика становится приёмочными критериями задачи и уезжает в tasks.md. Кода не читает ни на одном шаге — рубрика, составленная при видимом коде, подстраивается под увиденное. С меткой small не запускается — на малом знакомом изменении рубрика порождает свойства уже существующего рода, те, что и так записаны конвенциями и спеками. Только чтение." tools: Read, Grep, Glob, Bash model: opus color: yellow @@ -31,13 +31,11 @@ color: yellow **Документа нет — строка на каждый, отдельно.** Нет `docs/review.md`: «рода узлов и прецеденты неизвестны; требование „минимум три пункта специфичны для типа узла" выполнено по общей практике, а не по этому проекту». Нет инвариантов -в `CLAUDE.md`: `critical` по основанию «нарушен инвариант проекта» в фазе 2 не -присваивай и скажи об этом. Одной строкой за два документа не отделывайся — -чинятся они разным. +в `CLAUDE.md`: `critical` по основанию «нарушен инвариант проекта» не присваивай +и скажи об этом. Одной строкой за два документа не отделывайся — чинятся они +разным. -## Порядок фаз обязателен - -### Фаза 1 — рубрика. Код читать ЗАПРЕЩЕНО +## Рубрика. Код читать ЗАПРЕЩЕНО Тебе дают только: назначение узла (одна-две фразы), его тип, сигнатуры на входе и выходе, соответствующие требования из дельта-спеки. **Не открывай файлы @@ -86,28 +84,29 @@ color: yellow Тот же вопрос на **готовом коде** задаёт эксплуатационный проход (вопрос 9); здесь он задаётся дизайну. -Выведи рубрику **до** любых находок. Она — часть результата, даже если код -окажется идеальным. +Выведи рубрику **до** любых находок. Она — часть результата, даже если +задуманное окажется безупречным. -### Фаза 2 — оценка +## По рубрике судится задуманное, а не код -Выполняется только если тебя позвали на готовый код (вне стадии ревью дизайна). -Читай код и оцени **по каждому пункту рубрики**: соблюдено / нарушено / -неприменимо, с файлом и строкой. +Пройди рубрику против **дельта-спеки и дизайна**. Находка — там, где задуманное +пункту прямо противоречит либо оставляет его неопределённым в месте, где +определённость обязательна («что происходит при перекрытии тиков» не сказано ни +в спеке, ни в дизайне). Остальные пункты уезжают приёмочными критериями в +`tasks.md` change: там их и проверит приёмка. -**Новые критерии на этой фазе не добавляются.** Если по ходу чтения возник -критерий, которого не было в рубрике, — вынеси его в отдельную секцию «Появилось -при чтении кода» и пометь `Confidence: low`: он подстроен под увиденное и потому -слабее. +**Оценки кода у этого прохода нет, и это решение, а не пробел.** Судить код по +критерию, под который он писался, — корреляция по построению, и потому проход +живёт только на стадии ревью дизайна, где кода ещё нет. Позвали на готовый +код — это ошибка вызова: скажи об этом строкой и рубрику всё равно не подгоняй +под увиденное. ## Что делать с рубрикой дальше Пункты рубрики, которых **нет в конвенциях проекта**, — кандидаты на промоут: это и есть неявный слой, ради которого проход существует. Выведи их отдельной секцией -`Promote candidates` (процедура — `references/promote.md`). - -На стадии ревью дизайна (кода ещё нет) фаза 2 не выполняется: рубрика уезжает в -`tasks.md` change как приёмочные критерии. +`Promote candidates` (процедура — +`${CLAUDE_PLUGIN_ROOT}/skills/review/references/promote.md`). ## Чего этот проход принципиально не может поймать @@ -122,22 +121,21 @@ color: yellow ## Формат вывода -1. `## Рубрика` — нумерованный список свойств (порождена до чтения кода). -2. `## Оценка` — по каждому пункту: соблюдено/нарушено/неприменимо + файл:строка - (только вне стадии ревью дизайна). -3. Находки по контракту — только по нарушенным пунктам. -4. `## Появилось при чтении кода` — если было. -5. `## Promote candidates`. -6. Обязательный блок: +1. `## Рубрика` — нумерованный список свойств (порождена до чтения спеки). +2. `## Разбор` — по каждому пункту: покрыт задуманным / противоречие / + не определён / неприменим, со ссылкой на требование или раздел дизайна. +3. Находки по контракту — только по пунктам с противоречием и неопределённостью. +4. `## Promote candidates`. +5. Обязательный блок: ``` ## Coverage of this pass -- проверено: <какие пункты рубрики против каких файлов> +- проверено: <какие пункты рубрики против каких требований и разделов дизайна> - не проверялось и почему: ... -- принципиально недоступно этому проходу: рантайм, сверка со спекой, межмодульные связи +- принципиально недоступно этому проходу: код, рантайм, сверка со спекой, межмодульные связи ``` ## Ограничения -Только чтение. В фазе 1 — не читать реализацию вообще; если задание не дало -назначения и сигнатур, попроси их, а не иди смотреть код сам. +Только чтение, и реализацию не читать вообще; если задание не дало назначения и +сигнатур, попроси их, а не иди смотреть код сам. diff --git a/av-dev-code/agents/review-scope.md b/av-dev-code/agents/review-scope.md index 7080cab..3bd3590 100644 --- a/av-dev-code/agents/review-scope.md +++ b/av-dev-code/agents/review-scope.md @@ -74,6 +74,15 @@ color: green | **`tasks.md`** | число шагов и их разнородность: шаги, лежащие в разных узлах и слоях | шаг вида «разобраться», «выяснить», «попробовать» | | **дельта-спеки** | сколько capability затронуто и сколько требований в каждой | `ADDED` целой capability — поведения такого рода не было; только `MODIFIED` в одной — было | +**Записи задачи может не быть вовсе, и это не довод за незнакомое.** Задача +приходит текстом или из проекта без плагина задач — тогда раздела «Затрагивает» +нет **по построению**, а не потому, что границы не назвали. Отличай: +запись есть, а раздела в ней нет → незнакомое, как сказано в таблице; записи нет +→ строка источника снимается, обе оси выводятся из остальных четырёх, и это +называется в плане строкой «записи задачи нет, оси выведены по четырём +источникам». Иначе всякая задача без плагина задач систематически едет в `large` +за то, чего никто не терял. + **`design.md` информативен и своим отсутствием.** Его нет — либо задача тривиальна (тогда это подтверждает малое и знакомое), либо нетривиальную завели без разбора решения, и тогда «форму знали заранее» ничем не подтверждено: считай diff --git a/av-dev-code/skills/openspec/SKILL.md b/av-dev-code/skills/openspec/SKILL.md index 01ca4af..c781307 100644 --- a/av-dev-code/skills/openspec/SKILL.md +++ b/av-dev-code/skills/openspec/SKILL.md @@ -78,11 +78,14 @@ python3 $os form # слепок формы против жив Различать 1 и 3 обязательно: «форма разошлась» — рабочая ситуация, «openspec не отвечает» — нерабочая. -`check` проверяет пять вещей, и каждая — про молчащий пробел, а не про вкус: +`check` проверяет форму, и каждая проверка — про молчащий пробел, а не про вкус: каталог есть; имя именно `config.yaml` (`config.yml` OpenSpec не читает и об этом -не сообщает); `context` и `rules.specs` не остались примером, а правила называют -`SHALL`; `context` называет паспорт и `CLAUDE.md`; ключи под `rules:` — имена -артефактов схемы, а не свободные слова. +не сообщает); ключ `schema` называет ту схему, для которой форма описана; +`context` и `rules.specs` не остались примером, **а `SHALL` назван именно внутри +`rules.specs`** (в `context` он стоит и в образце, поэтому греп по файлу здесь +ничего не значит); `context` называет паспорт и `CLAUDE.md`; ключи под `rules:` — +имена артефактов схемы, а не свободные слова. Числа проверок здесь нет намеренно: +оно протухает от каждой добавленной. **Адреса требуются только к тем документам, которые в проекте есть.** Канон документов ставится отдельным плагином и может быть не подключён; требовать @@ -116,6 +119,9 @@ python3 $os form # слепок формы против жив - `av-dev-docs:init` — шагом заведения нового проекта, до первого документа; - `av-dev-docs:canon` в режиме `adopt` — если на переводимом проекте каталога нет или `config.yaml` остался примером; +- `av-dev-code:resolve` и `av-dev-code:review` — не вызовом по ходу, а отсылкой: + OpenSpec у обоих жёсткая предпосылка, и на проекте без каталога оба посылают + сюда вместо того, чтобы заводить его руками; - человек — когда конвейер отказался работать без источника требований. **Копия.** Дом правила — `shared/plugin-boundary.md` в репозитории плагинов. diff --git a/av-dev-code/skills/openspec/references/config-skeleton.md b/av-dev-code/skills/openspec/references/config-skeleton.md index 1ffb9e4..d972e42 100644 --- a/av-dev-code/skills/openspec/references/config-skeleton.md +++ b/av-dev-code/skills/openspec/references/config-skeleton.md @@ -67,6 +67,10 @@ rules: - "Сценарий — ровно #### (четыре решётки); три или список молча теряются" - "SHALL/MUST должно стоять в ПЕРВОМ абзаце требования: валидатор смотрит только его" - "Заголовки и WHEN/THEN/GIVEN — на английском, остальной текст на русском" + tasks: + - "Критерии приёмки задачи — отдельным блоком и дословно: файл задачи закрытие удалит, критерии обязаны его пережить" + - "Рубрика ревью дизайна, если оно её дало, идёт в тот же блок: приёмка судится по одному списку, а не по двум" + - "Шаг плана формулируется проверяемо — по нему видно «сделано / не сделано» без суждения" ``` **Четыре правила для `specs` сняты отказами валидатора, а не выведены из @@ -79,7 +83,14 @@ rules: артефактов**, а не сочиняется заново: третий пересказ одного и того же разошёлся бы с обоими. Требование поэтому стоит здесь, в момент порождения артефакта, а не в скилле, который спохватится позже. `design.md` при этом ещё и **сырьё для -ADR** — отвергнутый вариант с названной причиной и есть половина будущей записи. Блок `context` проект +ADR** — отвергнутый вариант с названной причиной и есть половина будущей записи. + +**Правила для `tasks` держит тот же скилл, и по той же причине — момент +порождения.** `tasks.md` — единственное, что переживает задачу: файл задачи +закрытие удаляет, а приёмка потом судится по критериям, которые в него +скопированы. Туда же ложится рубрика ревью дизайна, если оно её дало. Записанное +в момент порождения не приходится вспоминать шагом позже, когда артефакт уже +написан. Блок `context` проект дополняет своим (стек, разведка, особенности домена), но **адреса паспорта и `CLAUDE.md` обязательны** — отсутствие адреса к существующему документу `openspec.py check` называет отказом. diff --git a/av-dev-code/skills/openspec/scripts/openspec.py b/av-dev-code/skills/openspec/scripts/openspec.py index 737af41..0c264d0 100644 --- a/av-dev-code/skills/openspec/scripts/openspec.py +++ b/av-dev-code/skills/openspec/scripts/openspec.py @@ -134,6 +134,35 @@ def rules_keys(live: str) -> list[str]: return out +def rules_block(live: str, name: str) -> str: + """Строки правил, адресованных одному артефакту. + + Обход тот же, что у `rules_keys`, и по той же причине: искать по всему файлу + нельзя. Литеральный скаляр `context` называет `SHALL` уже в образце, поэтому + проверка «правила называют SHALL» грепом по файлу проходила при **пустом** + `rules.specs` — то есть молчала ровно в том случае, ради которого написана. + """ + out: list[str] = [] + in_rules = False + in_name = False + for line in live.splitlines(): + if not line.strip(): + continue + if not line[0].isspace(): + in_rules = line.startswith("rules:") + in_name = False + continue + if not in_rules: + continue + m = re.fullmatch(r" ([A-Za-z_-]+):\s*", line) + if m: + in_name = m.group(1) == name + continue + if in_name: + out.append(line) + return "\n".join(out) + + def check_form(root: Path, rep: Report) -> None: """Настройка заведена и не осталась примером из коробки.""" os_dir = root / "openspec" @@ -197,12 +226,12 @@ def check_form(root: Path, rep: Report) -> None: if pointer not in live: rep.error(f"openspec/config.yaml не называет {pointer} — {why}") - if "rules" not in keys or "specs:" not in live: + if "rules" not in keys or "specs" not in rules_keys(live): rep.error( "в openspec/config.yaml нет rules.specs — придирки валидатора " "нигде не записаны, и каждое предложение узнаёт их отказом" ) - elif "SHALL" not in live: + elif "SHALL" not in rules_block(live, "specs"): rep.error( "rules.specs в openspec/config.yaml не называет SHALL — " "требование без этого литерала валидатор отвергает, а правило " diff --git a/av-dev-code/skills/resolve/SKILL.md b/av-dev-code/skills/resolve/SKILL.md index b40abd5..38c743f 100644 --- a/av-dev-code/skills/resolve/SKILL.md +++ b/av-dev-code/skills/resolve/SKILL.md @@ -17,11 +17,13 @@ description: "Решить одну задачу от постановки до ## Предпосылки - **OpenSpec и скиллы `opsx:*` — жёсткая предпосылка, а не опция.** На них стоят - шаги 2, 6 и 8, проход `review-specs` и ревью дизайна (они завязаны на - `openspec/changes//specs/*/spec.md` и на `openspec validate --strict`). - **Проект без OpenSpec этим скиллом не ведётся** — подключай OpenSpec, а не - вырождай цикл: ветка деградации здесь не пишется, потому что непроверенная - ветка деградации хуже честного отказа. + шаги 2, 6 и 8, шаг Р2 разведки, проход `review-specs` и ревью дизайна (они + завязаны на `openspec/changes//specs/*/spec.md` и на + `openspec validate --strict`). **Проект без OpenSpec этим скиллом не ведётся** — + подключай OpenSpec, а не вырождай цикл: ветка деградации здесь не пишется, + потому что непроверенная ветка деградации хуже честного отказа. Заводить + руками не надо: каталог и настройку в `config.yaml` делает скилл + `av-dev-code:openspec`. - **Проектные копии этих скиллов и агентов удаляются при установке плагина** (`.claude/skills/` — голые имена `resolve`, `review`, а у проектов прошлого поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`, и с префиксом @@ -136,7 +138,8 @@ flowchart TD r3 -.->|"кода не будет"| rout s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 --> s10 --> s11 s3 -.->|"план задачи: та же метка"| s7 - s7 -.->|"находка отменяет дизайн"| s5 + s5 -.->|"скорректировать:
меняются дельта-спеки"| s3 + s7 -.->|"находка отменяет дизайн:
меняются дельта-спеки"| s3 ``` Схема — **сводка**: содержание каждого шага в его разделе ниже, и при @@ -213,7 +216,9 @@ flowchart TD возвращает задачу `reopen` с причиной, а доклад по критериям приёмки становится единственным, по чему приёмка вообще возможна. - **Заведением задач из урожая ревью.** Отложенные находки отдаются **списком**; - превращать их в задачи — работа того, кто ведёт задачи проекта. + превращать их в задачи — работа `av-dev-tasks:tasks`, у него на этот вход + отдельный сценарий «задачи из ревью и аудита». Плагина нет — урожай остаётся + списком в докладе, и это говорится строкой. - **Определением ценности.** «Нужна ли эта функциональность» — не вопрос этого скилла ни на одном шаге. Чекпоинт спрашивает «так ли решаем», а не «надо ли». @@ -519,6 +524,11 @@ flowchart TD (разметка выведена из дельта-спек) и **вернись на чекпоинт шага 5** с тем, что изменилось и почему. +**Это правило старше правила о развилке.** Находка класса `развилка`, чьё +основание — «надо менять спеку», подпадает под оба; побеждает возврат на +чекпоинт, а вопрос в запись при нём остаётся, но возврата не заменяет. Иначе +одобренный дизайн переделывался бы записанным вопросом, то есть молча. + Тихо переделать утверждённое нельзя. Чекпоинт, который можно обойти находкой ревью, не значит ничего, а человек при этом уверен, что одобрил именно то, что уехало в коммит. @@ -526,8 +536,9 @@ flowchart TD **Урожай — списком, не задачами.** Отложенные находки (реальный `major` не для этого мерджа, развилка, решённая «потом», пачка `nit`) собери в секцию доклада `Урожай`: формулировка, оракул, провенанс. Задачи из него **заводит не этот -скилл** — у того, кто ведёт задачи проекта, своя нарезка, свой формат и свои -правила дублей. Твоя обязанность — не потерять и передать. +скилл** — их заводит `av-dev-tasks:tasks` своим сценарием «задачи из ревью и +аудита»: своя нарезка, свой формат, свои правила дублей. Твоя обязанность — не +потерять и передать. **Границы покрытия из отчёта не выбрасывай** — они уезжают в финальный доклад сжатой строкой. Отчёт, из которого исчезло «что проверить было невозможно», @@ -564,6 +575,13 @@ flowchart TD [references/project-facts.md](../review/references/project-facts.md) конвейера ревью перечисляет все документы канона с их предметом. Пройди по этому перечню — каждый документ получает строку, отрицание остаётся обязательным. + +**Двух домов в том перечне нет намеренно, а синку они нужны: `docs/adr/` и +`docs/research/`.** Проход ревью их не открывает — процессные, — но ADR как раз +**главный выход синка**: решение, принятое по ходу задачи, без этой строки +теряется систематически. Добавь их к перечню руками: `adr/` — решение с ценой, +принятое в этой задаче; `research/` — записка разведки, если ветка была +исследовательской. Триггеры при этом ты знаешь хуже, и это называется в докладе строкой: «синк сделан по перечню документов, без списка триггеров — плагина `av-dev-docs` нет». Канона в проекте тоже нет — назови это исходом и предложи `av-dev-docs:canon`. @@ -573,9 +591,10 @@ flowchart TD Коммить **в текущую ветку** (`git rev-parse --abbrev-ref HEAD`), сам ветку не создавай и не переключай, ничего не пушь. -Сообщение — по-русски, скиллом `av-dev-git:commit`, если он подключён (первая -строка «что сделано», тело списком 1–3 пункта, без трейлеров). Одна задача — один -осмысленный коммит. +Сообщение — по-русски, **скиллом `av-dev-git:commit`**: форму сообщения держит +он, и здесь она не пересказывается. Вызов не разрешился — плагина в проекте нет: +напиши сообщение сам и скажи строкой доклада, что форму коммита не сверял никто. +Одна задача — один осмысленный коммит. ### 11. Закрыть задачу — **после коммита, не раньше** diff --git a/av-dev-code/skills/review/SKILL.md b/av-dev-code/skills/review/SKILL.md index 2d5f149..8deba07 100644 --- a/av-dev-code/skills/review/SKILL.md +++ b/av-dev-code/skills/review/SKILL.md @@ -54,12 +54,12 @@ description: "Конвейер ревью изменения, устроенны непроверенная ветка деградации хуже честного отказа. Заводить руками не надо: этим владеет скилл `av-dev-code:openspec` — он заводит каталог и заменяет пример в `config.yaml` настройкой. Его же зовут `av-dev-docs:init` на новом - проекте и `canon adopt` на переводимом. + проекте и `av-dev-docs:canon` в режиме `adopt` — на переводимом. - **Документы канона** — см. следующий раздел. - **Проектные копии этих скиллов и агентов удаляются при установке.** Если в проекте уже лежат свои `.claude/skills/review`, `.claude/skills/review-pipeline`, `.claude/skills/task-pipeline`, - `.claude/skills/resolve` или + `.claude/skills/task-batch`, `.claude/skills/resolve` или `.claude/agents/<проект>-review-*.md` — снеси их. Иначе короткое имя разрешится в устаревшую проектную копию, молча и без признаков подмены. @@ -121,8 +121,8 @@ description: "Конвейер ревью изменения, устроенны критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не открывает никто. -Дом канона этой раскладки — `av-dev-docs`, `references/canon.md`, раздел «Три -категории документов». Конвейер её **читатель**: категории и имена тем он берёт +Дом канона этой раскладки — скилл `av-dev-docs:canon`, раздел «Три категории +документов». Конвейер её **читатель**: категории и имена тем он берёт оттуда и своих не заводит. Отсюда то, ради чего правило и заведено: **`docs/` перестаёт быть просто @@ -866,8 +866,9 @@ Recall темы `conventions` равен длине конвенций прое - **всегда** — `review-specs` в режиме «дизайн ДО кода». Дельта-спеки сверяются на каждой задаче: это самый дешёвый чекпоинт конвейера, и он ловит то, что на готовом коде уже не чинят; -- **со `medium`** — `review-rubric` (фаза 1 без фазы 2: рубрика на задуманный - узел становится приёмочными критериями и уезжает в `tasks.md`); +- **со `medium`** — `review-rubric`: рубрика на задуманный узел, по ней же + разбирается дельта-спека, а сами пункты уезжают приёмочными критериями в + `tasks.md`; - **только в `large`** — `review-architecture` на предложении: можно ли выразить существующими понятиями — **включая конструкции стандартной библиотеки**, — не появляется ли второй способ. Вопрос «не изобретаем ли то, что уже есть в @@ -900,7 +901,7 @@ flowchart TD plan[/"план разметки задачи:
размер, сложность, метка"/] proposal["предложение: proposal.md + дельта-спеки"] specs["specs (режим «дизайн ДО кода») — всегда"] - rubric["rubric, фаза 1 → приёмочные критерии в tasks.md"] + rubric["rubric → приёмочные критерии в tasks.md"] arch["architecture на предложении"] author["вопрос автору: три формы решения и компромисс каждой"] fix["шаг пайплайна: правка спек, развилки — вопросом в запись"] @@ -942,8 +943,11 @@ flowchart TD - Находка не для этого мерджа, но реальная (отложенный `major`, развилка, решённая «потом»), — не теряется, но **и не заводится здесь**. Конвейер отдаёт её **списком урожая** в отчёте: формулировка, оракул, провенанс (какой проход, - какой change). Заведение задач принадлежит тому, кто ведёт задачи проекта, — - у него свой формат, своя нарезка и свои правила дублей. Мелочь класса `nit` + какой change). Заведение задач принадлежит `av-dev-tasks:tasks` — зови его со + списком урожая, у него на этот вход отдельный сценарий «задачи из ревью и + аудита»: свой формат, кластеризация по причине, дедуп против беклога и + кладбища. Плагина нет — урожай остаётся списком в отчёте, и это говорится + строкой доклада: задачи из него не заведёт никто. Мелочь класса `nit` идёт в урожай одной пачкой, а не записью на находку. - `Promote candidates` — по процедуре [references/promote.md](references/promote.md): находка → конвенция → правило линтера → **удаление формулировки из конвенций**. diff --git a/av-dev-code/skills/review/references/project-facts.md b/av-dev-code/skills/review/references/project-facts.md index 8e6e665..d8bd6d6 100644 --- a/av-dev-code/skills/review/references/project-facts.md +++ b/av-dev-code/skills/review/references/project-facts.md @@ -8,9 +8,8 @@ `av-dev-docs`, и проход читает их напрямую: пути жёсткие, посредник не нужен, а второй дом для тех же фактов разошёлся бы и выглядел актуальным. -Определение канона — в плагине `av-dev-docs`, -`skills/canon/references/canon.md`. Здесь только карта «тема → её дом → что -оттуда берётся». +Определение канона держит скилл `av-dev-docs:canon`. Здесь только карта «тема → +её дом → что оттуда берётся». ## Карта тем diff --git a/av-dev-code/skills/review/references/review-journal.md b/av-dev-code/skills/review/references/review-journal.md index 6e511f6..958a7ed 100644 --- a/av-dev-code/skills/review/references/review-journal.md +++ b/av-dev-code/skills/review/references/review-journal.md @@ -41,8 +41,7 @@ ## Форма записи **Это дом формы, и у него есть копия.** Скелет `docs/review.md`, который кладёт -в проект `av-dev-docs` (`skills/canon/references/skeletons.md`), повторяет её -дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы +в проект `av-dev-docs:canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет. Дословность сверяет `scripts/copies.py` маркетплейса по маркерам ниже — но diff --git a/av-dev-code/skills/review/references/review-levels.md b/av-dev-code/skills/review/references/review-levels.md index a188961..86846ee 100644 --- a/av-dev-code/skills/review/references/review-levels.md +++ b/av-dev-code/skills/review/references/review-levels.md @@ -94,9 +94,9 @@ костяк — гейт, спеки, код, триаж**. Разрезать задачу, обе половины которой остаются в одной метке, значит заплатить костяк дважды за ту же проверку. Резать стоит там, где разрез **снимает доказательство с большей части диффа**. -Шов и правило нарезки живут у того, кто ведёт задачи, — скилл `av-dev-tasks:tasks`, -его `references/split.md`. Пути туда конвейер не выносит: за пределы своего -плагина он ходит вызовом скилла, а не файлом. +Шов и правило нарезки живут у того, кто ведёт задачи, — скилл +`av-dev-tasks:tasks`, его раздел о нарезке. Пути туда конвейер не выносит: за +пределы своего плагина он ходит вызовом скилла, а не файлом. Разметка в костяк не входит — она платится один раз на задачу, а не один раз на прогон, и потому **разрез задачи её не удваивает**. Это единственное, что стало diff --git a/av-dev-docs/agents/doc-wording.md b/av-dev-docs/agents/doc-wording.md index b1a9ef4..25c121a 100644 --- a/av-dev-docs/agents/doc-wording.md +++ b/av-dev-docs/agents/doc-wording.md @@ -1,6 +1,6 @@ --- name: doc-wording -description: "Вычитка языка документов проекта по информационному стилю — паспорт, архитектура, конвенции, безопасность, решения ADR, записки разведки, CLAUDE.md. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в имени файла. Отдаёт готовые формулировки на замену и ничего не правит сам. Записи каталога задач вычитывает отдельный агент task-wording, их форму — task-form. Использовать после правки документов, после adopt и после повышения версии канона. Только чтение." +description: "Вычитка языка документов проекта по информационному стилю — паспорт, архитектура, конвенции, безопасность, решения ADR, записки разведки, CLAUDE.md. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в имени файла. Отдаёт готовые формулировки на замену и ничего не правит сам. Записи каталога задач вычитывает отдельный агент task-wording, их форму — task-form. Зовётся по названной пачке правленных документов, а не на весь канон: последним шагом синка документации (av-dev-docs:docs), шагом заведения проекта (av-dev-docs:init), шагами adopt и upgrade скилла av-dev-docs:canon. Скилл healthcheck его не зовёт — там сверка утверждений, а не языка. Только чтение." tools: Read, Grep, Glob model: sonnet color: green diff --git a/av-dev-docs/skills/canon/SKILL.md b/av-dev-docs/skills/canon/SKILL.md index 5a3f970..ee250e5 100644 --- a/av-dev-docs/skills/canon/SKILL.md +++ b/av-dev-docs/skills/canon/SKILL.md @@ -52,7 +52,7 @@ python3 $ds version --dir <корень> # версия кано ``` **Формы `openspec/config.yaml` здесь больше нет.** Каталог принадлежит конвейеру, -и форму смотрит его скрипт — `av-dev-code`, скилл `openspec`, команда +и форму смотрит его скрипт — скилл `av-dev-code:openspec`, команда `openspec.py check`. Проект работает по OpenSpec, а плагина конвейера нет — форму не проверяет никто, и это надо сказать строкой доклада, а не считать, что она верна. @@ -244,6 +244,18 @@ capability), `openspec/config.yaml`. **Передай им объявленное переходное состояние из шага 5** — иначе честная строка в незаполненном слоте вернётся находкой, а это не поломка, а объявленный долг. +### 7. Вычитай написанное — агент `doc-wording` + +Судьи смотрят утверждения, а `adopt` только что **писал текст**: честные строки +в пустые слоты, переписанные при переносе абзацы, шапки перенесённых документов. +Язык этого текста не проверяет никто другой, а зовущий здесь по определению тот, +кто его и написал. + +Позови агента **по названной пачке** — документы, которые ты завёл или правил, +плюс перенесённые целиком. Весь канон ему не нужен: он работает по списку, и +список же служит ему словарём терминов. Находки — готовые формулировки, +подставляешь их ты. + ## `upgrade` — канон вырос 1. `docs.py version` — версия проекта и версия скрипта. @@ -255,6 +267,10 @@ capability), `openspec/config.yaml`. 4. Подними `canon` в `docs/.pm.json` до текущей. 5. `docs.py check`. 6. **Позови судей** — Skill `av-dev-docs:healthcheck`. +7. **Позови вычитку** — агент `doc-wording`, но **только по тем документам, + которых записи журнала коснулись**, и только если правка была текстовой, а не + переименованием файла. Записи журнала пишутся руками в проектной прозе, и + дописанный по журналу раздел — такой же свежий текст, как на синке. Записи журнала описывают **что сделать проекту**. Если запись этого не говорит — это дефект журнала, и о нём надо сказать, а не догадываться. diff --git a/av-dev-docs/skills/canon/references/canon.md b/av-dev-docs/skills/canon/references/canon.md index e3a8010..09d1867 100644 --- a/av-dev-docs/skills/canon/references/canon.md +++ b/av-dev-docs/skills/canon/references/canon.md @@ -402,7 +402,8 @@ kebab-case.** Причина не эстетическая: имя файла с род работы, и называется оно **`research` без раздела «Вопрос»**. Такая запись в работу не берётся и лежит в конце своей категории. -Раскладку, форму записи и алгоритм работы над каждым типом держит скилл `tasks`. +Раскладку, форму записи и алгоритм работы над каждым типом держит скилл +`av-dev-tasks:tasks`. ### `CLAUDE.md` @@ -432,8 +433,8 @@ kebab-case.** Причина не эстетическая: имя файла с **Файл канону не принадлежит, и проверяет его тоже не канон.** Каталог `openspec/` — предпосылка конвейера: без него не работают ни `opsx:propose`, ни ревью дизайна, ни сверка требований. Заводит его, настраивает и **проверяет -форму** плагин `av-dev-code`, скилл `openspec`: там образец файла, там же -скрипт `openspec.py check`. `docs.py` о файле не говорит ничего. +форму** скилл `av-dev-code:openspec`: там образец файла, там же скрипт +`openspec.py check`. `docs.py` о файле не говорит ничего. Канон называет его здесь по одной причине: `openspec/specs/` — **дом темы `requirements`**, и без этой строки карта тем неполна. На форму самого diff --git a/av-dev-docs/skills/canon/references/skeletons.md b/av-dev-docs/skills/canon/references/skeletons.md index 03e7877..61a3be4 100644 --- a/av-dev-docs/skills/canon/references/skeletons.md +++ b/av-dev-docs/skills/canon/references/skeletons.md @@ -19,9 +19,12 @@ `` … ``, копия — `` … ``; `scripts/copies.py` маркетплейса требует дословного -совпадения. Комментарии невидимы в отрендеренном markdown и уезжают в проект -вместе со скелетом — там они говорят читателю, что у текста есть дом. Правишь -текст внутри маркеров — правь дом, а не копию. +совпадения. Правишь текст внутри маркеров — правь дом, а не копию. + +**Сама пара маркеров в проект не переносится.** Это машинерия маркетплейса: +путь в ней ведёт в дерево плагина, и в репозитории проекта он не разрешится ни +во что. Кладя скелет, копируй содержимое между маркерами, а строки +`` и `` оставляй здесь. ## `docs/passport.md` @@ -354,6 +357,9 @@ ``` +Пара маркеров `копия:` внутри — машинерия маркетплейса; в `docs/review.md` +проекта уезжает только содержимое между ними (см. выше). + Новый проект: «Дефектов пока не было. Настройка конвейера появится с первым ревью.» diff --git a/av-dev-docs/skills/docs/SKILL.md b/av-dev-docs/skills/docs/SKILL.md index f85acad..d0fac27 100644 --- a/av-dev-docs/skills/docs/SKILL.md +++ b/av-dev-docs/skills/docs/SKILL.md @@ -75,6 +75,19 @@ description: Вести содержимое документов канона которых работа не касалась, — расхождение, внесённое правкой в одном месте, там и живёт. +## Вычитка — наоборот, здесь + +**Язык правленого вычитывается на синке, и зовёшь агента `doc-wording` ты.** +Довод обратный доводу про судей: он читает **только названную пачку**, стоит +дёшево и ищет ровно то, что портится в момент письма, — залог, оценку без факта, +жаргон, термин без ввода. Ждать сессии здесь нечего: через месяц никто уже не +помнит, какую фразу имел в виду автор. + +Позови его **последним шагом синка**, отдав список файлов, которых чек-лист +коснулся, — и назови этот список в промпте: по нему же он судит, известен ли +термин. Ничего не правивший синк агента не зовёт. Находки он отдаёт готовыми +формулировками, подставляешь их ты. + ## ADR — промоут, а не второе сочинение Обоснование уже написано: `opsx:propose` кладёт `design.md` в каждый change, и diff --git a/av-dev-docs/skills/init/SKILL.md b/av-dev-docs/skills/init/SKILL.md index eaa0561..f645639 100644 --- a/av-dev-docs/skills/init/SKILL.md +++ b/av-dev-docs/skills/init/SKILL.md @@ -1,6 +1,6 @@ --- name: init -description: "Завести новый проект — сессия вопросов и ответов по свободному описанию замысла, из которой рождается первичная документация по канону av-dev: паспорт, CLAUDE.md с инвариантами и командами, модель угроз с периметром, первые цели в роадмапе и скелет остальных документов. OpenSpec заводит не сам, а вызовом скилла av-dev-code:openspec — каталог принадлежит конвейеру; плагина конвейера нет — шаг пропускается строкой доклада. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл canon." +description: "Завести новый проект — сессия вопросов и ответов по свободному описанию замысла, из которой рождается первичная документация по канону av-dev: паспорт, CLAUDE.md с инвариантами и командами, модель угроз с периметром и скелет остальных документов; первые цели собирает интервью, а записывает их вызовом скилла av-dev-tasks:tasks — роадмап принадлежит плагину задач. OpenSpec заводит не сам, а вызовом скилла av-dev-code:openspec — каталог принадлежит конвейеру; плагина конвейера нет — шаг пропускается строкой доклада. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл canon." --- # Заведение нового проекта @@ -26,12 +26,17 @@ description: "Завести новый проект — сессия вопро | `passport.md` | `architecture.md` | | `CLAUDE.md` | `database.md` | | `security.md` | `conventions/` | -| `tasks/ROADMAP.md` — первые цели | `research/`, `adr/` | -| `docs/.pm.json` | `review.md` — журнал пуст, настройка появится с первым ревью | +| `docs/.pm.json` | `research/`, `adr/` | +| | `review.md` — журнал пуст, настройка появится с первым ревью | Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет, заводится первой задачей». Проход читает её как факт. +**`tasks/ROADMAP.md` в таблице нет намеренно.** Первые цели `init` собирает +интервью (блок 6), но записывает их не он: каталогом задач и формой целей владеет +`av-dev-tasks:tasks`, и это шаг 7. Плагина нет — цели остаются списком в докладе, +роадмапа в проекте не появляется, и это говорится строкой. + ## Порядок интервью — зависимость, а не удобство Каждый блок опирается на ответ предыдущего; переставлять нельзя. @@ -66,7 +71,7 @@ description: "Завести новый проект — сессия вопро ## Обращение к соседним плагинам -Два шага из девяти — вызовы чужого: OpenSpec заводит конвейер, каталог задач +Два шага порядка работы — вызовы чужого: OpenSpec заводит конвейер, каталог задач ведёт плагин задач. Ни того, ни другого `init` не делает руками. **Копия.** Дом правила — `shared/plugin-boundary.md` в репозитории плагинов. @@ -123,8 +128,14 @@ description: "Завести новый проект — сессия вопро тоже строка доклада. 8. `docs.py check` из скилла `canon` — до отсутствия дрейфа. Замечания о незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа. -9. Покажи человеку, что получилось, и **отдельным списком** — что выведено из - брифа, что предположено, что осталось неизвестным. Правят по этим строкам. +9. **Вычитай написанное — агент `doc-wording`**, по пачке заполненных документов + (`passport.md`, `CLAUDE.md`, `security.md`). Здесь он нужен сильнее, чем где + бы то ни было: весь текст сочинён только что и по свободному брифу человека, а + бриф — это как раз залог, оценки без факта и жаргон. Скелеты с честной строкой + в пачку не клади, вычитывать в них нечего. Находки — готовые формулировки, + подставляешь их ты. +10. Покажи человеку, что получилось, и **отдельным списком** — что выведено из + брифа, что предположено, что осталось неизвестным. Правят по этим строкам. ## Что дальше diff --git a/av-dev-tasks/skills/tasks/references/from-review.md b/av-dev-tasks/skills/tasks/references/from-review.md index 4032a13..58f41fe 100644 --- a/av-dev-tasks/skills/tasks/references/from-review.md +++ b/av-dev-tasks/skills/tasks/references/from-review.md @@ -12,6 +12,12 @@ не файл-на-находку.** Если у ревью был триаж — половина работы уже сделана, бери его выход. Если нет — триажируй сам, прежде чем заводить. +**Штатный отправитель — `av-dev-code:review`** (и `av-dev-code:resolve`, который +его вызывает): задач он не заводит сам, а отдаёт отложенные находки **списком +урожая** — формулировка, оракул, провенанс — и хранит отчёт триажа вместе с +изменением. Приходит и любой другой разбор, вплоть до пересказа человеком; тогда +триажа нет и шаг 1 порядка делается руками. + ## Находка агента — не задача Мнение агента — **гипотеза, пока у неё нет свидетельства** (падающий тест,