From f0dd8f70c14f965078f78f8eb626401904a69790 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 9 Aug 2026 14:17:19 +0300 Subject: [PATCH] =?UTF-8?q?openspec=20=D1=83=D0=B5=D1=85=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=B2=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D0=B9=D0=B5=D1=80:=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B2=D0=BE=D0=B4=D0=B8=D1=82=20=D0=B5=D0=B3=D0=BE?= =?UTF-8?q?=20=D0=BF=D0=B0=D0=B9=D0=BF=D0=BB=D0=B0=D0=B9=D0=BD,=20=D0=BA?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=BD=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE?= =?UTF-8?q?=20=D0=B2=D1=8B=D1=81=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2=D0=B0=D0=B5?= =?UTF-8?q?=D1=82=D1=81=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Версия 7 объявила openspec/ слотом канона: init его заводил, adopt тоже, образец config.yaml лежал в скелетах, отсутствие каталога docs.py считал отказом. Разрез был проведён не там. По OpenSpec работает конвейер — без каталога не запускаются ни opsx:propose, ни ревью дизайна, ни сверка требований, — а канон документов о нём только высказывался. Проект, которому конвейер не нужен, получал отказ за отсутствие того, чем не пользуется. Появился скилл av-dev-pipeline:openspec: заводит каталог, заменяет закомментированный пример в config.yaml настройкой, объясняет разрез между ссылкой и пересказом — утверждение, опровергаемое открытием другого файла, это пересказ; строка, говорящая какой файл открыть, это ссылка. Образец переехал туда же, в references/config-skeleton.md, а в скелетах канона остался указатель. init и canon adopt OpenSpec больше не заводят, а зовут скилл конвейера через пространство имён. Вызов не разрешился — плагина конвейера нет, и это строка доклада, а не поломка: docs.py о каталоге тогда тоже молчит. Отсутствие openspec/ стало неприменимостью вместо отказа, остальные четыре проверки формы идут только при живом каталоге. На фикстуре без openspec дрейф упал с 10 пунктов до 9. Что осталось на месте и названо честно: проверка формы config.yaml и сторож версии (docs.py openspec-form) пока живут в скрипте канона. Перенести их значит завести в конвейере свой скрипт, а этого у него нет ни одного. У файла сейчас два плагина — один заводит, другой проверяет, — и это временное состояние, а не задуманное; в журнале версий оно записано так же. Канон повышен до версии 9. Запись не двигает ни одного файла проекта: меняется только то, кто их заводит. Но в ней названа потеря, которую легко не заметить — проект по OpenSpec без установленного пайплайна теперь не услышит от docs.py ничего про свою настройку, и молчание это законное. Гейт зелёный, скиллов стало десять. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 5 +- av-dev-docs/skills/canon/SKILL.md | 11 ++- av-dev-docs/skills/canon/references/canon.md | 24 +++-- .../skills/canon/references/changelog.md | 38 ++++++++ .../skills/canon/references/skeletons.md | 86 ++-------------- av-dev-docs/skills/canon/scripts/docs.py | 14 ++- av-dev-docs/skills/init/SKILL.md | 35 +++---- av-dev-pipeline/.claude-plugin/plugin.json | 2 +- av-dev-pipeline/skills/openspec/SKILL.md | 97 +++++++++++++++++++ .../openspec/references/config-skeleton.md | 79 +++++++++++++++ .../skills/review-pipeline/SKILL.md | 5 +- 11 files changed, 278 insertions(+), 118 deletions(-) create mode 100644 av-dev-pipeline/skills/openspec/SKILL.md create mode 100644 av-dev-pipeline/skills/openspec/references/config-skeleton.md diff --git a/README.md b/README.md index 6f9c962..f75aa56 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,10 @@ вычитывают их два отдельных прохода: `task-form` (форма записи) и `task-wording` (язык записей); - `session` — ритуал между спринтами и ведение спринта. -- **av-dev-pipeline** — исполнение. **Требует OpenSpec.** +- **av-dev-pipeline** — исполнение. **Требует OpenSpec и сам его заводит.** + - `openspec` — завести и настроить `openspec/` в проекте: `openspec init`, + замена примера в `config.yaml` настройкой канонической формы. Каталог + принадлежит конвейеру, а не канону: без конвейера он проекту не нужен; - `task-pipeline` — задача через полный цикл SDD, от постановки до коммита; - `task-batch` — несколько задач разом, каждая в своём worktree; - `review-pipeline` — конвейер ревью **по темам**: документ проекта либо diff --git a/av-dev-docs/skills/canon/SKILL.md b/av-dev-docs/skills/canon/SKILL.md index 7fe35f9..e8ac96f 100644 --- a/av-dev-docs/skills/canon/SKILL.md +++ b/av-dev-docs/skills/canon/SKILL.md @@ -149,11 +149,12 @@ capability), `openspec/config.yaml`. 1. `docs/.pm.json` с `{"canon": <текущая версия>}` и путём миграций, если БД есть; 2. каталоги канона и скелет **по [references/skeletons.md](references/skeletons.md)**: незаполненное — одной честной информативной строкой, а не «TBD»; -3. **OpenSpec, если его нет** — `openspec init --tools claude`, и `config.yaml` - по тому же скелету. Каталог есть, а `config.yaml` из коробки — тот же случай, - что отсутствие: закомментированный пример выглядит настройкой и не является - ею. Пересказ инвариантов, конвенций и правил ревью из `context` вычисти - ссылкой на дом — на переводимом проекте он там почти наверняка есть; +3. **OpenSpec, если его нет или `config.yaml` остался примером** — **вызови + Skill `av-dev-pipeline:openspec`**. Каталог принадлежит конвейеру, и команда + заведения с формой файла живут там. Пересказ инвариантов, конвенций и правил + ревью из `context` вычисти ссылкой на дом — на переводимом проекте он там + почти наверняка есть. Вызов не разрешился — плагина конвейера нет, и это + строка доклада, а не поломка: `docs.py` о каталоге тогда тоже молчит; 4. переносы содержимого; 5. каталог задач — **вызови скилл `av-dev-tasks:tasks`**, сценарий адаптации: он владеет форматом задач. Он же переименует транслитные слаги в английские и diff --git a/av-dev-docs/skills/canon/references/canon.md b/av-dev-docs/skills/canon/references/canon.md index 052ee66..148753c 100644 --- a/av-dev-docs/skills/canon/references/canon.md +++ b/av-dev-docs/skills/canon/references/canon.md @@ -114,7 +114,7 @@ openspec/ | `database.*` | источник | `operations` — схема и настройки с числами | | `CLAUDE.md`, `AGENTS.md` | источник | `autotests` (семантика гейта); инварианты — сквозные | | `openspec/specs/` | источник | `requirements` | -| `openspec/config.yaml` | процессный | — (настройка порождения артефактов, слой **до** тем) | +| `openspec/config.yaml` | процессный | — (настройка порождения артефактов, слой **до** тем; заводит конвейер) | | `tasks/` | процессный | — (чужое владение: плагин `av-dev-tasks`) | | `review.*` | процессный | — (настройка самого конвейера, слой **над** темами) | | `adr.*` | процессный | — | @@ -432,11 +432,18 @@ kebab-case.** Причина не эстетическая: имя файла с пересказ конвенций и инварианты сюда не пишутся: у них есть свои дома, и второй дом разойдётся на первой же правке. -**Каталог `openspec/` — часть канона, а не соседняя технология.** В нём дом темы -`requirements`, и заводится он командой: `openspec init --tools claude`. Её -выполняет `init` на новом проекте и `adopt` на переводимом; из канона она названа -поимённо потому, что её печатает отказ `docs.py`, а отказ без команды заставляет -искать её в другом месте. +**Каталог `openspec/` принадлежит конвейеру, а не канону.** В нём дом темы +`requirements`, и нужен он тому, кто по OpenSpec работает: без каталога не +работают ни `opsx:propose`, ни ревью дизайна, ни сверка требований. Заводит и +настраивает его скилл `av-dev-pipeline:openspec`; `init` и `adopt` его только +зовут. Команда (`openspec init --tools claude`) названа здесь поимённо потому, +что её печатает вывод `docs.py`, а адрес без команды заставляет искать её в +другом месте. + +**Отсюда и односторонность: канон о файле высказывается, но его не требует.** +`docs.py check` проверяет форму, **если каталог есть**, и говорит +«неприменимо», если его нет. Проект без конвейера живёт без OpenSpec законно, и +отказом это быть не может. **Файл из коробки настройкой не является.** `openspec init` кладёт `config.yaml`, где и `context`, и `rules` лежат закомментированным примером. Такой файл читается @@ -447,7 +454,8 @@ kebab-case.** Причина не эстетическая: имя файла с Проверяется пять вещей, и каждая — про молчащий пробел, а не про вкус: -1. **`openspec/` есть.** Нет — нет и дома темы `requirements`. +1. **`openspec/` есть.** Нет — проверка неприменима, и это не отказ: каталог + нужен конвейеру, а не канону. Остальные четыре идут только при живом каталоге. 2. **Имя файла `config.yaml`.** `config.yml` OpenSpec не читает и об этом не сообщает: настройка, написанная в файл с таким именем, пропадает целиком. 3. **`context` и `rules.specs` не остались примером.** Правила для `specs` @@ -570,7 +578,7 @@ OpenSpec переименует артефакт или сменит схему ```json { - "canon": 8, + "canon": 9, "migrations": "internal/store/migrations" } ``` diff --git a/av-dev-docs/skills/canon/references/changelog.md b/av-dev-docs/skills/canon/references/changelog.md index 8d622ef..2d2c522 100644 --- a/av-dev-docs/skills/canon/references/changelog.md +++ b/av-dev-docs/skills/canon/references/changelog.md @@ -13,6 +13,44 @@ upgrade` идёт по записям снизу вверх от версии п --- +## Версия 9 — 2026-08-09 + +OpenSpec уехал в конвейер. Каталог `openspec/` версией 7 был объявлен слотом +канона: `init` его заводил, `adopt` тоже, образец `config.yaml` лежал в скелетах, +а отсутствие каталога `docs.py` считал отказом. Разрез был проведён не там. По +OpenSpec работает конвейер — без каталога не запускаются ни `opsx:propose`, ни +ревью дизайна, ни сверка требований, — а канон документов о нём только +высказывался. Проект, которому конвейер не нужен, получал отказ за отсутствие +того, чем не пользуется. + +**Что появилось.** Скилл `av-dev-pipeline:openspec`: заводит каталог, заменяет +закомментированный пример в `config.yaml` настройкой, объясняет разрез между +ссылкой и пересказом. Образец файла переехал туда же — в +`references/config-skeleton.md` того скилла. + +**Что изменилось.** `init` и `canon adopt` OpenSpec больше не заводят, а **зовут +скилл конвейера**; вызов не разрешился — плагина конвейера нет, и это строка +доклада, а не поломка. Отсутствие `openspec/` для `docs.py check` стало +неприменимостью вместо отказа: остальные четыре проверки формы идут только при +живом каталоге. + +**Что осталось на месте и почему.** Проверка формы `config.yaml` и сторож версии +(`docs.py openspec-form`) пока живут в скрипте канона — переносить их значит +заводить в конвейере свой скрипт, а этого у него нет ни одного. Разрез названного +это не отменяет, но и не завершает: **у файла сейчас два плагина — один заводит, +другой проверяет**, и это временное состояние, а не задуманное. + +**Что сделать проекту.** + +1. Ничего не переносить: файлы проекта эта версия не двигает. Меняется только то, + кто их заводит. +2. Проверить, что плагин `av-dev-pipeline` установлен, если проект работает по + OpenSpec. Без него `docs.py check` про каталог промолчит — и молчание это + законное, так что отсутствие настройки перестанет ловиться само. +3. Проект **не** работает по OpenSpec: убедиться, что `openspec/` нет, и + перестать держать его пустым ради проверки. Она больше не требует каталога. +4. `docs/.pm.json`: `"canon": 9`. + ## Версия 8 — 2026-08-09 Канон отпустил каталог задач. Плагин `av-dev-pm` расколот на `av-dev-docs` diff --git a/av-dev-docs/skills/canon/references/skeletons.md b/av-dev-docs/skills/canon/references/skeletons.md index e0cd17e..4107049 100644 --- a/av-dev-docs/skills/canon/references/skeletons.md +++ b/av-dev-docs/skills/canon/references/skeletons.md @@ -419,89 +419,19 @@ severity стоит здесь, а не выводится каждым прох ## `openspec/config.yaml` -Каталог `openspec/` заводится командой — `openspec init --tools claude`, — и она -кладёт `config.yaml` с закомментированным примером внутри. Пример **заменяется -целиком**: нетронутый файл выглядит настроенным, а работает как пустой. - -**Это маршрутизатор, а не второй дом фактов.** Сюда пишут ровно то, что нужно -**в момент порождения артефакта** и чего в этот момент ещё никто не открыл: -язык, правила именования capability, придирки валидатора и **адреса** документов -канона. Пересказ паспорта, инвариантов, конвенций и правил ревью сюда не -переносится: расходится он молча, а замечают это в уже написанном предложении. - -```yaml -schema: spec-driven - -context: | - Language: Russian - Пиши на русском, но: - - Структурные заголовки оставляй на английском: - ## ADDED/MODIFIED/REMOVED Requirements, ### Requirement:, #### Scenario: - - Ключевые слова GIVEN/WHEN/THEN и RFC 2119 (SHALL/MUST/SHOULD) — на английском - - Технические термины, пути и код — на английском - - Имена capabilities: - - Capability — это ПОВЕДЕНИЕ или домен системы, а не пакет кода (совпадение с - именем пакета допустимо, но не критерий). - - Существительное, понятное без знания кода: ingest, parsing, storage, - read-api. НЕ store/httpapi — это реализация. - - Гранулярность по принципу «требования меняются вместе». Дробить, когда в - одной спеке смешиваются разные заботы. Переименовать дёшево (RENAMED - Requirements) — не дроби преждевременно в маленьком проекте. - - RFC 2119 — требование валидатора, не стиль: - - Каждое ### Requirement ОБЯЗАНО содержать литерал SHALL или MUST, иначе - `openspec validate` падает. Поэтому эти слова и WHEN/THEN не русифицируем. - - Что это за проект — читай перед предложением, а не отсюда: - - docs/passport.md — цель, её граница (чем проект НЕ является), потребители, - типовые сценарии, референсы; - - CLAUDE.md — инварианты с severity и семантика гейта; - - docs/architecture.md — устройство; docs/security.md — периметр; - docs/adr/ — почему решено так; docs/research/ — что уже измерено. - Пересказа этих документов здесь нет намеренно: второй дом факта расходится с - первым молча, и заметно это становится в предложении, которое уже написано. - - Ревью: правило выбора метки и состав проходов здесь не пересказываем — их дом - скилл av-dev-pipeline:review-pipeline, проектная настройка — docs/review.md. - - Конвенции кода: механизированное проверяет гейт, прозой остаётся - docs/conventions/. Ни состав шагов гейта, ни перечень конвенций здесь не - пересказываем: и то и другое растёт по ходу задач. - - Развилка или блокер — сперва prior art. Готовые решения смотрим в референсах - паспорта, отвергаем — с названной причиной, и причина идёт в design.md этого - же изменения. - -rules: - proposal: - - Capabilities называй по поведению или домену системы, не по пакету кода - specs: - # Кавычки обязательны: без них YAML обрежет строку на первом '#'. - - "Каждое ### Requirement обязано содержать SHALL или MUST (иначе валидация падает)" - - "Сценарий — ровно #### (четыре решётки); три или список молча теряются" - - "SHALL/MUST должно стоять в ПЕРВОМ абзаце требования: валидатор смотрит только его" - - "Заголовки и WHEN/THEN/GIVEN — на английском, остальной текст на русском" -``` - -**Четыре правила для `specs` сняты отказами валидатора, а не выведены из -документации** — потому и записаны дословно: без них каждое второе предложение -узнаёт их падением `openspec validate --strict`. Блок `context` проект -дополняет своим (стек, разведка, особенности домена), но **адреса паспорта и -`CLAUDE.md` обязательны** — их отсутствие `docs.py check` называет отказом. - -**Ключи под `rules:` — имена артефактов схемы**, а не свободные слова: -`proposal`, `specs`, `design`, `tasks`. Правило под чужим именем не применяется -и об этом не сообщает, поэтому `rules.spec` вместо `rules.specs` даёт конфиг, -выглядящий написанным и не работающий; `docs.py check` такой ключ называет. -Перечень артефактов задаёт OpenSpec, а не канон, — за его актуальностью следит -`docs.py openspec-form`. +**Образец переехал.** Файл заводит и заполняет плагин конвейера — скилл +`av-dev-pipeline:openspec`, — потому что по OpenSpec работает он, а не канон +документов. Проект без конвейера каталога `openspec/` не имеет вовсе, и образец +файла, которого у него нет, в скелетах канона лежал бы мёртвым грузом. +Канон о нём всё ещё **высказывается**, но только в одну сторону: `docs.py check` +проверяет форму, **если каталог есть**, и молчит, если его нет. Что именно +проверяется — [canon.md](canon.md), раздел `openspec/config.yaml`. ## `docs/.pm.json` ```json { - "canon": 8 + "canon": 9 } ``` diff --git a/av-dev-docs/skills/canon/scripts/docs.py b/av-dev-docs/skills/canon/scripts/docs.py index 8ce55b0..70c483f 100644 --- a/av-dev-docs/skills/canon/scripts/docs.py +++ b/av-dev-docs/skills/canon/scripts/docs.py @@ -25,7 +25,7 @@ from dataclasses import dataclass, field from pathlib import Path from typing import NoReturn -CANON_VERSION = 8 +CANON_VERSION = 9 OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4 @@ -528,9 +528,15 @@ def check_openspec(root: Path, rep: Report) -> None: """ os_dir = root / "openspec" if not os_dir.is_dir(): - rep.error( - "нет openspec/ — там дом темы requirements (openspec/specs/) и " - f"настройка генерации артефактов; заводится `{OPENSPEC_INIT}`" + # Каталог принадлежит конвейеру, а не канону: там дом темы requirements + # и настройка генерации артефактов, и нужен он тому, кто по OpenSpec + # работает. Проект без конвейера живёт без него законно, поэтому здесь + # неприменимость, а не отказ. Заводит каталог скилл + # av-dev-pipeline:openspec; команда названа на случай, если плагина нет. + rep.skip( + "нет openspec/ — проверка неприменима. Каталог заводит скилл " + f"av-dev-pipeline:openspec (`{OPENSPEC_INIT}`); без конвейера " + "он проекту не нужен" ) return diff --git a/av-dev-docs/skills/init/SKILL.md b/av-dev-docs/skills/init/SKILL.md index c01f485..7267457 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 (openspec init) с настроенным openspec/config.yaml — дом темы requirements, без которого не работают ни propose, ни ревью. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл canon." +description: "Завести новый проект — сессия вопросов и ответов по свободному описанию замысла, из которой рождается первичная документация по канону av-dev: паспорт, CLAUDE.md с инвариантами и командами, модель угроз с периметром, первые цели в роадмапе и скелет остальных документов. OpenSpec заводит не сам, а вызовом скилла av-dev-pipeline:openspec — каталог принадлежит конвейеру; плагина конвейера нет — шаг пропускается строкой доклада. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл canon." --- # Заведение нового проекта @@ -28,7 +28,6 @@ description: "Завести новый проект — сессия вопро | `security.md` | `conventions/` | | `docs/tasks/ROADMAP.md` — первые цели | `research/`, `adr/` | | `docs/.pm.json` | `review.md` — журнал пуст, настройка появится с первым ревью | -| `openspec/config.yaml` | | Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет, заводится первой задачей». Проход читает её как факт. @@ -69,30 +68,28 @@ description: "Завести новый проект — сессия вопро 1. Прочитай бриф целиком. Выпиши, на какие блоки интервью ответ уже есть. 2. Проведи интервью итерациями по ≤3 вопроса. -3. **Заведи OpenSpec: `openspec init --tools claude`.** Каталог `openspec/` — - часть канона, а не соседняя технология: в нём дом темы `requirements`, и без - него не работают ни `opsx:propose`, ни ревью дизайна, ни сверка требований. - Команда кладёт ещё `.claude/skills/openspec-*` и `.claude/commands/opsx/*` — - это её нормальная работа, не трогай их. +3. **OpenSpec — вызови Skill `av-dev-pipeline:openspec`.** Он заводит каталог и + заменяет пример в `config.yaml` настройкой. Делается это **до первого + документа**: без `openspec/` не работают ни `opsx:propose`, ни ревью дизайна, + ни сверка требований. Каталог принадлежит конвейеру, а не канону, поэтому + здесь только вызов — ни команды, ни формы файла `init` не знает. + + **Вызов не разрешился — плагина конвейера в проекте нет.** Это законный исход, + а не поломка: проект без конвейера живёт без OpenSpec. Скажи это строкой в + докладе и иди дальше; `docs.py check` о каталоге тоже промолчит. 4. Заведи `docs/.pm.json` с текущей версией канона. 5. Напиши заполняемые документы. **Бриф переезжает в `passport.md`** и отдельным файлом не остаётся: два дома для одного замысла разойдутся на первом же уточнении. 6. Заведи скелет остальных по [скелетам](../canon/references/skeletons.md) — каждый с честной строкой. -7. **Заполни `openspec/config.yaml`** по тем же скелетам. Файл из коробки — - закомментированный пример на английском; он **заменяется целиком**, потому что - нетронутый выглядит настроенным, а работает как пустой. Пиши туда только то, - что нужно **в момент порождения артефакта**: язык, правила именования - capability, придирки валидатора и **адреса** `docs/passport.md` и `CLAUDE.md`. - Инварианты, конвенции и правило ревью не пересказывай — у них есть дома, и - второй дом разойдётся с первым молча. -8. Каталог задач и первые цели — **вызови скилл `av-dev-tasks:tasks`**: он владеет - форматом целей и задач. -9. `docs.py check` из скилла `canon` — до отсутствия дрейфа. Замечания о +7. Каталог задач и первые цели — **вызови скилл `av-dev-tasks:tasks`**: он владеет + форматом целей и задач. Не разрешился — учёт задач остаётся владельцу, и это + тоже строка доклада. +8. `docs.py check` из скилла `canon` — до отсутствия дрейфа. Замечания о незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа. -10. Покажи человеку, что получилось, и **отдельным списком** — что выведено из - брифа, что предположено, что осталось неизвестным. Правят по этим строкам. +9. Покажи человеку, что получилось, и **отдельным списком** — что выведено из + брифа, что предположено, что осталось неизвестным. Правят по этим строкам. ## Что дальше diff --git a/av-dev-pipeline/.claude-plugin/plugin.json b/av-dev-pipeline/.claude-plugin/plugin.json index c3ee315..c228604 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. Задача принимается и обычным текстом. Плагины av-dev-docs и av-dev-tasks опциональны: первый даёт документы канона, из которых проходы читают проектную конкретику, второй — учёт задач; без них прогон деградирует поразрядно и называет это строкой.", + "description": "Проведение задачи через полный цикл Spec Driven Development и конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом, независимой реализацией и обязательным триажем; плюс прогон нескольких задач разом по одной в изолированном worktree. Требует OpenSpec и сам его заводит скиллом openspec. Задача принимается и обычным текстом. Плагины av-dev-docs и av-dev-tasks опциональны: первый даёт документы канона, из которых проходы читают проектную конкретику, второй — учёт задач; без них прогон деградирует поразрядно и называет это строкой.", "author": { "name": "Anton Vakhrushev", "email": "anwinged@gmail.com" diff --git a/av-dev-pipeline/skills/openspec/SKILL.md b/av-dev-pipeline/skills/openspec/SKILL.md new file mode 100644 index 0000000..bb5006f --- /dev/null +++ b/av-dev-pipeline/skills/openspec/SKILL.md @@ -0,0 +1,97 @@ +--- +name: openspec +description: "Завести и настроить OpenSpec в проекте — openspec init --tools claude, замена закомментированного примера в openspec/config.yaml на настройку канонической формы (язык, правила именования capability, придирки валидатора, адреса паспорта и CLAUDE.md), проверка, что форма не разошлась с живой версией инструмента. Использовать, когда в проекте нет каталога openspec/, когда config.yaml остался примером из коробки, когда заводят новый проект или переводят чужой и дошли до шага OpenSpec, а также когда конвейер отказался работать без источника требований. Каталог openspec нужен именно конвейеру: без него не работают ни opsx:propose, ни ревью дизайна, ни сверка требований." +--- + +# OpenSpec в проекте + +Каталог `openspec/` — **предпосылка конвейера**, а не канона документов. Без него +не работают ни `opsx:propose`, ни ревью дизайна, ни `review-specs`: у требований +не остаётся дома. Поэтому заводит и настраивает его этот плагин — тот, кто по +OpenSpec и работает. + +Канон документов о файле всё ещё высказывается, но односторонне: `docs.py check` +проверяет форму `config.yaml`, **если каталог есть**, и молчит, если его нет. +Проект без конвейера живёт без OpenSpec законно. + +## Два шага, и второй важнее первого + +**1. Завести.** + +``` +openspec init --tools claude +``` + +Команда кладёт ещё `.claude/skills/openspec-*` и `.claude/commands/opsx/*` — это +её нормальная работа, не трогай их. + +**2. Заменить пример.** `openspec init` кладёт `config.yaml`, где `context` и +`rules` — закомментированный пример на английском. **Файл из коробки хуже +отсутствующего:** он есть, он валиден, имя правильное, — и читается как +настроенный, работая как пустой. Узнаётся это по уже написанному предложению: на +другом языке, с capability по имени пакета, без единого `SHALL`. + +Пример **заменяется целиком** по образцу: +[references/config-skeleton.md](references/config-skeleton.md). + +## Что туда пишут, а что нет + +**Это маршрутизатор, а не второй дом фактов.** Внутрь идёт ровно то, что нужно +**в момент порождения артефакта** и чего в этот момент ещё никто не открыл: язык, +правила именования capability, придирки валидатора и **адреса** документов +проекта. + +Пересказ паспорта, инвариантов, конвенций и правил ревью сюда **не переносится**. +Место для второго дома здесь самое частое: `context` читается при порождении +каждого артефакта, туда удобно дописать «чтобы агент знал», и так заводятся копии +инвариантов, состава гейта и правил выбора метки. Расходятся они молча, а +замечают это в уже написанном предложении. + +Разрез, по которому отличают одно от другого: **утверждение, которое можно +опровергнуть, открыв другой файл проекта, — пересказ; строка, которая говорит, +какой файл открыть, — ссылка.** Машина этот разрез не проверяет; его смотрит +агент `doc-consistency` из плагина канона, когда тот подключён. + +Два адреса обязательны — `docs/passport.md` и `CLAUDE.md`: предложение пишется до +того, как кто-либо откроет `docs/`, и без них его пишут, не зная ни границы +домена, ни инвариантов. Их отсутствие `docs.py check` называет отказом. + +## Форма сверяется с живым инструментом + +Схема (`spec-driven`) и перечень артефактов (`proposal`, `specs`, `design`, +`tasks`) — **состояние чужого инструмента**, а не наше решение. OpenSpec +переименует артефакт: правила под прежним именем перестанут применяться, конфиг +останется выглядеть написанным, и молчат при этом все три стороны. + +Сторож — сравнение версий, и живёт он пока в `docs.py` плагина канона: + +``` +python3 <канон>/skills/canon/scripts/docs.py openspec-form +``` + +`check` каждым прогоном сравнивает `major.minor` установленного OpenSpec с той +версией, на которой форма сверялась, и при расхождении просит эту команду. Она +ничего не правит — спрашивает инструмент и печатает, что разошлось. **Чинится +расхождение в плагине, а не в проекте.** + +Плагина канона в проекте нет — сторожа тоже нет, и это надо назвать строкой, а не +считать, что форма верна. + +## Кто зовёт этот скилл + +- `av-dev-docs:init` — шагом заведения нового проекта, до первого документа; +- `av-dev-docs:canon` в режиме `adopt` — если на переводимом проекте каталога нет + или `config.yaml` остался примером; +- человек — когда конвейер отказался работать без источника требований. + +Вызов идёт **через пространство имён**, а не путём в дерево плагина. Не +разрешился — плагина конвейера в проекте нет, и тогда OpenSpec заводит человек +командой выше; скажи это строкой, а путь не выдумывай. + +## Чего этот скилл не делает + +- **Не пишет спеки и предложения.** Это `opsx:propose` и пайплайн задачи. +- **Не ведёт документы канона** — их дом плагин `av-dev-docs`, и адреса в + `context` только на них ссылаются. +- **Не чинит расхождение формы с версией OpenSpec в проекте.** Оно чинится в + плагине: константы скрипта, образец здесь, запись в журнал версий канона. diff --git a/av-dev-pipeline/skills/openspec/references/config-skeleton.md b/av-dev-pipeline/skills/openspec/references/config-skeleton.md new file mode 100644 index 0000000..300d046 --- /dev/null +++ b/av-dev-pipeline/skills/openspec/references/config-skeleton.md @@ -0,0 +1,79 @@ +# Образец `openspec/config.yaml` + +Каталог `openspec/` заводится командой — `openspec init --tools claude`, — и она +кладёт `config.yaml` с закомментированным примером внутри. Пример **заменяется +целиком**: нетронутый файл выглядит настроенным, а работает как пустой. + +**Это маршрутизатор, а не второй дом фактов.** Сюда пишут ровно то, что нужно +**в момент порождения артефакта** и чего в этот момент ещё никто не открыл: +язык, правила именования capability, придирки валидатора и **адреса** документов +канона. Пересказ паспорта, инвариантов, конвенций и правил ревью сюда не +переносится: расходится он молча, а замечают это в уже написанном предложении. + +```yaml +schema: spec-driven + +context: | + Language: Russian + Пиши на русском, но: + - Структурные заголовки оставляй на английском: + ## ADDED/MODIFIED/REMOVED Requirements, ### Requirement:, #### Scenario: + - Ключевые слова GIVEN/WHEN/THEN и RFC 2119 (SHALL/MUST/SHOULD) — на английском + - Технические термины, пути и код — на английском + + Имена capabilities: + - Capability — это ПОВЕДЕНИЕ или домен системы, а не пакет кода (совпадение с + именем пакета допустимо, но не критерий). + - Существительное, понятное без знания кода: ingest, parsing, storage, + read-api. НЕ store/httpapi — это реализация. + - Гранулярность по принципу «требования меняются вместе». Дробить, когда в + одной спеке смешиваются разные заботы. Переименовать дёшево (RENAMED + Requirements) — не дроби преждевременно в маленьком проекте. + + RFC 2119 — требование валидатора, не стиль: + - Каждое ### Requirement ОБЯЗАНО содержать литерал SHALL или MUST, иначе + `openspec validate` падает. Поэтому эти слова и WHEN/THEN не русифицируем. + + Что это за проект — читай перед предложением, а не отсюда: + - docs/passport.md — цель, её граница (чем проект НЕ является), потребители, + типовые сценарии, референсы; + - CLAUDE.md — инварианты с severity и семантика гейта; + - docs/architecture.md — устройство; docs/security.md — периметр; + docs/adr/ — почему решено так; docs/research/ — что уже измерено. + Пересказа этих документов здесь нет намеренно: второй дом факта расходится с + первым молча, и заметно это становится в предложении, которое уже написано. + + Ревью: правило выбора метки и состав проходов здесь не пересказываем — их дом + скилл av-dev-pipeline:review-pipeline, проектная настройка — docs/review.md. + + Конвенции кода: механизированное проверяет гейт, прозой остаётся + docs/conventions/. Ни состав шагов гейта, ни перечень конвенций здесь не + пересказываем: и то и другое растёт по ходу задач. + + Развилка или блокер — сперва prior art. Готовые решения смотрим в референсах + паспорта, отвергаем — с названной причиной, и причина идёт в design.md этого + же изменения. + +rules: + proposal: + - Capabilities называй по поведению или домену системы, не по пакету кода + specs: + # Кавычки обязательны: без них YAML обрежет строку на первом '#'. + - "Каждое ### Requirement обязано содержать SHALL или MUST (иначе валидация падает)" + - "Сценарий — ровно #### (четыре решётки); три или список молча теряются" + - "SHALL/MUST должно стоять в ПЕРВОМ абзаце требования: валидатор смотрит только его" + - "Заголовки и WHEN/THEN/GIVEN — на английском, остальной текст на русском" +``` + +**Четыре правила для `specs` сняты отказами валидатора, а не выведены из +документации** — потому и записаны дословно: без них каждое второе предложение +узнаёт их падением `openspec validate --strict`. Блок `context` проект +дополняет своим (стек, разведка, особенности домена), но **адреса паспорта и +`CLAUDE.md` обязательны** — их отсутствие `docs.py check` называет отказом. + +**Ключи под `rules:` — имена артефактов схемы**, а не свободные слова: +`proposal`, `specs`, `design`, `tasks`. Правило под чужим именем не применяется +и об этом не сообщает, поэтому `rules.spec` вместо `rules.specs` даёт конфиг, +выглядящий написанным и не работающий; `docs.py check` такой ключ называет. +Перечень артефактов задаёт OpenSpec, а не канон, — за его актуальностью следит +`docs.py openspec-form`. diff --git a/av-dev-pipeline/skills/review-pipeline/SKILL.md b/av-dev-pipeline/skills/review-pipeline/SKILL.md index 1f039bc..831391c 100644 --- a/av-dev-pipeline/skills/review-pipeline/SKILL.md +++ b/av-dev-pipeline/skills/review-pipeline/SKILL.md @@ -52,8 +52,9 @@ description: "Конвейер ревью изменения, устроенны требований. **Проект без OpenSpec этим конвейером не проверяется** — подключай OpenSpec, а не понижай прогон: ветка деградации здесь не пишется, потому что непроверенная ветка деградации хуже честного отказа. Заводить руками не надо: - `av-dev-docs:init` делает `openspec init` на новом проекте, `canon adopt` — на - переводимом, и оба кладут `openspec/config.yaml` канонической формы. + этим владеет скилл `av-dev-pipeline:openspec` — он заводит каталог и заменяет + пример в `config.yaml` настройкой. Его же зовут `av-dev-docs:init` на новом + проекте и `canon adopt` на переводимом. - **Документы канона** — см. следующий раздел. - **Проектные копии этих скиллов и агентов удаляются при установке.** Если в проекте уже лежат свои `.claude/skills/review-pipeline`,