Решение 51 отдало OpenSpec конвейеру и честно оставило хвост: проверка формы и сторож версии остались в docs.py, потому что своего скрипта у пайплайна не было ни одного. Хвост не косметический — это ровно то состояние, против которого написан весь канон: у файла два владельца, один заводит, другой проверяет, и разойтись они могут молча. 252 строки переехали в av-dev-pipeline/skills/openspec/scripts/openspec.py: пять проверок формы, сторож версии, сверка слепка с живым инструментом. Команды две — check --dir <корень> и form; коды выхода общие со всеми скриптами av-dev. Из docs.py удалены константы OPENSPEC_*, check_openspec, openspec_cli, check_openspec_fresh, rules_keys и подкоманда openspec-form; про config.yaml он больше не говорит ничего, кроме строки границы механизируемого — что форму смотрит чужой скрипт. openspec/specs/ он по-прежнему знает: это дом темы requirements и часть карты тем. Переезд оплатился сразу, и не тем, чего ждали. Прежняя проверка требовала, чтобы context называл docs/passport.md и CLAUDE.md, безусловно — то есть на проекте без канона документов требовала ссылку на несуществующий файл. Пока код жил в скрипте канона, допущение «канон есть» было незаметным: скрипт канона запускают там, где канон есть. В скрипте конвейера то же допущение стало видно на первом прогоне. Теперь адрес требуется только к существующему документу, отсутствие идёт строкой «не проверялось» с названной ценой — без канона конвейер работает вслепую. Заодно починен хвост от раскола плагинов: pyrefly project-includes в pyproject всё ещё указывали на av-dev-pm. Линтер на явных файлах работал, а на обходе проекта не проверял ничего. Проверено пятью случаями: нет openspec (1), годный конфиг (0), опечатка в имени артефакта под rules (1), проект без канона (0, с двумя строками «не проверялось»), неизвестная команда (2). Канон повышен до версии 10. Главное в записи — тихая потеря: форму раньше проверял docs.py check заодно, теперь нужен отдельный шаг openspec.py check в гейте, иначе незаменённый пример в config.yaml перестанет ловиться. Решение — 52. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
81 lines
6.4 KiB
Markdown
81 lines
6.4 KiB
Markdown
# Образец `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` обязательны** — отсутствие адреса к существующему документу
|
|
`openspec.py check` называет отказом.
|
|
|
|
**Ключи под `rules:` — имена артефактов схемы**, а не свободные слова:
|
|
`proposal`, `specs`, `design`, `tasks`. Правило под чужим именем не применяется
|
|
и об этом не сообщает, поэтому `rules.spec` вместо `rules.specs` даёт конфиг,
|
|
выглядящий написанным и не работающий; `openspec.py check` такой ключ называет.
|
|
Перечень артефактов задаёт OpenSpec, а не мы, — за его актуальностью следит
|
|
`openspec.py form`.
|