Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
441469d78d
|
||
|
|
423f9798ef
|
||
|
|
7d559e60ec
|
||
|
|
a00e132f29
|
||
|
|
95c9499f06
|
||
|
|
6b162c421d
|
||
|
|
de12a4d8a3
|
@@ -6,19 +6,9 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "av-dev-docs",
|
"name": "av-dev",
|
||||||
"source": "./av-dev-docs",
|
"source": "./av-dev",
|
||||||
"description": "Документация проекта: канон раскладки (CLAUDE.md плюс docs/ — паспорт, архитектура, схема БД, безопасность, конвенции, разведка, ADR, журнал ревью), роли документов и правило единственного дома. Три операции одной машиной сравнения — check, adopt, upgrade — со скриптом docs.py; заведение нового проекта интервью по брифу; ведение содержимого по ходу разработки: синк после сделанной задачи, ADR промоутом из архивного design.md, записка разведки, запись дефекта в журнал ревью. Смысловое, чего скрипт не видит, судит скилл healthcheck двумя агентами разом — doc-consistency (документы между собой и с openspec) и doc-code-drift (факты против кода); язык документов вычитывает агент doc-wording. Задач не ведёт — это плагин av-dev-tasks, и он опционален."
|
"description": "Личный процесс разработки одним плагином: документы проекта, учёт работ и работа по задачам. Документы — канон раскладки (CLAUDE.md плюс docs/: паспорт, архитектура, схема БД, безопасность, конвенции, разведка, ADR, журнал ревью), три операции одной машиной сравнения в doc-canon (check, adopt, upgrade) со скриптом docs.py, заведение нового проекта интервью в doc-init, ведение содержимого по ходу разработки в doc-sync, суд о смысловом здоровье в doc-healthcheck двумя агентами разом. Учёт — задачи и цели каталогом markdown-файлов в task-track, у записи тип (goal, feature, fix, chore, research), и тип решает её схему; приоритет расставляет интерактивный task-groom порядком строк. Работа — одна задача от постановки до закрытия скиллом code-resolve: точка входа одна, сценария три (решение циклом SDD с чекпоинтом, разведка без кода, обслуживание без change), и выбирает сценарий сам скилл, прочитав постановку; плюс конвейер ревью code-review с детерминированным гейтом, сверкой со спеками и обязательным триажем, плюс code-openspec, который заводит и проверяет openspec/. Версия раскладки и настройки живут в .av-dev.toml в корне репозитория. Части включаются следом в проекте, а не установкой: нет docs/ — документы не ведутся, нет каталога задач — учёт остаётся владельцу, нет openspec/ — его заводит сценарий решения. Коммиты — отдельный плагин av-dev-git."
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "av-dev-tasks",
|
|
||||||
"source": "./av-dev-tasks",
|
|
||||||
"description": "Задачи и цели каталогом markdown-файлов: одна запись — файл в items/ плюс строка ровно в одном индексе, у записи тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение из диалога с фильтром и дедупом, разбор находок ревью в задачи, декомпозиция на независимо полезные части, гигиена полей, согласованность индексов скриптом tasks.py. Приоритет — явный порядок строк в беклоге, и расставляет его скилл groom: интерактивный разбор на два вопроса, что сейчас самое важное и что перестало быть важным. Готовность записи к работе проверяет команда ready. Записи вычитывают два прохода: task-form (форма записи) и task-wording (язык). Канон документов ведёт плагин av-dev-docs, он опционален. Задач не выполняет — этим занимается конвейер проекта."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "av-dev-code",
|
|
||||||
"source": "./av-dev-code",
|
|
||||||
"description": "Одна задача от постановки до закрытия скиллом resolve: точка входа одна, сценария три, и выбирает сценарий сам скилл, прочитав постановку. Сценарий решения — полный цикл Spec Driven Development с плановым чекпоинтом после ревью дизайна: объяснение человеческим языком, повод скорректировать ход решения. Сценарий разведки (тип research, сырая идея, мутная постановка) кода не пишет и change не заводит: его чекпоинт вариантов стоит до первого написанного требования, а исход уезжает в документы канона и в задачи; выбранный способ реализуется следующим прогоном, который запускает человек. Сценарий обслуживания (тип chore: тулчейн, зависимости, сборка, гит-хуки, перенос, чистка) change не заводит и планового стопа не имеет: спека там не меняется по построению, поэтому цикл SDD остаётся без входа, а ревью идёт фиксированным планом без метки. Смена сценария по ходу — событие с названным исходом, а не тихий поворот. Плюс конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом и обязательным триажем. OpenSpec нужен сценарию решения, и плагин сам его заводит скиллом openspec; разведка обходится без него. Задача принимается и обычным текстом. Плагины av-dev-docs и av-dev-tasks опциональны: первый даёт документы канона, из которых проходы читают проектную конкретику, второй — учёт задач; без них прогон деградирует поразрядно и называет это строкой."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "av-dev-git",
|
"name": "av-dev-git",
|
||||||
|
|||||||
@@ -3919,3 +3919,67 @@ change по нему не будет никогда, — и такое реше
|
|||||||
когда весь материал для неё у исполнителя. Стоп обязан принести названный
|
когда весь материал для неё у исполнителя. Стоп обязан принести названный
|
||||||
тип, объяснение и закрытый список решений — иначе выбор делается вслепую или
|
тип, объяснение и закрытый список решений — иначе выбор делается вслепую или
|
||||||
не делается вовсе, и работа доезжает до коммита не тем процессом.
|
не делается вовсе, и работа доезжает до коммита не тем процессом.
|
||||||
|
|
||||||
|
|
||||||
|
## 64. Три плагина слились в один: раскол платили, а не пользовались (2026-08-13)
|
||||||
|
|
||||||
|
Плагинов было три — `av-dev-docs`, `av-dev-tasks`, `av-dev-code`, — и разрез
|
||||||
|
между ними шёл по признаку «ставится порознь» (решение 51). Признак был выбран
|
||||||
|
верно, но **посылка под ним не проверялась**: за всё время подмножество не
|
||||||
|
понадобилось ни разу, а платился раскол постоянно.
|
||||||
|
|
||||||
|
Цена измерена, а не оценена: шестьдесят с лишним вызовов между скиллами при цикле
|
||||||
|
зависимостей `docs → code → docs`, язык проектных текстов четырьмя помеченными
|
||||||
|
копиями по 213 строк, словарь сопровождения двумя, правило границы семью,
|
||||||
|
дюжина веток «плагина нет» — и `copies.py`, заведённый ровно затем, чтобы это
|
||||||
|
не разъезжалось молча.
|
||||||
|
|
||||||
|
**Довод «а вдруг понадобится» снят наблюдением владельца, а не спором.** Ждали
|
||||||
|
случая «документы и задачи без OpenSpec» — например, ansible-репозиторий. Он
|
||||||
|
уже покрыт: сценарий обслуживания в `resolve` OpenSpec не требует по
|
||||||
|
построению, а `docs.py` считает отсутствие `openspec/` неприменимостью, а не
|
||||||
|
отказом. То есть режим, ради которого держали раскол, работает и в слитом
|
||||||
|
плагине.
|
||||||
|
|
||||||
|
**Слияние оказалось дешевле, чем выглядело, потому что граница была сделана
|
||||||
|
правильно.** Присутствие соседа узнавалось **следом в проекте**
|
||||||
|
(`.docs.json`, `.tasks.json`, `openspec/config.yaml`), а не перечнем
|
||||||
|
установленных плагинов. Значит мягкость поведения держалась на состоянии
|
||||||
|
проекта и пережила слияние без единой правки логики: сменилась упаковка, а не
|
||||||
|
механика. Дом правила переехал из `plugin-boundary.md` в `absence.md` и стал
|
||||||
|
говорить о том, чем он и был на деле, — о частях раскладки, которых может не
|
||||||
|
быть.
|
||||||
|
|
||||||
|
**Версия стала одна и начинается с 1.** Две версии — канон 14 и формат задач 1 —
|
||||||
|
двигались порознь, потому что порознь ставились плагины; с одним плагином два
|
||||||
|
числа означали бы только вопрос, по какому журналу повышать. Прежние журналы
|
||||||
|
закрыты и не переписаны: адрес, верный на день записи, остаётся свидетельством.
|
||||||
|
Служебный файл один, `.av-dev.toml` в корне репозитория, и **формат выбран ради
|
||||||
|
комментариев** — файл живёт в чужом репозитории, и назначение числа должно
|
||||||
|
читаться из него самого, а не из документации плагина. Отсюда правило записи:
|
||||||
|
скрипт правит строку, а не переписывает файл.
|
||||||
|
|
||||||
|
**Возможность расколоть обратно не потеряна.** Понадобится инфраструктурный
|
||||||
|
плагин — раскол будет переименованием пространства имён, а не переделкой:
|
||||||
|
граница по-прежнему держится на следе в проекте. Платить за эту возможность
|
||||||
|
копиями сегодня незачем.
|
||||||
|
|
||||||
|
### Что из этого следует
|
||||||
|
|
||||||
|
218. **Разрез, оправданный сценарием, обязан этот сценарий однажды увидеть.**
|
||||||
|
«Ставится порознь» — проверяемое утверждение, и проверяется оно не
|
||||||
|
рассуждением, а тем, поставил ли кто-нибудь половину. Пока не поставил,
|
||||||
|
разрез оплачивается копиями за случай, которого нет.
|
||||||
|
219. **Механика, привязанная к состоянию проекта, переживает перестановку
|
||||||
|
плагинов; привязанная к их составу — нет.** Это и есть практическая разница
|
||||||
|
между «узнаём следом» и «узнаём перечнем», и обнаруживается она только на
|
||||||
|
слиянии или расколе.
|
||||||
|
220. **Копия дословного текста — плата за неразрешимый путь, а не за важность
|
||||||
|
правила.** Путь разрешился — копия становится вторым домом без причины.
|
||||||
|
Остаётся она там, где текст обязан лежать **внутри промпта**: устав агента,
|
||||||
|
`SKILL.md` скилла и скелет, уезжающий в проект. Разрез проверяемый: файл,
|
||||||
|
который модель получает целиком, против файла, за которым она идёт
|
||||||
|
отдельным чтением.
|
||||||
|
221. **Формат служебного файла выбирается по тому, кто его читает.** Читает
|
||||||
|
человек в чужом репозитории через полгода — значит комментарии, значит
|
||||||
|
TOML, значит построчная правка вместо перезаписи.
|
||||||
|
|||||||
@@ -9,83 +9,98 @@
|
|||||||
|
|
||||||
## Плагины
|
## Плагины
|
||||||
|
|
||||||
- **av-dev-docs** — документация проекта. Владеет `docs/` и `CLAUDE.md`.
|
Плагина два: **av-dev** — весь процесс, и **av-dev-git** — сообщения коммитов.
|
||||||
- `init` — новый проект: интервью по свободному описанию замысла → первичная
|
Второй работает в любом репозитории, в том числе не приведённом к канону. До 13 августа 2026 процесс жил тремя плагинами
|
||||||
документация;
|
(`av-dev-docs`, `av-dev-tasks`, `av-dev-code`); раскол делался под раздельную
|
||||||
- `canon` — привести проект к канону документов: `check` / `adopt` /
|
установку, она не понадобилась ни разу, и плагины слились — тема 64
|
||||||
`upgrade`, плюс скрипт `docs.py`. Там же лежит копия языка проектных
|
[DECISIONS.md](DECISIONS.md).
|
||||||
текстов — информационный стиль, англицизмы, жаргон; дом у него общий,
|
|
||||||
`shared/language.md`;
|
|
||||||
- `healthcheck` — здоровье документации **судом, а не машиной**: не разошлись
|
|
||||||
ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом —
|
|
||||||
`doc-consistency` (документы между собой и с openspec) и `doc-code-drift`
|
|
||||||
(факты против кода) — и разбирает урожай порциями. Дорого, поэтому не на
|
|
||||||
каждой задаче. Язык документов вычитывает отдельный агент `doc-wording`, и
|
|
||||||
зовут его не отсюда, а те, кто только что писал текст: `docs`, `init` и
|
|
||||||
`canon`;
|
|
||||||
- `docs` — содержимое канона по ходу разработки: ADR из архивного
|
|
||||||
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
|
||||||
архитектуры.
|
|
||||||
- **av-dev-tasks** — учёт работ. Владеет каталогом задач.
|
|
||||||
- `tasks` — задачи и цели каталогом markdown-файлов, у каждой записи тип
|
|
||||||
(`goal`, `feature`, `fix`, `chore`, `research`), и тип задаёт её схему;
|
|
||||||
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
|
||||||
`task-wording` (язык записей);
|
|
||||||
- `groom` — груминг беклога: что сейчас самое важное и что перестало быть
|
|
||||||
важным. Ответ записывается **порядком строк** — приоритет это свойство
|
|
||||||
очереди, и живёт он в индексе. Интерактивный: разбирает вопросы пачкой,
|
|
||||||
переоценивает порциями по 5–8, расставляет верх очереди с доводом на
|
|
||||||
каждое движение.
|
|
||||||
- **av-dev-code** — работа по задачам: разведка, решение и проверка сделанного.
|
|
||||||
Владеет `openspec/`. **Требует OpenSpec и сам его заводит** — кроме сценариев
|
|
||||||
разведки и обслуживания, которым он не нужен.
|
|
||||||
- `openspec` — завести, настроить и **проверить** `openspec/` в проекте:
|
|
||||||
`openspec init`, замена примера в `config.yaml` настройкой канонической
|
|
||||||
формы, скрипт `openspec.py` (форма файла + сверка слепка с живой версией
|
|
||||||
инструмента). Каталог принадлежит конвейеру, а не канону: без конвейера он
|
|
||||||
проекту не нужен, и `docs.py` о нём молчит;
|
|
||||||
- `resolve` — одна задача от постановки до закрытия. **Точка входа одна, а
|
|
||||||
сценария три, и выбирает сценарий сам скилл, прочитав постановку:**
|
|
||||||
классифицировать задачу до вызова человек всё равно не может — «есть ли
|
|
||||||
очевидный способ решения» и «меняется ли спека» видно после чтения записи.
|
|
||||||
**Решение** идёт циклом SDD с чекпоинтом после ревью дизайна: объяснение
|
|
||||||
человеческим языком, повод скорректировать ход.
|
|
||||||
**Обслуживание** (тип `chore`: тулчейн и сборка, зависимости, гит-хуки,
|
|
||||||
перенос, чистка) change не заводит и планового стопа не имеет вовсе: дельта-спек
|
|
||||||
у него нет **по построению**, то есть цикл SDD здесь не урезан, а остаётся без
|
|
||||||
входа. Ревью идёт фиксированным планом без метки и без разметчика — `autotests`
|
|
||||||
и `operations`, плюс `conventions` с техническим разбором, если дифф трогает
|
|
||||||
код; главный шаг сценария — синк документации, потому что обслуживание чаще
|
|
||||||
прочих двигает как раз те факты, которые сверяются с кодом. Правка гейта
|
|
||||||
сверяется по составу проверок, а не по цвету. Нашлась дельта-спека — задача
|
|
||||||
**оказалась шире своего типа**: работа останавливается, тип называется
|
|
||||||
(`fix` или `feature`), человек получает объяснение простым языком и два
|
|
||||||
решения — переформулировать запись и решать её процессом того типа следующим
|
|
||||||
прогоном либо прекратить; «доделать как обслуживание» решением не является.
|
|
||||||
**Разведка** (тип `research`, сырая идея, мутная постановка) кода не пишет
|
|
||||||
вовсе: её чекпоинт — варианты, 2–4 способа решить с ценой каждого, — стоит до
|
|
||||||
первого написанного требования, а исход уезжает в документы канона и в
|
|
||||||
задачи. Обе пачки — документы и записи — **вычитываются перед коммитом**
|
|
||||||
своими проходами: `doc-wording` по документам, `task-form` и `task-wording`
|
|
||||||
по записям. Выбранный способ реализуется **следующим прогоном**, и запускает его
|
|
||||||
человек: смена сценария по ходу — событие с названным исходом, а не тихий
|
|
||||||
поворот. Все три сценария лежат справочниками и одинаково —
|
|
||||||
`references/solve.md`, `references/maintain.md` и `references/research.md`; в
|
|
||||||
самом скилле только вход, развилка и правила, не зависящие от сценария.
|
|
||||||
OpenSpec нужен решению, разведке и обслуживанию — нет;
|
|
||||||
- `review` — конвейер ревью **по темам**: документ проекта либо
|
|
||||||
заводит тему проверки, либо питает чужую тему источником, либо процессный и в
|
|
||||||
ревью не читается вовсе. Разметка идёт **один раз на задачу**, сразу после
|
|
||||||
`propose`: агент `review-scope` меряет изменение по двум осям — размер и
|
|
||||||
сложность — и берёт метку как максимум по ним. Одна метка правит **обе**
|
|
||||||
стадии ревью: дизайна (`small` — только сверка спек; `medium` — плюс
|
|
||||||
рубрика; `large` — плюс архитектурный проход) и кода (`small` — гейт, спеки,
|
|
||||||
код, триаж; `medium` — плюс приёмник тем; `large` — плюс доказательство:
|
|
||||||
запуск, замер, построенный путь, 5–10% задач). Десять агентов-проходов.
|
|
||||||
- **av-dev-git** — `commit`: сообщения в личном стиле.
|
|
||||||
|
|
||||||
Соглашение об именах: имя **плагина** длинное с префиксом `av-dev-`, имена
|
Имя **скилла** несёт префикс прежнего плагина: `doc-`, `task-`, `code-`. Вызов
|
||||||
**скилов** внутри — короткие. Вызов выходит вида `/av-dev-<плагин>:<скилл>`.
|
выходит вида `/av-dev:<скилл>`.
|
||||||
|
|
||||||
|
### av-dev — документы, учёт, работа
|
||||||
|
|
||||||
|
**Документы проекта.** Владеют `docs/` и `CLAUDE.md`.
|
||||||
|
|
||||||
|
- `doc-init` — новый проект: интервью по свободному описанию замысла →
|
||||||
|
первичная документация;
|
||||||
|
- `doc-canon` — привести проект к канону документов: `check` / `adopt` /
|
||||||
|
`upgrade`, плюс скрипт `docs.py`. Он же ведёт журнал версий раскладки —
|
||||||
|
общий, и на документы, и на каталог задач;
|
||||||
|
- `doc-healthcheck` — здоровье документации **судом, а не машиной**: не
|
||||||
|
разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон
|
||||||
|
разом — `doc-consistency` (документы между собой и с openspec) и
|
||||||
|
`doc-code-drift` (факты против кода) — и разбирает урожай порциями. Дорого,
|
||||||
|
поэтому не на каждой задаче. Язык документов вычитывает отдельный агент
|
||||||
|
`doc-wording`, и зовут его не отсюда, а те, кто только что писал текст:
|
||||||
|
`doc-sync`, `doc-init` и `doc-canon`;
|
||||||
|
- `doc-sync` — содержимое канона по ходу разработки: ADR из архивного
|
||||||
|
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
||||||
|
архитектуры.
|
||||||
|
|
||||||
|
**Учёт работ.** Владеет каталогом задач.
|
||||||
|
|
||||||
|
- `task-track` — задачи и цели каталогом markdown-файлов, у каждой записи тип
|
||||||
|
(`goal`, `feature`, `fix`, `chore`, `research`), и тип задаёт её схему;
|
||||||
|
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
||||||
|
`task-wording` (язык записей);
|
||||||
|
- `task-groom` — груминг беклога: что сейчас самое важное и что перестало быть
|
||||||
|
важным. Ответ записывается **порядком строк** — приоритет это свойство
|
||||||
|
очереди, и живёт он в индексе. Интерактивный: разбирает вопросы пачкой,
|
||||||
|
переоценивает порциями по 5–8, расставляет верх очереди с доводом на каждое
|
||||||
|
движение.
|
||||||
|
|
||||||
|
**Работа по задачам.** Владеет `openspec/`. **Сценарий решения требует OpenSpec
|
||||||
|
и заводит его сам** — разведке и обслуживанию он не нужен.
|
||||||
|
|
||||||
|
- `code-openspec` — завести, настроить и **проверить** `openspec/` в проекте:
|
||||||
|
`openspec init`, замена примера в `config.yaml` настройкой канонической формы,
|
||||||
|
скрипт `openspec.py` (форма файла + сверка слепка с живой версией
|
||||||
|
инструмента). Каталог принадлежит конвейеру, а не канону: без конвейера он
|
||||||
|
проекту не нужен, и `docs.py` о нём молчит;
|
||||||
|
- `code-resolve` — одна задача от постановки до закрытия. **Точка входа одна, а
|
||||||
|
сценария три, и выбирает сценарий сам скилл, прочитав постановку:**
|
||||||
|
классифицировать задачу до вызова человек всё равно не может — «есть ли
|
||||||
|
очевидный способ решения» и «меняется ли спека» видно после чтения записи.
|
||||||
|
**Решение** идёт циклом SDD с чекпоинтом после ревью дизайна: объяснение
|
||||||
|
человеческим языком, повод скорректировать ход.
|
||||||
|
**Обслуживание** (тип `chore`: тулчейн и сборка, зависимости, гит-хуки,
|
||||||
|
перенос, чистка) change не заводит и планового стопа не имеет вовсе:
|
||||||
|
дельта-спек у него нет **по построению**, то есть цикл SDD здесь не урезан, а
|
||||||
|
остаётся без входа. Ревью идёт фиксированным планом без метки и без
|
||||||
|
разметчика — `autotests` и `operations`, плюс `conventions` с техническим
|
||||||
|
разбором, если дифф трогает код; главный шаг сценария — синк документации,
|
||||||
|
потому что обслуживание чаще прочих двигает как раз те факты, которые
|
||||||
|
сверяются с кодом. Правка гейта сверяется по составу проверок, а не по цвету.
|
||||||
|
Нашлась дельта-спека — задача **оказалась шире своего типа**: работа
|
||||||
|
останавливается, тип называется (`fix` или `feature`), человек получает
|
||||||
|
объяснение простым языком и два решения — переформулировать запись и решать её
|
||||||
|
процессом того типа следующим прогоном либо прекратить; «доделать как
|
||||||
|
обслуживание» решением не является.
|
||||||
|
**Разведка** (тип `research`, сырая идея, мутная постановка) кода не пишет
|
||||||
|
вовсе: её чекпоинт — варианты, 2–4 способа решить с ценой каждого, — стоит до
|
||||||
|
первого написанного требования, а исход уезжает в документы канона и в задачи.
|
||||||
|
Обе пачки — документы и записи — **вычитываются перед коммитом** своими
|
||||||
|
проходами: `doc-wording` по документам, `task-form` и `task-wording` по
|
||||||
|
записям. Выбранный способ реализуется **следующим прогоном**, и запускает его
|
||||||
|
человек: смена сценария по ходу — событие с названным исходом, а не тихий
|
||||||
|
поворот. Все три сценария лежат справочниками и одинаково —
|
||||||
|
`references/solve.md`, `references/maintain.md` и `references/research.md`; в
|
||||||
|
самом скилле только вход, развилка и правила, не зависящие от сценария;
|
||||||
|
- `code-review` — конвейер ревью **по темам**: документ проекта либо заводит
|
||||||
|
тему проверки, либо питает чужую тему источником, либо процессный и в ревью не
|
||||||
|
читается вовсе. Разметка идёт **один раз на задачу**, сразу после `propose`:
|
||||||
|
агент `review-scope` меряет изменение по двум осям — размер и сложность — и
|
||||||
|
берёт метку как максимум по ним. Одна метка правит **обе** стадии ревью:
|
||||||
|
дизайна (`small` — только сверка спек; `medium` — плюс рубрика; `large` — плюс
|
||||||
|
архитектурный проход) и кода (`small` — гейт, спеки, код, триаж; `medium` —
|
||||||
|
плюс приёмник тем; `large` — плюс доказательство: запуск, замер, построенный
|
||||||
|
путь, 5–10% задач). Десять агентов-проходов.
|
||||||
|
|
||||||
|
### av-dev-git
|
||||||
|
|
||||||
|
`commit` — сообщения в личном стиле. Отдельным плагином потому, что нужен и в
|
||||||
|
репозитории, который к канону не приведён и никогда не будет.
|
||||||
|
|
||||||
Кто кого зовёт. Сплошная стрелка — вызов скилла через пространство имён, не
|
Кто кого зовёт. Сплошная стрелка — вызов скилла через пространство имён, не
|
||||||
импорт; пунктир — совет позвать, а не вызов. Агенты-проходы в графе не показаны:
|
импорт; пунктир — совет позвать, а не вызов. Агенты-проходы в графе не показаны:
|
||||||
@@ -93,21 +108,23 @@
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
subgraph pipe["av-dev-code — исполнение; сценарий решения требует OpenSpec"]
|
subgraph avdev["av-dev — один плагин, девять скиллов"]
|
||||||
direction LR
|
subgraph pipe["работа по задачам; сценарий решения требует OpenSpec"]
|
||||||
tp["resolve<br/>3 сценария: разведка,<br/>решение, обслуживание"] --> rp["review<br/>10 агентов-проходов"]
|
direction LR
|
||||||
osp["openspec<br/>заводит и проверяет openspec/"]
|
tp["code-resolve<br/>3 сценария: разведка,<br/>решение, обслуживание"] --> rp["code-review<br/>10 агентов-проходов"]
|
||||||
end
|
osp["code-openspec<br/>заводит и проверяет openspec/"]
|
||||||
subgraph docsp["av-dev-docs — документация, владеет docs/"]
|
end
|
||||||
direction LR
|
subgraph docsp["документы, владеют docs/"]
|
||||||
init["init"]
|
direction LR
|
||||||
canon["canon"]
|
init["doc-init"]
|
||||||
docs["docs"]
|
canon["doc-canon"]
|
||||||
hc["healthcheck"]
|
docs["doc-sync"]
|
||||||
end
|
hc["doc-healthcheck"]
|
||||||
subgraph tasksp["av-dev-tasks — учёт работ"]
|
end
|
||||||
direction LR
|
subgraph tasksp["учёт работ"]
|
||||||
groom["groom"] --> tasks["tasks"]
|
direction LR
|
||||||
|
groom["task-groom"] --> tasks["task-track"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
init --> tasks
|
init --> tasks
|
||||||
init --> osp
|
init --> osp
|
||||||
@@ -127,18 +144,17 @@ flowchart TB
|
|||||||
tp --> tasks
|
tp --> tasks
|
||||||
```
|
```
|
||||||
|
|
||||||
Зависимости **взаимные, но каждая мягкая**. `av-dev-code` зовёт обоих соседей;
|
**Скиллы зовут друг друга полным именем, а не по пути.** Внутри одного плагина
|
||||||
обратные вызовы тоже есть — `av-dev-docs:init` и `av-dev-docs:canon` заводят
|
путь бы разрешился, но короткое имя разрешается в устаревшую проектную копию из
|
||||||
OpenSpec скиллом `av-dev-code:openspec`, `av-dev-docs:docs` берёт у
|
`.claude/skills/` — молча и без признаков подмены.
|
||||||
`av-dev-code:review` форму записи журнала дефектов и процедуру промоута,
|
|
||||||
`av-dev-docs:canon` и `av-dev-docs:healthcheck` зовут `av-dev-tasks:tasks`.
|
**Отсутствовать может не плагин, а часть раскладки проекта**: `.av-dev.toml`,
|
||||||
**Мягкая** значит, что у любого вызова есть ветка «не разрешился»: соседа в
|
`docs/`, каталог задач, `openspec/`. Тогда вызывающий называет строкой, чего теперь не делает
|
||||||
проекте нет — вызывающий называет строкой, чего теперь не делает никто, и работу
|
никто, и работу не останавливает. Правило целиком —
|
||||||
не останавливает. Как именно зовут соседа и что делают, когда вызов не
|
[shared/absence.md](av-dev/shared/absence.md): оно нужно почти каждому скиллу, и
|
||||||
разрешился, — `shared/plugin-boundary.md`: правило нужно большинству скиллов, и
|
ни один им не владеет. Там же, в `shared/`, живут язык проектных текстов и
|
||||||
ни один плагин им не владеет. То, что нужно нескольким дословно — граница
|
словарь сопровождения; скиллы читают их по ссылке, а дословной копией они
|
||||||
плагинов, язык проектных текстов, словарь сопровождения, — живёт домом в
|
уезжают только в уставы вычитки — туда, где текст обязан лежать внутри промпта.
|
||||||
`shared/` и уезжает в каждый плагин помеченной копией.
|
|
||||||
|
|
||||||
## Канон документов проекта
|
## Канон документов проекта
|
||||||
|
|
||||||
@@ -147,7 +163,7 @@ OpenSpec скиллом `av-dev-code:openspec`, `av-dev-docs:docs` берёт у
|
|||||||
`docs/` (паспорт, архитектура, база, безопасность, конвенции, разведка, ADR,
|
`docs/` (паспорт, архитектура, база, безопасность, конвенции, разведка, ADR,
|
||||||
ревью, задачи) и `openspec/` — **раскладка целиком, роли документов и правило
|
ревью, задачи) и `openspec/` — **раскладка целиком, роли документов и правило
|
||||||
единственного дома живут одним домом**:
|
единственного дома живут одним домом**:
|
||||||
[canon.md](av-dev-docs/skills/canon/references/canon.md). Здесь она не
|
[canon.md](av-dev/skills/doc-canon/references/canon.md). Здесь она не
|
||||||
пересказывается: копия перечня путей уже расходилась с домом, и как раз в
|
пересказывается: копия перечня путей уже расходилась с домом, и как раз в
|
||||||
обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает
|
обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает
|
||||||
нарушением.
|
нарушением.
|
||||||
@@ -163,20 +179,23 @@ OpenSpec скиллом `av-dev-code:openspec`, `av-dev-docs:docs` берёт у
|
|||||||
|
|
||||||
Отдельного файла-брифа при этом нет — проходы читают документы напрямую; карта
|
Отдельного файла-брифа при этом нет — проходы читают документы напрямую; карта
|
||||||
«тема → её дом → что оттуда берётся» —
|
«тема → её дом → что оттуда берётся» —
|
||||||
[project-facts.md](av-dev-code/skills/review/references/project-facts.md).
|
[project-facts.md](av-dev/skills/code-review/references/project-facts.md).
|
||||||
|
|
||||||
Прийти в старый проект и перевести его на канон — `/av-dev-docs:canon`. Канон
|
Прийти в старый проект и перевести его на канон — `/av-dev:doc-canon`.
|
||||||
версионируется, и проекты повышаются по [журналу
|
Раскладка версионируется, и проекты повышаются по [журналу
|
||||||
версий](av-dev-docs/skills/canon/references/changelog.md); версия проекта живёт в
|
версий](av-dev/skills/doc-canon/references/changelog.md).
|
||||||
`docs/.docs.json`.
|
|
||||||
|
|
||||||
**Версий две, и они независимы.** У каталога задач своя — ключ `tasks` в
|
**Версия одна, и живёт она в `.av-dev.toml` в корне репозитория** — вместе с
|
||||||
`<каталог задач>/.tasks.json`, свой [журнал
|
настройками: `[docs] migrations` и секция `[tasks]`, которая говорит, где лежит
|
||||||
версий](av-dev-tasks/skills/tasks/references/changelog.md) и своё повышение
|
каталог задач и как названы его части. Версий было две, пока плагинов было три и проект мог
|
||||||
скиллом `/av-dev-tasks:tasks`. Плагины ставятся порознь: у проекта, взявшего учёт
|
взять учёт работ без канона документов; теперь плагин один, и второе число
|
||||||
работ без канона документов, `docs/` нет вовсе, и общее число оказалось бы домом,
|
означало бы только вопрос, по какому журналу повышать. Прежние
|
||||||
которого у половины проектов не существует. Имя служебного файла при этом
|
`docs/.docs.json` и `<каталог задач>/.tasks.json` не читаются: увидев их,
|
||||||
называет владельца — `.docs.json`, `.tasks.json`, `openspec/config.yaml`.
|
`docs.py check` называет прежнюю раскладку и зовёт `upgrade` — запись 1
|
||||||
|
журнала. Формат TOML взят ради комментариев: файл лежит в репозитории проекта,
|
||||||
|
и назначение числа читают из него самого, а скрипты правят строку, а не
|
||||||
|
переписывают файл. Имя служебного файла по-прежнему называет владельца —
|
||||||
|
`.av-dev.toml`, `openspec/config.yaml`.
|
||||||
|
|
||||||
## Подключение
|
## Подключение
|
||||||
|
|
||||||
@@ -191,9 +210,7 @@ cd /path/to/project
|
|||||||
claude plugin marketplace add https://git.vakhrushev.me/av/dev-skills.git --scope project
|
claude plugin marketplace add https://git.vakhrushev.me/av/dev-skills.git --scope project
|
||||||
|
|
||||||
# плагины: scope обязателен, умолчание у команды — user, а нам нужен project
|
# плагины: scope обязателен, умолчание у команды — user, а нам нужен project
|
||||||
claude plugin install av-dev-docs@av-dev-skills --scope project
|
claude plugin install av-dev@av-dev-skills --scope project
|
||||||
claude plugin install av-dev-tasks@av-dev-skills --scope project
|
|
||||||
claude plugin install av-dev-code@av-dev-skills --scope project
|
|
||||||
claude plugin install av-dev-git@av-dev-skills --scope project
|
claude plugin install av-dev-git@av-dev-skills --scope project
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -209,9 +226,7 @@ claude plugin install av-dev-git@av-dev-skills --scope project
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"enabledPlugins": {
|
"enabledPlugins": {
|
||||||
"av-dev-docs@av-dev-skills": true,
|
"av-dev@av-dev-skills": true,
|
||||||
"av-dev-tasks@av-dev-skills": true,
|
|
||||||
"av-dev-code@av-dev-skills": true,
|
|
||||||
"av-dev-git@av-dev-skills": true
|
"av-dev-git@av-dev-skills": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,9 +255,7 @@ claude plugin marketplace update av-dev-skills
|
|||||||
|
|
||||||
# 2. снимки плагинов — из каталога проекта, где они установлены
|
# 2. снимки плагинов — из каталога проекта, где они установлены
|
||||||
cd /path/to/project
|
cd /path/to/project
|
||||||
claude plugin update av-dev-docs@av-dev-skills --scope project
|
claude plugin update av-dev@av-dev-skills --scope project
|
||||||
claude plugin update av-dev-tasks@av-dev-skills --scope project
|
|
||||||
claude plugin update av-dev-code@av-dev-skills --scope project
|
|
||||||
claude plugin update av-dev-git@av-dev-skills --scope project
|
claude plugin update av-dev-git@av-dev-skills --scope project
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -316,7 +329,7 @@ claude plugin uninstall <плагин>@av-dev-skills --scope project
|
|||||||
<plugin>/skills/<skill>/references/ что читается по ссылке из скилла
|
<plugin>/skills/<skill>/references/ что читается по ссылке из скилла
|
||||||
<plugin>/skills/<skill>/scripts/ tasks.py, docs.py, openspec.py
|
<plugin>/skills/<skill>/scripts/ tasks.py, docs.py, openspec.py
|
||||||
<plugin>/agents/ charter'ы сабагентов
|
<plugin>/agents/ charter'ы сабагентов
|
||||||
shared/ дома правил, общих для нескольких плагинов
|
av-dev/shared/ дома правил и общий читатель .av-dev.toml
|
||||||
scripts/ проверки репозитория и пересборка копий
|
scripts/ проверки репозитория и пересборка копий
|
||||||
pyproject.toml линтеры скриптов, только для этого репозитория
|
pyproject.toml линтеры скриптов, только для этого репозитория
|
||||||
lefthook.yml гейт коммита: проверки документов
|
lefthook.yml гейт коммита: проверки документов
|
||||||
@@ -362,7 +375,7 @@ python3 scripts/frontmatter.py # 0 в порядке, 1 расхождени
|
|||||||
а не «имя не то»;
|
а не «имя не то»;
|
||||||
- **цвет charter'а, не отвечающий его модели.** Цвет кодирует модель, а не роль
|
- **цвет charter'а, не отвечающий его модели.** Цвет кодирует модель, а не роль
|
||||||
прохода — раскладка живёт в
|
прохода — раскладка живёт в
|
||||||
[review/SKILL.md](av-dev-code/skills/review/SKILL.md),
|
[code-review/SKILL.md](av-dev/skills/code-review/SKILL.md),
|
||||||
разделе «Модель по проходу», здесь только её механизация. Держаться вниманием
|
разделе «Модель по проходу», здесь только её механизация. Держаться вниманием
|
||||||
правило не может: цвет ставится один раз при заведении charter'а, а модель
|
правило не может: цвет ставится один раз при заведении charter'а, а модель
|
||||||
потом меняется калибровкой;
|
потом меняется калибровкой;
|
||||||
@@ -398,19 +411,25 @@ python3 scripts/copies.py # 0 сошлось, 1 расхождение, 2 р
|
|||||||
сверку не входят. Маркер, уехавший в проект вместе со скелетом, там полезен: он
|
сверку не входят. Маркер, уехавший в проект вместе со скелетом, там полезен: он
|
||||||
говорит, что у текста есть дом и правится он там.
|
говорит, что у текста есть дом и правится он там.
|
||||||
|
|
||||||
**Дом правила, общего для нескольких плагинов, лежит в `shared/` и ни одному из
|
**Дом правила, общего нескольким скиллам, лежит в `av-dev/shared/` и ни одному
|
||||||
них не принадлежит.** Так живёт язык проектных текстов: он одинаково нужен
|
из них не принадлежит.** Так живут язык проектных текстов, словарь
|
||||||
документам канона и задачам, и хранить его внутри одного плагина значило бы
|
сопровождения и правило об отсутствующих частях раскладки: каждое нужно
|
||||||
отдать общее правило во владение половине. Так же живёт граница плагинов —
|
многим, и хранить его внутри одного скилла значило бы отдать общее правило во
|
||||||
правило обращения к соседу. Плагин везёт копию и потому остаётся
|
владение части.
|
||||||
самодостаточным — `shared/` нужен этому репозиторию, а не установленному
|
|
||||||
плагину.
|
**Копия при этом делается не всегда.** Пока плагинов было три, копия была
|
||||||
|
единственным способом: путь в дерево соседа не разрешался. Внутри одного дерева
|
||||||
|
справочник читается **по ссылке**, и дословная копия остаётся ровно там, где
|
||||||
|
текст обязан лежать **внутри самого промпта**: в уставе агента, где правило и
|
||||||
|
есть критерий суждения; в `SKILL.md`, который целиком и есть промпт скилла, — за
|
||||||
|
ссылкой скилл пошёл бы отдельным чтением, а правило нужно ему в тот момент,
|
||||||
|
когда он решает; и в скелетах, уезжающих в репозиторий проекта.
|
||||||
|
|
||||||
**Дом ставится в `shared/` только тогда, когда владельца нет.** У адресов
|
**Дом ставится в `shared/` только тогда, когда владельца нет.** У адресов
|
||||||
владелец есть: раскладку `docs/` держит канон, каталог задач — плагин задач, и
|
владелец есть: раскладку `docs/` держит `doc-canon`, каталог задач —
|
||||||
переносить их наружу значило бы отобрать у владельца его же предмет. Общее без
|
`task-track`, и переносить их наружу значило бы отобрать у владельца его же
|
||||||
владельца едет копией из `shared/`; чужое с владельцем остаётся дома, а
|
предмет. Общее без владельца живёт в `shared/`; чужое с владельцем остаётся
|
||||||
потребитель на него ссылается.
|
дома, а потребитель на него ссылается.
|
||||||
|
|
||||||
Скрипт ловит четыре вещи: копия разошлась с домом (с диффом), копия указывает не
|
Скрипт ловит четыре вещи: копия разошлась с домом (с диффом), копия указывает не
|
||||||
на тот файл, дом остался без копий, разметка сломана. Чего он **не** ловит —
|
на тот файл, дом остался без копий, разметка сломана. Чего он **не** ловит —
|
||||||
|
|||||||
+2
-2
@@ -84,7 +84,7 @@ check` сверяет версию, но не то, что миграционн
|
|||||||
смотрели» обязательна в докладе `check`, `adopt`, интейка, штурма и всех пяти
|
смотрели» обязательна в докладе `check`, `adopt`, интейка, штурма и всех пяти
|
||||||
агентов канона и задач (`doc-consistency`, `doc-code-drift`, `doc-wording`,
|
агентов канона и задач (`doc-consistency`, `doc-code-drift`, `doc-wording`,
|
||||||
`task-form`, `task-wording`) — девять мест, и проверить её исполнение некому:
|
`task-form`, `task-wording`) — девять мест, и проверить её исполнение некому:
|
||||||
приёмщик и исполнитель одно лицо (`groom/SKILL.md`, «Стимулы»). Выродившаяся
|
приёмщик и исполнитель одно лицо (`task-groom/SKILL.md`, «Стимулы»). Выродившаяся
|
||||||
строка **хуже отсутствия**: доклад выглядит проверенным.
|
строка **хуже отсутствия**: доклад выглядит проверенным.
|
||||||
|
|
||||||
Приём не правится: это гипотеза об износе, а не находка, и менять работающее по
|
Приём не правится: это гипотеза об износе, а не находка, и менять работающее по
|
||||||
@@ -95,7 +95,7 @@ check` сверяет версию, но не то, что миграционн
|
|||||||
**Форма ADR при пересмотре решения.** Парный статус («старая запись получает
|
**Форма ADR при пересмотре решения.** Парный статус («старая запись получает
|
||||||
`заменено на`») судит агент `doc-consistency` — правило 6 его устава. Охват был
|
`заменено на`») судит агент `doc-consistency` — правило 6 его устава. Охват был
|
||||||
открытым вопросом, пока агент звался пачкой, отобранной работой; переезд вызова
|
открытым вопросом, пока агент звался пачкой, отобранной работой; переезд вызова
|
||||||
в `av-dev-docs:healthcheck` с пачкой «весь канон» его снял. Остаётся зазор до
|
в `av-dev:doc-healthcheck` с пачкой «весь канон» его снял. Остаётся зазор до
|
||||||
ближайшего прогона `healthcheck` и отсутствие механической проверки — то есть
|
ближайшего прогона `healthcheck` и отсутствие механической проверки — то есть
|
||||||
пересмотр, сделанный сегодня, судится тогда, когда позовут сверку, а не в момент
|
пересмотр, сделанный сегодня, судится тогда, когда позовут сверку, а не в момент
|
||||||
правки.
|
правки.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
- **риски, открытые вопросы и принятые пределы** — [REMAINING.md](REMAINING.md);
|
- **риски, открытые вопросы и принятые пределы** — [REMAINING.md](REMAINING.md);
|
||||||
- **почему решено так** — [DECISIONS.md](DECISIONS.md), записи датированы;
|
- **почему решено так** — [DECISIONS.md](DECISIONS.md), записи датированы;
|
||||||
- **шаги повышения проекта с версии канона на версию** — журнал версий
|
- **шаги повышения проекта с версии канона на версию** — журнал версий
|
||||||
([changelog.md](av-dev-docs/skills/canon/references/changelog.md)). Пересказ их
|
([changelog.md](av-dev/skills/doc-canon/references/changelog.md)). Пересказ их
|
||||||
сюда был бы вторым домом, и прежний план на этом уже разъезжался: он повторял
|
сюда был бы вторым домом, и прежний план на этом уже разъезжался: он повторял
|
||||||
записи версий 3, 4 и 5 построчно, и половина повторов протухла молча.
|
записи версий 3, 4 и 5 построчно, и половина повторов протухла молча.
|
||||||
|
|
||||||
@@ -15,13 +15,16 @@
|
|||||||
|
|
||||||
## Где мы сейчас
|
## Где мы сейчас
|
||||||
|
|
||||||
Плагинов четыре, и каждый ставится отдельно: `av-dev-docs` (канон документов и
|
Плагина два: `av-dev` — весь процесс девятью скиллами (`doc-*` — документы,
|
||||||
их содержимое), `av-dev-tasks` (задачи и цели), `av-dev-code` (код по задачам:
|
`task-*` — учёт работ, `code-*` — работа по задачам), и `av-dev-git` —
|
||||||
цикл SDD, конвейер ревью, OpenSpec), `av-dev-git`. Общее, что нужно нескольким
|
сообщения коммитов. Прежние три (`av-dev-docs`, `av-dev-tasks`, `av-dev-code`)
|
||||||
дословно, живёт домом в `shared/` и уезжает копиями.
|
слились 13 августа 2026, тема 64 DECISIONS. Общее, что нужно нескольким скиллам,
|
||||||
|
живёт домом в `av-dev/shared/`.
|
||||||
|
|
||||||
Канон документов — **версия 14**; формат задач — **версия 1**, своя и со своим журналом. Живые проекты стоят на 2–3 и на плагине
|
Раскладка — **версия 1**, одна на документы и на каталог задач, в
|
||||||
`av-dev-pm`, которого больше нет.
|
`.av-dev.toml` в корне проекта. Живые проекты стоят на каноне 2–3 и на плагине
|
||||||
|
`av-dev-pm`, которого больше нет: им идти сперва по закрытому журналу канона до
|
||||||
|
14, потом по записи 1 действующего.
|
||||||
|
|
||||||
Бумажная часть закрыта аудитом четырёх плагинов и четырьмя пропусками правок
|
Бумажная часть закрыта аудитом четырёх плагинов и четырьмя пропусками правок
|
||||||
(DECISIONS, запись 59). Всё, что ниже, проверяется **только на живом коде**.
|
(DECISIONS, запись 59). Всё, что ниже, проверяется **только на живом коде**.
|
||||||
@@ -35,19 +38,18 @@
|
|||||||
### healthlog — первым
|
### healthlog — первым
|
||||||
|
|
||||||
- [ ] переустановить плагины: снять `av-dev-pm` и `av-dev-pipeline`, поставить
|
- [ ] переустановить плагины: снять `av-dev-pm` и `av-dev-pipeline`, поставить
|
||||||
`av-dev-docs`, `av-dev-tasks`, `av-dev-code`, `av-dev-git`. Оба прежних
|
`av-dev` и `av-dev-git`. Прежние имена мертвы, и `plugin update` их не
|
||||||
имени мертвы, и `plugin update` их не переименует — только снять и
|
переименует — только снять и поставить. `marketplace update`, затем `plugin update` — одного шага мало
|
||||||
поставить. `marketplace update`, затем `plugin update` — одного шага мало
|
|
||||||
(README, «Обновление»)
|
(README, «Обновление»)
|
||||||
- [ ] удалить проектные копии: `.claude/skills/healthlog-{task,review}-pipeline`
|
- [ ] удалить проектные копии: `.claude/skills/healthlog-{task,review}-pipeline`
|
||||||
и девять `.claude/agents/healthlog-review-*.md`. Они прошлого поколения и
|
и девять `.claude/agents/healthlog-review-*.md`. Они прошлого поколения и
|
||||||
после переезда указывают на документы, которых уже не будет
|
после переезда указывают на документы, которых уже не будет
|
||||||
- [ ] `av-dev-docs:canon` в режиме `adopt` — он приведёт проект к канону 14
|
- [ ] `av-dev:doc-canon` в режиме `adopt` — он приведёт проект к раскладке 1
|
||||||
сразу, картой и с подтверждением. Файл-в-файл здесь не расписан: раскладку
|
сразу, картой и с подтверждением. Файл-в-файл здесь не расписан: раскладку
|
||||||
знает скилл, и второй перечень разошёлся бы с ним
|
знает скилл, и второй перечень разошёлся бы с ним
|
||||||
- [ ] каталог задач — в `tasks/` корня (канон 11), не в `docs/tasks/`, без
|
- [ ] каталог задач — в `tasks/` корня (канон 11), не в `docs/tasks/`, без
|
||||||
`SPRINT.md` (канон 12) и с версией формата в `tasks/.tasks.json` (журнал
|
`SPRINT.md` (канон 12); версия и настройки — в `.av-dev.toml` корня, там
|
||||||
задач, версия 1). Скилл задач зовётся из `adopt` сам
|
же секция `[tasks]`. Скилл задач зовётся из `adopt` сам
|
||||||
- [ ] гейт проекта: три шага вместо одного — `docs.py check`, `tasks.py check
|
- [ ] гейт проекта: три шага вместо одного — `docs.py check`, `tasks.py check
|
||||||
--dir tasks`, `openspec.py check`. **Второй и третий раньше не были
|
--dir tasks`, `openspec.py check`. **Второй и третий раньше не были
|
||||||
нужны:** согласованность задач тянул за собой `docs.py`, форму `config.yaml`
|
нужны:** согласованность задач тянул за собой `docs.py`, форму `config.yaml`
|
||||||
@@ -84,7 +86,7 @@
|
|||||||
|
|
||||||
## 4. Конвейер: что осталось после `resolve`
|
## 4. Конвейер: что осталось после `resolve`
|
||||||
|
|
||||||
Сам скилл написан (`av-dev-code:resolve`, три сценария — разведка, решение и
|
Сам скилл написан (`av-dev:code-resolve`, три сценария — разведка, решение и
|
||||||
обслуживание; чекпоинт есть у первых двух, у обслуживания планового стопа нет),
|
обслуживание; чекпоинт есть у первых двух, у обслуживания планового стопа нет),
|
||||||
`task-batch` удалён. Осталось то, что на бумаге не проверяется:
|
`task-batch` удалён. Осталось то, что на бумаге не проверяется:
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "av-dev-code",
|
|
||||||
"description": "Одна задача от постановки до закрытия скиллом resolve: точка входа одна, сценария три, и выбирает сценарий сам скилл, прочитав постановку. Сценарий решения — полный цикл Spec Driven Development с плановым чекпоинтом после ревью дизайна: объяснение человеческим языком, повод скорректировать ход решения. Сценарий разведки (тип research, сырая идея, мутная постановка) кода не пишет и change не заводит: его чекпоинт вариантов стоит до первого написанного требования, а исход уезжает в документы канона и в задачи; выбранный способ реализуется следующим прогоном, который запускает человек. Сценарий обслуживания (тип chore: тулчейн, зависимости, сборка, гит-хуки, перенос, чистка) change не заводит и планового стопа не имеет: спека там не меняется по построению, поэтому цикл SDD остаётся без входа, а ревью идёт фиксированным планом без метки. Смена сценария по ходу — событие с названным исходом, а не тихий поворот. Плюс конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом и обязательным триажем. OpenSpec нужен сценарию решения, и плагин сам его заводит скиллом openspec; разведка обходится без него. Задача принимается и обычным текстом. Плагины av-dev-docs и av-dev-tasks опциональны: первый даёт документы канона, из которых проходы читают проектную конкретику, второй — учёт задач; без них прогон деградирует поразрядно и называет это строкой.",
|
|
||||||
"author": {
|
|
||||||
"name": "Anton Vakhrushev",
|
|
||||||
"email": "anwinged@gmail.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "av-dev-docs",
|
|
||||||
"description": "Документация проекта: канон раскладки (CLAUDE.md плюс docs/ — паспорт, архитектура, схема БД, безопасность, конвенции, разведка, ADR, журнал ревью), роли документов и правило единственного дома. Три операции одной машиной сравнения — check, adopt, upgrade — со скриптом docs.py; заведение нового проекта интервью по брифу; ведение содержимого по ходу разработки: синк после сделанной задачи, ADR промоутом из архивного design.md, записка разведки, запись дефекта в журнал ревью. Смысловое, чего скрипт не видит, судит скилл healthcheck двумя агентами разом — doc-consistency (документы между собой и с openspec) и doc-code-drift (факты против кода); язык документов вычитывает агент doc-wording. Задач не ведёт — это плагин av-dev-tasks, и он опционален.",
|
|
||||||
"author": {
|
|
||||||
"name": "Anton Vakhrushev",
|
|
||||||
"email": "anwinged@gmail.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
# Язык проектных текстов
|
|
||||||
|
|
||||||
**Копия.** Дом — `shared/language.md` в репозитории плагинов; язык общий для
|
|
||||||
документов канона и для задач, и потому не принадлежит ни одному плагину.
|
|
||||||
Правится дом, а не этот файл: расхождение ловит `copies.py` на гейте коммита.
|
|
||||||
|
|
||||||
<!-- копия: язык-доктрина из shared/language.md -->
|
|
||||||
|
|
||||||
Правила — для всего, что пишется словами: задачи и цели, документы канона,
|
|
||||||
решения ADR, записки разведки, сообщения коммитов. Не для кода и не для
|
|
||||||
сообщений программы пользователю — там свои конвенции проекта.
|
|
||||||
|
|
||||||
Основа — **информационный стиль** Максима Ильяхова ([учебник
|
|
||||||
бюро](https://bureau.ru/projects/book-text/), книга «Пиши, сокращай»). Он
|
|
||||||
написан для рекламы, статей и писем, поэтому взят не целиком.
|
|
||||||
|
|
||||||
## Зачем он здесь
|
|
||||||
|
|
||||||
Проектный текст читают в двух положениях, и оба неудобные: **выбирают, брать ли
|
|
||||||
задачу**, глядя в строку индекса и один экран тела; и **возвращаются через
|
|
||||||
квартал**, не помня контекста. Оба положения наказывают одно и то же — слова, не
|
|
||||||
несущие сведений. Информационный стиль ровно про это, и его польза здесь не
|
|
||||||
эстетическая: текст, из которого нельзя достать факт, заставляет открывать код,
|
|
||||||
а это и есть цена, которой мы избегаем.
|
|
||||||
|
|
||||||
## Что взято сверх правил вычитки
|
|
||||||
|
|
||||||
Эти три требования судит человек, а не проход вычитки: находка по ним требует
|
|
||||||
увидеть текст целиком, а не фразу.
|
|
||||||
|
|
||||||
**Полезное действие.** У каждого текста есть вопрос, на который он отвечает, и
|
|
||||||
читатель, который этот вопрос задаёт. Не отвечает — не пишется. У задачи это
|
|
||||||
«зачем она нужна» и «что станет наблюдаемо иначе»; у документа канона — его
|
|
||||||
собственный вопрос («что это за система», «как сложено», «почему так решили»).
|
|
||||||
Текст, который не отвечает ни на чей вопрос, сокращается до нуля — это законный
|
|
||||||
исход правки.
|
|
||||||
|
|
||||||
**Параллельность.** Однородное пишется одинаково: пункты списка — одной
|
|
||||||
грамматической формой, разделы одного вида — одним порядком, заголовки одного
|
|
||||||
уровня — одним типом фразы. Разнобой читатель принимает за разницу по существу и
|
|
||||||
ищет её.
|
|
||||||
|
|
||||||
**Заголовок работает.** Заголовок называет содержание раздела, а не тему
|
|
||||||
вообще: «Что проверяет `check`», а не «О проверках». Заголовков ставится
|
|
||||||
столько, чтобы длинный текст можно было просматривать, а не только читать
|
|
||||||
подряд.
|
|
||||||
|
|
||||||
## Что отброшено намеренно
|
|
||||||
|
|
||||||
Инфостиль написан для текстов, где читателя надо удержать. Проектный текст
|
|
||||||
читают потому, что надо, и держать его нечем. Отсюда три расхождения:
|
|
||||||
|
|
||||||
- **Парцелляция и рубленые фразы — нет.** Приём «Коротко. Ещё короче. Вот так»
|
|
||||||
ломает причинную связь, а в решении и в задаче ценность именно в ней:
|
|
||||||
«поэтому», «иначе», «раз так» несут смысл и остаются.
|
|
||||||
- **Не всякое вводное — мусор.** «Если», «иначе», «при таком-то условии»,
|
|
||||||
«в отличие от» — это условия и противопоставления, то есть сведения. Режутся
|
|
||||||
вводные, которые не меняют смысл предложения.
|
|
||||||
- **Скобки и точка с запятой остаются.** В технической записи скобки несут
|
|
||||||
уточнение — имя команды, единицы, слаг, — и запрет на них удлинил бы текст, а
|
|
||||||
не сократил. Запрет на многоточие принимаем: в проектном тексте оно значит
|
|
||||||
«дописать позже», и такой текст лучше не публиковать.
|
|
||||||
|
|
||||||
И общее: инфостиль призывает «снять корону с себя и надеть на читателя». Здесь
|
|
||||||
читатель — **ты сам через квартал** и тот, кто возьмёт задачу. Писать для них
|
|
||||||
значит называть состояние и остаток, а не пересказывать, как было интересно
|
|
||||||
разбираться.
|
|
||||||
|
|
||||||
<!-- /копия: язык-доктрина -->
|
|
||||||
|
|
||||||
## Правила
|
|
||||||
|
|
||||||
<!-- копия: язык-правила из shared/language.md -->
|
|
||||||
|
|
||||||
У каждого правила названа причина: она же говорит, где правило **не**
|
|
||||||
применяется.
|
|
||||||
|
|
||||||
1. **Глагол вместо отглагольного существительного, активный залог.** «Обработчик
|
|
||||||
не проверяет владельца», а не «проверка владельца не осуществляется»;
|
|
||||||
«скрипт переписывает индекс», а не «индекс переписывается скриптом».
|
|
||||||
Отглагольное существительное прячет того, кто действует, — а в техническом
|
|
||||||
тексте важен именно он. Страдательный залог **остаётся**, когда деятель
|
|
||||||
неизвестен или неважен: «файл удаляется» верно, если удаляет любая из трёх
|
|
||||||
команд.
|
|
||||||
|
|
||||||
2. **Факт вместо оценки.** «Время ответа доходит до 800 мс», а не «работает
|
|
||||||
медленно»; «тело 40 МиБ держит блокировку 5 секунд», а не «большие тела
|
|
||||||
тормозят». Оценка допустима, когда факт стоит рядом, в той же фразе. Без
|
|
||||||
факта это настроение, а не сведение, — и находка тем ценнее, что оценку
|
|
||||||
потом не проверить.
|
|
||||||
|
|
||||||
3. **Стоп-слова.** Канцелярит (является, осуществляется, в целях, в рамках,
|
|
||||||
данный, вышеуказанный), вводные-паразиты (в общем, как известно, стоит
|
|
||||||
отметить), усилители (очень, крайне, достаточно, абсолютно, максимально),
|
|
||||||
синонимы одного качества («понятный и простой»), неопределённое
|
|
||||||
(соответствующий, определённый, некоторый).
|
|
||||||
|
|
||||||
Проверка одна: **вычеркни слово — смысл изменился, оставляй.** И осторожно с
|
|
||||||
вводными: «если», «иначе», «при таком-то условии», «в отличие от» несут
|
|
||||||
условие и противопоставление, то есть сведения, — их не трогают.
|
|
||||||
|
|
||||||
4. **Одна мысль — одно предложение.** Предложение с двумя независимыми
|
|
||||||
утверждениями делится. **Причинную связь не режут**: «поэтому», «иначе», «раз
|
|
||||||
так» — смысл, а не длина; рубленые фразы ради краткости тут вредят.
|
|
||||||
|
|
||||||
**Поля меты не делятся.** «Зачем» в мете задачи по формату — одно
|
|
||||||
предложение: оно повторяется строкой индекса, и второму там не поместиться.
|
|
||||||
Тесно — сокращают, но не делят. То же с любым полем вида `- **Имя:** …`.
|
|
||||||
|
|
||||||
5. **Англицизм, у которого есть живое русское слово, заменяется.**
|
|
||||||
|
|
||||||
| Калька | Русский аналог |
|
|
||||||
| --- | --- |
|
|
||||||
| флоу | поток, процесс, сценарий |
|
|
||||||
| фикс, зафиксить | исправление, исправить, починить |
|
|
||||||
| чекать | проверять |
|
|
||||||
| апрув, заапрувить | согласование, согласовать |
|
|
||||||
| best-effort | по возможности |
|
|
||||||
| кейс | случай, сценарий |
|
|
||||||
| перформанс | производительность |
|
|
||||||
| матчинг, смэтчить | сопоставление, сопоставить |
|
|
||||||
| зарелизить | выпустить, выложить |
|
|
||||||
| отрефакторить | переписать, разделить, убрать второй путь |
|
|
||||||
|
|
||||||
Насильно не переводится то, что является **именем вещи**: термины технологий
|
|
||||||
и протоколов (`SQL`, `API`, `CSV`, `N+1`, `IDOR`), имена классов, методов,
|
|
||||||
полей, таблиц и команд, слаг, а также термин, у которого нет точного русского
|
|
||||||
эквивалента и который в команде уже прижился.
|
|
||||||
|
|
||||||
Цель — простой и точный текст, а не пуризм. Русский аналог звучит коряво или
|
|
||||||
искажает смысл — остаётся термин.
|
|
||||||
|
|
||||||
6. **Слово из своего словаря не трогается — список закрыт.** Оговорка «термин
|
|
||||||
прижился» без списка проверяема на глаз и потому не проверяема: прижившимся
|
|
||||||
выглядит любое слово, встреченное трижды.
|
|
||||||
|
|
||||||
| Термин | Что называет |
|
|
||||||
| --- | --- |
|
|
||||||
| интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции |
|
|
||||||
| триаж | стадия конвейера, сводящая находки в решение |
|
|
||||||
| провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство |
|
|
||||||
| дедуп, дедупликация | сверка нового против уже лежащего |
|
|
||||||
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
|
||||||
| дифф, `--base` | разница между состояниями в git |
|
|
||||||
| промпт | текст, которым зовут модель |
|
|
||||||
| change, capability, spec | сущности OpenSpec, имена вещей чужого инструмента |
|
|
||||||
| generative, applicative | роды проходов ревью, вводятся определением по месту |
|
|
||||||
| чекпоинт | плановый стоп работы, на котором ждут ответа человека. «Остановка» называет любой перерыв, «согласование» — обряд одобрения, а здесь место в процессе, назначенное заранее |
|
|
||||||
| синк | сверка каждого документа канона с только что сделанной работой, с обязательным отрицанием по нетронутым. «Обновление документации» называет исход, а не работу, и молчит о принуждённом отрицании |
|
|
||||||
|
|
||||||
**Список закрыт.** Слово не отсюда и не из таблицы имён вещей выше — находка,
|
|
||||||
а не «принятый стиль»: у него либо есть живой русский аналог, либо оно
|
|
||||||
требует ввода одной строкой при первом употреблении.
|
|
||||||
|
|
||||||
Отсюда же читается снятое. Эти слова из текстов убраны, и возвращать их не
|
|
||||||
надо: **конфляция** (смешение), **декорреляция** (разведённость, разведён с
|
|
||||||
кем-то), **непоймание** (почему не поймали), **эвал-сет** (проверочный
|
|
||||||
набор), **гайд** (руководство), **опиниативный** (проход с мнением). Каждое
|
|
||||||
было латинизмом или калькой при живом русском слове, и каждое к моменту снятия
|
|
||||||
жило в трёх-шести файлах разом — то есть выглядело словарём, не будучи им.
|
|
||||||
|
|
||||||
7. **Жаргон и метафоры заменяются прямым называнием.** Автору образ понятен,
|
|
||||||
читателю — нет.
|
|
||||||
|
|
||||||
| Метафора-жаргон | Прямо |
|
|
||||||
| --- | --- |
|
|
||||||
| рычаг (кэша, отбора) | условие отбора, параметр |
|
|
||||||
| навешен не на тот счётчик | завязан не на тот счётчик |
|
|
||||||
| переширокий матчинг по имени | слишком грубое сопоставление по имени, слишком много слабых совпадений |
|
|
||||||
| костыль | временное решение, обходной путь — и в чём именно |
|
|
||||||
| просело, отвалилось | стало медленнее на столько-то, перестало отвечать |
|
|
||||||
|
|
||||||
Проверка: **фраза требует, чтобы читатель додумал образ, — заменяется
|
|
||||||
буквальным описанием того, что происходит.**
|
|
||||||
|
|
||||||
8. **Термин, которого нет в документах проекта, вводится одной строкой или не
|
|
||||||
употребляется.** Термин, не встречающийся ни в паспорте, ни в архитектуре, ни
|
|
||||||
в конвенциях, — свой словарь у отдельной записи, а это самый дешёвый способ
|
|
||||||
сделать беклог нечитаемым для того, кто вернётся к нему через квартал.
|
|
||||||
Заменять незнакомый термин догадкой нельзя: догадка о предметной области
|
|
||||||
дороже непонятного слова, потому что выглядит понятной.
|
|
||||||
|
|
||||||
**Слово, занятое в другом смысле, — то же нарушение.** Термин, который в
|
|
||||||
одном документе проекта значит одно, а здесь другое, ломает оба.
|
|
||||||
|
|
||||||
9. **Имя файла — английское слово по сути, а не транслит.** `queue-as-table`, а
|
|
||||||
не `ochered-tablicej`; `move-parse-strict`, а не `razbor-hoda`. Транслит
|
|
||||||
нечитаем тому, кто ищет по смыслу, и не сокращается, а имя стоит в ссылках,
|
|
||||||
коммитах и путях, которые набирают руками. Переименование — **перенос ссылок
|
|
||||||
одним проходом**, а не правка одного файла.
|
|
||||||
|
|
||||||
<!-- /копия: язык-правила -->
|
|
||||||
|
|
||||||
## Порог правки
|
|
||||||
|
|
||||||
<!-- копия: порог-правки из shared/language.md -->
|
|
||||||
|
|
||||||
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
|
||||||
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
|
||||||
перестают читать весь список, и вместе с ним пропадают настоящие находки.
|
|
||||||
Сомневаешься — не правь. Формулировка, которая просто **не твоя**, — не находка.
|
|
||||||
|
|
||||||
**Систематичность нарушения — не довод в его пользу.** Одна и та же ошибка в
|
|
||||||
пяти файлах не становится «принятым стилем»: чаще это значит, что правило не
|
|
||||||
применялось вовсе, — и находка тем важнее. «Так сделано везде» годится как
|
|
||||||
основание для **одной находки на весь набор** («правило N нарушено в пяти
|
|
||||||
записях, перечень: …»), но не как основание промолчать. Принятым считается
|
|
||||||
только то, что назвал зовущий или что записано в конвенциях проекта.
|
|
||||||
|
|
||||||
<!-- /копия: порог-правки -->
|
|
||||||
|
|
||||||
И обратное: язык правится **по ходу той операции, которая записи касается**.
|
|
||||||
Беклог не переписывают ради языка.
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "av-dev-tasks",
|
|
||||||
"description": "Задачи и цели каталогом markdown-файлов: одна запись — файл в items/ плюс строка ровно в одном индексе, у записи тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение из диалога с фильтром и дедупом, разбор находок ревью в задачи, декомпозиция на независимо полезные части, гигиена полей, согласованность индексов скриптом tasks.py. Приоритет — явный порядок строк в беклоге, и расставляет его скилл groom: интерактивный разбор на два вопроса, что сейчас самое важное и что перестало быть важным. Готовность записи к работе проверяет команда ready. Записи вычитывают два прохода: task-form (форма записи) и task-wording (язык). Канон документов ведёт плагин av-dev-docs, он опционален. Задач не выполняет — этим занимается конвейер проекта.",
|
|
||||||
"author": {
|
|
||||||
"name": "Anton Vakhrushev",
|
|
||||||
"email": "anwinged@gmail.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
# Язык проектных текстов
|
|
||||||
|
|
||||||
**Копия.** Дом — `shared/language.md` в репозитории плагинов; язык общий для
|
|
||||||
документов канона и для задач, и потому не принадлежит ни одному плагину.
|
|
||||||
Правится дом, а не этот файл: расхождение ловит `copies.py` на гейте коммита.
|
|
||||||
|
|
||||||
<!-- копия: язык-доктрина из shared/language.md -->
|
|
||||||
|
|
||||||
Правила — для всего, что пишется словами: задачи и цели, документы канона,
|
|
||||||
решения ADR, записки разведки, сообщения коммитов. Не для кода и не для
|
|
||||||
сообщений программы пользователю — там свои конвенции проекта.
|
|
||||||
|
|
||||||
Основа — **информационный стиль** Максима Ильяхова ([учебник
|
|
||||||
бюро](https://bureau.ru/projects/book-text/), книга «Пиши, сокращай»). Он
|
|
||||||
написан для рекламы, статей и писем, поэтому взят не целиком.
|
|
||||||
|
|
||||||
## Зачем он здесь
|
|
||||||
|
|
||||||
Проектный текст читают в двух положениях, и оба неудобные: **выбирают, брать ли
|
|
||||||
задачу**, глядя в строку индекса и один экран тела; и **возвращаются через
|
|
||||||
квартал**, не помня контекста. Оба положения наказывают одно и то же — слова, не
|
|
||||||
несущие сведений. Информационный стиль ровно про это, и его польза здесь не
|
|
||||||
эстетическая: текст, из которого нельзя достать факт, заставляет открывать код,
|
|
||||||
а это и есть цена, которой мы избегаем.
|
|
||||||
|
|
||||||
## Что взято сверх правил вычитки
|
|
||||||
|
|
||||||
Эти три требования судит человек, а не проход вычитки: находка по ним требует
|
|
||||||
увидеть текст целиком, а не фразу.
|
|
||||||
|
|
||||||
**Полезное действие.** У каждого текста есть вопрос, на который он отвечает, и
|
|
||||||
читатель, который этот вопрос задаёт. Не отвечает — не пишется. У задачи это
|
|
||||||
«зачем она нужна» и «что станет наблюдаемо иначе»; у документа канона — его
|
|
||||||
собственный вопрос («что это за система», «как сложено», «почему так решили»).
|
|
||||||
Текст, который не отвечает ни на чей вопрос, сокращается до нуля — это законный
|
|
||||||
исход правки.
|
|
||||||
|
|
||||||
**Параллельность.** Однородное пишется одинаково: пункты списка — одной
|
|
||||||
грамматической формой, разделы одного вида — одним порядком, заголовки одного
|
|
||||||
уровня — одним типом фразы. Разнобой читатель принимает за разницу по существу и
|
|
||||||
ищет её.
|
|
||||||
|
|
||||||
**Заголовок работает.** Заголовок называет содержание раздела, а не тему
|
|
||||||
вообще: «Что проверяет `check`», а не «О проверках». Заголовков ставится
|
|
||||||
столько, чтобы длинный текст можно было просматривать, а не только читать
|
|
||||||
подряд.
|
|
||||||
|
|
||||||
## Что отброшено намеренно
|
|
||||||
|
|
||||||
Инфостиль написан для текстов, где читателя надо удержать. Проектный текст
|
|
||||||
читают потому, что надо, и держать его нечем. Отсюда три расхождения:
|
|
||||||
|
|
||||||
- **Парцелляция и рубленые фразы — нет.** Приём «Коротко. Ещё короче. Вот так»
|
|
||||||
ломает причинную связь, а в решении и в задаче ценность именно в ней:
|
|
||||||
«поэтому», «иначе», «раз так» несут смысл и остаются.
|
|
||||||
- **Не всякое вводное — мусор.** «Если», «иначе», «при таком-то условии»,
|
|
||||||
«в отличие от» — это условия и противопоставления, то есть сведения. Режутся
|
|
||||||
вводные, которые не меняют смысл предложения.
|
|
||||||
- **Скобки и точка с запятой остаются.** В технической записи скобки несут
|
|
||||||
уточнение — имя команды, единицы, слаг, — и запрет на них удлинил бы текст, а
|
|
||||||
не сократил. Запрет на многоточие принимаем: в проектном тексте оно значит
|
|
||||||
«дописать позже», и такой текст лучше не публиковать.
|
|
||||||
|
|
||||||
И общее: инфостиль призывает «снять корону с себя и надеть на читателя». Здесь
|
|
||||||
читатель — **ты сам через квартал** и тот, кто возьмёт задачу. Писать для них
|
|
||||||
значит называть состояние и остаток, а не пересказывать, как было интересно
|
|
||||||
разбираться.
|
|
||||||
|
|
||||||
<!-- /копия: язык-доктрина -->
|
|
||||||
|
|
||||||
## Правила
|
|
||||||
|
|
||||||
<!-- копия: язык-правила из shared/language.md -->
|
|
||||||
|
|
||||||
У каждого правила названа причина: она же говорит, где правило **не**
|
|
||||||
применяется.
|
|
||||||
|
|
||||||
1. **Глагол вместо отглагольного существительного, активный залог.** «Обработчик
|
|
||||||
не проверяет владельца», а не «проверка владельца не осуществляется»;
|
|
||||||
«скрипт переписывает индекс», а не «индекс переписывается скриптом».
|
|
||||||
Отглагольное существительное прячет того, кто действует, — а в техническом
|
|
||||||
тексте важен именно он. Страдательный залог **остаётся**, когда деятель
|
|
||||||
неизвестен или неважен: «файл удаляется» верно, если удаляет любая из трёх
|
|
||||||
команд.
|
|
||||||
|
|
||||||
2. **Факт вместо оценки.** «Время ответа доходит до 800 мс», а не «работает
|
|
||||||
медленно»; «тело 40 МиБ держит блокировку 5 секунд», а не «большие тела
|
|
||||||
тормозят». Оценка допустима, когда факт стоит рядом, в той же фразе. Без
|
|
||||||
факта это настроение, а не сведение, — и находка тем ценнее, что оценку
|
|
||||||
потом не проверить.
|
|
||||||
|
|
||||||
3. **Стоп-слова.** Канцелярит (является, осуществляется, в целях, в рамках,
|
|
||||||
данный, вышеуказанный), вводные-паразиты (в общем, как известно, стоит
|
|
||||||
отметить), усилители (очень, крайне, достаточно, абсолютно, максимально),
|
|
||||||
синонимы одного качества («понятный и простой»), неопределённое
|
|
||||||
(соответствующий, определённый, некоторый).
|
|
||||||
|
|
||||||
Проверка одна: **вычеркни слово — смысл изменился, оставляй.** И осторожно с
|
|
||||||
вводными: «если», «иначе», «при таком-то условии», «в отличие от» несут
|
|
||||||
условие и противопоставление, то есть сведения, — их не трогают.
|
|
||||||
|
|
||||||
4. **Одна мысль — одно предложение.** Предложение с двумя независимыми
|
|
||||||
утверждениями делится. **Причинную связь не режут**: «поэтому», «иначе», «раз
|
|
||||||
так» — смысл, а не длина; рубленые фразы ради краткости тут вредят.
|
|
||||||
|
|
||||||
**Поля меты не делятся.** «Зачем» в мете задачи по формату — одно
|
|
||||||
предложение: оно повторяется строкой индекса, и второму там не поместиться.
|
|
||||||
Тесно — сокращают, но не делят. То же с любым полем вида `- **Имя:** …`.
|
|
||||||
|
|
||||||
5. **Англицизм, у которого есть живое русское слово, заменяется.**
|
|
||||||
|
|
||||||
| Калька | Русский аналог |
|
|
||||||
| --- | --- |
|
|
||||||
| флоу | поток, процесс, сценарий |
|
|
||||||
| фикс, зафиксить | исправление, исправить, починить |
|
|
||||||
| чекать | проверять |
|
|
||||||
| апрув, заапрувить | согласование, согласовать |
|
|
||||||
| best-effort | по возможности |
|
|
||||||
| кейс | случай, сценарий |
|
|
||||||
| перформанс | производительность |
|
|
||||||
| матчинг, смэтчить | сопоставление, сопоставить |
|
|
||||||
| зарелизить | выпустить, выложить |
|
|
||||||
| отрефакторить | переписать, разделить, убрать второй путь |
|
|
||||||
|
|
||||||
Насильно не переводится то, что является **именем вещи**: термины технологий
|
|
||||||
и протоколов (`SQL`, `API`, `CSV`, `N+1`, `IDOR`), имена классов, методов,
|
|
||||||
полей, таблиц и команд, слаг, а также термин, у которого нет точного русского
|
|
||||||
эквивалента и который в команде уже прижился.
|
|
||||||
|
|
||||||
Цель — простой и точный текст, а не пуризм. Русский аналог звучит коряво или
|
|
||||||
искажает смысл — остаётся термин.
|
|
||||||
|
|
||||||
6. **Слово из своего словаря не трогается — список закрыт.** Оговорка «термин
|
|
||||||
прижился» без списка проверяема на глаз и потому не проверяема: прижившимся
|
|
||||||
выглядит любое слово, встреченное трижды.
|
|
||||||
|
|
||||||
| Термин | Что называет |
|
|
||||||
| --- | --- |
|
|
||||||
| интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции |
|
|
||||||
| триаж | стадия конвейера, сводящая находки в решение |
|
|
||||||
| провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство |
|
|
||||||
| дедуп, дедупликация | сверка нового против уже лежащего |
|
|
||||||
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
|
||||||
| дифф, `--base` | разница между состояниями в git |
|
|
||||||
| промпт | текст, которым зовут модель |
|
|
||||||
| change, capability, spec | сущности OpenSpec, имена вещей чужого инструмента |
|
|
||||||
| generative, applicative | роды проходов ревью, вводятся определением по месту |
|
|
||||||
| чекпоинт | плановый стоп работы, на котором ждут ответа человека. «Остановка» называет любой перерыв, «согласование» — обряд одобрения, а здесь место в процессе, назначенное заранее |
|
|
||||||
| синк | сверка каждого документа канона с только что сделанной работой, с обязательным отрицанием по нетронутым. «Обновление документации» называет исход, а не работу, и молчит о принуждённом отрицании |
|
|
||||||
|
|
||||||
**Список закрыт.** Слово не отсюда и не из таблицы имён вещей выше — находка,
|
|
||||||
а не «принятый стиль»: у него либо есть живой русский аналог, либо оно
|
|
||||||
требует ввода одной строкой при первом употреблении.
|
|
||||||
|
|
||||||
Отсюда же читается снятое. Эти слова из текстов убраны, и возвращать их не
|
|
||||||
надо: **конфляция** (смешение), **декорреляция** (разведённость, разведён с
|
|
||||||
кем-то), **непоймание** (почему не поймали), **эвал-сет** (проверочный
|
|
||||||
набор), **гайд** (руководство), **опиниативный** (проход с мнением). Каждое
|
|
||||||
было латинизмом или калькой при живом русском слове, и каждое к моменту снятия
|
|
||||||
жило в трёх-шести файлах разом — то есть выглядело словарём, не будучи им.
|
|
||||||
|
|
||||||
7. **Жаргон и метафоры заменяются прямым называнием.** Автору образ понятен,
|
|
||||||
читателю — нет.
|
|
||||||
|
|
||||||
| Метафора-жаргон | Прямо |
|
|
||||||
| --- | --- |
|
|
||||||
| рычаг (кэша, отбора) | условие отбора, параметр |
|
|
||||||
| навешен не на тот счётчик | завязан не на тот счётчик |
|
|
||||||
| переширокий матчинг по имени | слишком грубое сопоставление по имени, слишком много слабых совпадений |
|
|
||||||
| костыль | временное решение, обходной путь — и в чём именно |
|
|
||||||
| просело, отвалилось | стало медленнее на столько-то, перестало отвечать |
|
|
||||||
|
|
||||||
Проверка: **фраза требует, чтобы читатель додумал образ, — заменяется
|
|
||||||
буквальным описанием того, что происходит.**
|
|
||||||
|
|
||||||
8. **Термин, которого нет в документах проекта, вводится одной строкой или не
|
|
||||||
употребляется.** Термин, не встречающийся ни в паспорте, ни в архитектуре, ни
|
|
||||||
в конвенциях, — свой словарь у отдельной записи, а это самый дешёвый способ
|
|
||||||
сделать беклог нечитаемым для того, кто вернётся к нему через квартал.
|
|
||||||
Заменять незнакомый термин догадкой нельзя: догадка о предметной области
|
|
||||||
дороже непонятного слова, потому что выглядит понятной.
|
|
||||||
|
|
||||||
**Слово, занятое в другом смысле, — то же нарушение.** Термин, который в
|
|
||||||
одном документе проекта значит одно, а здесь другое, ломает оба.
|
|
||||||
|
|
||||||
9. **Имя файла — английское слово по сути, а не транслит.** `queue-as-table`, а
|
|
||||||
не `ochered-tablicej`; `move-parse-strict`, а не `razbor-hoda`. Транслит
|
|
||||||
нечитаем тому, кто ищет по смыслу, и не сокращается, а имя стоит в ссылках,
|
|
||||||
коммитах и путях, которые набирают руками. Переименование — **перенос ссылок
|
|
||||||
одним проходом**, а не правка одного файла.
|
|
||||||
|
|
||||||
<!-- /копия: язык-правила -->
|
|
||||||
|
|
||||||
## Порог правки
|
|
||||||
|
|
||||||
<!-- копия: порог-правки из shared/language.md -->
|
|
||||||
|
|
||||||
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
|
||||||
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
|
||||||
перестают читать весь список, и вместе с ним пропадают настоящие находки.
|
|
||||||
Сомневаешься — не правь. Формулировка, которая просто **не твоя**, — не находка.
|
|
||||||
|
|
||||||
**Систематичность нарушения — не довод в его пользу.** Одна и та же ошибка в
|
|
||||||
пяти файлах не становится «принятым стилем»: чаще это значит, что правило не
|
|
||||||
применялось вовсе, — и находка тем важнее. «Так сделано везде» годится как
|
|
||||||
основание для **одной находки на весь набор** («правило N нарушено в пяти
|
|
||||||
записях, перечень: …»), но не как основание промолчать. Принятым считается
|
|
||||||
только то, что назвал зовущий или что записано в конвенциях проекта.
|
|
||||||
|
|
||||||
<!-- /копия: порог-правки -->
|
|
||||||
|
|
||||||
И обратное: язык правится **по ходу той операции, которая записи касается**.
|
|
||||||
Беклог не переписывают ради языка.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Сопровождение и эксплуатация
|
|
||||||
|
|
||||||
**Копия.** Дом — `shared/operations.md` в репозитории плагинов. Словарь общий для
|
|
||||||
роадмапа, архитектуры и темы ревью `operations`, и не принадлежит ни одному из
|
|
||||||
трёх — правится дом, а не этот файл.
|
|
||||||
|
|
||||||
Скиллу задач он нужен для секции `Сопровождение` в `ROADMAP.md`: она отвечает не
|
|
||||||
на «что приложение будет уметь», а на «чем его держат», и путать эти два вопроса
|
|
||||||
нельзя.
|
|
||||||
|
|
||||||
<!-- копия: сопровождение-словарь из shared/operations.md -->
|
|
||||||
|
|
||||||
Одна тема живёт в трёх местах, и путать их слова нельзя.
|
|
||||||
|
|
||||||
**Сопровождение** — всё, чем держат проект: инструмент и сборка, процесс,
|
|
||||||
выкладка, метрики и логи, инфраструктура, дежурство. **Эксплуатация** — его
|
|
||||||
часть: работа системы на проде. Целое и часть, и никогда наоборот.
|
|
||||||
|
|
||||||
| Место | Уровень | Что там |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `ROADMAP.md`, секция `Сопровождение` | план | **работы**, которые собираемся делать: цели и их задачи |
|
|
||||||
| `architecture.md`, раздел «Эксплуатация» | состояние | **как устроено сейчас**: где работает, что рядом, кто перезапускает |
|
|
||||||
| тема ревью `operations` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
|
||||||
|
|
||||||
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
|
||||||
пользователю, а это другая работа.
|
|
||||||
|
|
||||||
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
|
||||||
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
|
||||||
целей: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
|
||||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
|
||||||
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
|
||||||
|
|
||||||
<!-- /копия: сопровождение-словарь -->
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "av-dev",
|
||||||
|
"description": "Личный процесс разработки одним плагином: документы проекта, учёт работ и работа по задачам. Документы — канон раскладки (CLAUDE.md плюс docs/: паспорт, архитектура, схема БД, безопасность, конвенции, разведка, ADR, журнал ревью), три операции одной машиной сравнения в doc-canon (check, adopt, upgrade) со скриптом docs.py, заведение нового проекта интервью в doc-init, ведение содержимого по ходу разработки в doc-sync, суд о смысловом здоровье в doc-healthcheck двумя агентами разом. Учёт — задачи и цели каталогом markdown-файлов в task-track, у записи тип (goal, feature, fix, chore, research), и тип решает её схему; приоритет расставляет интерактивный task-groom порядком строк. Работа — одна задача от постановки до закрытия скиллом code-resolve: точка входа одна, сценария три (решение циклом SDD с чекпоинтом, разведка без кода, обслуживание без change), и выбирает сценарий сам скилл, прочитав постановку; плюс конвейер ревью code-review с детерминированным гейтом, сверкой со спеками и обязательным триажем, плюс code-openspec, который заводит и проверяет openspec/. Версия раскладки и настройки живут в .av-dev.toml в корне репозитория. Части включаются следом в проекте, а не установкой: нет docs/ — документы не ведутся, нет каталога задач — учёт остаётся владельцу, нет openspec/ — его заводит сценарий решения. Коммиты — отдельный плагин av-dev-git.",
|
||||||
|
"author": {
|
||||||
|
"name": "Anton Vakhrushev",
|
||||||
|
"email": "anwinged@gmail.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: doc-code-drift
|
name: doc-code-drift
|
||||||
description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .docs.json, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Зовётся скиллом av-dev-docs:healthcheck — на весь канон разом; он же зовётся шагом adopt и шагом upgrade. Только чтение."
|
description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .av-dev.toml, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Зовётся скиллом av-dev:doc-healthcheck — на весь канон разом; он же зовётся шагом adopt и шагом upgrade. Только чтение."
|
||||||
tools: Read, Grep, Glob, Bash
|
tools: Read, Grep, Glob, Bash
|
||||||
model: sonnet
|
model: sonnet
|
||||||
color: green
|
color: green
|
||||||
@@ -37,7 +37,7 @@ color: green
|
|||||||
|
|
||||||
## Что тебе дают
|
## Что тебе дают
|
||||||
|
|
||||||
Корень проекта. Читаешь `CLAUDE.md`, `docs/**`, `docs/.docs.json`,
|
Корень проекта. Читаешь `CLAUDE.md`, `docs/**`, `.av-dev.toml`,
|
||||||
`openspec/specs/**` — и репозиторий: манифесты зависимостей, конфиги, файлы
|
`openspec/specs/**` — и репозиторий: манифесты зависимостей, конфиги, файлы
|
||||||
сборки и CI, дерево пакетов.
|
сборки и CI, дерево пакетов.
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ color: green
|
|||||||
держит прежнее имя.
|
держит прежнее имя.
|
||||||
|
|
||||||
3. **Пути** — все, которые канон обязывает называть: `migrations` из
|
3. **Пути** — все, которые канон обязывает называть: `migrations` из
|
||||||
`docs/.docs.json`, `testdata`, временный каталог, пути в запретах `CLAUDE.md`.
|
`.av-dev.toml`, `testdata`, временный каталог, пути в запретах `CLAUDE.md`.
|
||||||
Проверка: существует ли. Путь в запрете, которого нет, — находка **особого
|
Проверка: существует ли. Путь в запрете, которого нет, — находка **особого
|
||||||
рода**: запрет, который не на что наложить, читается как соблюдённый, а на
|
рода**: запрет, который не на что наложить, читается как соблюдённый, а на
|
||||||
деле охраняет пустоту, пока настоящий каталог зовётся иначе.
|
деле охраняет пустоту, пока настоящий каталог зовётся иначе.
|
||||||
@@ -147,7 +147,7 @@ color: green
|
|||||||
```
|
```
|
||||||
факт источник проверено чем итог
|
факт источник проверено чем итог
|
||||||
имя основной ветки CLAUDE.md git branch сошлось
|
имя основной ветки CLAUDE.md git branch сошлось
|
||||||
путь миграций docs/.docs.json ls РАЗОШЛОСЬ
|
путь миграций .av-dev.toml ls РАЗОШЛОСЬ
|
||||||
внешние зависимости architecture.md go.mod 2 не названы
|
внешние зависимости architecture.md go.mod 2 не названы
|
||||||
единые точки: парсер входа architecture.md grep по формату сошлось
|
единые точки: парсер входа architecture.md grep по формату сошлось
|
||||||
настройки БД database.md — не проверено
|
настройки БД database.md — не проверено
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: doc-consistency
|
name: doc-consistency
|
||||||
description: "Сверка документов канона между собой и с openspec: один факт, живущий в двух домах, прямое противоречие между документами (периметр, зависимости, обратимость), поведение системы, осевшее в architecture.md вместо спек, capability без обзора или с пересказом требований, число без провенанса в research, ADR без ссылки на источник (архивный design.md либо записка разведки) и без парного статуса при замене, заглушка вместо честной строки в пустом слоте. Читает docs/ и openspec/, кода не читает. Отдаёт готовые формулировки и ничего не правит сам. Соответствие документов коду смотрит агент doc-code-drift, язык — doc-wording. Зовётся скиллом av-dev-docs:healthcheck — на весь канон разом; он же зовётся шагом adopt и шагом upgrade. На отдельной задаче и на синке документации не звать. Только чтение."
|
description: "Сверка документов канона между собой и с openspec: один факт, живущий в двух домах, прямое противоречие между документами (периметр, зависимости, обратимость), поведение системы, осевшее в architecture.md вместо спек, capability без обзора или с пересказом требований, число без провенанса в research, ADR без ссылки на источник (архивный design.md либо записка разведки) и без парного статуса при замене, заглушка вместо честной строки в пустом слоте. Читает docs/ и openspec/, кода не читает. Отдаёт готовые формулировки и ничего не правит сам. Соответствие документов коду смотрит агент doc-code-drift, язык — doc-wording. Зовётся скиллом av-dev:doc-healthcheck — на весь канон разом; он же зовётся шагом adopt и шагом upgrade. На отдельной задаче и на синке документации не звать. Только чтение."
|
||||||
tools: Read, Grep, Glob
|
tools: Read, Grep, Glob
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -15,11 +15,12 @@ color: yellow
|
|||||||
машина, а что человек», и её правая колонка — твой устав дословно.
|
машина, а что человек», и её правая колонка — твой устав дословно.
|
||||||
|
|
||||||
Карта домов, по которой ты судишь о правиле 1, — дословная копия канона; дом её
|
Карта домов, по которой ты судишь о правиле 1, — дословная копия канона; дом её
|
||||||
`av-dev-docs/skills/canon/references/canon.md`, раздел «Правило единственного
|
`av-dev/skills/doc-canon/references/canon.md`, раздел «Правило единственного
|
||||||
дома», и правится она там. Здесь она стоит потому, что ты работаешь в
|
дома», и правится она там. Здесь она стоит потому, что устав — это твой промпт
|
||||||
репозитории проекта, где плагина может не быть вовсе.
|
целиком: за ссылкой ты пошёл бы отдельным чтением, а карта нужна тебе в тот
|
||||||
|
момент, когда ты судишь.
|
||||||
|
|
||||||
<!-- копия: карта-домов из av-dev-docs/skills/canon/references/canon.md -->
|
<!-- копия: карта-домов из av-dev/skills/doc-canon/references/canon.md -->
|
||||||
| Факт | Дом |
|
| Факт | Дом |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| поведение системы | `openspec/specs/<capability>/spec.md` |
|
| поведение системы | `openspec/specs/<capability>/spec.md` |
|
||||||
@@ -47,7 +48,7 @@ color: yellow
|
|||||||
|
|
||||||
Корень проекта. Твоё чтение — `docs/**`, `CLAUDE.md`, `openspec/specs/**` и
|
Корень проекта. Твоё чтение — `docs/**`, `CLAUDE.md`, `openspec/specs/**` и
|
||||||
`openspec/config.yaml`. **Каталог задач не твой** — он лежит в `tasks/` (или в
|
`openspec/config.yaml`. **Каталог задач не твой** — он лежит в `tasks/` (или в
|
||||||
`docs/tasks/` на непереехавшем проекте), принадлежит другому плагину и ведётся
|
`docs/tasks/` на непереехавшем проекте), принадлежит другому скиллу и ведётся
|
||||||
своим скриптом; не открывай его ни в той форме, ни в другой. Плюс
|
своим скриптом; не открывай его ни в той форме, ни в другой. Плюс
|
||||||
`openspec/changes/archive/`, когда проверяешь ADR: там лежат `design.md`, из
|
`openspec/changes/archive/`, когда проверяешь ADR: там лежат `design.md`, из
|
||||||
которых записи промоутятся. **Источник у ADR бывает и второй — записка
|
которых записи промоутятся. **Источник у ADR бывает и второй — записка
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: doc-wording
|
name: doc-wording
|
||||||
description: "Вычитка языка документов проекта по информационному стилю — паспорт, архитектура, конвенции, безопасность, решения ADR, записки разведки, CLAUDE.md. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в имени файла. Отдаёт готовые формулировки на замену и ничего не правит сам. Записи каталога задач вычитывает отдельный агент task-wording, их форму — task-form. Зовётся по названной пачке правленных документов, а не на весь канон: последним шагом синка документации (av-dev-docs:docs), шагом заведения проекта (av-dev-docs:init), шагами adopt и upgrade скилла av-dev-docs:canon. Скилл healthcheck его не зовёт — там сверка утверждений, а не языка. Только чтение."
|
description: "Вычитка языка документов проекта по информационному стилю — паспорт, архитектура, конвенции, безопасность, решения ADR, записки разведки, CLAUDE.md. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в имени файла. Отдаёт готовые формулировки на замену и ничего не правит сам. Записи каталога задач вычитывает отдельный агент task-wording, их форму — task-form. Зовётся по названной пачке правленных документов, а не на весь канон: последним шагом синка документации (av-dev:doc-sync), шагом заведения проекта (av-dev:doc-init), шагами adopt и upgrade скилла av-dev:doc-canon. Скилл healthcheck его не зовёт — там сверка утверждений, а не языка. Только чтение."
|
||||||
tools: Read, Grep, Glob
|
tools: Read, Grep, Glob
|
||||||
model: sonnet
|
model: sonnet
|
||||||
color: green
|
color: green
|
||||||
@@ -31,10 +31,10 @@ color: green
|
|||||||
|
|
||||||
## Правила
|
## Правила
|
||||||
|
|
||||||
Дом — `shared/language.md` в репозитории плагинов, и там же объяснено, зачем
|
Дом — `shared/language.md` в репозитории плагина, и там же объяснено, зачем
|
||||||
стиль вообще нужен. Здесь только то, что нужно тебе для работы.
|
стиль вообще нужен. Здесь только то, что нужно тебе для работы.
|
||||||
|
|
||||||
<!-- копия: язык-правила из shared/language.md -->
|
<!-- копия: язык-правила из av-dev/shared/language.md -->
|
||||||
|
|
||||||
У каждого правила названа причина: она же говорит, где правило **не**
|
У каждого правила названа причина: она же говорит, где правило **не**
|
||||||
применяется.
|
применяется.
|
||||||
@@ -184,7 +184,7 @@ color: green
|
|||||||
**Машинной проверке — вообще ничего.** Всё, что ловит `docs.py check` (пути
|
**Машинной проверке — вообще ничего.** Всё, что ловит `docs.py check` (пути
|
||||||
канона, файлы вне канона, имена файлов, битые ссылки, версия канона, нетронутые
|
канона, файлы вне канона, имена файлов, битые ссылки, версия канона, нетронутые
|
||||||
плейсхолдеры, маркеры долга) и что ловит `openspec.py check` скилла
|
плейсхолдеры, маркеры долга) и что ловит `openspec.py check` скилла
|
||||||
`av-dev-code:openspec` (форма `openspec/config.yaml`), **не пиши даже
|
`av-dev:code-openspec` (форма `openspec/config.yaml`), **не пиши даже
|
||||||
строкой**: это не потерянная находка, а уже проверенное. Повторять машинную
|
строкой**: это не потерянная находка, а уже проверенное. Повторять машинную
|
||||||
проверку словами — заводить второй дом для одного правила.
|
проверку словами — заводить второй дом для одного правила.
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ color: green
|
|||||||
|
|
||||||
## Порог вмешательства
|
## Порог вмешательства
|
||||||
|
|
||||||
<!-- копия: порог-правки из shared/language.md -->
|
<!-- копия: порог-правки из av-dev/shared/language.md -->
|
||||||
|
|
||||||
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
||||||
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
||||||
@@ -218,7 +218,7 @@ color: green
|
|||||||
|
|
||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
<!-- копия: вычитка-доклад из shared/language.md -->
|
<!-- копия: вычитка-доклад из av-dev/shared/language.md -->
|
||||||
|
|
||||||
Находки по одной, в порядке важности: залог и оценки → жаргон и англицизмы →
|
Находки по одной, в порядке важности: залог и оценки → жаргон и англицизмы →
|
||||||
стоп-слова. Первые меняют, **что** читатель понимает; последние — только сколько
|
стоп-слова. Первые меняют, **что** читатель понимает; последние — только сколько
|
||||||
@@ -13,7 +13,7 @@ color: yellow
|
|||||||
равно опасен.
|
равно опасен.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
**Ты помечен «держит машину»** — за тем, чтобы построенный путь можно было
|
**Ты помечен «держит машину»** — за тем, чтобы построенный путь можно было
|
||||||
@@ -73,7 +73,7 @@ color: yellow
|
|||||||
находкой.
|
находкой.
|
||||||
|
|
||||||
Карта «что нужно проходу → где лежит» —
|
Карта «что нужно проходу → где лежит» —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`.
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`.
|
||||||
|
|
||||||
**Деградация поразрядная, и каждый пробел называется своей строкой.**
|
**Деградация поразрядная, и каждый пробел называется своей строкой.**
|
||||||
`docs/security.md` нет — работай по общей рамке ниже, `critical` не присваивай и
|
`docs/security.md` нет — работай по общей рамке ниже, `critical` не присваивай и
|
||||||
@@ -25,7 +25,7 @@ color: yellow
|
|||||||
и граф зависимостей есть только у тебя.
|
и граф зависимостей есть только у тебя.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
## Вход (собери до чтения диффа)
|
## Вход (собери до чтения диффа)
|
||||||
@@ -52,7 +52,7 @@ grep по именам концепций) и скажи об этом в гра
|
|||||||
- дельта-спеки change.
|
- дельта-спеки change.
|
||||||
|
|
||||||
Карта «что нужно проходу → где лежит» —
|
Карта «что нужно проходу → где лежит» —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`.
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`.
|
||||||
|
|
||||||
Дифф — **последним, не первым**: он должен ложиться на карту, а не задавать её.
|
Дифф — **последним, не первым**: он должен ложиться на карту, а не задавать её.
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ color: green
|
|||||||
намеренно нет, из темы не выпадает — она уходит в границы покрытия.
|
намеренно нет, из темы не выпадает — она уходит в границы покрытия.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании). Русская проза, идентификаторы и
|
(точный путь конвейер передаёт в задании). Русская проза, идентификаторы и
|
||||||
команды — в оригинале.
|
команды — в оригинале.
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ color: green
|
|||||||
запускать запрещено, с путями.
|
запускать запрещено, с путями.
|
||||||
|
|
||||||
Карта «что нужно проходу → где лежит» —
|
Карта «что нужно проходу → где лежит» —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`.
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`.
|
||||||
|
|
||||||
**Семантики гейта в `CLAUDE.md` нет** — найди команду сама (`Taskfile.yml`,
|
**Семантики гейта в `CLAUDE.md` нет** — найди команду сама (`Taskfile.yml`,
|
||||||
`Makefile`, `justfile`, `scripts/`) и выполни её, но: `critical` по основанию
|
`Makefile`, `justfile`, `scripts/`) и выполни её, но: `critical` по основанию
|
||||||
@@ -96,7 +96,7 @@ color: green
|
|||||||
просило: она может стоить минут и трогать данные.
|
просило: она может стоить минут и трогать данные.
|
||||||
- **Правило есть в конвенциях, но не в линтере.** Если по ходу видно, что отказ
|
- **Правило есть в конвенциях, но не в линтере.** Если по ходу видно, что отказ
|
||||||
или замечание могло быть поймано правилом, — пиши `Promote candidate` по
|
или замечание могло быть поймано правилом, — пиши `Promote candidate` по
|
||||||
процедуре `${CLAUDE_PLUGIN_ROOT}/skills/review/references/promote.md`.
|
процедуре `${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/promote.md`.
|
||||||
|
|
||||||
## Что читать не нужно
|
## Что читать не нужно
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ color: yellow
|
|||||||
самый дорогой проход, вместо которого его позвали.
|
самый дорогой проход, вместо которого его позвали.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
## Что тебе даёт план прогона
|
## Что тебе даёт план прогона
|
||||||
@@ -152,7 +152,7 @@ color: yellow
|
|||||||
**Молча отменённое решение ADR больше не проверяет никто, и это сознательно.**
|
**Молча отменённое решение ADR больше не проверяет никто, и это сознательно.**
|
||||||
Раньше вопрос стоял здесь и требовал чтения индекса решений; теперь `docs/adr/` —
|
Раньше вопрос стоял здесь и требовал чтения индекса решений; теперь `docs/adr/` —
|
||||||
процессный документ, и прогон его не открывает. Расхождение изменения с записанным
|
процессный документ, и прогон его не открывает. Расхождение изменения с записанным
|
||||||
решением ловит сверка документации — скилл `av-dev-docs:healthcheck`. Строка об
|
решением ловит сверка документации — скилл `av-dev:doc-healthcheck`. Строка об
|
||||||
этом обязательна в твоих границах покрытия.
|
этом обязательна в твоих границах покрытия.
|
||||||
|
|
||||||
**Карты проекта и графа зависимостей у тебя нет** — они стоят широкого входа, то
|
**Карты проекта и графа зависимостей у тебя нет** — они стоят широкого входа, то
|
||||||
@@ -58,7 +58,7 @@ color: yellow
|
|||||||
его неизбежным.
|
его неизбежным.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании). Русская проза, идентификаторы и пути —
|
(точный путь конвейер передаёт в задании). Русская проза, идентификаторы и пути —
|
||||||
в оригинале. Читай реальный код, ничего не выдумывай.
|
в оригинале. Читай реальный код, ничего не выдумывай.
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ color: green
|
|||||||
увидит владелец сервиса, и дойди до строки кода.
|
увидит владелец сервиса, и дойди до строки кода.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
**Ты помечен «держит машину».** Конвейер за это ставит тебя в цепочку с другими
|
**Ты помечен «держит машину».** Конвейер за это ставит тебя в цепочку с другими
|
||||||
@@ -46,7 +46,7 @@ color: green
|
|||||||
`docs/research/` процессный документ, и прогон его не открывает; чужое число
|
`docs/research/` процессный документ, и прогон его не открывает; чужое число
|
||||||
неизвестной свежести делало находку похожей на доказанную, ничего не доказывая.
|
неизвестной свежести делало находку похожей на доказанную, ничего не доказывая.
|
||||||
Почему именно так и какие ещё есть стыки —
|
Почему именно так и какие ещё есть стыки —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`, раздел
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`, раздел
|
||||||
«Сшивать обязаны проходы». Там же карта «что нужно проходу → где лежит».
|
«Сшивать обязаны проходы». Там же карта «что нужно проходу → где лежит».
|
||||||
|
|
||||||
Два обстоятельства почти всегда меняют цену отказов, и если документы их
|
Два обстоятельства почти всегда меняют цену отказов, и если документы их
|
||||||
@@ -11,7 +11,7 @@ color: yellow
|
|||||||
**порождаешь сам** — и делаешь это до того, как увидишь код.
|
**порождаешь сам** — и делаешь это до того, как увидишь код.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании). Русская проза, идентификаторы — в
|
(точный путь конвейер передаёт в задании). Русская проза, идентификаторы — в
|
||||||
оригинале.
|
оригинале.
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ color: yellow
|
|||||||
сформулированное по прецеденту, сильнее любого общего.
|
сформулированное по прецеденту, сильнее любого общего.
|
||||||
|
|
||||||
Карта «что нужно проходу → где лежит» —
|
Карта «что нужно проходу → где лежит» —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`.
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`.
|
||||||
|
|
||||||
**Документа нет — строка на каждый, отдельно.** Нет `docs/review.md`: «рода
|
**Документа нет — строка на каждый, отдельно.** Нет `docs/review.md`: «рода
|
||||||
узлов и прецеденты неизвестны; требование „минимум три пункта специфичны для
|
узлов и прецеденты неизвестны; требование „минимум три пункта специфичны для
|
||||||
@@ -106,7 +106,7 @@ color: yellow
|
|||||||
Пункты рубрики, которых **нет в конвенциях проекта**, — кандидаты на промоут: это
|
Пункты рубрики, которых **нет в конвенциях проекта**, — кандидаты на промоут: это
|
||||||
и есть неявный слой, ради которого проход существует. Выведи их отдельной секцией
|
и есть неявный слой, ради которого проход существует. Выведи их отдельной секцией
|
||||||
`Promote candidates` (процедура —
|
`Promote candidates` (процедура —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/promote.md`).
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/promote.md`).
|
||||||
|
|
||||||
## Чего этот проход принципиально не может поймать
|
## Чего этот проход принципиально не может поймать
|
||||||
|
|
||||||
@@ -75,12 +75,12 @@ color: green
|
|||||||
| **дельта-спеки** | сколько capability затронуто и сколько требований в каждой | `ADDED` целой capability — поведения такого рода не было; только `MODIFIED` в одной — было |
|
| **дельта-спеки** | сколько capability затронуто и сколько требований в каждой | `ADDED` целой capability — поведения такого рода не было; только `MODIFIED` в одной — было |
|
||||||
|
|
||||||
**Записи задачи может не быть вовсе, и это не довод за незнакомое.** Задача
|
**Записи задачи может не быть вовсе, и это не довод за незнакомое.** Задача
|
||||||
приходит текстом или из проекта без плагина задач — тогда раздела «Затрагивает»
|
приходит текстом или из проекта без каталога задач — тогда раздела «Затрагивает»
|
||||||
нет **по построению**, а не потому, что границы не назвали. Отличай:
|
нет **по построению**, а не потому, что границы не назвали. Отличай:
|
||||||
запись есть, а раздела в ней нет → незнакомое, как сказано в таблице; записи нет
|
запись есть, а раздела в ней нет → незнакомое, как сказано в таблице; записи нет
|
||||||
→ строка источника снимается, обе оси выводятся из остальных четырёх, и это
|
→ строка источника снимается, обе оси выводятся из остальных четырёх, и это
|
||||||
называется в плане строкой «записи задачи нет, оси выведены по четырём
|
называется в плане строкой «записи задачи нет, оси выведены по четырём
|
||||||
источникам». Иначе всякая задача без плагина задач систематически едет в `large`
|
источникам». Иначе всякая задача без каталога задач систематически едет в `large`
|
||||||
за то, чего никто не терял.
|
за то, чего никто не терял.
|
||||||
|
|
||||||
**`design.md` информативен и своим отсутствием.** Его нет — либо задача
|
**`design.md` информативен и своим отсутствием.** Его нет — либо задача
|
||||||
@@ -117,7 +117,7 @@ color: green
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **тема** | `conventions.*`, `security.*`, `architecture.*`, любой свой документ проекта | заводишь строку темы и назначаешь исполнителя |
|
| **тема** | `conventions.*`, `security.*`, `architecture.*`, любой свой документ проекта | заводишь строку темы и назначаешь исполнителя |
|
||||||
| **источник темы** | `passport.*`, `database.*` | называешь адресом **внутри** строки чужой темы, своей строки не заводишь |
|
| **источник темы** | `passport.*`, `database.*` | называешь адресом **внутри** строки чужой темы, своей строки не заводишь |
|
||||||
| **процессный** | `tasks/`, `review.*`, `adr.*`, `research.*`, `.docs.json` | называешь строкой «процессный», исполнителя нет и не должно быть |
|
| **процессный** | `tasks/`, `review.*`, `adr.*`, `research.*`, `.av-dev.toml` | называешь строкой «процессный», исполнителя нет и не должно быть |
|
||||||
|
|
||||||
`docs/review.*` при этом ты читаешь — но как **настройку конвейера**, откуда
|
`docs/review.*` при этом ты читаешь — но как **настройку конвейера**, откуда
|
||||||
берутся вопросы по темам и триггеры метки, а не как тему. `adr.*` и `research.*`
|
берутся вопросы по темам и триггеры метки, а не как тему. `adr.*` и `research.*`
|
||||||
@@ -128,7 +128,7 @@ color: green
|
|||||||
**Каждая запись в `docs/` обязана попасть в план строкой своей категории.** Не «я
|
**Каждая запись в `docs/` обязана попасть в план строкой своей категории.** Не «я
|
||||||
посмотрел и решил» — перечислением. Это и есть проверка твоей работы: план
|
посмотрел и решил» — перечислением. Это и есть проверка твоей работы: план
|
||||||
сверяется с `ls docs/` за секунду, и пропущенный документ виден без рассуждения.
|
сверяется с `ls docs/` за секунду, и пропущенный документ виден без рассуждения.
|
||||||
`docs/.docs.json` — единственное исключение: служебный файл, не документ, в плане
|
`.av-dev.toml` — единственное исключение: служебный файл, не документ, в плане
|
||||||
не упоминается.
|
не упоминается.
|
||||||
|
|
||||||
**Категории `источник` и `процессный` закрыты — они перечислены выше поимённо.**
|
**Категории `источник` и `процессный` закрыты — они перечислены выше поимённо.**
|
||||||
@@ -195,7 +195,7 @@ color: green
|
|||||||
**Ты меряешь изменение по двум независимым осям и называешь обе.** Метка — не
|
**Ты меряешь изменение по двум независимым осям и называешь обе.** Метка — не
|
||||||
ответ на один вопрос, а максимум по двум измерениям.
|
ответ на один вопрос, а максимум по двум измерениям.
|
||||||
|
|
||||||
Ниже рабочая выжимка. Дом правила — скилл `av-dev-code:review`,
|
Ниже рабочая выжимка. Дом правила — скилл `av-dev:code-review`,
|
||||||
`references/review-levels.md`: там разобрано, почему оси именно эти, чем `small`
|
`references/review-levels.md`: там разобрано, почему оси именно эти, чем `small`
|
||||||
дешевле `medium` и какие доли служат проверкой правила. Открывай его, когда
|
дешевле `medium` и какие доли служат проверкой правила. Открывай его, когда
|
||||||
метка **спорная или оспорена**; на обычной задаче хватает того, что здесь.
|
метка **спорная или оспорена**; на обычной задаче хватает того, что здесь.
|
||||||
@@ -10,7 +10,7 @@ color: yellow
|
|||||||
Development на OpenSpec). Оптика — требования, а не стиль кода.
|
Development на OpenSpec). Оптика — требования, а не стиль кода.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании). Русская проза; идентификаторы, пути и
|
(точный путь конвейер передаёт в задании). Русская проза; идентификаторы, пути и
|
||||||
ключевые слова спек (`SHALL`, `GIVEN/WHEN/THEN`) — в оригинале. Читай реальные
|
ключевые слова спек (`SHALL`, `GIVEN/WHEN/THEN`) — в оригинале. Читай реальные
|
||||||
файлы перед выводом, ничего не выдумывай.
|
файлы перед выводом, ничего не выдумывай.
|
||||||
@@ -49,7 +49,7 @@ Development на OpenSpec). Оптика — требования, а не ст
|
|||||||
|
|
||||||
Пути спек жёсткие: актуальные — `openspec/specs/<capability>/spec.md`, дельты —
|
Пути спек жёсткие: актуальные — `openspec/specs/<capability>/spec.md`, дельты —
|
||||||
`openspec/changes/<id>/specs/`. Карта «что нужно проходу → где лежит» —
|
`openspec/changes/<id>/specs/`. Карта «что нужно проходу → где лежит» —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`.
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`.
|
||||||
|
|
||||||
**Нет инвариантов в `CLAUDE.md`** — сверяй только спеку с кодом, `critical` по
|
**Нет инвариантов в `CLAUDE.md`** — сверяй только спеку с кодом, `critical` по
|
||||||
основанию «нарушен инвариант проекта» не присваивай и дай строку: «инвариантов в
|
основанию «нарушен инвариант проекта» не присваивай и дай строку: «инвариантов в
|
||||||
@@ -16,7 +16,7 @@ color: yellow
|
|||||||
Потолок в 7 пунктов защищает код, а не читателя.
|
Потолок в 7 пунктов защищает код, а не читателя.
|
||||||
|
|
||||||
Контракт находок и формат финального отчёта —
|
Контракт находок и формат финального отчёта —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
## Вход
|
## Вход
|
||||||
@@ -47,7 +47,7 @@ color: yellow
|
|||||||
целиком уезжают в границы покрытия и **не сливаются в один список**.
|
целиком уезжают в границы покрытия и **не сливаются в один список**.
|
||||||
|
|
||||||
Карта «что нужно проходу → где лежит» —
|
Карта «что нужно проходу → где лежит» —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`.
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`.
|
||||||
|
|
||||||
**Деградация поразрядная, и ты — тот, кто собирает её строки в один список,
|
**Деградация поразрядная, и ты — тот, кто собирает её строки в один список,
|
||||||
сохраняя каждую.** Свою часть
|
сохраняя каждую.** Свою часть
|
||||||
@@ -207,7 +207,7 @@ severity:
|
|||||||
|
|
||||||
1. **Решения проекта не сверялись.** `docs/adr.*` — процессный документ, прогон
|
1. **Решения проекта не сверялись.** `docs/adr.*` — процессный документ, прогон
|
||||||
его не открывает. Расхождение изменения с записанным решением ловит сверка
|
его не открывает. Расхождение изменения с записанным решением ловит сверка
|
||||||
документации — скилл `av-dev-docs:healthcheck`, а не ревью.
|
документации — скилл `av-dev:doc-healthcheck`, а не ревью.
|
||||||
2. **Записанные наблюдения проекта не использовались.** `docs/research.*` — тоже
|
2. **Записанные наблюдения проекта не использовались.** `docs/research.*` — тоже
|
||||||
процессный. Всякое число в находках снято проходом на этом прогоне; числа без
|
процессный. Всякое число в находках снято проходом на этом прогоне; числа без
|
||||||
приложенной команды замера в отчёте быть не должно.
|
приложенной команды замера в отчёте быть не должно.
|
||||||
@@ -12,7 +12,7 @@ color: green
|
|||||||
|
|
||||||
Оптика — смысл записи в её собственных рамках. Ты **не** судишь, нужна ли
|
Оптика — смысл записи в её собственных рамках. Ты **не** судишь, нужна ли
|
||||||
задача, верно ли выбрана цель и не крупна ли она: это разбор, и его ведёт
|
задача, верно ли выбрана цель и не крупна ли она: это разбор, и его ведёт
|
||||||
человек со скиллом `tasks`.
|
человек со скиллом `task-track`.
|
||||||
|
|
||||||
Границу с языком держи твёрдо. **Залог, оценки, стоп-слова, англицизмы, жаргон**
|
Границу с языком держи твёрдо. **Залог, оценки, стоп-слова, англицизмы, жаргон**
|
||||||
— у агента `task-wording`, и тебе они не поручены даже там, где бросаются в
|
— у агента `task-wording`, и тебе они не поручены даже там, где бросаются в
|
||||||
@@ -148,7 +148,7 @@ color: green
|
|||||||
|
|
||||||
## Порог вмешательства
|
## Порог вмешательства
|
||||||
|
|
||||||
<!-- копия: порог-правки из shared/language.md -->
|
<!-- копия: порог-правки из av-dev/shared/language.md -->
|
||||||
|
|
||||||
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
||||||
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
||||||
@@ -39,10 +39,10 @@ color: green
|
|||||||
|
|
||||||
## Правила
|
## Правила
|
||||||
|
|
||||||
Дом — `shared/language.md` в репозитории плагинов, и там же объяснено, зачем
|
Дом — `shared/language.md` в репозитории плагина, и там же объяснено, зачем
|
||||||
стиль вообще нужен. Здесь только то, что нужно тебе для работы.
|
стиль вообще нужен. Здесь только то, что нужно тебе для работы.
|
||||||
|
|
||||||
<!-- копия: язык-правила из shared/language.md -->
|
<!-- копия: язык-правила из av-dev/shared/language.md -->
|
||||||
|
|
||||||
У каждого правила названа причина: она же говорит, где правило **не**
|
У каждого правила названа причина: она же говорит, где правило **не**
|
||||||
применяется.
|
применяется.
|
||||||
@@ -175,7 +175,7 @@ color: green
|
|||||||
отдельной задачи — самый дешёвый способ сделать беклог нечитаемым тому, кто
|
отдельной задачи — самый дешёвый способ сделать беклог нечитаемым тому, кто
|
||||||
вернётся к нему через квартал.
|
вернётся к нему через квартал.
|
||||||
|
|
||||||
**Правило 9, имя файла.** Кириллицу в слаге и не-kebab-case ловит `tasks.py` —
|
**Правило 9, имя файла.** Кириллицу в слаге и не-kebab-case ловит `tasks.py check` —
|
||||||
про них молчи. Твоё — **транслит**, потому что машина проверяет его эвристикой и
|
про них молчи. Твоё — **транслит**, потому что машина проверяет его эвристикой и
|
||||||
ловит не всё: `sostoyanie-partii` проходит мимо неё. Находка — готовый
|
ловит не всё: `sostoyanie-partii` проходит мимо неё. Находка — готовый
|
||||||
английский слаг на замену плюс напоминание, что переименование это перенос
|
английский слаг на замену плюс напоминание, что переименование это перенос
|
||||||
@@ -201,7 +201,7 @@ color: green
|
|||||||
тоже не твоя находка: твоя — язык того, что уже написано.
|
тоже не твоя находка: твоя — язык того, что уже написано.
|
||||||
|
|
||||||
**Содержание работы**: нужна ли задача, верно ли выбрана цель, не крупна ли она,
|
**Содержание работы**: нужна ли задача, верно ли выбрана цель, не крупна ли она,
|
||||||
достаточна ли декомпозиция. Это разбор, и его ведёт человек со скиллом `tasks`.
|
достаточна ли декомпозиция. Это разбор, и его ведёт человек со скиллом `task-track`.
|
||||||
|
|
||||||
**Полезное действие, параллельность и работающий заголовок** — тоже не твои.
|
**Полезное действие, параллельность и работающий заголовок** — тоже не твои.
|
||||||
Они в доктрине языка, судит их человек: находка по ним требует увидеть текст
|
Они в доктрине языка, судит их человек: находка по ним требует увидеть текст
|
||||||
@@ -209,7 +209,7 @@ color: green
|
|||||||
|
|
||||||
## Порог вмешательства
|
## Порог вмешательства
|
||||||
|
|
||||||
<!-- копия: порог-правки из shared/language.md -->
|
<!-- копия: порог-правки из av-dev/shared/language.md -->
|
||||||
|
|
||||||
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
||||||
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
||||||
@@ -230,7 +230,7 @@ color: green
|
|||||||
|
|
||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
<!-- копия: вычитка-доклад из shared/language.md -->
|
<!-- копия: вычитка-доклад из av-dev/shared/language.md -->
|
||||||
|
|
||||||
Находки по одной, в порядке важности: залог и оценки → жаргон и англицизмы →
|
Находки по одной, в порядке важности: залог и оценки → жаргон и англицизмы →
|
||||||
стоп-слова. Первые меняют, **что** читатель понимает; последние — только сколько
|
стоп-слова. Первые меняют, **что** читатель понимает; последние — только сколько
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Чего может не быть
|
||||||
|
|
||||||
|
**Это дом.** Правило нужно почти каждому скиллу: любой приходит в проект, где
|
||||||
|
может не оказаться ни документов канона, ни каталога задач, ни `openspec/`, а
|
||||||
|
рядом может не стоять внешний плагин, которого он ждёт. Ни один скилл правилом
|
||||||
|
не владеет, поэтому дом стоит в `shared/`, а скиллы везут **копии**, помеченные
|
||||||
|
разметкой `copies.py`.
|
||||||
|
|
||||||
|
Правится **здесь**. Копия, поправленная у себя, — расхождение, а не правка.
|
||||||
|
|
||||||
|
До слияния правило называлось «граница плагинов» и говорило о соседе:
|
||||||
|
`av-dev-docs`, `av-dev-tasks` и `av-dev-code` ставились порознь, и каждый обязан
|
||||||
|
был пережить отсутствие двоих. Плагин теперь один, а правило осталось, и не по
|
||||||
|
инерции: **отсутствовала всё это время не установка, а раскладка проекта**, и
|
||||||
|
узнавалась она следом на диске, а не перечнем плагинов. Перечень того, чего
|
||||||
|
может не быть, стал короче на три имени — механика не изменилась вовсе.
|
||||||
|
|
||||||
|
Правило завели по подсчёту: к первому расколу оно стояло в пяти местах в пяти
|
||||||
|
редакциях, и три из пяти молчали о том, ради чего написано, — что делать, когда
|
||||||
|
недостающее нашлось.
|
||||||
|
|
||||||
|
<!-- дом: отсутствие -->
|
||||||
|
|
||||||
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
|
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||||
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
|
поведении.
|
||||||
|
|
||||||
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
|
|
||||||
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
|
<!-- /дом: отсутствие -->
|
||||||
|
|
||||||
|
**Что в дом не идёт: чем именно оборачивается нехватка у тебя.** «Нет каталога
|
||||||
|
задач — учёт остаётся владельцу» знает конвейер; «нет `openspec/` — `docs.py`
|
||||||
|
о каталоге молчит» знает канон. Правило общее, последствие местное, и держать
|
||||||
|
последствия здесь значило бы завести дом, который знает про всех своих
|
||||||
|
потребителей.
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Служебный файл проекта `.av-dev.toml`: чтение, запись, узнавание прежних.
|
||||||
|
|
||||||
|
**Это дом.** Файл один на весь плагин, поэтому и разбор у него один: `docs.py`
|
||||||
|
и `tasks.py` берут настройки отсюда, а не каждый своим кодом. Два разбора одного
|
||||||
|
формата — это два дома для одной схемы, и расходятся они молча: первым
|
||||||
|
разъезжается не значение ключа, а то, что скрипт делает, ключа не увидев.
|
||||||
|
|
||||||
|
Формат TOML выбран ради **комментариев**: файл лежит в чужом репозитории, и
|
||||||
|
человек, открывший его через полгода, обязан прочитать в нём, что означает
|
||||||
|
число. JSON комментариев не знает, и объяснение приходилось держать в
|
||||||
|
документации, то есть в другом файле.
|
||||||
|
|
||||||
|
Читается `tomllib` из стандартной библиотеки (python 3.11+), пишется руками:
|
||||||
|
писателя TOML в стандартной библиотеке нет, а комментарии переживают только
|
||||||
|
построчную правку. Поэтому версия двигается заменой одной строки, а не
|
||||||
|
перезаписью файла — иначе повышение канона стирало бы то, ради чего формат и
|
||||||
|
взят.
|
||||||
|
|
||||||
|
Схема:
|
||||||
|
|
||||||
|
version = 1 # версия раскладки av-dev, целое число
|
||||||
|
|
||||||
|
[docs]
|
||||||
|
migrations = "путь/к/миграциям" # необязателен: есть БД — есть ключ
|
||||||
|
|
||||||
|
[tasks]
|
||||||
|
dir = "tasks" # каталог задач от корня репозитория
|
||||||
|
items = "items" # имена частей каталога — необязательны
|
||||||
|
backlog = "BACKLOG.md"
|
||||||
|
roadmap = "ROADMAP.md"
|
||||||
|
|
||||||
|
Коды выхода зовущих скриптов — общий словарь av-dev; отсюда возвращается
|
||||||
|
исключение `ConfigError`, а решает по нему вызывающий.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Имя файла называет владельца: раскладку ведёт плагин `av-dev`. До слияния
|
||||||
|
# плагинов файлов было два — `docs/.docs.json` (версия канона) и
|
||||||
|
# `<каталог задач>/.tasks.json` (версия формата задач), и версии двигались
|
||||||
|
# порознь, потому что плагины ставились порознь. Плагин теперь один, версия
|
||||||
|
# одна, и дом у неё в корне репозитория: настройки нужны и проекту без `docs/`,
|
||||||
|
# и проекту без каталога задач, а корень есть у обоих.
|
||||||
|
CONFIG_NAME = ".av-dev.toml"
|
||||||
|
|
||||||
|
# Прежние дома. Читаются не для работы, а для узнавания: увидели — говорим
|
||||||
|
# «старая раскладка, нужен upgrade», и это одна строка вместо отказа, за которым
|
||||||
|
# человек идёт заводить второй файл рядом с первым.
|
||||||
|
LEGACY = ("docs/.docs.json", "docs/.pm.json")
|
||||||
|
LEGACY_TASKS = ".tasks.json"
|
||||||
|
|
||||||
|
# Версия раскладки — одна на плагин. Журнал версий — references/changelog.md
|
||||||
|
# скилла `doc-canon`, повышает его операция `upgrade`.
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
VERSION_KEY = "version"
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigError(Exception):
|
||||||
|
"""Файл есть, но прочитать его нельзя: битый TOML или не та схема."""
|
||||||
|
|
||||||
|
|
||||||
|
def find_root(start: Path | None = None) -> Path | None:
|
||||||
|
"""Корень проекта: где лежит `.av-dev.toml`, иначе где лежит `.git`.
|
||||||
|
|
||||||
|
Обе опоры нужны: до `adopt` файла ещё нет, а работать по каталогу задач уже
|
||||||
|
можно. Возвращается None, когда нет ни того, ни другого, — тогда зовущий сам
|
||||||
|
решает, отказ это или неприменимость.
|
||||||
|
|
||||||
|
**Подъём останавливается на первом `.git`, и это не деталь.** Репозиторий
|
||||||
|
внутри репозитория — обычное дело, и без границы конфиг соседа выигрывал бы
|
||||||
|
у собственного: вложенный проект объявлялся бы здоровым по чужому файлу, а
|
||||||
|
запись настроек уходила бы в чужой репозиторий. Свой файл ищется **до**
|
||||||
|
границы включительно, чужой не ищется вовсе.
|
||||||
|
"""
|
||||||
|
here = (start or Path.cwd()).resolve()
|
||||||
|
for base in (here, *here.parents):
|
||||||
|
if (base / CONFIG_NAME).is_file():
|
||||||
|
return base
|
||||||
|
if (base / ".git").exists():
|
||||||
|
return base # корень репозитория есть, настроек в нём нет
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def read(root: Path) -> dict:
|
||||||
|
"""Настройки проекта. Файла нет — пустой словарь, это не ошибка."""
|
||||||
|
path = root / CONFIG_NAME
|
||||||
|
if not path.is_file():
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
# Читаем байтами: `tomllib.load` сам знает про кодировку TOML, а
|
||||||
|
# `read_text` на файле не в UTF-8 роняет UnicodeDecodeError — ошибку
|
||||||
|
# окружения, которая ушла бы наружу внутренним сбоем.
|
||||||
|
with path.open("rb") as fh:
|
||||||
|
data = tomllib.load(fh)
|
||||||
|
except tomllib.TOMLDecodeError as exc:
|
||||||
|
raise ConfigError(f"{CONFIG_NAME} не разбирается как TOML: {exc}") from exc
|
||||||
|
except (OSError, ValueError) as exc:
|
||||||
|
raise ConfigError(f"{CONFIG_NAME} не читается: {exc}") from exc
|
||||||
|
_validate(data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# Ключи верхнего уровня. Секции знают свои ключи сами: `[docs]` проверяет
|
||||||
|
# `docs.py`, `[tasks]` — `tasks.py`. Здесь только то, что образует сам файл.
|
||||||
|
TOP_KEYS = (VERSION_KEY, "docs", "tasks")
|
||||||
|
|
||||||
|
|
||||||
|
def check_keys(data: dict, known: tuple[str, ...], where: str) -> None:
|
||||||
|
"""Неизвестный ключ — отказ, а не безмолвный пропуск.
|
||||||
|
|
||||||
|
Ключ, положенный не туда (`migrations` верхним уровнем вместо `[docs]` —
|
||||||
|
ровно так он лежал в прежнем `.docs.json`, и ровно так его перенесут руками),
|
||||||
|
иначе не значит ничего: проверка объявляет себя неприменимой, отчёт выходит
|
||||||
|
зелёным, и на месте настройки оказывается тишина.
|
||||||
|
"""
|
||||||
|
unknown = sorted(set(data) - set(known))
|
||||||
|
if unknown:
|
||||||
|
raise ConfigError(
|
||||||
|
f"{CONFIG_NAME}: неизвестные ключи {where}: {', '.join(unknown)}"
|
||||||
|
f" (известны: {', '.join(known)})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _validate(data: dict) -> None:
|
||||||
|
got = data.get(VERSION_KEY)
|
||||||
|
if VERSION_KEY in data and (isinstance(got, bool) or not isinstance(got, int)):
|
||||||
|
raise ConfigError(
|
||||||
|
f"{CONFIG_NAME}: ключ «{VERSION_KEY}» — версия раскладки,"
|
||||||
|
f" ожидалось целое число, а не {got!r}"
|
||||||
|
)
|
||||||
|
for name in ("docs", "tasks"):
|
||||||
|
got_section = data.get(name)
|
||||||
|
if got_section is not None and not isinstance(got_section, dict):
|
||||||
|
raise ConfigError(
|
||||||
|
f"{CONFIG_NAME}: секция [{name}] — ожидалась таблица настроек,"
|
||||||
|
f" а не {got_section!r}"
|
||||||
|
)
|
||||||
|
check_keys(data, TOP_KEYS, "верхнего уровня")
|
||||||
|
|
||||||
|
|
||||||
|
def section(cfg: dict, name: str) -> dict:
|
||||||
|
got = cfg.get(name, {})
|
||||||
|
return got if isinstance(got, dict) else {}
|
||||||
|
|
||||||
|
|
||||||
|
def version(cfg: dict) -> int | None:
|
||||||
|
got = cfg.get(VERSION_KEY)
|
||||||
|
return got if isinstance(got, int) and not isinstance(got, bool) else None
|
||||||
|
|
||||||
|
|
||||||
|
def legacy_files(root: Path, tasks_dir: Path | None = None) -> list[str]:
|
||||||
|
"""Следы прежней раскладки — то, что говорит «проект жил до слияния».
|
||||||
|
|
||||||
|
Каталог задач передаётся отдельно: до чтения настроек его путь неизвестен, а
|
||||||
|
искать `.tasks.json` по всему дереву значит гадать.
|
||||||
|
"""
|
||||||
|
root = root.resolve()
|
||||||
|
found = [rel for rel in LEGACY if (root / rel).is_file()]
|
||||||
|
for base in filter(None, (tasks_dir, root / "tasks", root / "docs" / "tasks")):
|
||||||
|
# Каталог задач приходит и относительным — таким его печатают в
|
||||||
|
# сообщениях; для сравнения с корнем он обязан быть абсолютным.
|
||||||
|
path = (base if base.is_absolute() else Path.cwd() / base) / LEGACY_TASKS
|
||||||
|
if not path.is_file():
|
||||||
|
continue
|
||||||
|
path = path.resolve()
|
||||||
|
rel = path.relative_to(root).as_posix() if path.is_relative_to(root) else str(path)
|
||||||
|
if rel not in found:
|
||||||
|
found.append(rel)
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
def quote(value: str) -> str:
|
||||||
|
"""Значение как строка TOML: экранирование, а не конкатенация в кавычки.
|
||||||
|
|
||||||
|
Без него имя файла с кавычкой или путь с обратной косой чертой ломают
|
||||||
|
**весь** файл: `tomllib` отказывается разбирать его целиком, и оба скрипта
|
||||||
|
после этого отвечают кодом 3 на любую команду. Пишет сюда машина, а
|
||||||
|
последствия достаются человеку, который такого имени не выбирал.
|
||||||
|
"""
|
||||||
|
out = value.replace("\\", "\\\\").replace('"', '\\"')
|
||||||
|
out = out.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
|
||||||
|
return f'"{out}"'
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_comment(line: str) -> str:
|
||||||
|
"""Строка без хвостового комментария. Кавычки уважаются: `#` внутри них — текст."""
|
||||||
|
quoted = False
|
||||||
|
for i, ch in enumerate(line):
|
||||||
|
if ch == '"' and (i == 0 or line[i - 1] != "\\"):
|
||||||
|
quoted = not quoted
|
||||||
|
elif ch == "#" and not quoted:
|
||||||
|
return line[:i]
|
||||||
|
return line
|
||||||
|
|
||||||
|
|
||||||
|
def _is_header(line: str, name: str | None = None) -> bool:
|
||||||
|
"""Заголовок секции — по разбору, а не по совпадению строки.
|
||||||
|
|
||||||
|
`[tasks] # имена частей` — законный TOML и ровно та возможность, ради
|
||||||
|
которой формат и взят. Сравнение строк её не узнаёт, дописывает вторую
|
||||||
|
таблицу с тем же именем, и `tomllib` отвергает файл целиком.
|
||||||
|
"""
|
||||||
|
body = _strip_comment(line).strip()
|
||||||
|
if not (body.startswith("[") and body.endswith("]")):
|
||||||
|
return False
|
||||||
|
return name is None or body[1:-1].strip() == name
|
||||||
|
|
||||||
|
|
||||||
|
def set_version(root: Path, number: int) -> None:
|
||||||
|
"""Двинуть версию, не тронув остального: правится одна строка.
|
||||||
|
|
||||||
|
Перезапись файла целиком стёрла бы комментарии — то единственное, ради чего
|
||||||
|
формат и выбран.
|
||||||
|
|
||||||
|
**Ищется только ключ верхнего уровня** — то есть выше первого заголовка
|
||||||
|
секции. `version` внутри `[docs]` принадлежит проекту и значит что угодно
|
||||||
|
своё; двинув его, мы объявили бы приведённым не то, о чём речь, и оставили
|
||||||
|
бы настоящую версию неназванной. Ключа нет вовсе — строка встаёт первой, до
|
||||||
|
всякой секции, по той же причине.
|
||||||
|
"""
|
||||||
|
path = root / CONFIG_NAME
|
||||||
|
lines = path.read_text(encoding="utf-8").splitlines() if path.is_file() else []
|
||||||
|
end = next((i for i, ln in enumerate(lines) if _is_header(ln)), len(lines))
|
||||||
|
# Значение берётся до комментария и может быть каким угодно — в том числе
|
||||||
|
# строкой в кавычках: файл правят руками. Заменяется оно целиком, иначе
|
||||||
|
# рядом появился бы второй ключ `version`, и файл перестал бы разбираться.
|
||||||
|
pattern = re.compile(rf"^(\s*{VERSION_KEY}\s*=\s*)([^#]*?)(\s*(?:#.*)?)$")
|
||||||
|
for i in range(end):
|
||||||
|
match = pattern.match(lines[i])
|
||||||
|
if match:
|
||||||
|
lines[i] = f"{match.group(1)}{number}{match.group(3)}"
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
lines.insert(0, f"{VERSION_KEY} = {number}")
|
||||||
|
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def merge_section(root: Path, name: str, values: dict) -> list[str]:
|
||||||
|
"""Дописать ключи в секцию, не тронув остального. Возвращает дописанное.
|
||||||
|
|
||||||
|
Правка построчная по той же причине, что и у версии: перезапись файла
|
||||||
|
целиком стёрла бы комментарии. Ключ, который в секции уже есть, не трогается
|
||||||
|
вовсе — файл в чужом репозитории правит человек, и затирать его значение
|
||||||
|
своим умолчанием нельзя. **Что дописано, а что нет, решает зовущий:** список
|
||||||
|
возвращается, и молчать о неписаном ему нельзя.
|
||||||
|
"""
|
||||||
|
path = root / CONFIG_NAME
|
||||||
|
lines = path.read_text(encoding="utf-8").splitlines() if path.is_file() else []
|
||||||
|
start = next((i for i, ln in enumerate(lines) if _is_header(ln, name)), None)
|
||||||
|
if start is None:
|
||||||
|
if not values:
|
||||||
|
return []
|
||||||
|
block = ([""] if lines and lines[-1].strip() else []) + [f"[{name}]"]
|
||||||
|
block += [f"{k} = {quote(v)}" for k, v in values.items()]
|
||||||
|
path.write_text("\n".join([*lines, *block]) + "\n", encoding="utf-8")
|
||||||
|
return list(values)
|
||||||
|
end = next((i for i in range(start + 1, len(lines)) if _is_header(lines[i])),
|
||||||
|
len(lines))
|
||||||
|
body = lines[start + 1:end]
|
||||||
|
have = {ln.split("=", 1)[0].strip() for ln in map(_strip_comment, body)
|
||||||
|
if "=" in ln}
|
||||||
|
added = [k for k in values if k not in have]
|
||||||
|
if not added:
|
||||||
|
return []
|
||||||
|
# Пустые строки в хвосте секции — отбивка перед следующим заголовком.
|
||||||
|
# Дописываем до неё, а её возвращаем на место: иначе файл слипается.
|
||||||
|
trailing = 0
|
||||||
|
while body and not body[-1].strip():
|
||||||
|
body.pop()
|
||||||
|
trailing += 1
|
||||||
|
insert = [f"{k} = {quote(values[k])}" for k in added]
|
||||||
|
lines[start + 1:end] = [*body, *insert, *([""] * trailing)]
|
||||||
|
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
return added
|
||||||
|
|
||||||
|
|
||||||
|
def missing_keys(root: Path, name: str, values: dict) -> dict:
|
||||||
|
"""Ключи секции, которые уже есть и разошлись с тем, что мы собирались дать.
|
||||||
|
|
||||||
|
`merge_section` чужого значения не трогает — и правильно делает, — но
|
||||||
|
промолчать о расхождении нельзя: `dir` из настроек и `--dir` из вызова,
|
||||||
|
разойдясь, оставляют каталог, до которого потом не дотянется никто.
|
||||||
|
"""
|
||||||
|
have = section(read(root), name)
|
||||||
|
return {k: have[k] for k, v in values.items() if k in have and have[k] != v}
|
||||||
|
|
||||||
|
|
||||||
|
def skeleton(number: int, docs: dict | None = None, tasks: dict | None = None) -> str:
|
||||||
|
"""Свежий файл с комментариями — тем, ради чего взят TOML.
|
||||||
|
|
||||||
|
Пустая секция пишется всё равно: строка «ключа нет, потому что БД нет»
|
||||||
|
читается как решение, а её отсутствие — как недосмотр.
|
||||||
|
"""
|
||||||
|
docs, tasks = docs or {}, tasks or {}
|
||||||
|
out = [
|
||||||
|
"# Раскладка av-dev в этом проекте: версия и настройки проверок.",
|
||||||
|
"# Файл ведут скиллы плагина, править руками можно — комментарии свои.",
|
||||||
|
"",
|
||||||
|
f"{VERSION_KEY} = {number}"
|
||||||
|
" # версия раскладки; обратной совместимости нет, есть «приведён» и «нет»",
|
||||||
|
"",
|
||||||
|
"[docs]",
|
||||||
|
]
|
||||||
|
if docs.get("migrations"):
|
||||||
|
out += [
|
||||||
|
"# каталог миграций: по нему docs.py сверяет схему с database.md",
|
||||||
|
f"migrations = {quote(docs['migrations'])}",
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
out += ['# migrations = "путь/к/миграциям" — появится, когда появится БД']
|
||||||
|
out += ["", "[tasks]",
|
||||||
|
"# каталог задач от корня репозитория; имена частей — умолчания скрипта",
|
||||||
|
f"dir = {quote(tasks.get('dir', 'tasks'))}"]
|
||||||
|
for key in ("items", "backlog", "roadmap"):
|
||||||
|
if tasks.get(key):
|
||||||
|
out.append(f"{key} = {quote(tasks[key])}")
|
||||||
|
return "\n".join(out) + "\n"
|
||||||
@@ -1,26 +1,26 @@
|
|||||||
# Язык проектных текстов
|
# Язык проектных текстов
|
||||||
|
|
||||||
**Это дом.** Файл не входит ни в один плагин: язык общий для документов канона и
|
**Это дом.** Файл не принадлежит ни одному скиллу: язык общий для документов
|
||||||
для задач, и хранить его внутри одного из них значило бы отдать общее правило во
|
канона, для задач и для решений ADR, и хранить его внутри одного из них значило
|
||||||
владение половине. Плагины везут **копии**, помеченные разметкой `copies.py`, и
|
бы отдать общее правило во владение части. Скиллы читают **этот файл** по
|
||||||
расхождение ловит гейт коммита, а не внимание.
|
ссылке; дословные **копии**, помеченные разметкой `copies.py`, уезжают только в
|
||||||
|
уставы вычитки — там текст обязан лежать внутри самого промпта, потому что
|
||||||
|
именно он и есть критерий суждения. Расхождение ловит гейт коммита, а не
|
||||||
|
внимание.
|
||||||
|
|
||||||
Правится **здесь**. Копия, поправленная у себя, — расхождение, а не правка.
|
Правится **здесь**. Копия, поправленная у себя, — расхождение, а не правка.
|
||||||
|
|
||||||
Три блока, и делятся они по потребителю, а не по теме:
|
Два блока копируются, и делятся они по потребителю, а не по теме:
|
||||||
|
|
||||||
| Блок | Что в нём | Кто копирует |
|
| Блок | Что в нём | Кто копирует |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `язык-доктрина` | зачем стиль нужен, что взято сверх устава, что отброшено | справочник языка в плагине |
|
| `язык-правила` | девять правил, по которым судят текст | уставы вычитки |
|
||||||
| `язык-правила` | девять правил, по которым судят текст | справочник и уставы вычитки |
|
| `порог-правки` | когда находка не заводится | уставы вычитки, `task-form` |
|
||||||
| `порог-правки` | когда находка не заводится | справочник, уставы вычитки, `task-form` |
|
|
||||||
|
|
||||||
`порог-правки` вынесен из правил намеренно: он нужен и тому, кто правил языка не
|
`порог-правки` вынесен из правил намеренно: он нужен и тому, кто правил языка не
|
||||||
проверяет вовсе, — а вложенных блоков разметка не знает, и внутри `язык-правила`
|
проверяет вовсе, — а вложенных блоков разметка не знает, и внутри `язык-правила`
|
||||||
его было бы не забрать отдельно.
|
его было бы не забрать отдельно.
|
||||||
|
|
||||||
<!-- дом: язык-доктрина -->
|
|
||||||
|
|
||||||
Правила — для всего, что пишется словами: задачи и цели, документы канона,
|
Правила — для всего, что пишется словами: задачи и цели, документы канона,
|
||||||
решения ADR, записки разведки, сообщения коммитов. Не для кода и не для
|
решения ADR, записки разведки, сообщения коммитов. Не для кода и не для
|
||||||
сообщений программы пользователю — там свои конвенции проекта.
|
сообщений программы пользователю — там свои конвенции проекта.
|
||||||
@@ -81,8 +81,6 @@
|
|||||||
значит называть состояние и остаток, а не пересказывать, как было интересно
|
значит называть состояние и остаток, а не пересказывать, как было интересно
|
||||||
разбираться.
|
разбираться.
|
||||||
|
|
||||||
<!-- /дом: язык-доктрина -->
|
|
||||||
|
|
||||||
## Правила
|
## Правила
|
||||||
|
|
||||||
<!-- дом: язык-правила -->
|
<!-- дом: язык-правила -->
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
# Сопровождение и эксплуатация
|
# Сопровождение и эксплуатация
|
||||||
|
|
||||||
**Это дом.** Словарь «чем держат проект» назван в трёх местах трёх разных
|
**Это дом.** Словарь «чем держат проект» назван в трёх местах трёх разных
|
||||||
плагинов: секция `Сопровождение` в роадмапе (`av-dev-tasks`), раздел
|
скиллов: секция `Сопровождение` в роадмапе (`task-track`), раздел «Эксплуатация»
|
||||||
«Эксплуатация» в `architecture.md` (`av-dev-docs`) и тема ревью `operations`
|
в `architecture.md` (`doc-canon`) и тема ревью `operations` (`code-review`). Ни
|
||||||
(`av-dev-code`). Ни один из трёх им не владеет, поэтому дом стоит снаружи, а
|
один из трёх им не владеет, поэтому дом стоит в `shared/`.
|
||||||
плагины везут копии.
|
|
||||||
|
|
||||||
Три перечня «чем держат проект» уже разъезжались — на «метриках и логах» против
|
Три перечня «чем держат проект» уже разъезжались — на «метриках и логах» против
|
||||||
«мониторинга», — и разъехались молча. Отсюда дословная копия вместо ссылки.
|
«мониторинга», — и разъехались молча. Пока скиллы жили тремя плагинами, отсюда
|
||||||
|
уезжали дословные копии: путь в чужое дерево не разрешался. Теперь дерево одно —
|
||||||
<!-- дом: сопровождение-словарь -->
|
кому словарь нужен, тот открывает **этот файл**, и сверять машиной больше нечего.
|
||||||
|
|
||||||
Одна тема живёт в трёх местах, и путать их слова нельзя.
|
Одна тема живёт в трёх местах, и путать их слова нельзя.
|
||||||
|
|
||||||
@@ -32,4 +31,3 @@
|
|||||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
||||||
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
||||||
|
|
||||||
<!-- /дом: сопровождение-словарь -->
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: openspec
|
name: code-openspec
|
||||||
description: "Завести и настроить OpenSpec в проекте — openspec init --tools claude, замена закомментированного примера в openspec/config.yaml на настройку канонической формы (язык, правила именования capability, придирки валидатора, адреса паспорта и CLAUDE.md), проверка формы своим скриптом openspec.py (имя файла, схема, незаменённый пример, адреса документов, ключи rules против артефактов схемы) и сверка слепка с живой версией инструмента. Использовать, когда в проекте нет каталога openspec/, когда config.yaml остался примером из коробки, когда заводят новый проект или переводят чужой и дошли до шага OpenSpec, а также когда конвейер отказался работать без источника требований. Каталог openspec нужен именно конвейеру: без него не работают ни opsx:propose, ни ревью дизайна, ни сверка требований."
|
description: "Завести и настроить OpenSpec в проекте — openspec init --tools claude, замена закомментированного примера в openspec/config.yaml на настройку канонической формы (язык, правила именования capability, придирки валидатора, адреса паспорта и CLAUDE.md), проверка формы своим скриптом openspec.py (имя файла, схема, незаменённый пример, адреса документов, ключи rules против артефактов схемы) и сверка слепка с живой версией инструмента. Использовать, когда в проекте нет каталога openspec/, когда config.yaml остался примером из коробки, когда заводят новый проект или переводят чужой и дошли до шага OpenSpec, а также когда конвейер отказался работать без источника требований. Каталог openspec нужен именно конвейеру: без него не работают ни opsx:propose, ни ревью дизайна, ни сверка требований."
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -7,11 +7,12 @@ description: "Завести и настроить OpenSpec в проекте
|
|||||||
|
|
||||||
Каталог `openspec/` — **предпосылка конвейера**, а не канона документов. Без него
|
Каталог `openspec/` — **предпосылка конвейера**, а не канона документов. Без него
|
||||||
не работают ни `opsx:propose`, ни ревью дизайна, ни `review-specs`: у требований
|
не работают ни `opsx:propose`, ни ревью дизайна, ни `review-specs`: у требований
|
||||||
не остаётся дома. Поэтому заводит и настраивает его этот плагин — тот, кто по
|
не остаётся дома. Поэтому заводит и настраивает его этот скилл — тот, кто по
|
||||||
OpenSpec и работает.
|
OpenSpec и работает.
|
||||||
|
|
||||||
Канон документов о файле не высказывается вовсе: `docs.py` его не открывает и об
|
Канон документов о файле не высказывается вовсе: `docs.py` его не открывает и об
|
||||||
его отсутствии молчит. Проект без конвейера живёт без OpenSpec законно, и
|
его отсутствии молчит. Проект, не ведущий задачи циклом SDD, живёт без OpenSpec
|
||||||
|
законно, и
|
||||||
проверять там нечего. За каноном остаётся одно — **единственный дом**: не
|
проверять там нечего. За каноном остаётся одно — **единственный дом**: не
|
||||||
пересказан ли в `context` документ, у которого есть свой файл. Это суждение, а не
|
пересказан ли в `context` документ, у которого есть свой файл. Это суждение, а не
|
||||||
форма, и смотрит его агент.
|
форма, и смотрит его агент.
|
||||||
@@ -44,7 +45,7 @@ openspec init --tools claude
|
|||||||
проекта.
|
проекта.
|
||||||
|
|
||||||
Сюда же — **требования к форме `proposal` и `design`**, на которых стоит чекпоинт
|
Сюда же — **требования к форме `proposal` и `design`**, на которых стоит чекпоинт
|
||||||
скилла `av-dev-code:resolve`: объяснение человеку собирается из этих двух
|
скилла `av-dev:code-resolve`: объяснение человеку собирается из этих двух
|
||||||
артефактов, и требование к ним обязано применяться в момент, когда их пишут, а не
|
артефактов, и требование к ним обязано применяться в момент, когда их пишут, а не
|
||||||
вспоминаться шагом позже. Образец их содержит.
|
вспоминаться шагом позже. Образец их содержит.
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ openspec init --tools claude
|
|||||||
Разрез, по которому отличают одно от другого: **утверждение, которое можно
|
Разрез, по которому отличают одно от другого: **утверждение, которое можно
|
||||||
опровергнуть, открыв другой файл проекта, — пересказ; строка, которая говорит,
|
опровергнуть, открыв другой файл проекта, — пересказ; строка, которая говорит,
|
||||||
какой файл открыть, — ссылка.** Машина этот разрез не проверяет; его смотрит
|
какой файл открыть, — ссылка.** Машина этот разрез не проверяет; его смотрит
|
||||||
агент `doc-consistency` из плагина канона, когда тот подключён.
|
агент `doc-consistency`, когда документы канона в проекте есть.
|
||||||
|
|
||||||
Два адреса обязательны — `docs/passport.md` и `CLAUDE.md`: предложение пишется до
|
Два адреса обязательны — `docs/passport.md` и `CLAUDE.md`: предложение пишется до
|
||||||
того, как кто-либо откроет `docs/`, и без них его пишут, не зная ни границы
|
того, как кто-либо откроет `docs/`, и без них его пишут, не зная ни границы
|
||||||
@@ -67,7 +68,7 @@ openspec init --tools claude
|
|||||||
## Инструмент
|
## Инструмент
|
||||||
|
|
||||||
```
|
```
|
||||||
os="$CLAUDE_PLUGIN_ROOT/skills/openspec/scripts/openspec.py"
|
os="$CLAUDE_PLUGIN_ROOT/skills/code-openspec/scripts/openspec.py"
|
||||||
|
|
||||||
python3 $os check --dir <корень> # форма config.yaml в проекте
|
python3 $os check --dir <корень> # форма config.yaml в проекте
|
||||||
python3 $os form # слепок формы против живого OpenSpec
|
python3 $os form # слепок формы против живого OpenSpec
|
||||||
@@ -87,9 +88,9 @@ python3 $os form # слепок формы против жив
|
|||||||
имена артефактов схемы, а не свободные слова. Числа проверок здесь нет намеренно:
|
имена артефактов схемы, а не свободные слова. Числа проверок здесь нет намеренно:
|
||||||
оно протухает от каждой добавленной.
|
оно протухает от каждой добавленной.
|
||||||
|
|
||||||
**Адреса требуются только к тем документам, которые в проекте есть.** Канон
|
**Адреса требуются только к тем документам, которые в проекте есть.** Документы
|
||||||
документов ставится отдельным плагином и может быть не подключён; требовать
|
канона могут быть не заведены; требовать ссылку на несуществующий файл значит
|
||||||
ссылку на несуществующий файл значит требовать битую ссылку. Нет
|
требовать битую ссылку. Нет
|
||||||
`docs/passport.md` — проверка по нему идёт строкой «не проверялось», и там же
|
`docs/passport.md` — проверка по нему идёт строкой «не проверялось», и там же
|
||||||
сказано, что без канона конвейер работает вслепую.
|
сказано, что без канона конвейер работает вслепую.
|
||||||
|
|
||||||
@@ -116,54 +117,61 @@ python3 $os form # слепок формы против жив
|
|||||||
|
|
||||||
## Кто зовёт этот скилл
|
## Кто зовёт этот скилл
|
||||||
|
|
||||||
- `av-dev-docs:init` — шагом заведения нового проекта, до первого документа;
|
- `av-dev:doc-init` — шагом заведения нового проекта, до первого документа;
|
||||||
- `av-dev-docs:canon` в режиме `adopt` — если на переводимом проекте каталога нет
|
- `av-dev:doc-canon` в режиме `adopt` — если на переводимом проекте каталога нет
|
||||||
или `config.yaml` остался примером;
|
или `config.yaml` остался примером;
|
||||||
- `av-dev-code:resolve` и `av-dev-code:review` — не вызовом по ходу, а отсылкой:
|
- `av-dev:code-resolve` и `av-dev:code-review` — не вызовом по ходу, а отсылкой:
|
||||||
OpenSpec у обоих жёсткая предпосылка, и на проекте без каталога оба посылают
|
OpenSpec у обоих жёсткая предпосылка, и на проекте без каталога оба посылают
|
||||||
сюда вместо того, чтобы заводить его руками;
|
сюда вместо того, чтобы заводить его руками;
|
||||||
- человек — когда конвейер отказался работать без источника требований.
|
- человек — когда конвейер отказался работать без источника требований.
|
||||||
|
|
||||||
**Копия.** Дом правила — `shared/plugin-boundary.md` в репозитории плагинов.
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
Правится дом, а не этот файл.
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
месте.
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, `av-dev-tasks:tasks`,
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
`av-dev-code:review`. Короткое имя может разрешиться в устаревшую проектную копию
|
| --- | --- | --- |
|
||||||
из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в поведении.
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт
|
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||||
только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
прочитает его сам.
|
поведении.
|
||||||
|
|
||||||
**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя,
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
Здесь это значит: вызов не разрешился — плагина конвейера в проекте нет, и тогда
|
<!-- /копия: отсутствие -->
|
||||||
OpenSpec заводит человек командой выше.
|
|
||||||
|
Здесь это значит: документов канона в проекте может не быть, и тогда `context`
|
||||||
|
называет только те адреса, которые есть, — строкой доклада говорится, что без
|
||||||
|
паспорта предложение пишут, не зная границы домена.
|
||||||
|
|
||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
- **Не пишет спеки и предложения.** Это `opsx:propose` и конвейер задачи.
|
- **Не пишет спеки и предложения.** Это `opsx:propose` и конвейер задачи.
|
||||||
- **Не ведёт документы канона** — их дом плагин `av-dev-docs`, и адреса в
|
- **Не ведёт документы канона** — их дом скилл `av-dev:doc-canon`, и адреса в
|
||||||
`context` только на них ссылаются.
|
`context` только на них ссылаются.
|
||||||
- **Не чинит расхождение формы с версией OpenSpec в проекте.** Оно чинится в
|
- **Не чинит расхождение формы с версией OpenSpec в проекте.** Оно чинится в
|
||||||
плагине: константы скрипта, образец здесь, запись в журнал версий канона.
|
плагине: константы скрипта, образец здесь, запись в журнал версий канона.
|
||||||
- **Не судит, ссылается `context` на документы или пересказывает их.** Машине
|
- **Не судит, ссылается `context` на документы или пересказывает их.** Машине
|
||||||
этот разрез не виден; его смотрит агент `doc-consistency` из плагина канона.
|
этот разрез не виден; его смотрит агент `doc-consistency`. Документов канона в
|
||||||
Плагина нет — эту проверку не делает никто, и так и скажи.
|
проекте нет — сверять пересказ не с чем, и так и скажи.
|
||||||
+2
-2
@@ -44,7 +44,7 @@ context: |
|
|||||||
первым молча, и заметно это становится в предложении, которое уже написано.
|
первым молча, и заметно это становится в предложении, которое уже написано.
|
||||||
|
|
||||||
Ревью: правило выбора метки и состав проходов здесь не пересказываем — их дом
|
Ревью: правило выбора метки и состав проходов здесь не пересказываем — их дом
|
||||||
скилл av-dev-code:review, проектная настройка — docs/review.md.
|
скилл av-dev:code-review, проектная настройка — docs/review.md.
|
||||||
|
|
||||||
Конвенции кода: механизированное проверяет гейт, прозой остаётся
|
Конвенции кода: механизированное проверяет гейт, прозой остаётся
|
||||||
docs/conventions/. Ни состав шагов гейта, ни перечень конвенций здесь не
|
docs/conventions/. Ни состав шагов гейта, ни перечень конвенций здесь не
|
||||||
@@ -78,7 +78,7 @@ rules:
|
|||||||
узнаёт их падением `openspec validate --strict`.
|
узнаёт их падением `openspec validate --strict`.
|
||||||
|
|
||||||
**Правила для `proposal` и `design` держат чекпоинт скилла
|
**Правила для `proposal` и `design` держат чекпоинт скилла
|
||||||
`av-dev-code:resolve`.** Там работа останавливается и человеку объясняют, в
|
`av-dev:code-resolve`.** Там работа останавливается и человеку объясняют, в
|
||||||
чём проблема и как её решают, — а объяснение **собирается из этих двух
|
чём проблема и как её решают, — а объяснение **собирается из этих двух
|
||||||
артефактов**, а не сочиняется заново: третий пересказ одного и того же разошёлся
|
артефактов**, а не сочиняется заново: третий пересказ одного и того же разошёлся
|
||||||
бы с обоими. Требование поэтому стоит здесь, в момент порождения артефакта, а не
|
бы с обоими. Требование поэтому стоит здесь, в момент порождения артефакта, а не
|
||||||
+6
-6
@@ -4,7 +4,7 @@
|
|||||||
Каталог `openspec/` — предпосылка **конвейера**, а не канона документов: без него
|
Каталог `openspec/` — предпосылка **конвейера**, а не канона документов: без него
|
||||||
не работают ни `opsx:propose`, ни ревью дизайна, ни сверка требований. Поэтому и
|
не работают ни `opsx:propose`, ни ревью дизайна, ни сверка требований. Поэтому и
|
||||||
проверка формы живёт здесь, рядом со скиллом, который каталог заводит. Раньше она
|
проверка формы живёт здесь, рядом со скиллом, который каталог заводит. Раньше она
|
||||||
жила в `docs.py` плагина канона, и у файла было два владельца: один заводит,
|
жила в `docs.py`, у скилла канона, и у файла было два владельца: один заводит,
|
||||||
другой проверяет.
|
другой проверяет.
|
||||||
|
|
||||||
Проверяется то, что **молчит при поломке**. Файл из коробки хуже отсутствующего:
|
Проверяется то, что **молчит при поломке**. Файл из коробки хуже отсутствующего:
|
||||||
@@ -114,7 +114,7 @@ def rules_keys(live: str) -> list[str]:
|
|||||||
|
|
||||||
Идём от строки `rules:` до следующего ключа нулевой колонки, а не ищем
|
Идём от строки `rules:` до следующего ключа нулевой колонки, а не ищем
|
||||||
отступ по всему файлу: блок `context: |` — литеральный скаляр, внутри него
|
отступ по всему файлу: блок `context: |` — литеральный скаляр, внутри него
|
||||||
строки вида «Language: Russian» и «av-dev-code:review» выглядят
|
строки вида «Language: Russian» и «av-dev:code-review» выглядят
|
||||||
ключами и дали бы находку на ровном месте. Проверено на живом конфиге,
|
ключами и дали бы находку на ровном месте. Проверено на живом конфиге,
|
||||||
который так и падал.
|
который так и падал.
|
||||||
"""
|
"""
|
||||||
@@ -219,8 +219,8 @@ def check_form(root: Path, rep: Report) -> None:
|
|||||||
if not (root / where).exists():
|
if not (root / where).exists():
|
||||||
rep.skip(
|
rep.skip(
|
||||||
f"{where} в проекте нет — ссылка на него в context не "
|
f"{where} в проекте нет — ссылка на него в context не "
|
||||||
f"требуется. Документы канона ведёт отдельный плагин "
|
f"требуется. Документы канона проект не завёл, и без них "
|
||||||
f"(av-dev-docs), и без него конвейер работает вслепую"
|
f"конвейер работает вслепую: заводит их av-dev:doc-canon"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
if pointer not in live:
|
if pointer not in live:
|
||||||
@@ -289,8 +289,8 @@ def report(rep: Report) -> int:
|
|||||||
"документов проекта и ключи rules против артефактов схемы. Чего она не\n"
|
"документов проекта и ключи rules против артефактов схемы. Чего она не\n"
|
||||||
"видит — **пересказ вместо ссылки**: утверждение, которое можно\n"
|
"видит — **пересказ вместо ссылки**: утверждение, которое можно\n"
|
||||||
"опровергнуть, открыв другой файл проекта, от строки «открой такой-то\n"
|
"опровергнуть, открыв другой файл проекта, от строки «открой такой-то\n"
|
||||||
"файл» она не отличает. Это суждение агента `doc-consistency` из плагина\n"
|
"файл» она не отличает. Это суждение агента `doc-consistency`; документов\n"
|
||||||
"канона документов; нет плагина — нет и этой проверки, и так и скажи."
|
"канона в проекте нет — сверять пересказ не с чем, и так и скажи."
|
||||||
)
|
)
|
||||||
if rep.errors:
|
if rep.errors:
|
||||||
print(f"\nИтог: дрейф, {len(rep.errors)} пунктов.")
|
print(f"\nИтог: дрейф, {len(rep.errors)} пунктов.")
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: resolve
|
name: code-resolve
|
||||||
description: "Взять одну задачу и довести её до закрытия. Одна точка входа, три сценария, и выбирает сценарий сам скилл, прочитав постановку. Способ известен и меняется поведение — сценарий решения: цикл Spec Driven Development (opsx propose → разметка → ревью дизайна → чекпоинт с объяснением человеческим языком → opsx apply → ревью кода → archive → синк документации → коммит → закрытие). Способ известен, а спека не меняется (тип chore: тулчейн, зависимости, сборка, гит-хуки, перенос, чистка) — сценарий обслуживания: правка → гейт со сверкой состава проверок → ревью фиксированным планом без change (autotests, operations, плюс conventions, если тронут код) → синк документации → коммит → закрытие; планового стопа нет, change не заводится. Нашлась дельта-спека — задача оказалась шире своего типа: стоп с объяснением простым языком и двумя решениями человека, переформулировать запись в fix или feature и решать её процессом того типа следующим прогоном либо прекратить работу. Способа нет, постановка мутная, тип research — сценарий разведки: вопрос и рамки → чтение документов, кода и внешних источников (можно opsx:explore) → чекпоинт вариантов: 2–4 способа решить, цена каждого, что становится невозможным, рекомендация → ответ уезжает в документы канона, исход — в задачи → вычитка написанного → коммит → закрытие. Разведка кода не пишет и change не заводит, а выбранный способ реализуется следующим прогоном. На входе путь к файлу задачи, её слаг или просто текст. Использовать, когда просят взять, сделать или решить задачу, обновить зависимости или сборку, разобраться, изучить, сравнить подходы, проработать сырую идею, ответить на вопрос из беклога."
|
description: "Взять одну задачу и довести её до закрытия. Одна точка входа, три сценария, и выбирает сценарий сам скилл, прочитав постановку. Способ известен и меняется поведение — сценарий решения: цикл Spec Driven Development (opsx propose → разметка → ревью дизайна → чекпоинт с объяснением человеческим языком → opsx apply → ревью кода → archive → синк документации → коммит → закрытие). Способ известен, а спека не меняется (тип chore: тулчейн, зависимости, сборка, гит-хуки, перенос, чистка) — сценарий обслуживания: правка → гейт со сверкой состава проверок → ревью фиксированным планом без change (autotests, operations, плюс conventions, если тронут код) → синк документации → коммит → закрытие; планового стопа нет, change не заводится. Нашлась дельта-спека — задача оказалась шире своего типа: стоп с объяснением простым языком и двумя решениями человека, переформулировать запись в fix или feature и решать её процессом того типа следующим прогоном либо прекратить работу. Способа нет, постановка мутная, тип research — сценарий разведки: вопрос и рамки → чтение документов, кода и внешних источников (можно opsx:explore) → чекпоинт вариантов: 2–4 способа решить, цена каждого, что становится невозможным, рекомендация → ответ уезжает в документы канона, исход — в задачи → вычитка написанного → коммит → закрытие. Разведка кода не пишет и change не заводит, а выбранный способ реализуется следующим прогоном. На входе путь к файлу задачи, её слаг или просто текст. Использовать, когда просят взять, сделать или решить задачу, обновить зависимости или сборку, разобраться, изучить, сравнить подходы, проработать сырую идею, ответить на вопрос из беклога."
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -36,9 +36,9 @@ description: "Взять одну задачу и довести её до за
|
|||||||
`openspec validate --strict`). **Проект без OpenSpec этим скиллом не ведётся** —
|
`openspec validate --strict`). **Проект без OpenSpec этим скиллом не ведётся** —
|
||||||
подключай OpenSpec, а не вырождай цикл сценария; почему ветка деградации здесь
|
подключай OpenSpec, а не вырождай цикл сценария; почему ветка деградации здесь
|
||||||
не
|
не
|
||||||
пишется, сказано в `av-dev-code:review`, раздел «Предпосылки», и дом у этого
|
пишется, сказано в `av-dev:code-review`, раздел «Предпосылки», и дом у этого
|
||||||
довода там. Заводить руками не надо: каталог и настройку в `config.yaml`
|
довода там. Заводить руками не надо: каталог и настройку в `config.yaml`
|
||||||
делает скилл `av-dev-code:openspec`. **Сценариям разведки и обслуживания
|
делает скилл `av-dev:code-openspec`. **Сценариям разведки и обслуживания
|
||||||
OpenSpec не нужен** — они не заводят change; `opsx:explore` берётся разведкой,
|
OpenSpec не нужен** — они не заводят change; `opsx:explore` берётся разведкой,
|
||||||
если плагин есть.
|
если плагин есть.
|
||||||
- **Проектные копии этих скиллов и агентов удаляются при установке плагина**
|
- **Проектные копии этих скиллов и агентов удаляются при установке плагина**
|
||||||
@@ -48,50 +48,56 @@ description: "Взять одну задачу и довести её до за
|
|||||||
`.claude/agents/<проект>-review-*.md`). Две копии одного скилла расходятся, и
|
`.claude/agents/<проект>-review-*.md`). Две копии одного скилла расходятся, и
|
||||||
побеждает та, что короче названа.
|
побеждает та, что короче названа.
|
||||||
|
|
||||||
### Обращение к соседним плагинам
|
### Чего может не быть
|
||||||
|
|
||||||
**Копия.** Дом — `shared/plugin-boundary.md` в репозитории плагинов: правило
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина: правило
|
||||||
общее для всех, кто зовёт чужое, и ни один плагин им не владеет. Правится дом, а
|
общее для всех, кто приходит в чужой проект, и ни один скилл им не владеет.
|
||||||
не этот файл.
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
месте.
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, `av-dev-tasks:tasks`,
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
`av-dev-code:review`. Короткое имя может разрешиться в устаревшую проектную копию
|
| --- | --- | --- |
|
||||||
из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в поведении.
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт
|
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||||
только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
прочитает его сам.
|
поведении.
|
||||||
|
|
||||||
**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя,
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
Скилл зовёт `av-dev-code:review`, `av-dev-docs:docs` и
|
<!-- /копия: отсутствие -->
|
||||||
`av-dev-tasks:tasks`. Чем оборачивается отсутствие каждого — на самих шагах и в
|
|
||||||
разделе «Границы».
|
Скилл зовёт `av-dev:code-review`, `av-dev:doc-sync` и `av-dev:task-track` —
|
||||||
|
все трое в этом же плагине и разрешаются всегда. Чем оборачивается отсутствие
|
||||||
|
части раскладки, под которую они работают, сказано на самих шагах сценариев.
|
||||||
|
|
||||||
Перед стартом прочитай `CLAUDE.md` проекта и то, на что он ссылается, если ещё
|
Перед стартом прочитай `CLAUDE.md` проекта и то, на что он ссылается, если ещё
|
||||||
не в контексте. Проектные факты, нужные ревью — инварианты, семантика гейта,
|
не в контексте. Проектные факты, нужные ревью — инварианты, семантика гейта,
|
||||||
объёмы, модель угроз, прецеденты, — живут в **документах канона** `av-dev-docs`;
|
объёмы, модель угроз, прецеденты, — живут в **документах канона**;
|
||||||
карта «что где» — `references/project-facts.md` конвейера ревью.
|
карта «что где» — `references/project-facts.md` конвейера ревью.
|
||||||
|
|
||||||
**Документов канона нет — проект к нему не приведён.** Скажи это строкой и
|
**Документов канона нет — проект к нему не приведён.** Скажи это строкой и
|
||||||
предложи скилл `av-dev-docs:canon`: одна операция на проект против поразрядной
|
предложи скилл `av-dev:doc-canon`: одна операция на проект против поразрядной
|
||||||
деградации на каждой задаче. Работу при этом не останавливай.
|
деградации на каждой задаче. Работу при этом не останавливай.
|
||||||
|
|
||||||
## Вход
|
## Вход
|
||||||
@@ -101,7 +107,7 @@ description: "Взять одну задачу и довести её до за
|
|||||||
лезь и приоритеты не интерпретируй: что делать дальше, решает не этот скилл.
|
лезь и приоритеты не интерпретируй: что делать дальше, решает не этот скилл.
|
||||||
|
|
||||||
**Запись из каталога сперва проверяется на готовность, и проверяет её машина.**
|
**Запись из каталога сперва проверяется на готовность, и проверяет её машина.**
|
||||||
Вызови Skill `av-dev-tasks:tasks` и попроси прогнать `ready <слаг>`: он смотрит
|
Вызови Skill `av-dev:task-track` и попроси прогнать `ready <слаг>`: он смотрит
|
||||||
тип, цель у `feature`, пустой ли раздел вопросов и собраны ли разделы схемы
|
тип, цель у `feature`, пустой ли раздел вопросов и собраны ли разделы схемы
|
||||||
типа. Судить это глазами нельзя — ровно тот случай, где машина дешевле и точнее,
|
типа. Судить это глазами нельзя — ровно тот случай, где машина дешевле и точнее,
|
||||||
а цена ошибки отложенная: недостающие критерии приёмки обнаружатся на приёмке,
|
а цена ошибки отложенная: недостающие критерии приёмки обнаружатся на приёмке,
|
||||||
@@ -114,7 +120,7 @@ description: "Взять одну задачу и довести её до за
|
|||||||
**Отказ `ready` сценарий не выбирает.** Запись `research` без раздела «Вопрос»
|
**Отказ `ready` сценарий не выбирает.** Запись `research` без раздела «Вопрос»
|
||||||
(сырьё) — отказ и здесь: у неё нет вопроса, и разведывать нечего.
|
(сырьё) — отказ и здесь: у неё нет вопроса, и разведывать нечего.
|
||||||
|
|
||||||
Плагина `av-dev-tasks` в проекте нет или задача пришла текстом — прогонять
|
Каталога задач в проекте нет или задача пришла текстом — прогонять
|
||||||
нечего. Тогда прочитай постановку сам и скажи строкой, что готовность машиной не
|
нечего. Тогда прочитай постановку сам и скажи строкой, что готовность машиной не
|
||||||
проверялась; работу при этом не останавливай.
|
проверялась; работу при этом не останавливай.
|
||||||
|
|
||||||
@@ -172,7 +178,7 @@ description: "Взять одну задачу и довести её до за
|
|||||||
запись и решать процессом того типа следующим прогоном** либо **прекратить
|
запись и решать процессом того типа следующим прогоном** либо **прекратить
|
||||||
работу**. Третьего — «доделать как обслуживание» — нет. Исход в обоих случаях
|
работу**. Третьего — «доделать как обслуживание» — нет. Исход в обоих случаях
|
||||||
«меняется спека»; сделанное остаётся в рабочем дереве незакоммиченным, тип
|
«меняется спека»; сделанное остаётся в рабочем дереве незакоммиченным, тип
|
||||||
меняет `av-dev-tasks:tasks` и только после ответа. Подробно —
|
меняет `av-dev:task-track` и только после ответа. Подробно —
|
||||||
[maintain.md](references/maintain.md), раздел «Дельта нашлась по ходу»;
|
[maintain.md](references/maintain.md), раздел «Дельта нашлась по ходу»;
|
||||||
- **обслуживание → разведка**: форма правки неизвестна (мажорное обновление,
|
- **обслуживание → разведка**: форма правки неизвестна (мажорное обновление,
|
||||||
смена сборщика). **Стоп** с исходом «нужна разведка», по тому же основанию,
|
смена сборщика). **Стоп** с исходом «нужна разведка», по тому же основанию,
|
||||||
@@ -195,7 +201,7 @@ description: "Взять одну задачу и довести её до за
|
|||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
in["вход: файл, слаг или текст"]
|
in["вход: файл, слаг или текст"]
|
||||||
ready["ready: готовность записи<br/>av-dev-tasks:tasks"]
|
ready["ready: готовность записи<br/>av-dev:task-track"]
|
||||||
fork{"есть очевидный<br/>способ решения?"}
|
fork{"есть очевидный<br/>способ решения?"}
|
||||||
fork2{"меняется ли<br/>спека?"}
|
fork2{"меняется ли<br/>спека?"}
|
||||||
solve["сценарий решения<br/>references/solve.md<br/>код, ревью, архив, коммит"]
|
solve["сценарий решения<br/>references/solve.md<br/>код, ревью, архив, коммит"]
|
||||||
@@ -259,7 +265,7 @@ flowchart TD
|
|||||||
что успели узнать, где остановились и почему.
|
что успели узнать, где остановились и почему.
|
||||||
|
|
||||||
**Что остатком не является — правило живёт не здесь.** Канонический текст с обеими
|
**Что остатком не является — правило живёт не здесь.** Канонический текст с обеими
|
||||||
оговорками — в плагине `av-dev-tasks`, скилл `av-dev-tasks:groom`, раздел
|
оговорками — в скилле `av-dev:task-groom`, раздел
|
||||||
`## Вопрос, блокер, необратимое`, подраздел «Отличать вопрос от застревания».
|
`## Вопрос, блокер, необратимое`, подраздел «Отличать вопрос от застревания».
|
||||||
Правило принадлежит управлению задачами, потому что решает **сделана задача или
|
Правило принадлежит управлению задачами, потому что решает **сделана задача или
|
||||||
вышла**, — это исход планирования, а не исполнения. **Ссылайся, не
|
вышла**, — это исход планирования, а не исполнения. **Ссылайся, не
|
||||||
@@ -272,7 +278,7 @@ flowchart TD
|
|||||||
Оба порога — стоп: первый поднимает решение до начала записи, второй даёт исход
|
Оба порога — стоп: первый поднимает решение до начала записи, второй даёт исход
|
||||||
«не доведена».
|
«не доведена».
|
||||||
|
|
||||||
Плагин `av-dev-tasks` не подключён — правило не отменяется, а становится
|
Каталога задач в проекте нет — правило не отменяется, а становится
|
||||||
осторожнее: прежде чем записать зависящее от нерешённого куда бы то ни было —
|
осторожнее: прежде чем записать зависящее от нерешённого куда бы то ни было —
|
||||||
в хранилище, в журнал, в витрину или наружу, — спрашивай человека.
|
в хранилище, в журнал, в витрину или наружу, — спрашивай человека.
|
||||||
|
|
||||||
@@ -295,10 +301,10 @@ flowchart TD
|
|||||||
- **Беклогом, целями и приоритетами.** Задача приходит извне. Скилл её не
|
- **Беклогом, целями и приоритетами.** Задача приходит извне. Скилл её не
|
||||||
выбирает, не приоритизирует, не заводит и не переоценивает.
|
выбирает, не приоритизирует, не заводит и не переоценивает.
|
||||||
- **Форматом задач и документов.** Индексы и документы канона руками не правятся,
|
- **Форматом задач и документов.** Индексы и документы канона руками не правятся,
|
||||||
путь к чужому скрипту не выдумывается: этим владеют `av-dev-tasks:tasks` и
|
путь к чужому скрипту не выдумывается: этим владеют `av-dev:task-track` и
|
||||||
`av-dev-docs:docs`. Закрытие — работа этого скилла, и это осознанное решение с
|
`av-dev:doc-sync`. Закрытие — работа этого скилла, и это осознанное решение с
|
||||||
названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек на
|
названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек на
|
||||||
груминге (`av-dev-tasks:groom`) возвращает задачу `reopen` с причиной, а доклад
|
груминге (`av-dev:task-groom`) возвращает задачу `reopen` с причиной, а доклад
|
||||||
по критериям приёмки становится единственным, по чему приёмка вообще возможна.
|
по критериям приёмки становится единственным, по чему приёмка вообще возможна.
|
||||||
- **Определением ценности.** «Нужна ли эта функциональность» — не вопрос этого
|
- **Определением ценности.** «Нужна ли эта функциональность» — не вопрос этого
|
||||||
скилла ни на одном шаге и ни в одном сценарии. Чекпоинт решения спрашивает «так
|
скилла ни на одном шаге и ни в одном сценарии. Чекпоинт решения спрашивает «так
|
||||||
+18
-20
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
||||||
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
||||||
ход; общее для всех трёх сценариев — вход, обращение к соседним плагинам, правило
|
ход; общее для всех трёх сценариев — вход, чего может не быть, правило
|
||||||
записанного вопроса, правило необратимого — живёт в SKILL.md и тут не
|
записанного вопроса, правило необратимого — живёт в SKILL.md и тут не
|
||||||
пересказывается.
|
пересказывается.
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
|
|
||||||
- **переформулировать запись** — тип меняется на названный, и дальше задача идёт
|
- **переформулировать запись** — тип меняется на названный, и дальше задача идёт
|
||||||
**процессом своего типа**: сценарием решения, следующим прогоном. Формат записи
|
**процессом своего типа**: сценарием решения, следующим прогоном. Формат записи
|
||||||
правит `av-dev-tasks:tasks`, а не ты: у нового типа своя схема разделов, и
|
правит `av-dev:task-track`, а не ты: у нового типа своя схема разделов, и
|
||||||
готовность её проверит `ready` — той же машиной, что и на входе. Прогон
|
готовность её проверит `ready` — той же машиной, что и на входе. Прогон
|
||||||
обслуживания на этом кончается, исход — «меняется спека»;
|
обслуживания на этом кончается, исход — «меняется спека»;
|
||||||
- **прекратить работу** — человек не готов расширять задачу сейчас. Исход тот
|
- **прекратить работу** — человек не готов расширять задачу сейчас. Исход тот
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
|
|
||||||
Как и разведке, обслуживанию OpenSpec не нужен: оно не заводит change, не пишет
|
Как и разведке, обслуживанию OpenSpec не нужен: оно не заводит change, не пишет
|
||||||
дельта-спек и не архивирует. Ни один из проходов его плана ревью на дельта-спеки
|
дельта-спек и не архивирует. Ни один из проходов его плана ревью на дельта-спеки
|
||||||
не завязан — это сказано и в `av-dev-code:review`, раздел «Прогон без change».
|
не завязан — это сказано и в `av-dev:code-review`, раздел «Прогон без change».
|
||||||
Каталога в проекте нет — обслуживание идёт целиком, и деградацией это не
|
Каталога в проекте нет — обслуживание идёт целиком, и деградацией это не
|
||||||
является.
|
является.
|
||||||
|
|
||||||
@@ -145,10 +145,10 @@ flowchart TD
|
|||||||
s1["1. прочитать задачу<br/>критерии приёмки и границы"]
|
s1["1. прочитать задачу<br/>критерии приёмки и границы"]
|
||||||
s2["2. сделать правку<br/>гейт тронут — сверить состав, не цвет"]
|
s2["2. сделать правку<br/>гейт тронут — сверить состав, не цвет"]
|
||||||
s3["3. гейт проекта до зелёного"]
|
s3["3. гейт проекта до зелёного"]
|
||||||
s4["4. ревью фиксированным планом<br/>av-dev-code:review, без change"]
|
s4["4. ревью фиксированным планом<br/>av-dev:code-review, без change"]
|
||||||
s5["5. синк документации — av-dev-docs:docs"]
|
s5["5. синк документации — av-dev:doc-sync"]
|
||||||
s6["6. коммит работы — av-dev-git:commit"]
|
s6["6. коммит работы — av-dev-git:commit"]
|
||||||
s7["7. закрыть задачу — av-dev-tasks:tasks,<br/>вторым коммитом учёта"]
|
s7["7. закрыть задачу — av-dev:task-track,<br/>вторым коммитом учёта"]
|
||||||
out["исход назван"]
|
out["исход назван"]
|
||||||
|
|
||||||
in --> s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> out
|
in --> s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> out
|
||||||
@@ -214,7 +214,7 @@ ADR: список источников канон закрыл двумя — а
|
|||||||
**Проверка на «заодно».** Обслуживание любит склеиваться в пачку — обновить
|
**Проверка на «заодно».** Обслуживание любит склеиваться в пачку — обновить
|
||||||
зависимости, переписать сборку и убрать мёртвый код одной задачей. Не мерджится
|
зависимости, переписать сборку и убрать мёртвый код одной задачей. Не мерджится
|
||||||
порознь — это несколько задач: объявляй исход **не доведена** с причиной «задача
|
порознь — это несколько задач: объявляй исход **не доведена** с причиной «задача
|
||||||
не одна» и останавливайся. Нарезкой владеет `av-dev-tasks:tasks`, а не ты, и
|
не одна» и останавливайся. Нарезкой владеет `av-dev:task-track`, а не ты, и
|
||||||
делать её по ходу нельзя — получится один коммит, в котором обновление
|
делать её по ходу нельзя — получится один коммит, в котором обновление
|
||||||
зависимости не отделить от чистки.
|
зависимости не отделить от чистки.
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@ ADR: список источников канон закрыл двумя — а
|
|||||||
|
|
||||||
### 4. Ревью — план фиксирован сценарием
|
### 4. Ревью — план фиксирован сценарием
|
||||||
|
|
||||||
Вызови Skill **`av-dev-code:review`**, дав базу диффа, режим и **план сценария**.
|
Вызови Skill **`av-dev:code-review`**, дав базу диффа, режим и **план сценария**.
|
||||||
Change ты не передаёшь — его нет.
|
Change ты не передаёшь — его нет.
|
||||||
|
|
||||||
**Разметчик здесь не зовётся, и это правило, а не пропуск.** Обе оси, по которым
|
**Разметчик здесь не зовётся, и это правило, а не пропуск.** Обе оси, по которым
|
||||||
@@ -302,11 +302,11 @@ Change ты не передаёшь — его нет.
|
|||||||
|
|
||||||
Отработка — как в решении: помеченное `инлайн` чини сам и не логируй, `развилка`
|
Отработка — как в решении: помеченное `инлайн` чини сам и не логируй, `развилка`
|
||||||
— вопросом в запись. После правок снова гейт. Отложенные находки собери в секцию
|
— вопросом в запись. После правок снова гейт. Отложенные находки собери в секцию
|
||||||
доклада `Урожай`; задачи из него заводит `av-dev-tasks:tasks`, не ты.
|
доклада `Урожай`; задачи из него заводит `av-dev:task-track`, не ты.
|
||||||
|
|
||||||
### 5. Синк документации — главный шаг этого сценария
|
### 5. Синк документации — главный шаг этого сценария
|
||||||
|
|
||||||
**Вызови Skill `av-dev-docs:docs`.** Правило то же и такое же жёсткое:
|
**Вызови Skill `av-dev:doc-sync`.** Правило то же и такое же жёсткое:
|
||||||
**принуждённое отрицание** — каждый документ канона либо назван обновлённым, либо
|
**принуждённое отрицание** — каждый документ канона либо назван обновлённым, либо
|
||||||
получает «не требуется, потому что…». Нетронутые группируются одной строкой.
|
получает «не требуется, потому что…». Нетронутые группируются одной строкой.
|
||||||
|
|
||||||
@@ -331,11 +331,9 @@ Change ты не передаёшь — его нет.
|
|||||||
«ничего не решали, поменяли оснастку».
|
«ничего не решали, поменяли оснастку».
|
||||||
|
|
||||||
Список документов и их триггеров здесь не дублируется — он в чек-листе скилла
|
Список документов и их триггеров здесь не дублируется — он в чек-листе скилла
|
||||||
`av-dev-docs:docs`; копия уже однажды разошлась с оригиналом. Плагина в проекте
|
`av-dev:doc-sync`; копия уже однажды разошлась с оригиналом. Документов канона в
|
||||||
нет — иди за перечнем в свой reference,
|
проекте нет — синка нет вовсе: скажи это исходом и предложи завести канон
|
||||||
[references/project-facts.md](../../review/references/project-facts.md) конвейера
|
скиллом `av-dev:doc-canon`.
|
||||||
ревью, добавь `adr/` руками и скажи строкой, что синк сделан по перечню
|
|
||||||
документов, без списка триггеров.
|
|
||||||
|
|
||||||
### 6. Коммит
|
### 6. Коммит
|
||||||
|
|
||||||
@@ -348,13 +346,13 @@ Change ты не передаёшь — его нет.
|
|||||||
|
|
||||||
### 7. Закрыть задачу — после коммита, не раньше
|
### 7. Закрыть задачу — после коммита, не раньше
|
||||||
|
|
||||||
**Вызови Skill `av-dev-tasks:tasks`** и попроси закрыть задачу как реализованную.
|
**Вызови Skill `av-dev:task-track`** и попроси закрыть задачу как реализованную.
|
||||||
Порядок обязателен: закрытие удаляет файл задачи, и сделанное до коммита оно
|
Порядок обязателен: закрытие удаляет файл задачи, и сделанное до коммита оно
|
||||||
оставило бы задачу закрытой без следа работы, если шаг 6 упадёт.
|
оставило бы задачу закрытой без следа работы, если шаг 6 упадёт.
|
||||||
|
|
||||||
**Закрытие тоже коммитится — вторым коммитом, тут же**, сообщением про учёт:
|
**Закрытие тоже коммитится — вторым коммитом, тут же**, сообщением про учёт:
|
||||||
`закрыта задача <slug>`. Плагина нет — ничего не выдумывай: скажи, что учёт
|
`закрыта задача <slug>`. Каталога задач в проекте нет — ничего не выдумывай:
|
||||||
остаётся за владельцем, и назови исход.
|
скажи, что учёт остаётся за владельцем, и назови исход.
|
||||||
|
|
||||||
## Границы: чего обслуживание не делает
|
## Границы: чего обслуживание не делает
|
||||||
|
|
||||||
@@ -363,13 +361,13 @@ Change ты не передаёшь — его нет.
|
|||||||
сценария, у которой есть проверяемый признак, и она же единственная, которую
|
сценария, у которой есть проверяемый признак, и она же единственная, которую
|
||||||
выгодно нарушить молча.
|
выгодно нарушить молча.
|
||||||
- **Не переписывает запись задачи сам.** Тип ты **предлагаешь** с причиной,
|
- **Не переписывает запись задачи сам.** Тип ты **предлагаешь** с причиной,
|
||||||
меняет его `av-dev-tasks:tasks` и только после ответа человека: исполнитель,
|
меняет его `av-dev:task-track` и только после ответа человека: исполнитель,
|
||||||
переклеивший тип на ходу, назначает себе другой процесс и другую глубину
|
переклеивший тип на ходу, назначает себе другой процесс и другую глубину
|
||||||
проверки.
|
проверки.
|
||||||
- **Не решает, нужна ли работа.** Как и оба соседних сценария: «надо ли» —
|
- **Не решает, нужна ли работа.** Как и оба соседних сценария: «надо ли» —
|
||||||
вопрос человека.
|
вопрос человека.
|
||||||
- **Не нарезает пачку на задачи.** «Обновить зависимости и переписать сборку» —
|
- **Не нарезает пачку на задачи.** «Обновить зависимости и переписать сборку» —
|
||||||
это `av-dev-tasks:tasks` и его правила нарезки.
|
это `av-dev:task-track` и его правила нарезки.
|
||||||
- **Не выбирает форму правки, когда она незнакома, и не принимает решений с
|
- **Не выбирает форму правки, когда она незнакома, и не принимает решений с
|
||||||
ценой.** Мажорное обновление, смена инструмента, намеренный отказ идут
|
ценой.** Мажорное обновление, смена инструмента, намеренный отказ идут
|
||||||
разведкой: там есть чекпоинт вариантов и законный источник для ADR, здесь нет
|
разведкой: там есть чекпоинт вариантов и законный источник для ADR, здесь нет
|
||||||
+29
-31
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
||||||
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
||||||
ход; общее для обоих сценариев — вход, обращение к соседним плагинам, правило
|
ход; общее для всех трёх сценариев — вход, чего может не быть, правило
|
||||||
необратимого, доклад — живёт в SKILL.md и тут не пересказывается.
|
необратимого, доклад — живёт в SKILL.md и тут не пересказывается.
|
||||||
|
|
||||||
**Разведка кончается своим исходом, а не переходом к коду.** Выбранный способ
|
**Разведка кончается своим исходом, а не переходом к коду.** Выбранный способ
|
||||||
@@ -20,24 +20,25 @@
|
|||||||
**OpenSpec этому сценарию не нужен**, и это единственное место скилла, где он не
|
**OpenSpec этому сценарию не нужен**, и это единственное место скилла, где он не
|
||||||
предпосылка. Разведка не заводит change и не пишет дельта-спеки: её артефакты —
|
предпосылка. Разведка не заводит change и не пишет дельта-спеки: её артефакты —
|
||||||
документы канона и записи каталога задач. Скилл `opsx:explore` полезен и зовётся,
|
документы канона и записи каталога задач. Скилл `opsx:explore` полезен и зовётся,
|
||||||
когда плагин в проекте есть; не разрешился — разведка идёт чтением документов,
|
когда внешний плагин установлен; не разрешился — разведка идёт чтением документов,
|
||||||
кода и внешних источников, и это говорится строкой доклада, а не отменяет
|
кода и внешних источников, и это говорится строкой доклада, а не отменяет
|
||||||
работу.
|
работу.
|
||||||
|
|
||||||
## Кого зовёт этот сценарий
|
## Кого зовёт этот сценарий
|
||||||
|
|
||||||
`av-dev-docs:docs` (ответ уезжает в документы канона), `av-dev-tasks:tasks`
|
`av-dev:doc-sync` (ответ уезжает в документы канона), `av-dev:task-track`
|
||||||
(задачи заводятся и уточняются), `av-dev-git:commit`. Правило обращения к соседям
|
(задачи заводятся и уточняются), `av-dev-git:commit`. Правило обращения к соседям
|
||||||
и ветка «вызов не разрешился» — общие, они в [SKILL.md](../SKILL.md).
|
и правило «чего может не быть» — общие, они в [SKILL.md](../SKILL.md).
|
||||||
|
|
||||||
**Отсутствие канона бьёт по разведке сильнее, чем по решению**, и сказать об этом
|
**Отсутствие канона бьёт по разведке сильнее, чем по решению**, и сказать об этом
|
||||||
строкой мало: без документов у ответа нет дома, и знание осядет в переписке.
|
строкой мало: без документов у ответа нет дома, и знание осядет в переписке.
|
||||||
Назови исход и предложи `av-dev-docs:canon`; работу не останавливай, но адрес
|
Назови исход и предложи `av-dev:doc-canon`; работу не останавливай, но адрес
|
||||||
ответа тогда выбираешь сам и говоришь об этом вслух.
|
ответа тогда выбираешь сам и говоришь об этом вслух.
|
||||||
|
|
||||||
## Что этот сценарий требует от входа
|
## Что этот сценарий требует от входа
|
||||||
|
|
||||||
Вход общий у обоих сценариев (SKILL.md, раздел «Вход»); своего здесь три условия.
|
Вход общий у всех трёх сценариев (SKILL.md, раздел «Вход»); своего здесь три
|
||||||
|
условия.
|
||||||
|
|
||||||
**Паспорт читается раньше кода.** Граница домена и «чем проект **не** является»
|
**Паспорт читается раньше кода.** Граница домена и «чем проект **не** является»
|
||||||
отсекают половину вариантов до того, как их начнут сравнивать, — а сравнение
|
отсекают половину вариантов до того, как их начнут сравнивать, — а сравнение
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
причиной «запись это сырьё»: у неё нет вопроса, а разведка без вопроса
|
причиной «запись это сырьё»: у неё нет вопроса, а разведка без вопроса
|
||||||
превращается в чтение всего подряд с отчётом «интересно, но неприменимо».
|
превращается в чтение всего подряд с отчётом «интересно, но неприменимо».
|
||||||
Дописывать вопрос за автора не твоя работа — этим занят штурм сырья в
|
Дописывать вопрос за автора не твоя работа — этим занят штурм сырья в
|
||||||
`av-dev-tasks:tasks`. Назови, чего не хватает, и остановись.
|
`av-dev:task-track`. Назови, чего не хватает, и остановись.
|
||||||
|
|
||||||
**Разведка пришла текстом — сформулируй вопрос сам одной фразой и покажи
|
**Разведка пришла текстом — сформулируй вопрос сам одной фразой и покажи
|
||||||
формулировку в первой же реплике.** Разведка, чей вопрос не назван вслух,
|
формулировку в первой же реплике.** Разведка, чей вопрос не назван вслух,
|
||||||
@@ -61,11 +62,11 @@ flowchart TD
|
|||||||
s1["1. вопрос и рамки<br/>сырьё без «Вопроса» — отказ"]
|
s1["1. вопрос и рамки<br/>сырьё без «Вопроса» — отказ"]
|
||||||
s2["2. разведка: документы, код,<br/>внешние источники, opsx:explore"]
|
s2["2. разведка: документы, код,<br/>внешние источники, opsx:explore"]
|
||||||
s3(["3. ЧЕКПОИНТ: варианты<br/>2–4 способа, цена каждого,<br/>что становится невозможным"])
|
s3(["3. ЧЕКПОИНТ: варианты<br/>2–4 способа, цена каждого,<br/>что становится невозможным"])
|
||||||
s4["4. ответ в документы канона<br/>av-dev-docs:docs"]
|
s4["4. ответ в документы канона<br/>av-dev:doc-sync"]
|
||||||
s5["5. задачи: завести и уточнить<br/>av-dev-tasks:tasks"]
|
s5["5. задачи: завести и уточнить<br/>av-dev:task-track"]
|
||||||
s6["6. вычитка написанного:<br/>документы и записи задач"]
|
s6["6. вычитка написанного:<br/>документы и записи задач"]
|
||||||
s7["7. гейт проекта, затем коммит<br/>av-dev-git:commit"]
|
s7["7. гейт проекта, затем коммит<br/>av-dev-git:commit"]
|
||||||
s8["8. закрыть разведку — av-dev-tasks:tasks,<br/>вторым коммитом учёта"]
|
s8["8. закрыть разведку — av-dev:task-track,<br/>вторым коммитом учёта"]
|
||||||
out["исход назван: знание, задачи,<br/>отказ или «не доведена»"]
|
out["исход назван: знание, задачи,<br/>отказ или «не доведена»"]
|
||||||
|
|
||||||
in --> s1 --> s2 --> s3
|
in --> s1 --> s2 --> s3
|
||||||
@@ -103,11 +104,11 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
**читающий** прогон (запрос, замер, скрипт в песочнице), чей результат уезжает
|
**читающий** прогон (запрос, замер, скрипт в песочнице), чей результат уезжает
|
||||||
в ответ с провенансом и который ничего не оставляет в репозитории.
|
в ответ с провенансом и который ничего не оставляет в репозитории.
|
||||||
- **Приоритетом.** Заведённая задача встаёт в конец своей секции; где ей стоять
|
- **Приоритетом.** Заведённая задача встаёт в конец своей секции; где ей стоять
|
||||||
в очереди, решает человек на груминге (`av-dev-tasks:groom`). Разведка, сама
|
в очереди, решает человек на груминге (`av-dev:task-groom`). Разведка, сама
|
||||||
ставящая свой исход первым в беклоге, назначает приоритет тому, что только что
|
ставящая свой исход первым в беклоге, назначает приоритет тому, что только что
|
||||||
придумала.
|
придумала.
|
||||||
- **Форматом задач и документов.** Индексы и документы руками не правятся: их
|
- **Форматом задач и документов.** Индексы и документы руками не правятся: их
|
||||||
ведут `av-dev-tasks:tasks` и `av-dev-docs:docs`. Твоё — содержание ответа, их —
|
ведут `av-dev:task-track` и `av-dev:doc-sync`. Твоё — содержание ответа, их —
|
||||||
форма и дом.
|
форма и дом.
|
||||||
- **Определением ценности.** «Нужно ли это делать вообще» — вопрос человека.
|
- **Определением ценности.** «Нужно ли это делать вообще» — вопрос человека.
|
||||||
Разведка отвечает «как это можно сделать и чего каждый способ стоит».
|
Разведка отвечает «как это можно сделать и чего каждый способ стоит».
|
||||||
@@ -243,7 +244,7 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
|
|
||||||
### 4. Ответ в документы канона
|
### 4. Ответ в документы канона
|
||||||
|
|
||||||
**Вызови Skill `av-dev-docs:docs`**: он владеет содержимым документов канона.
|
**Вызови Skill `av-dev:doc-sync`**: он владеет содержимым документов канона.
|
||||||
Передай ему ответ, адрес из шага 1 и провенанс каждого числа — писать содержание
|
Передай ему ответ, адрес из шага 1 и провенанс каждого числа — писать содержание
|
||||||
за тебя он не будет, но дом и форму держит он. Вычитка языка — тоже его агент, но
|
за тебя он не будет, но дом и форму держит он. Вычитка языка — тоже его агент, но
|
||||||
момент её назван отдельно, шагом 6: пачка собирается из шагов 4 и 5 и до конца
|
момент её назван отдельно, шагом 6: пачка собирается из шагов 4 и 5 и до конца
|
||||||
@@ -265,17 +266,13 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
незачем. Но **названо должно быть каждое место, куда ты писал**, — доклад без
|
незачем. Но **названо должно быть каждое место, куда ты писал**, — доклад без
|
||||||
перечня адресов неотличим от доклада о ненаписанном.
|
перечня адресов неотличим от доклада о ненаписанном.
|
||||||
|
|
||||||
**Плагина `av-dev-docs` нет** — путь в его дерево не разрешится ниоткуда, поэтому
|
**Документов канона в проекте нет** — писать ответ некуда: назови это исходом,
|
||||||
за перечнем документов иди в **свой** reference:
|
предложи завести канон скиллом `av-dev:doc-canon` и оставь ответ в докладе
|
||||||
[references/project-facts.md](../../review/references/project-facts.md) конвейера
|
целиком, чтобы работа не пропала. Заводить `docs/` мимо канона нельзя.
|
||||||
ревью. `docs/adr/` и `docs/research/` в нём отсутствуют намеренно (проход ревью их
|
|
||||||
не открывает), а разведке нужны как раз они — добавь их руками. Пиши сам, скажи
|
|
||||||
строкой: «ответ записан без скилла документации — форму и вычитку не сверял
|
|
||||||
никто».
|
|
||||||
|
|
||||||
### 5. Задачи: завести и уточнить
|
### 5. Задачи: завести и уточнить
|
||||||
|
|
||||||
**Вызови Skill `av-dev-tasks:tasks`.** Он владеет форматом, дедупом и индексами;
|
**Вызови Skill `av-dev:task-track`.** Он владеет форматом, дедупом и индексами;
|
||||||
путь к его скрипту не выясняй и индексы руками не правь.
|
путь к его скрипту не выясняй и индексы руками не правь.
|
||||||
|
|
||||||
Что просишь сделать:
|
Что просишь сделать:
|
||||||
@@ -292,8 +289,8 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
способ надуть беклог: она порождает идеи быстрее, чем кто-либо успевает их
|
способ надуть беклог: она порождает идеи быстрее, чем кто-либо успевает их
|
||||||
оценивать, а заведённая задача живёт, пока её кто-то не выкинет руками.
|
оценивать, а заведённая задача живёт, пока её кто-то не выкинет руками.
|
||||||
|
|
||||||
Плагина нет — задачи остаются **списком формулировок в докладе**, и это говорится
|
Каталога задач в проекте нет — задачи остаются **списком формулировок в
|
||||||
строкой: учёт работ остаётся за владельцем.
|
докладе**, и это говорится строкой: учёт работ остаётся за владельцем.
|
||||||
|
|
||||||
### 6. Вычитка написанного — до гейта, не после
|
### 6. Вычитка написанного — до гейта, не после
|
||||||
|
|
||||||
@@ -305,11 +302,11 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
Пачка **собирается только сейчас**, и поэтому шаг стоит здесь: раньше пятого шага
|
Пачка **собирается только сейчас**, и поэтому шаг стоит здесь: раньше пятого шага
|
||||||
она не полна, а после коммита вычитка уже правит закоммиченное.
|
она не полна, а после коммита вычитка уже правит закоммиченное.
|
||||||
|
|
||||||
- **Документы** — агент `doc-wording`, владеет им `av-dev-docs:docs` (раздел
|
- **Документы** — агент `doc-wording`, владеет им `av-dev:doc-sync` (раздел
|
||||||
«Вычитка»). Пачка — адреса, названные на шаге 4, включая `docs/adr/` и
|
«Вычитка»). Пачка — адреса, названные на шаге 4, включая `docs/adr/` и
|
||||||
`docs/research/`.
|
`docs/research/`.
|
||||||
- **Записи задач** — два прохода, сперва `task-form`, затем `task-wording`;
|
- **Записи задач** — два прохода, сперва `task-form`, затем `task-wording`;
|
||||||
владеет ими `av-dev-tasks:tasks` (раздел «Вычитка: два прохода»). Пачка —
|
владеет ими `av-dev:task-track` (раздел «Вычитка: два прохода»). Пачка —
|
||||||
заведённые и уточнённые на шаге 5 записи. Заголовок и «зачем» правятся **не
|
заведённые и уточнённые на шаге 5 записи. Заголовок и «зачем» правятся **не
|
||||||
молча**: покажи предложенное вместе с тем, что было.
|
молча**: покажи предложенное вместе с тем, что было.
|
||||||
|
|
||||||
@@ -319,15 +316,16 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
раз тот же файл не гоняй.
|
раз тот же файл не гоняй.
|
||||||
|
|
||||||
**Судей канона — `doc-consistency` и `doc-code-drift` — здесь не зови.** Они
|
**Судей канона — `doc-consistency` и `doc-code-drift` — здесь не зови.** Они
|
||||||
идут на весь канон разом, стоят дорого, и владеет ими `av-dev-docs:healthcheck`,
|
идут на весь канон разом, стоят дорого, и владеет ими `av-dev:doc-healthcheck`,
|
||||||
момент вызова которого выбирает человек. Нужно суждение о согласованности — скажи
|
момент вызова которого выбирает человек. Нужно суждение о согласованности — скажи
|
||||||
строкой и предложи `healthcheck`, а не зови агентов сам.
|
строкой и предложи `healthcheck`, а не зови агентов сам.
|
||||||
|
|
||||||
Ни один проход ничего не правит: они возвращают готовые формулировки,
|
Ни один проход ничего не правит: они возвращают готовые формулировки,
|
||||||
подставляешь их ты — и уже с подставленными идёшь на гейт.
|
подставляешь их ты — и уже с подставленными идёшь на гейт.
|
||||||
|
|
||||||
Плагина нет — вызов не разрешится: скажи строкой, что написанное не вычитывал
|
Проходы вычитки — агенты этого же плагина, и разрешаются они всегда. Не
|
||||||
никто, и обходного пути не выдумывай.
|
разрешились — это поломка установки, а не раскладки проекта: скажи строкой, что
|
||||||
|
написанное не вычитывал никто, и обходного пути не выдумывай.
|
||||||
|
|
||||||
### 7. Гейт и коммит
|
### 7. Гейт и коммит
|
||||||
|
|
||||||
@@ -351,7 +349,7 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
|
|
||||||
### 8. Закрыть разведку — после коммита, не раньше
|
### 8. Закрыть разведку — после коммита, не раньше
|
||||||
|
|
||||||
**Вызови Skill `av-dev-tasks:tasks`** и попроси закрыть запись: ответ записан —
|
**Вызови Skill `av-dev:task-track`** и попроси закрыть запись: ответ записан —
|
||||||
`close --implemented`, ушла без ответа — `close --reason`, и строка уезжает в
|
`close --implemented`, ушла без ответа — `close --reason`, и строка уезжает в
|
||||||
кладбище.
|
кладбище.
|
||||||
|
|
||||||
@@ -365,8 +363,8 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
учёт, а не про работу: `закрыта задача <slug>`. Правило «одна разведка — один
|
учёт, а не про работу: `закрыта задача <slug>`. Правило «одна разведка — один
|
||||||
коммит» про работу, а учёт — не работа.
|
коммит» про работу, а учёт — не работа.
|
||||||
|
|
||||||
Плагина в проекте нет — **ничего не выдумывай**: скажи в докладе, что учёт задач
|
Каталога задач в проекте нет — **ничего не выдумывай**: скажи в докладе, что
|
||||||
остаётся за владельцем, и назови исход.
|
учёт задач остаётся за владельцем, и назови исход.
|
||||||
|
|
||||||
## Доклад разведки
|
## Доклад разведки
|
||||||
|
|
||||||
+21
-32
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
||||||
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
||||||
ход; общее для обоих сценариев — вход, обращение к соседним плагинам, правило
|
ход; общее для всех трёх сценариев — вход, чего может не быть, правило
|
||||||
записанного вопроса, правило необратимого — живёт в SKILL.md и тут не
|
записанного вопроса, правило необратимого — живёт в SKILL.md и тут не
|
||||||
пересказывается.
|
пересказывается.
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
Тонкая обёртка над каноническими скиллами `opsx:propose` / `opsx:apply` /
|
Тонкая обёртка над каноническими скиллами `opsx:propose` / `opsx:apply` /
|
||||||
`opsx:archive` — зови их через Skill, не переизобретай их шаги. Ревью — скилл
|
`opsx:archive` — зови их через Skill, не переизобретай их шаги. Ревью — скилл
|
||||||
`av-dev-code:review`; он же держит правило выбора метки, а называет её агент
|
`av-dev:code-review`; он же держит правило выбора метки, а называет её агент
|
||||||
`review-scope` — один раз на задачу, для обеих стадий ревью.
|
`review-scope` — один раз на задачу, для обеих стадий ревью.
|
||||||
|
|
||||||
## Ход работы
|
## Ход работы
|
||||||
@@ -32,9 +32,9 @@ flowchart TD
|
|||||||
s6["6. opsx:apply — код, гейт,<br/>поведенческая верификация"]
|
s6["6. opsx:apply — код, гейт,<br/>поведенческая верификация"]
|
||||||
s7["7. ревью кода, та же метка<br/>+ отработка замечаний"]
|
s7["7. ревью кода, та же метка<br/>+ отработка замечаний"]
|
||||||
s8["8. opsx:archive"]
|
s8["8. opsx:archive"]
|
||||||
s9["9. синк документации — av-dev-docs:docs"]
|
s9["9. синк документации — av-dev:doc-sync"]
|
||||||
s10["10. коммит работы — av-dev-git:commit"]
|
s10["10. коммит работы — av-dev-git:commit"]
|
||||||
s11["11. закрыть задачу — av-dev-tasks:tasks,<br/>вторым коммитом учёта"]
|
s11["11. закрыть задачу — av-dev:task-track,<br/>вторым коммитом учёта"]
|
||||||
|
|
||||||
in --> s1
|
in --> s1
|
||||||
s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 --> s10 --> s11
|
s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 --> s10 --> s11
|
||||||
@@ -144,7 +144,7 @@ flowchart TD
|
|||||||
|
|
||||||
### 4. Ревью дизайна — ДО кода, состав по метке
|
### 4. Ревью дизайна — ДО кода, состав по метке
|
||||||
|
|
||||||
Вызови Skill **`av-dev-code:review`**, дав ссылку на change `<id>`,
|
Вызови Skill **`av-dev:code-review`**, дав ссылку на change `<id>`,
|
||||||
**план разметки с шага 3** и указание, что это ревью дизайна.
|
**план разметки с шага 3** и указание, что это ревью дизайна.
|
||||||
|
|
||||||
Состав приходит планом, а не решается здесь:
|
Состав приходит планом, а не решается здесь:
|
||||||
@@ -231,7 +231,7 @@ flowchart TD
|
|||||||
|
|
||||||
### 7. Ревью кода — та же метка
|
### 7. Ревью кода — та же метка
|
||||||
|
|
||||||
Вызови Skill **`av-dev-code:review`**, дав ссылку на change `<id>`,
|
Вызови Skill **`av-dev:code-review`**, дав ссылку на change `<id>`,
|
||||||
базу диффа, **план разметки с шага 3** и режим запуска.
|
базу диффа, **план разметки с шага 3** и режим запуска.
|
||||||
|
|
||||||
**Метку ты не выбираешь, и это правило, а не упрощение.** Её назвал
|
**Метку ты не выбираешь, и это правило, а не упрощение.** Её назвал
|
||||||
@@ -239,7 +239,7 @@ flowchart TD
|
|||||||
оси. Причина в разведённости: ты только что написал этот код, и решать, насколько
|
оси. Причина в разведённости: ты только что написал этот код, и решать, насколько
|
||||||
глубоко его проверять, тебе нельзя — под давлением «я почти закончил» решение
|
глубоко его проверять, тебе нельзя — под давлением «я почти закончил» решение
|
||||||
известно заранее. Правило выбора живёт в скилле конвейера —
|
известно заранее. Правило выбора живёт в скилле конвейера —
|
||||||
`av-dev-code:review`, `references/review-levels.md`; проектные
|
`av-dev:code-review`, `references/review-levels.md`; проектные
|
||||||
триггеры — в `docs/review.*`, подраздел «Триггеры метки».
|
триггеры — в `docs/review.*`, подраздел «Триггеры метки».
|
||||||
|
|
||||||
**Метка не пересматривается по факту диффа.** Дифф может выйти крупнее, чем
|
**Метка не пересматривается по факту диффа.** Дифф может выйти крупнее, чем
|
||||||
@@ -298,7 +298,7 @@ flowchart TD
|
|||||||
**Урожай — списком, не задачами.** Отложенные находки (реальный `major` не для
|
**Урожай — списком, не задачами.** Отложенные находки (реальный `major` не для
|
||||||
этого мерджа, развилка, решённая «потом», пачка `nit`) собери в секцию доклада
|
этого мерджа, развилка, решённая «потом», пачка `nit`) собери в секцию доклада
|
||||||
`Урожай`: формулировка, оракул, провенанс. Задачи из него **заводит не этот
|
`Урожай`: формулировка, оракул, провенанс. Задачи из него **заводит не этот
|
||||||
скилл** — их заводит `av-dev-tasks:tasks` своим сценарием «задачи из ревью и
|
скилл** — их заводит `av-dev:task-track` своим сценарием «задачи из ревью и
|
||||||
аудита»: своя нарезка, свой формат, свои правила дублей. Твоя обязанность — не
|
аудита»: своя нарезка, свой формат, свои правила дублей. Твоя обязанность — не
|
||||||
потерять и передать.
|
потерять и передать.
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ flowchart TD
|
|||||||
|
|
||||||
### 9. Синк документации
|
### 9. Синк документации
|
||||||
|
|
||||||
**Вызови Skill `av-dev-docs:docs`**: он владеет содержимым документов канона и
|
**Вызови Skill `av-dev:doc-sync`**: он владеет содержимым документов канона и
|
||||||
ведёт чек-лист синка.
|
ведёт чек-лист синка.
|
||||||
|
|
||||||
**Правило одно и оно жёсткое: принуждённое отрицание.** Доклад обязан назвать
|
**Правило одно и оно жёсткое: принуждённое отрицание.** Доклад обязан назвать
|
||||||
@@ -329,24 +329,13 @@ flowchart TD
|
|||||||
работает только обязательное отрицание.
|
работает только обязательное отрицание.
|
||||||
|
|
||||||
**Список документов и их триггеров здесь не дублируется** — он в чек-листе скилла
|
**Список документов и их триггеров здесь не дублируется** — он в чек-листе скилла
|
||||||
`av-dev-docs:docs`, и копия уже однажды разошлась с оригиналом, потеряв два
|
`av-dev:doc-sync`, и копия уже однажды разошлась с оригиналом, потеряв два
|
||||||
триггера.
|
триггера.
|
||||||
|
|
||||||
**Плагина `av-dev-docs` в проекте нет** — путь в его дерево не разрешится ниоткуда,
|
**Документов канона в проекте нет** — синка нет вовсе: назови это исходом и
|
||||||
поэтому за списком иди в **свой** reference:
|
предложи завести канон скиллом `av-dev:doc-canon`. Придумывать раскладку под
|
||||||
[references/project-facts.md](../../review/references/project-facts.md)
|
задачу нельзя: документ, заведённый мимо канона, окажется вторым домом ровно
|
||||||
конвейера ревью перечисляет все документы канона с их предметом. Пройди по этому
|
тому, что канон потом заведёт своим.
|
||||||
перечню — каждый документ получает строку, отрицание остаётся обязательным.
|
|
||||||
|
|
||||||
**Двух домов в том перечне нет намеренно, а синку они нужны: `docs/adr/` и
|
|
||||||
`docs/research/`.** Проход ревью их не открывает — процессные, — но ADR как раз
|
|
||||||
**главный выход синка**: решение, принятое по ходу задачи, без этой строки
|
|
||||||
теряется систематически. Добавь их к перечню руками: `adr/` — решение с ценой,
|
|
||||||
принятое в этой задаче; `research/` — если по ходу узналось новое о внешнем мире
|
|
||||||
(записку разведки, предшествовавшей задаче, пишет не этот сценарий).
|
|
||||||
Триггеры при этом ты знаешь хуже, и это называется в докладе строкой: «синк
|
|
||||||
сделан по перечню документов, без списка триггеров — плагина `av-dev-docs` нет».
|
|
||||||
Канона в проекте тоже нет — назови это исходом и предложи `av-dev-docs:canon`.
|
|
||||||
|
|
||||||
### 10. Коммит
|
### 10. Коммит
|
||||||
|
|
||||||
@@ -360,7 +349,7 @@ flowchart TD
|
|||||||
|
|
||||||
### 11. Закрыть задачу — **после коммита, не раньше**
|
### 11. Закрыть задачу — **после коммита, не раньше**
|
||||||
|
|
||||||
**Вызови Skill `av-dev-tasks:tasks`** и попроси закрыть задачу как реализованную —
|
**Вызови Skill `av-dev:task-track`** и попроси закрыть задачу как реализованную —
|
||||||
он владеет форматом и двигает строку индекса сам. Путь к его скрипту не выясняй и
|
он владеет форматом и двигает строку индекса сам. Путь к его скрипту не выясняй и
|
||||||
индексы руками не правь: мост между плагинами — вызов скилла, а не путь.
|
индексы руками не правь: мост между плагинами — вызов скилла, а не путь.
|
||||||
|
|
||||||
@@ -368,15 +357,15 @@ flowchart TD
|
|||||||
оставило бы задачу закрытой без единого следа работы, если шаг 10 упадёт.
|
оставило бы задачу закрытой без единого следа работы, если шаг 10 упадёт.
|
||||||
|
|
||||||
**Закрытие тоже коммитится — вторым коммитом, тут же.** Удаление файла задачи и
|
**Закрытие тоже коммитится — вторым коммитом, тут же.** Удаление файла задачи и
|
||||||
правка индексов (их имена знает `av-dev-tasks`, не ты) — это правки в рабочем
|
правка индексов (их имена знает `av-dev:task-track`, не ты) — это правки в рабочем
|
||||||
дереве, и оставить их незакоммиченными нельзя: закрытие, не доехавшее до основной
|
дереве, и оставить их незакоммиченными нельзя: закрытие, не доехавшее до основной
|
||||||
ветки, оставит задачу открытой молча, а опора «индексы под git показывают, что и
|
ветки, оставит задачу открытой молча, а опора «индексы под git показывают, что и
|
||||||
когда закрыто» без коммита — пустые слова. Сообщение короткое, про учёт, а не про
|
когда закрыто» без коммита — пустые слова. Сообщение короткое, про учёт, а не про
|
||||||
работу: `закрыта задача <slug>`. Это второй коммит осознанно: правило «одна задача
|
работу: `закрыта задача <slug>`. Это второй коммит осознанно: правило «одна задача
|
||||||
— один осмысленный коммит» про работу, а учёт — не работа.
|
— один осмысленный коммит» про работу, а учёт — не работа.
|
||||||
|
|
||||||
Плагина в проекте нет — вызов не разрешится. Тогда **ничего не выдумывай**: скажи
|
Каталога задач в проекте нет — **ничего не выдумывай**: скажи в докладе, что
|
||||||
в докладе, что учёт задач остаётся за владельцем, и назови исход.
|
учёт задач остаётся за владельцем, и назови исход.
|
||||||
|
|
||||||
## Доклад решения
|
## Доклад решения
|
||||||
|
|
||||||
@@ -404,9 +393,9 @@ flowchart TD
|
|||||||
написал код, план сверяется по темам, непокрытое называется строкой, а
|
написал код, план сверяется по темам, непокрытое называется строкой, а
|
||||||
расхождение с одобренным — отдельным пунктом доклада.
|
расхождение с одобренным — отдельным пунктом доклада.
|
||||||
- **Заведение задач из урожая ревью — не твоя работа.** Отложенные находки
|
- **Заведение задач из урожая ревью — не твоя работа.** Отложенные находки
|
||||||
отдаются **списком**; превращает их в задачи `av-dev-tasks:tasks`, у него на
|
отдаются **списком**; превращает их в задачи `av-dev:task-track`, у него на
|
||||||
этот вход отдельный сценарий «задачи из ревью и аудита». Плагина нет — урожай
|
этот вход отдельный сценарий «задачи из ревью и аудита». Каталога задач в
|
||||||
остаётся списком в докладе, и это говорится строкой.
|
проекте нет — урожай остаётся списком в докладе, и это говорится строкой.
|
||||||
- **Способ решения ты не выбираешь.** Он приходит известным: из постановки, из
|
- **Способ решения ты не выбираешь.** Он приходит известным: из постановки, из
|
||||||
разведки, от человека. Выбор между двумя подходами с разной ценой делается в
|
разведки, от человека. Выбор между двумя подходами с разной ценой делается в
|
||||||
разведке, у своего чекпоинта, — не по ходу этого сценария.
|
разведке, у своего чекпоинта, — не по ходу этого сценария.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review
|
name: code-review
|
||||||
description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Разметка задачи идёт один раз, после propose: агент review-scope выводит размер и сложность, из их максимума — метка, и раздаёт темы проходам обеих стадий. Метка правит и ревью дизайна (small — только specs; medium — плюс rubric; large — плюс architecture), и ревью кода (small — гейт, спеки, код, триаж; medium — плюс приёмник тем; large — плюс доказательство: враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе). Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает проходы с мнением, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона av-dev-docs. Вызывается из скилла resolve — двумя стадиями: ревью дизайна до кода и ревью кода после apply. Третий вызов идёт от сценария обслуживания: без change и без метки, фиксированным планом (autotests, operations, плюс conventions, если тронут код), разметчик при этом не запускается."
|
description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Разметка задачи идёт один раз, после propose: агент review-scope выводит размер и сложность, из их максимума — метка, и раздаёт темы проходам обеих стадий. Метка правит и ревью дизайна (small — только specs; medium — плюс rubric; large — плюс architecture), и ревью кода (small — гейт, спеки, код, триаж; medium — плюс приёмник тем; large — плюс доказательство: враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе). Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает проходы с мнением, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона проекта. Вызывается из скилла av-dev:code-resolve — двумя стадиями: ревью дизайна до кода и ревью кода после apply. Третий вызов идёт от сценария обслуживания: без change и без метки, фиксированным планом (autotests, operations, плюс conventions, если тронут код), разметчик при этом не запускается."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Конвейер ревью
|
# Конвейер ревью
|
||||||
@@ -44,7 +44,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
|
|
||||||
- **OpenSpec — жёсткая предпосылка, а не опция.** Ревью дизайна, проход
|
- **OpenSpec — жёсткая предпосылка, а не опция.** Ревью дизайна, проход
|
||||||
`review-specs` и
|
`review-specs` и
|
||||||
вызывающий скилл `av-dev-code:resolve` завязаны на дельта-спеки
|
вызывающий скилл `av-dev:code-resolve` завязаны на дельта-спеки
|
||||||
(`openspec/changes/<id>/specs/*/spec.md`), на актуальные спеки
|
(`openspec/changes/<id>/specs/*/spec.md`), на актуальные спеки
|
||||||
(`openspec/specs/`) и на `openspec validate --strict`. В проекте без OpenSpec
|
(`openspec/specs/`) и на `openspec validate --strict`. В проекте без OpenSpec
|
||||||
шаги, зовущие `opsx:explore` / `opsx:propose` / `opsx:apply` / `opsx:archive`,
|
шаги, зовущие `opsx:explore` / `opsx:propose` / `opsx:apply` / `opsx:archive`,
|
||||||
@@ -52,9 +52,9 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
требований. **Проект без OpenSpec этим конвейером не проверяется** — подключай
|
требований. **Проект без OpenSpec этим конвейером не проверяется** — подключай
|
||||||
OpenSpec, а не понижай прогон: ветка деградации здесь не пишется, потому что
|
OpenSpec, а не понижай прогон: ветка деградации здесь не пишется, потому что
|
||||||
непроверенная ветка деградации хуже честного отказа. Заводить руками не надо:
|
непроверенная ветка деградации хуже честного отказа. Заводить руками не надо:
|
||||||
этим владеет скилл `av-dev-code:openspec` — он заводит каталог и заменяет
|
этим владеет скилл `av-dev:code-openspec` — он заводит каталог и заменяет
|
||||||
пример в `config.yaml` настройкой. Его же зовут `av-dev-docs:init` на новом
|
пример в `config.yaml` настройкой. Его же зовут `av-dev:doc-init` на новом
|
||||||
проекте и `av-dev-docs:canon` в режиме `adopt` — на переводимом.
|
проекте и `av-dev:doc-canon` в режиме `adopt` — на переводимом.
|
||||||
**Предпосылка эта — про изменение поведения, а не про всякий прогон:**
|
**Предпосылка эта — про изменение поведения, а не про всякий прогон:**
|
||||||
сценарий обслуживания зовёт конвейер без change и без дельта-спек, и ни один
|
сценарий обслуживания зовёт конвейер без change и без дельта-спек, и ни один
|
||||||
проход его плана на них не завязан. См. «Прогон без change».
|
проход его плана на них не завязан. См. «Прогон без change».
|
||||||
@@ -66,40 +66,46 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
`.claude/agents/<проект>-review-*.md` — снеси их. Иначе короткое имя разрешится
|
`.claude/agents/<проект>-review-*.md` — снеси их. Иначе короткое имя разрешится
|
||||||
в устаревшую проектную копию, молча и без признаков подмены.
|
в устаревшую проектную копию, молча и без признаков подмены.
|
||||||
|
|
||||||
### Обращение к соседним плагинам
|
### Чего может не быть
|
||||||
|
|
||||||
**Копия.** Дом — `shared/plugin-boundary.md` в репозитории плагинов. Правится
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
дом, а не этот файл.
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
месте.
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, `av-dev-tasks:tasks`,
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
`av-dev-code:review`. Короткое имя может разрешиться в устаревшую проектную копию
|
| --- | --- | --- |
|
||||||
из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в поведении.
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт
|
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||||
только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
прочитает его сам.
|
поведении.
|
||||||
|
|
||||||
**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя,
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
Своих скиллов это касается ровно так же: `av-dev-code:review`,
|
<!-- /копия: отсутствие -->
|
||||||
`av-dev-code:resolve`, `av-dev-code:openspec` — подменяется короткое имя,
|
|
||||||
|
Своих скиллов это касается ровно так же: `av-dev:code-review`,
|
||||||
|
`av-dev:code-resolve`, `av-dev:code-openspec` — подменяется короткое имя,
|
||||||
а не чужое.
|
а не чужое.
|
||||||
|
|
||||||
## Темы, источники и процессные документы
|
## Темы, источники и процессные документы
|
||||||
@@ -118,7 +124,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **тема** | заводит направление проверки и требует исполнителя | `conventions.*`, `security.*`, `architecture.*`, свои документы проекта |
|
| **тема** | заводит направление проверки и требует исполнителя | `conventions.*`, `security.*`, `architecture.*`, свои документы проекта |
|
||||||
| **источник темы** | читает как материал чужой темы, своей не порождает | `passport.*`, `database.*`, `CLAUDE.md`/`AGENTS.md`, `openspec/specs/` |
|
| **источник темы** | читает как материал чужой темы, своей не порождает | `passport.*`, `database.*`, `CLAUDE.md`/`AGENTS.md`, `openspec/specs/` |
|
||||||
| **процессный документ** | не судит по нему изменение | `tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `docs/.docs.json` |
|
| **процессный документ** | не судит по нему изменение | `tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `.av-dev.toml` |
|
||||||
|
|
||||||
**Одна процессная запись всё же читается — `docs/review.*`.** В ней лежит
|
**Одна процессная запись всё же читается — `docs/review.*`.** В ней лежит
|
||||||
настройка самого конвейера: вопросы по темам, журнал дефектов, типовые узлы,
|
настройка самого конвейера: вопросы по темам, журнал дефектов, типовые узлы,
|
||||||
@@ -126,7 +132,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не
|
критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не
|
||||||
открывает никто.
|
открывает никто.
|
||||||
|
|
||||||
Дом канона этой раскладки — скилл `av-dev-docs:canon`, раздел «Три категории
|
Дом канона этой раскладки — скилл `av-dev:doc-canon`, раздел «Три категории
|
||||||
документов». Конвейер её **читатель**: категории и имена тем он берёт
|
документов». Конвейер её **читатель**: категории и имена тем он берёт
|
||||||
оттуда и своих не заводит.
|
оттуда и своих не заводит.
|
||||||
|
|
||||||
@@ -158,7 +164,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
читал решения, а эксплуатационный и `specs` — числа. Цена решения записана в
|
читал решения, а эксплуатационный и `specs` — числа. Цена решения записана в
|
||||||
каноне и повторяется здесь, потому что платит её конвейер: **расхождение
|
каноне и повторяется здесь, потому что платит её конвейер: **расхождение
|
||||||
изменения с записанным решением прогоном не ловится**, это работа сверки
|
изменения с записанным решением прогоном не ловится**, это работа сверки
|
||||||
документации — скилл `av-dev-docs:healthcheck`.
|
документации — скилл `av-dev:doc-healthcheck`.
|
||||||
Строка об этом обязательна в границах покрытия каждого прогона.
|
Строка об этом обязательна в границах покрытия каждого прогона.
|
||||||
|
|
||||||
**Своя тема проекта бывает двух происхождений, и обе законны:** документ, который
|
**Своя тема проекта бывает двух происхождений, и обе законны:** документ, который
|
||||||
@@ -185,7 +191,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
дом для тех же фактов разошёлся бы и выглядел актуальным.
|
дом для тех же фактов разошёлся бы и выглядел актуальным.
|
||||||
|
|
||||||
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
|
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
|
||||||
и предложи скилл `av-dev-docs:canon`: одна операция на проект против деградации на
|
и предложи скилл `av-dev:doc-canon`: одна операция на проект против деградации на
|
||||||
каждой задаче. Прогон при этом не останавливается.
|
каждой задаче. Прогон при этом не останавливается.
|
||||||
|
|
||||||
## Что получает каждый проход
|
## Что получает каждый проход
|
||||||
@@ -201,7 +207,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
прохода между метками;
|
прохода между метками;
|
||||||
- **контракт находок** — путь к
|
- **контракт находок** — путь к
|
||||||
[references/finding-contract.md](references/finding-contract.md) (в
|
[references/finding-contract.md](references/finding-contract.md) (в
|
||||||
установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/review/references/`);
|
установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/`);
|
||||||
- **изменение** — идентификатор change и путь к его дельта-спекам;
|
- **изменение** — идентификатор change и путь к его дельта-спекам;
|
||||||
- **база диффа**;
|
- **база диффа**;
|
||||||
- **метка, его глубина и режим** прогона — чтобы проход знал, что писать в
|
- **метка, его глубина и режим** прогона — чтобы проход знал, что писать в
|
||||||
@@ -643,7 +649,7 @@ flowchart TD
|
|||||||
|
|
||||||
## Прогон без change — сценарий обслуживания
|
## Прогон без change — сценарий обслуживания
|
||||||
|
|
||||||
Третий вызывающий конвейера — сценарий обслуживания скилла `av-dev-code:resolve`
|
Третий вызывающий конвейера — сценарий обслуживания скилла `av-dev:code-resolve`
|
||||||
(тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит **без
|
(тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит **без
|
||||||
change**: у работы, не меняющей поведения, дельта-спек нет по построению.
|
change**: у работы, не меняющей поведения, дельта-спек нет по построению.
|
||||||
|
|
||||||
@@ -672,7 +678,7 @@ change**: у работы, не меняющей поведения, дельт
|
|||||||
метке `small`. Все три обязаны быть названы в границах покрытия, а **сигнал о
|
метке `small`. Все три обязаны быть названы в границах покрытия, а **сигнал о
|
||||||
заниженной метке на таком прогоне не работает**: поднимать нечего.
|
заниженной метке на таком прогоне не работает**: поднимать нечего.
|
||||||
|
|
||||||
Дом плана — сценарий, а не этот скилл: `av-dev-code:resolve`,
|
Дом плана — сценарий, а не этот скилл: `av-dev:code-resolve`,
|
||||||
`references/maintain.md`, раздел «Ревью — план фиксирован сценарием».
|
`references/maintain.md`, раздел «Ревью — план фиксирован сценарием».
|
||||||
|
|
||||||
**Правило гейта на таком прогоне работает жёстче обычного.** Правка, которая
|
**Правило гейта на таком прогоне работает жёстче обычного.** Правка, которая
|
||||||
@@ -898,7 +904,7 @@ change»: сверять исход с планом триаж обязан и
|
|||||||
## Ревью дизайна — до кода
|
## Ревью дизайна — до кода
|
||||||
|
|
||||||
Запускается на первой стадии ревью (шаг 4 скилла
|
Запускается на первой стадии ревью (шаг 4 скилла
|
||||||
`av-dev-code:resolve`), когда change уже имеет `proposal.md` и
|
`av-dev:code-resolve`), когда change уже имеет `proposal.md` и
|
||||||
дельта-спеки, но кода ещё нет. Разметка задачи к этому моменту уже прошла — она
|
дельта-спеки, но кода ещё нет. Разметка задачи к этому моменту уже прошла — она
|
||||||
шагом раньше, и метка известна.
|
шагом раньше, и метка известна.
|
||||||
|
|
||||||
@@ -941,7 +947,7 @@ change»: сверять исход с планом триаж обязан и
|
|||||||
|
|
||||||
**Граф этой стадии свой, и он плоский.** Гейта нет — кода ещё нет, запускать
|
**Граф этой стадии свой, и он плоский.** Гейта нет — кода ещё нет, запускать
|
||||||
нечего; метка уже названа разметкой задачи; машину не держит ни один проход;
|
нечего; метка уже названа разметкой задачи; машину не держит ни один проход;
|
||||||
сток — не триаж, а шаг скилла `av-dev-code:resolve`, где замечания
|
сток — не триаж, а шаг скилла `av-dev:code-resolve`, где замечания
|
||||||
отрабатываются правкой спек. Триаж здесь не нужен: находок единицы, и каждая
|
отрабатываются правкой спек. Триаж здесь не нужен: находок единицы, и каждая
|
||||||
либо правит спеку, либо
|
либо правит спеку, либо
|
||||||
становится развилкой.
|
становится развилкой.
|
||||||
@@ -993,11 +999,11 @@ flowchart TD
|
|||||||
- Находка не для этого мерджа, но реальная (отложенный `major`, развилка,
|
- Находка не для этого мерджа, но реальная (отложенный `major`, развилка,
|
||||||
решённая «потом»), — не теряется, но **и не заводится здесь**. Конвейер отдаёт
|
решённая «потом»), — не теряется, но **и не заводится здесь**. Конвейер отдаёт
|
||||||
её **списком урожая** в отчёте: формулировка, оракул, провенанс (какой проход,
|
её **списком урожая** в отчёте: формулировка, оракул, провенанс (какой проход,
|
||||||
какой change). Заведение задач принадлежит `av-dev-tasks:tasks` — зови его со
|
какой change). Заведение задач принадлежит `av-dev:task-track` — зови его со
|
||||||
списком урожая, у него на этот вход отдельный сценарий «задачи из ревью и
|
списком урожая, у него на этот вход отдельный сценарий «задачи из ревью и
|
||||||
аудита»: свой формат, кластеризация по причине, дедуп против беклога и
|
аудита»: свой формат, кластеризация по причине, дедуп против беклога и
|
||||||
кладбища. Плагина нет — урожай остаётся списком в отчёте, и это говорится
|
кладбища. Каталога задач в проекте нет — урожай остаётся списком в отчёте, и
|
||||||
строкой доклада: задачи из него не заведёт никто. Мелочь класса `nit`
|
это говорится строкой доклада: задачи из него не заведёт никто. Мелочь класса `nit`
|
||||||
идёт в урожай одной пачкой, а не записью на находку.
|
идёт в урожай одной пачкой, а не записью на находку.
|
||||||
- `Promote candidates` — по процедуре [references/promote.md](references/promote.md):
|
- `Promote candidates` — по процедуре [references/promote.md](references/promote.md):
|
||||||
находка → конвенция → правило линтера → **удаление формулировки из конвенций**.
|
находка → конвенция → правило линтера → **удаление формулировки из конвенций**.
|
||||||
@@ -1010,7 +1016,7 @@ flowchart TD
|
|||||||
заведено: нулевой урожай при непустом отчёте виден сразу.
|
заведено: нулевой урожай при непустом отчёте виден сразу.
|
||||||
**Вместе с изменением он и переезжает:** после `opsx:archive` его адрес —
|
**Вместе с изменением он и переезжает:** после `opsx:archive` его адрес —
|
||||||
`openspec/changes/archive/<id>/review/`. Кто ищет отчёт после архивации
|
`openspec/changes/archive/<id>/review/`. Кто ищет отчёт после архивации
|
||||||
(приёмщик на груминге `av-dev-tasks:groom`, разбор дефекта), смотрит **оба**
|
(приёмщик на груминге `av-dev:task-groom`, разбор дефекта), смотрит **оба**
|
||||||
пути; «отчёта нет» объявляется, только когда пуст и архивный, иначе самый
|
пути; «отчёта нет» объявляется, только когда пуст и архивный, иначе самый
|
||||||
дорогой сценарий «состав ревью неизвестен, гоняем заново» срабатывает на
|
дорогой сценарий «состав ревью неизвестен, гоняем заново» срабатывает на
|
||||||
каждой доведённой задаче.
|
каждой доведённой задаче.
|
||||||
@@ -1107,7 +1113,7 @@ flowchart TD
|
|||||||
и где это лежит в документах проекта; таблица поразрядной деградации.
|
и где это лежит в документах проекта; таблица поразрядной деградации.
|
||||||
- [references/review-levels.md](references/review-levels.md) — дом правила выбора
|
- [references/review-levels.md](references/review-levels.md) — дом правила выбора
|
||||||
метки: две оси, спорное вниз, чем `small` дешевле, доли как проверка правила.
|
метки: две оси, спорное вниз, чем `small` дешевле, доли как проверка правила.
|
||||||
- Skill `av-dev-docs:canon` — приведение проекта к канону документов.
|
- Skill `av-dev:doc-canon` — приведение проекта к канону документов.
|
||||||
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
||||||
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
|
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
|
||||||
- [references/calibration.md](references/calibration.md) — калибровка инъекцией, вердикты keep/retune/drop.
|
- [references/calibration.md](references/calibration.md) — калибровка инъекцией, вердикты keep/retune/drop.
|
||||||
+2
-2
@@ -55,7 +55,7 @@ stateDiagram-v2
|
|||||||
поведения библиотеки и драйвера**, — и находка, воспроизведённая числом, отменила
|
поведения библиотеки и драйвера**, — и находка, воспроизведённая числом, отменила
|
||||||
решение, принятое по ощущению.
|
решение, принятое по ощущению.
|
||||||
|
|
||||||
## Состав проходов принадлежит плагину, а не проекту
|
## Состав проходов принадлежит скиллу, а не проекту
|
||||||
|
|
||||||
Проходы общие. Проект не может удалить проход — он может **не звать** его, и
|
Проходы общие. Проект не может удалить проход — он может **не звать** его, и
|
||||||
тогда это идёт строкой «не запускался» в границы покрытия, как любой другой
|
тогда это идёт строкой «не запускался» в границы покрытия, как любой другой
|
||||||
@@ -67,7 +67,7 @@ stateDiagram-v2
|
|||||||
- **правка charter'а — правка для всех проектов.** Прежде чем сужать
|
- **правка charter'а — правка для всех проектов.** Прежде чем сужать
|
||||||
формулировку под свою боль, проверь, не место ли ей в документах проекта: предмет проверки
|
формулировку под свою боль, проверь, не место ли ей в документах проекта: предмет проверки
|
||||||
живёт там, метод — в charter'е;
|
живёт там, метод — в charter'е;
|
||||||
- **удаление прохода из плагина требует замера на двух проектах**, а не на одном:
|
- **удаление прохода из конвейера требует замера на двух проектах**, а не на одном:
|
||||||
класс, не всплывший здесь, мог быть единственным работающим там.
|
класс, не всплывший здесь, мог быть единственным работающим там.
|
||||||
|
|
||||||
## Пробы дефектов по проходам
|
## Пробы дефектов по проходам
|
||||||
+4
-4
@@ -4,11 +4,11 @@
|
|||||||
нельзя нарушать, чем краснеет гейт и сколько данных реально идёт через узел,
|
нельзя нарушать, чем краснеет гейт и сколько данных реально идёт через узел,
|
||||||
выдаёт правдоподобные общие места: их дорого опровергать и нечем подтверждать.
|
выдаёт правдоподобные общие места: их дорого опровергать и нечем подтверждать.
|
||||||
|
|
||||||
Отдельного файла-брифа **нет**. Проектная конкретика живёт в документах канона
|
Отдельного файла-брифа **нет**. Проектная конкретика живёт в документах канона,
|
||||||
`av-dev-docs`, и проход читает их напрямую: пути жёсткие, посредник не нужен, а
|
и проход читает их напрямую: пути жёсткие, посредник не нужен, а
|
||||||
второй дом для тех же фактов разошёлся бы и выглядел актуальным.
|
второй дом для тех же фактов разошёлся бы и выглядел актуальным.
|
||||||
|
|
||||||
Определение канона держит скилл `av-dev-docs:canon`. Здесь только карта «тема →
|
Определение канона держит скилл `av-dev:doc-canon`. Здесь только карта «тема →
|
||||||
её дом → что оттуда берётся».
|
её дом → что оттуда берётся».
|
||||||
|
|
||||||
## Карта тем
|
## Карта тем
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
строка неотличима от «мы просто не стали» и перестаёт читаться на третьей задаче.
|
строка неотличима от «мы просто не стали» и перестаёт читаться на третьей задаче.
|
||||||
|
|
||||||
**Документов канона нет вовсе** — проект не приведён к канону. Это не повод
|
**Документов канона нет вовсе** — проект не приведён к канону. Это не повод
|
||||||
работать вслепую: скажи об этом строкой и предложи `av-dev-docs:canon`. Одна
|
работать вслепую: скажи об этом строкой и предложи `av-dev:doc-canon`. Одна
|
||||||
операция на проект против деградации на каждой задаче.
|
операция на проект против деградации на каждой задаче.
|
||||||
|
|
||||||
## Правило чтения
|
## Правило чтения
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
# Журнал дефектов
|
# Журнал дефектов
|
||||||
|
|
||||||
Артефакт проекта, а не плагина: файл живёт в репозитории — **`docs/review.md`**,
|
Артефакт проекта, а не плагина: файл живёт в репозитории — **`docs/review.md`**,
|
||||||
слот канона `av-dev-docs`. Здесь описано, зачем он и какой формы, потому что без
|
слот канона документов. Здесь описано, зачем он и какой формы, потому что без
|
||||||
него конвейер не учится: находки закрываются, а почему их не поймали — забывается,
|
него конвейер не учится: находки закрываются, а почему их не поймали — забывается,
|
||||||
и один и тот же класс проскакивает второй раз.
|
и один и тот же класс проскакивает второй раз.
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
## Форма записи
|
## Форма записи
|
||||||
|
|
||||||
**Это дом формы, и у него есть копия.** Скелет `docs/review.md`, который кладёт
|
**Это дом формы, и у него есть копия.** Скелет `docs/review.md`, который кладёт
|
||||||
в проект `av-dev-docs:canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы
|
в проект `av-dev:doc-canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы
|
||||||
здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе
|
здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе
|
||||||
проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет.
|
проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет.
|
||||||
Дословность сверяет `scripts/copies.py` маркетплейса по маркерам ниже — но
|
Дословность сверяет `scripts/copies.py` маркетплейса по маркерам ниже — но
|
||||||
+2
-2
@@ -95,8 +95,8 @@
|
|||||||
остаются в одной метке, значит заплатить костяк дважды за ту же проверку.
|
остаются в одной метке, значит заплатить костяк дважды за ту же проверку.
|
||||||
Резать стоит там, где разрез **снимает доказательство с большей части диффа**.
|
Резать стоит там, где разрез **снимает доказательство с большей части диффа**.
|
||||||
Шов и правило нарезки живут у того, кто ведёт задачи, — скилл
|
Шов и правило нарезки живут у того, кто ведёт задачи, — скилл
|
||||||
`av-dev-tasks:tasks`, его раздел о нарезке. Пути туда конвейер не выносит: за
|
`av-dev:task-track`, его раздел о нарезке. Пути туда конвейер не выносит: за
|
||||||
пределы своего плагина он ходит вызовом скилла, а не файлом.
|
пределы своего скилла он ходит вызовом, а не файлом.
|
||||||
|
|
||||||
Разметка в костяк не входит — она платится один раз на задачу, а не один раз на
|
Разметка в костяк не входит — она платится один раз на задачу, а не один раз на
|
||||||
прогон, и потому **разрез задачи её не удваивает**. Это единственное, что стало
|
прогон, и потому **разрез задачи её не удваивает**. Это единственное, что стало
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: canon
|
name: doc-canon
|
||||||
description: Привести проект к канону документов av-dev и держать его в соответствии — три операции одной машиной сравнения. check — что разошлось с текущей версией канона; adopt — перевод проекта из любой прежней раскладки (docs/specs, drafts, backlog, BRIEF.md, review-brief) в канон с переносом файлов; upgrade — повышение проекта с версии канона N до текущей по журналу версий. Использовать, когда просят проверить документацию проекта, перевести проект на канон, обновить его под новую версию канона или когда пришли в старый проект и надо понять, что в нём не так. Заведение нового проекта с нуля — скилл init.
|
description: Привести проект к канону документов av-dev и держать его в соответствии — три операции одной машиной сравнения. check — что разошлось с текущей версией канона; adopt — перевод проекта из любой прежней раскладки (docs/specs, drafts, backlog, BRIEF.md, review-brief) в канон с переносом файлов; upgrade — повышение проекта с версии канона N до текущей по журналу версий. Использовать, когда просят проверить документацию проекта, перевести проект на канон, обновить его под новую версию канона или когда пришли в старый проект и надо понять, что в нём не так. Заведение нового проекта с нуля — скилл av-dev:doc-init.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Приведение проекта к канону
|
# Приведение проекта к канону
|
||||||
@@ -20,14 +20,14 @@ description: Привести проект к канону документов
|
|||||||
- [references/skeletons.md](references/skeletons.md) — **что именно класть** в
|
- [references/skeletons.md](references/skeletons.md) — **что именно класть** в
|
||||||
каждый незаполненный слот. Не выдумывай заглушку своей формы: `docs.py`
|
каждый незаполненный слот. Не выдумывай заглушку своей формы: `docs.py`
|
||||||
узнаёт только плейсхолдер `<!-- заполнить: … -->` из шаблонов.
|
узнаёт только плейсхолдер `<!-- заполнить: … -->` из шаблонов.
|
||||||
- [references/language.md](references/language.md) — **как это написано словами**:
|
- [shared/language.md](../../shared/language.md) — **как это написано словами**:
|
||||||
информационный стиль, применённый к проектным текстам, таблицы англицизмов и
|
информационный стиль, применённый к проектным текстам, таблицы англицизмов и
|
||||||
жаргона. Раскладка отвечает, где текст лежит, — этот файл отвечает, каким он
|
жаргона. Раскладка отвечает, где текст лежит, — этот файл отвечает, каким он
|
||||||
должен быть. Правила общие для документов канона, задач, решений ADR и
|
должен быть. Правила общие для документов канона, задач, решений ADR и
|
||||||
записок разведки, и дом у них общий — `shared/language.md` в репозитории
|
записок разведки, и это их **дом**. Вычитывают их два прохода по охвату:
|
||||||
плагинов, а этот файл его копия. Вычитывают их два прохода по охвату:
|
|
||||||
документы — `doc-wording`, записи каталога задач — `task-wording`.
|
документы — `doc-wording`, записи каталога задач — `task-wording`.
|
||||||
- [references/changelog.md](references/changelog.md) — журнал версий канона.
|
- [references/changelog.md](references/changelog.md) — журнал версий раскладки;
|
||||||
|
закрытые журналы до слияния плагинов лежат рядом.
|
||||||
|
|
||||||
## Три правила, из которых всё следует
|
## Три правила, из которых всё следует
|
||||||
|
|
||||||
@@ -45,15 +45,16 @@ description: Привести проект к канону документов
|
|||||||
## Инструмент
|
## Инструмент
|
||||||
|
|
||||||
```
|
```
|
||||||
ds="$CLAUDE_PLUGIN_ROOT/skills/canon/scripts/docs.py"
|
ds="$CLAUDE_PLUGIN_ROOT/skills/doc-canon/scripts/docs.py"
|
||||||
|
|
||||||
python3 $ds check --dir <корень> [--base <rev>] # раскладка, ссылки, версия, сверки
|
python3 $ds check --dir <корень> [--base <rev>] # раскладка, ссылки, версия, сверки
|
||||||
python3 $ds version --dir <корень> # версия канона скрипта и проекта
|
python3 $ds version --dir <корень> # версия раскладки: скрипта и проекта
|
||||||
|
python3 $ds bump --dir <корень> # поднять версию проекта до версии скрипта
|
||||||
```
|
```
|
||||||
|
|
||||||
**Формы `openspec/config.yaml` здесь больше нет.** Каталог принадлежит конвейеру,
|
**Формы `openspec/config.yaml` здесь больше нет.** Каталог принадлежит конвейеру,
|
||||||
и форму смотрит его скрипт — скилл `av-dev-code:openspec`, команда
|
и форму смотрит его скрипт — скилл `av-dev:code-openspec`, команда
|
||||||
`openspec.py check`. Проект работает по OpenSpec, а плагина конвейера нет — форму
|
`openspec.py check`. Проект работает по OpenSpec, а каталога `openspec/` нет — форму
|
||||||
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
|
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
|
||||||
верна.
|
верна.
|
||||||
|
|
||||||
@@ -87,41 +88,47 @@ capability: незаполненный канон это переходное с
|
|||||||
формулировка казалась удачной при написании. Ни один из них ничего не правит —
|
формулировка казалась удачной при написании. Ни один из них ничего не правит —
|
||||||
оба возвращают готовые формулировки, подставляешь ты.
|
оба возвращают готовые формулировки, подставляешь ты.
|
||||||
|
|
||||||
## Обращение к соседним плагинам
|
## Чего может не быть
|
||||||
|
|
||||||
`adopt` зовёт двоих: `av-dev-code:openspec` (шаг 4, пункт 3) и
|
`adopt` зовёт двоих: `av-dev:code-openspec` (шаг 4, пункт 3) и
|
||||||
`av-dev-tasks:tasks` (шаг 4, пункт 5). Каталоги `openspec/` и `tasks/` каноном не
|
`av-dev:task-track` (шаг 4, пункт 5). Каталоги `openspec/` и `tasks/` каноном не
|
||||||
ведутся, и трогать их этому скиллу нечем, кроме вызова.
|
ведутся, и трогать их этому скиллу нечем, кроме вызова.
|
||||||
|
|
||||||
**Копия.** Дом правила — `shared/plugin-boundary.md` в репозитории плагинов.
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
Правится дом, а не этот файл.
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
месте.
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, `av-dev-tasks:tasks`,
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
`av-dev-code:review`. Короткое имя может разрешиться в устаревшую проектную копию
|
| --- | --- | --- |
|
||||||
из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в поведении.
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт
|
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||||
только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
прочитает его сам.
|
поведении.
|
||||||
|
|
||||||
**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя,
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
|
<!-- /копия: отсутствие -->
|
||||||
|
|
||||||
Чем оборачивается отсутствие каждого — на самих пунктах шага 4. `adopt` из-за
|
Чем оборачивается отсутствие каждого — на самих пунктах шага 4. `adopt` из-за
|
||||||
этого не останавливается ни в одном из двух случаев.
|
этого не останавливается ни в одном из двух случаев.
|
||||||
@@ -130,12 +137,12 @@ capability: незаполненный канон это переходное с
|
|||||||
|
|
||||||
1. `docs.py check`, при наличии базы диффа — с `--base`.
|
1. `docs.py check`, при наличии базы диффа — с `--base`.
|
||||||
2. **Судей документов на каждом `check` не зови.** Ими владеет отдельный скилл —
|
2. **Судей документов на каждом `check` не зови.** Ими владеет отдельный скилл —
|
||||||
`av-dev-docs:healthcheck`, — и там же записано, когда его звать: он дорог, и
|
`av-dev:doc-healthcheck`, — и там же записано, когда его звать: он дорог, и
|
||||||
прогон по каждому `check` не окупается. `check` отвечает на «сходится ли
|
прогон по каждому `check` не окупается. `check` отвечает на «сходится ли
|
||||||
форма», `healthcheck` — на «не разошлись ли утверждения».
|
форма», `doc-healthcheck` — на «не разошлись ли утверждения».
|
||||||
3. Доклад: вывод скрипта строкой исхода и **граница покрытия** — что смотрели и
|
3. Доклад: вывод скрипта строкой исхода и **граница покрытия** — что смотрели и
|
||||||
чего не смотрели. Если суждение здесь нужно, скажи это строкой и предложи
|
чего не смотрели. Если суждение здесь нужно, скажи это строкой и предложи
|
||||||
`healthcheck`, а не зови агентов сам.
|
`doc-healthcheck`, а не зови агентов сам.
|
||||||
|
|
||||||
Дрейф раскладки чинится переносом; смысловые находки — это либо правка
|
Дрейф раскладки чинится переносом; смысловые находки — это либо правка
|
||||||
документа, либо задача, если работы больше чем на абзац.
|
документа, либо задача, если работы больше чем на абзац.
|
||||||
@@ -178,17 +185,19 @@ capability), `openspec/config.yaml`.
|
|||||||
|
|
||||||
Порядок важен — он минимизирует окно, в котором ссылки битые:
|
Порядок важен — он минимизирует окно, в котором ссылки битые:
|
||||||
|
|
||||||
1. `docs/.docs.json` с `{"canon": <текущая версия>}` и путём миграций, если БД есть;
|
1. `.av-dev.toml` в корне: `version = <текущая версия>` и путь миграций в
|
||||||
|
`[docs]`, если БД есть;
|
||||||
2. каталоги канона и скелет **по [references/skeletons.md](references/skeletons.md)**:
|
2. каталоги канона и скелет **по [references/skeletons.md](references/skeletons.md)**:
|
||||||
незаполненное — одной честной информативной строкой, а не «TBD»;
|
незаполненное — одной честной информативной строкой, а не «TBD»;
|
||||||
3. **OpenSpec, если его нет или `config.yaml` остался примером** — **вызови
|
3. **OpenSpec, если его нет или `config.yaml` остался примером** — **вызови
|
||||||
Skill `av-dev-code:openspec`**. Каталог принадлежит конвейеру, и команда
|
Skill `av-dev:code-openspec`**. Каталог принадлежит конвейеру, и команда
|
||||||
заведения с формой файла живут там. Пересказ инвариантов, конвенций и правил
|
заведения с формой файла живут там. Пересказ инвариантов, конвенций и правил
|
||||||
ревью из `context` вычисти ссылкой на дом — на переводимом проекте он там
|
ревью из `context` вычисти ссылкой на дом — на переводимом проекте он там
|
||||||
почти наверняка есть. Вызов не разрешился — `docs.py` о каталоге тогда тоже
|
почти наверняка есть. Проект решил жить без OpenSpec — `docs.py` о каталоге
|
||||||
молчит, и форму `config.yaml` не проверяет никто; скажи это строкой;
|
тогда тоже молчит, и форму `config.yaml` не проверяет никто; скажи это
|
||||||
|
строкой;
|
||||||
4. переносы содержимого;
|
4. переносы содержимого;
|
||||||
5. каталог задач — **вызови скилл `av-dev-tasks:tasks`**, сценарий адаптации: он
|
5. каталог задач — **вызови скилл `av-dev:task-track`**, сценарий адаптации: он
|
||||||
владеет форматом задач. Он же переименует транслитные слаги в английские и
|
владеет форматом задач. Он же переименует транслитные слаги в английские и
|
||||||
тем же проходом починит перекрёстные ссылки;
|
тем же проходом починит перекрёстные ссылки;
|
||||||
6. починка ссылок на перенесённое во всём репозитории — `docs/`, `openspec/`,
|
6. починка ссылок на перенесённое во всём репозитории — `docs/`, `openspec/`,
|
||||||
@@ -203,10 +212,10 @@ capability), `openspec/config.yaml`.
|
|||||||
|
|
||||||
**Шагов в гейте три, и они независимы.** `docs.py check` не тянет за собой
|
**Шагов в гейте три, и они независимы.** `docs.py check` не тянет за собой
|
||||||
ни задачи, ни конвейер: без своих строк дрейф каталога задач и формы
|
ни задачи, ни конвейер: без своих строк дрейф каталога задач и формы
|
||||||
`openspec/config.yaml` перестаёт ловиться совсем. Ставь соседские шаги по
|
`openspec/config.yaml` перестаёт ловиться совсем. Ставь соседние шаги по
|
||||||
следу присутствия — `<каталог задач>/.tasks.json` есть, значит ставится
|
следу присутствия — каталог задач с индексом на месте, значит ставится
|
||||||
`tasks.py check --dir <каталог задач>`; `openspec/config.yaml` есть, значит
|
`tasks.py check --dir <каталог задач>`; `openspec/config.yaml` есть, значит
|
||||||
ставится `openspec.py check`. Следа нет — плагина в проекте нет, шаг не
|
ставится `openspec.py check`. Следа нет — этой части в проекте нет, шаг не
|
||||||
ставится, и это **строка доклада**, а не поломка: назови, чего теперь не
|
ставится, и это **строка доклада**, а не поломка: назови, чего теперь не
|
||||||
проверяет никто. У каждого шага своя переменная пути с тем же умолчанием на
|
проверяет никто. У каждого шага своя переменная пути с тем же умолчанием на
|
||||||
канонический путь маркетплейса; `$CLAUDE_PLUGIN_ROOT` в гейт не подставляй —
|
канонический путь маркетплейса; `$CLAUDE_PLUGIN_ROOT` в гейт не подставляй —
|
||||||
@@ -217,12 +226,12 @@ capability), `openspec/config.yaml`.
|
|||||||
отказ. Пересчитай эти пункты в докладе переходного состояния — не выдавай
|
отказ. Пересчитай эти пункты в докладе переходного состояния — не выдавай
|
||||||
их за поломку и не молчи о них.
|
их за поломку и не молчи о них.
|
||||||
|
|
||||||
**Задачи `docs.py` не проверяет** — их ведёт другой плагин, и согласованность
|
**Задачи `docs.py` не проверяет** — их ведёт другой скилл, и согласованность
|
||||||
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
|
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
|
||||||
его отчёт идёт в доклад отдельной строкой, и пункт «задачи без цели» в нём
|
его отчёт идёт в доклад отдельной строкой, и пункт «задачи без цели» в нём
|
||||||
зелёным не станет: цели не сочиняются адаптацией (запрет записан у того, кто
|
зелёным не станет: цели не сочиняются адаптацией (запрет записан у того, кто
|
||||||
ведёт задачи), их проставляет человек порциями переоценки на первом груминге —
|
ведёт задачи), их проставляет человек порциями переоценки на первом груминге —
|
||||||
скилл `av-dev-tasks:groom`.
|
скилл `av-dev:task-groom`.
|
||||||
|
|
||||||
### 5. Объяви переходное состояние
|
### 5. Объяви переходное состояние
|
||||||
|
|
||||||
@@ -241,7 +250,7 @@ capability), `openspec/config.yaml`.
|
|||||||
проекте обычно самый урожайный — правило единственного дома до адаптации никто не
|
проекте обычно самый урожайный — правило единственного дома до адаптации никто не
|
||||||
проверял.
|
проверял.
|
||||||
|
|
||||||
Вызови Skill **`av-dev-docs:healthcheck`** — он зовёт обоих судей на весь канон
|
Вызови Skill **`av-dev:doc-healthcheck`** — он зовёт обоих судей на весь канон
|
||||||
разом и держит разбор урожая порциями.
|
разом и держит разбор урожая порциями.
|
||||||
|
|
||||||
**Передай им объявленное переходное состояние из шага 5** — иначе честная строка
|
**Передай им объявленное переходное состояние из шага 5** — иначе честная строка
|
||||||
@@ -267,9 +276,11 @@ capability), `openspec/config.yaml`.
|
|||||||
3. Иначе иди по [changelog.md](references/changelog.md) снизу вверх от версии
|
3. Иначе иди по [changelog.md](references/changelog.md) снизу вверх от версии
|
||||||
проекта до текущей и делай названное в каждой записи. Записи независимы и
|
проекта до текущей и делай названное в каждой записи. Записи независимы и
|
||||||
применяются по порядку.
|
применяются по порядку.
|
||||||
4. Подними `canon` в `docs/.docs.json` до текущей.
|
4. Подними версию — `docs.py bump`. Он правит **строку**, а не переписывает
|
||||||
|
файл: комментарии в нём принадлежат проекту. Последним шагом, потому что
|
||||||
|
число объявляет пройденными записи журнала.
|
||||||
5. `docs.py check`.
|
5. `docs.py check`.
|
||||||
6. **Позови судей** — Skill `av-dev-docs:healthcheck`.
|
6. **Позови судей** — Skill `av-dev:doc-healthcheck`.
|
||||||
7. **Позови вычитку** — агент `doc-wording`, но **только по тем документам,
|
7. **Позови вычитку** — агент `doc-wording`, но **только по тем документам,
|
||||||
которых записи журнала коснулись**, и только если правка была текстовой, а не
|
которых записи журнала коснулись**, и только если правка была текстовой, а не
|
||||||
переименованием файла. Записи журнала пишутся руками в проектной прозе, и
|
переименованием файла. Записи журнала пишутся руками в проектной прозе, и
|
||||||
@@ -278,17 +289,16 @@ capability), `openspec/config.yaml`.
|
|||||||
Записи журнала описывают **что сделать проекту**. Если запись этого не говорит —
|
Записи журнала описывают **что сделать проекту**. Если запись этого не говорит —
|
||||||
это дефект журнала, и о нём надо сказать, а не догадываться.
|
это дефект журнала, и о нём надо сказать, а не догадываться.
|
||||||
|
|
||||||
**Каталог задач повышается своим журналом, а не этим.** У него своя версия
|
**Каталог задач повышается этим же журналом.** Версия одна на всю раскладку —
|
||||||
формата — ключ `tasks` в `<каталог задач>/.tasks.json`, — и двигает её плагин
|
`version` в `.av-dev.toml`, — и записи журнала говорят про обе половины: и про
|
||||||
`av-dev-tasks`. Запись канона вправе сказать «позови соседа», но не вправе
|
документы, и про каталог задач. Порознь версии жили, пока плагинов было три и
|
||||||
двигать чужое число: две версии, которые ходят по одному журналу, разъезжаются
|
проект мог взять одну половину без другой; с одним плагином два числа означали
|
||||||
на первом же проекте, поставившем один плагин без другого. Отстал каталог
|
бы только вопрос, по какому журналу повышать. Что каталог задач отстал, скажет
|
||||||
задач — это скажет `tasks.py check` своей строкой гейта, а повысит скилл
|
`tasks.py check` своей строкой гейта — той же версией, что и `docs.py`.
|
||||||
`av-dev-tasks:tasks`.
|
|
||||||
|
|
||||||
**Шаг 6 обязателен, и вот почему.** `check` сверяет **число** в `.docs.json` с
|
**Шаг 6 обязателен, и вот почему.** `check` сверяет **число** в `.av-dev.toml`
|
||||||
версией скрипта — и только его. Применена ли запись журнала **по существу**, он
|
с версией скрипта — и только его. Применена ли запись журнала **по существу**,
|
||||||
не знает: проект несёт `"canon": 6` и может не иметь того, чего требовала любая
|
он не знает: проект несёт `version` текущей версии и может не иметь того, чего требовала любая
|
||||||
из пройденных версий. Записи применяются руками (переименовать секцию, проставить
|
из пройденных версий. Записи применяются руками (переименовать секцию, проставить
|
||||||
типы, дописать раздел каждому `fix`), а ручной проход по нескольким записям
|
типы, дописать раздел каждому `fix`), а ручной проход по нескольким записям
|
||||||
подряд — ровно то место, где половина шага делается и забывается. Судьи и есть
|
подряд — ровно то место, где половина шага делается и забывается. Судьи и есть
|
||||||
@@ -303,8 +313,8 @@ capability), `openspec/config.yaml`.
|
|||||||
хуже отсутствующего: по нему будут строиться находки.
|
хуже отсутствующего: по нему будут строиться находки.
|
||||||
- **Не удаляет то, чьё содержимое не нашло дом.** Оригинал живёт, пока не
|
- **Не удаляет то, чьё содержимое не нашло дом.** Оригинал живёт, пока не
|
||||||
названо поимённо, куда переехал каждый его кусок.
|
названо поимённо, куда переехал каждый его кусок.
|
||||||
- **Не ведёт содержимое канона** — это скилл `docs`. Здесь только раскладка.
|
- **Не ведёт содержимое канона** — это скилл `doc-sync`. Здесь только раскладка.
|
||||||
- **Не заводит проект с нуля** — это скилл `init`.
|
- **Не заводит проект с нуля** — это скилл `doc-init`.
|
||||||
- **Не правит историю.** В старых коммитах старые пути остаются, и это нормально.
|
- **Не правит историю.** В старых коммитах старые пути остаются, и это нормально.
|
||||||
|
|
||||||
## Доклад
|
## Доклад
|
||||||
+58
-79
@@ -6,7 +6,7 @@
|
|||||||
[журнала](changelog.md). Литерал в шапке был третьим и отстал на первом же
|
[журнала](changelog.md). Литерал в шапке был третьим и отстал на первом же
|
||||||
повышении — версию 13 он пережил, объявляя канон двенадцатым.
|
повышении — версию 13 он пережил, объявляя канон двенадцатым.
|
||||||
|
|
||||||
Это **единственный дом определения канона**. Скиллы `init`, `canon` и `docs`
|
Это **единственный дом определения канона**. Скиллы `doc-init`, `doc-canon` и `doc-sync`
|
||||||
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
||||||
работать будет то, которое прочитали последним. Меняется канон — меняется этот
|
работать будет то, которое прочитали последним. Меняется канон — меняется этот
|
||||||
файл и появляется запись в [changelog.md](changelog.md).
|
файл и появляется запись в [changelog.md](changelog.md).
|
||||||
@@ -19,43 +19,20 @@
|
|||||||
Рядом лежит OpenSpec, у которого структура тоже строгая.
|
Рядом лежит OpenSpec, у которого структура тоже строгая.
|
||||||
|
|
||||||
Цена принята сознательно: плагин не переносится на чужой репозиторий как есть —
|
Цена принята сознательно: плагин не переносится на чужой репозиторий как есть —
|
||||||
чужой репозиторий **приводится** к канону скиллом `canon`.
|
чужой репозиторий **приводится** к канону скиллом `doc-canon`.
|
||||||
|
|
||||||
Раскладка отвечает, **где** текст лежит и на какой вопрос отвечает. Каким он
|
Раскладка отвечает, **где** текст лежит и на какой вопрос отвечает. Каким он
|
||||||
должен быть **словами** — общий для всех документов канона файл
|
должен быть **словами** — общий для всех документов канона файл
|
||||||
[language.md](language.md): информационный стиль, англицизмы, жаргон. Он
|
[shared/language.md](../../../shared/language.md): информационный стиль, англицизмы, жаргон. Он
|
||||||
относится и к задачам, и к решениям ADR, и к запискам разведки.
|
относится и к задачам, и к решениям ADR, и к запискам разведки.
|
||||||
|
|
||||||
## Сопровождение и эксплуатация — целое и часть
|
## Сопровождение и эксплуатация — целое и часть
|
||||||
|
|
||||||
**Копия.** Дом — `shared/operations.md` в репозитории плагинов: словарь
|
Словарь этой темы — [shared/operations.md](../../../shared/operations.md):
|
||||||
делят роадмап, архитектура и тема ревью `operations`, то есть три плагина,
|
целое и часть, три места одной темы (секция роадмапа, раздел архитектуры, тема
|
||||||
и ни один из трёх им не владеет. Правится дом, а не этот файл.
|
ревью `operations`) и граница с возможностями проекта. Здесь он не
|
||||||
|
пересказывается: копия жила рядом с домом в одном дереве и была ровно тем
|
||||||
<!-- копия: сопровождение-словарь из shared/operations.md -->
|
вторым домом, против которого правило и написано.
|
||||||
|
|
||||||
Одна тема живёт в трёх местах, и путать их слова нельзя.
|
|
||||||
|
|
||||||
**Сопровождение** — всё, чем держат проект: инструмент и сборка, процесс,
|
|
||||||
выкладка, метрики и логи, инфраструктура, дежурство. **Эксплуатация** — его
|
|
||||||
часть: работа системы на проде. Целое и часть, и никогда наоборот.
|
|
||||||
|
|
||||||
| Место | Уровень | Что там |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `ROADMAP.md`, секция `Сопровождение` | план | **работы**, которые собираемся делать: цели и их задачи |
|
|
||||||
| `architecture.md`, раздел «Эксплуатация» | состояние | **как устроено сейчас**: где работает, что рядом, кто перезапускает |
|
|
||||||
| тема ревью `operations` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
|
||||||
|
|
||||||
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
|
||||||
пользователю, а это другая работа.
|
|
||||||
|
|
||||||
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
|
||||||
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
|
||||||
целей: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
|
||||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
|
||||||
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
|
||||||
|
|
||||||
<!-- /копия: сопровождение-словарь -->
|
|
||||||
|
|
||||||
## Раскладка
|
## Раскладка
|
||||||
|
|
||||||
@@ -68,8 +45,9 @@
|
|||||||
CLAUDE.md памятка агенту: что это, стек, инварианты с
|
CLAUDE.md памятка агенту: что это, стек, инварианты с
|
||||||
severity, команды, семантика гейта, запреты
|
severity, команды, семантика гейта, запреты
|
||||||
AGENTS.md необязателен, лежит рядом; читается теми же
|
AGENTS.md необязателен, лежит рядом; читается теми же
|
||||||
|
.av-dev.toml версия раскладки и настройки проверок; лежит
|
||||||
|
в корне, потому что нужен и без docs/
|
||||||
docs/
|
docs/
|
||||||
.docs.json версия канона и пути, нужные проверкам
|
|
||||||
passport.md | passport/ зачем и для кого; чем НЕ является; сценарии
|
passport.md | passport/ зачем и для кого; чем НЕ является; сценарии
|
||||||
architecture.md | architecture/ как сложено — обзор; окружение и эксплуатация
|
architecture.md | architecture/ как сложено — обзор; окружение и эксплуатация
|
||||||
database.md | database/ схема хранилища; представление данных и настройки
|
database.md | database/ схема хранилища; представление данных и настройки
|
||||||
@@ -79,7 +57,7 @@ docs/
|
|||||||
adr.md | adr/ почему решено так; статусы, правило замены
|
adr.md | adr/ почему решено так; статусы, правило замены
|
||||||
review.md | review/ настройка конвейера под проект + журнал дефектов
|
review.md | review/ настройка конвейера под проект + журнал дефектов
|
||||||
<своя тема>.md | <своя тема>/ всё, что проект счёл нужным проверять
|
<своя тема>.md | <своя тема>/ всё, что проект счёл нужным проверять
|
||||||
tasks/ каталог задач — плагин av-dev-tasks, не канон;
|
tasks/ каталог задач — скилл task-track, не канон;
|
||||||
лежит в корне, вне docs/, и канон его не требует
|
лежит в корне, вне docs/, и канон его не требует
|
||||||
openspec/
|
openspec/
|
||||||
config.yaml только нужды генерации артефактов + ссылки
|
config.yaml только нужды генерации артефактов + ссылки
|
||||||
@@ -119,11 +97,11 @@ openspec/
|
|||||||
| `CLAUDE.md`, `AGENTS.md` | источник | `autotests` (семантика гейта); инварианты — сквозные |
|
| `CLAUDE.md`, `AGENTS.md` | источник | `autotests` (семантика гейта); инварианты — сквозные |
|
||||||
| `openspec/specs/` | источник | `requirements` |
|
| `openspec/specs/` | источник | `requirements` |
|
||||||
| `openspec/config.yaml` | процессный | — (настройка порождения артефактов, слой **до** тем; заводит конвейер) |
|
| `openspec/config.yaml` | процессный | — (настройка порождения артефактов, слой **до** тем; заводит конвейер) |
|
||||||
| `tasks/` | процессный | — (чужое владение: плагин `av-dev-tasks`) |
|
| `tasks/` | процессный | — (чужое владение: скилл `task-track`) |
|
||||||
| `review.*` | процессный | — (настройка самого конвейера, слой **над** темами) |
|
| `review.*` | процессный | — (настройка самого конвейера, слой **над** темами) |
|
||||||
| `adr.*` | процессный | — |
|
| `adr.*` | процессный | — |
|
||||||
| `research.*` | процессный | — |
|
| `research.*` | процессный | — |
|
||||||
| `.docs.json` | процессный | — (служебный файл, не документ) |
|
| `.av-dev.toml` | процессный | — (служебный файл, не документ) |
|
||||||
|
|
||||||
**Список тем открытый, и это не послабление, а механизм.** Категории
|
**Список тем открытый, и это не послабление, а механизм.** Категории
|
||||||
`источник` и `процессный` **закрыты** — они перечислены здесь поимённо и
|
`источник` и `процессный` **закрыты** — они перечислены здесь поимённо и
|
||||||
@@ -188,7 +166,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
какую тему питает. **Кто именно закрывает тему, здесь не указано намеренно**: это
|
какую тему питает. **Кто именно закрывает тему, здесь не указано намеренно**: это
|
||||||
зависит от метки прогона и меняется вместе с конвейером, а документ живёт
|
зависит от метки прогона и меняется вместе с конвейером, а документ живёт
|
||||||
дольше. Раскладку «тема → проход → глубина» держит скилл
|
дольше. Раскладку «тема → проход → глубина» держит скилл
|
||||||
`av-dev-code:review`.
|
`av-dev:code-review`.
|
||||||
|
|
||||||
**Общего словаря у канона с конвейером ровно три вида имён: имена категорий,
|
**Общего словаря у канона с конвейером ровно три вида имён: имена категорий,
|
||||||
имена тем и имена меток.** Категорий три — `тема`, `источник`, `процессный`;
|
имена тем и имена меток.** Категорий три — `тема`, `источник`, `процессный`;
|
||||||
@@ -366,16 +344,16 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
|
|
||||||
### `tasks/`
|
### `tasks/`
|
||||||
|
|
||||||
**Каталог задач канону не принадлежит.** Его ведёт отдельный плагин
|
**Каталог задач канону не принадлежит.** Его ведёт скилл `task-track` — своим
|
||||||
`av-dev-tasks` — своим скриптом, своим конфигом `<каталог>/.tasks.json`, своей
|
скриптом и своими проверками; где каталог лежит и как названы его части, говорит
|
||||||
версией формата в нём же и своим журналом версий. Канон о том числе не
|
секция `[tasks]` в `.av-dev.toml`. Версия там одна на всю раскладку: канон и
|
||||||
высказывается и его не двигает: повышает каталог задач тот, кто его ведёт.
|
каталог задач двигаются вместе, потому что ведёт их один плагин.
|
||||||
Канон **резервирует место** в `docs/` и внутрь не смотрит:
|
Канон **резервирует место** в `docs/` и внутрь не смотрит:
|
||||||
`docs.py` каталог не открывает, его отсутствия не считает дрейфом и согласованность
|
`docs.py` каталог не открывает, его отсутствия не считает дрейфом и согласованность
|
||||||
задач не проверяет. Проект, поставивший только канон документов, задач не ведёт
|
задач не проверяет. Проект, не заведший каталог задач, их не ведёт
|
||||||
вовсе, и отказом это быть не может.
|
вовсе, и отказом это быть не может.
|
||||||
|
|
||||||
Раскладку, форму записи и команды держит скилл `av-dev-tasks:tasks`. Ниже — то,
|
Раскладку, форму записи и команды держит скилл `av-dev:task-track`. Ниже — то,
|
||||||
от чего зависит, читается ли проект как продукт: канон высказывается об этом
|
от чего зависит, читается ли проект как продукт: канон высказывается об этом
|
||||||
потому, что роадмап отвечает на вопрос о **системе**, а не о работах.
|
потому, что роадмап отвечает на вопрос о **системе**, а не о работах.
|
||||||
|
|
||||||
@@ -400,10 +378,9 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
| 🔬 `research` | исход — знание, а не изменение |
|
| 🔬 `research` | исход — знание, а не изменение |
|
||||||
|
|
||||||
**Схемы записи здесь нет намеренно.** Какие разделы тип требует, нужна ли ему
|
**Схемы записи здесь нет намеренно.** Какие разделы тип требует, нужна ли ему
|
||||||
цель и берётся ли он в работу — скилл `av-dev-tasks:tasks`, раздел «Тип
|
цель и берётся ли он в работу — скилл `av-dev:task-track`, раздел «Тип
|
||||||
записи», подробно — по файлу на тип в его `references/task-<тип>.md`. Ссылки в
|
записи», подробно — по файлу на тип в его `references/task-<тип>.md`. Канон
|
||||||
дерево того плагина здесь нет намеренно: он ставится отдельно, и путь наружу
|
фиксирует **словарь**, потому что
|
||||||
разрешился бы не всегда. Канон фиксирует **словарь**, потому что
|
|
||||||
от него зависит, читается ли проект как продукт; схема — механика ведения задач,
|
от него зависит, читается ли проект как продукт; схема — механика ведения задач,
|
||||||
и второй её экземпляр разошёлся бы с первым (он и разошёлся: канон успел
|
и второй её экземпляр разошёлся бы с первым (он и разошёлся: канон успел
|
||||||
объявить цель у `fix` запрещённой, хотя она там необязательна).
|
объявить цель у `fix` запрещённой, хотя она там необязательна).
|
||||||
@@ -418,7 +395,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
работу не берётся и лежит в конце своей категории.
|
работу не берётся и лежит в конце своей категории.
|
||||||
|
|
||||||
Раскладку, форму записи и алгоритм работы над каждым типом держит скилл
|
Раскладку, форму записи и алгоритм работы над каждым типом держит скилл
|
||||||
`av-dev-tasks:tasks`.
|
`av-dev:task-track`.
|
||||||
|
|
||||||
### `CLAUDE.md`
|
### `CLAUDE.md`
|
||||||
|
|
||||||
@@ -440,7 +417,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
шкала ранжирования триажа и право проходов на `critical`;
|
шкала ранжирования триажа и право проходов на `critical`;
|
||||||
- **что считается сломанным** — красная проверка, обгоняющая развитие;
|
- **что считается сломанным** — красная проверка, обгоняющая развитие;
|
||||||
**ориентир по размеру порции**, если он замерялся. Оба слота читает скилл
|
**ориентир по размеру порции**, если он замерялся. Оба слота читает скилл
|
||||||
`av-dev-tasks:groom`, и имена их — его; названы они здесь потому, что дом
|
`av-dev:task-groom`, и имена их — его; названы они здесь потому, что дом
|
||||||
содержимого `CLAUDE.md` один и он тут.
|
содержимого `CLAUDE.md` один и он тут.
|
||||||
|
|
||||||
### `openspec/config.yaml`
|
### `openspec/config.yaml`
|
||||||
@@ -448,7 +425,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
**Файл канону не принадлежит, и проверяет его тоже не канон.** Каталог
|
**Файл канону не принадлежит, и проверяет его тоже не канон.** Каталог
|
||||||
`openspec/` — предпосылка конвейера: без него не работают ни `opsx:propose`, ни
|
`openspec/` — предпосылка конвейера: без него не работают ни `opsx:propose`, ни
|
||||||
ревью дизайна, ни сверка требований. Заводит его, настраивает и **проверяет
|
ревью дизайна, ни сверка требований. Заводит его, настраивает и **проверяет
|
||||||
форму** скилл `av-dev-code:openspec`: там образец файла, там же скрипт
|
форму** скилл `av-dev:code-openspec`: там образец файла, там же скрипт
|
||||||
`openspec.py check`. `docs.py` о файле не говорит ничего.
|
`openspec.py check`. `docs.py` о файле не говорит ничего.
|
||||||
|
|
||||||
Канон называет его здесь по одной причине: `openspec/specs/` — **дом темы
|
Канон называет его здесь по одной причине: `openspec/specs/` — **дом темы
|
||||||
@@ -537,7 +514,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
**Форма `openspec/config.yaml` в левой колонке отсутствует не по забывчивости.**
|
**Форма `openspec/config.yaml` в левой колонке отсутствует не по забывчивости.**
|
||||||
С канона 10 `docs.py` о файле не говорит ничего: имя, `schema`, незаменённый
|
С канона 10 `docs.py` о файле не говорит ничего: имя, `schema`, незаменённый
|
||||||
пример, адреса паспорта и `CLAUDE.md`, ключи `rules` и сторож версии OpenSpec —
|
пример, адреса паспорта и `CLAUDE.md`, ключи `rules` и сторож версии OpenSpec —
|
||||||
всё это смотрит `openspec.py check` скилла `av-dev-code:openspec`. Плагина
|
всё это смотрит `openspec.py check` скилла `av-dev:code-openspec`. Плагина
|
||||||
конвейера в проекте может не быть; тогда форму не проверяет никто, и это строка
|
конвейера в проекте может не быть; тогда форму не проверяет никто, и это строка
|
||||||
доклада.
|
доклада.
|
||||||
|
|
||||||
@@ -546,8 +523,8 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
читающие команды. Слитый агент делал бы одну половину поверхностной; тот же
|
читающие команды. Слитый агент делал бы одну половину поверхностной; тот же
|
||||||
разрез, что между `task-form` и `task-wording`.
|
разрез, что между `task-form` и `task-wording`.
|
||||||
|
|
||||||
**Зовутся оба одинаково и одним скиллом — `av-dev-docs:healthcheck`, на весь
|
**Зовутся оба одинаково и одним скиллом — `av-dev:doc-healthcheck`, на весь
|
||||||
канон разом; шагом `adopt` и шагом `upgrade` его зовёт `canon`.** Не на синке
|
канон разом; шагом `adopt` и шагом `upgrade` его зовёт `doc-canon`.** Не на синке
|
||||||
документации: `doc-consistency` на
|
документации: `doc-consistency` на
|
||||||
`opus` по каждой сделанной задаче не окупается, а расхождение между двумя документами по
|
`opus` по каждой сделанной задаче не окупается, а расхождение между двумя документами по
|
||||||
определению требует двух, и на большинстве задач синк правит один. Пачка,
|
определению требует двух, и на большинстве задач синк правит один. Пачка,
|
||||||
@@ -561,39 +538,41 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
архитектуру с кодом» задача без дна, и агент, которому её поставили, выдаёт
|
архитектуру с кодом» задача без дна, и агент, которому её поставили, выдаёт
|
||||||
правдоподобную труху вместо находок.
|
правдоподобную труху вместо находок.
|
||||||
|
|
||||||
## `docs/.docs.json`
|
## `.av-dev.toml`
|
||||||
|
|
||||||
```json
|
```toml
|
||||||
{
|
# Раскладка av-dev в этом проекте: версия и настройки проверок.
|
||||||
"canon": <текущая версия>,
|
|
||||||
"migrations": "internal/store/migrations"
|
version = 1 # версия раскладки
|
||||||
}
|
|
||||||
|
[docs]
|
||||||
|
migrations = "internal/store/migrations" # если БД есть
|
||||||
|
|
||||||
|
[tasks]
|
||||||
|
dir = "tasks" # каталог задач от корня репозитория
|
||||||
```
|
```
|
||||||
|
|
||||||
`canon` — версия канона, под которую проект приведён, целым числом: обратной
|
`version` — версия раскладки, под которую проект приведён, целым числом:
|
||||||
совместимости у канона нет, есть «приведён» и «не приведён». Число подставляет
|
обратной совместимости нет, есть «приведён» и «не приведён». Число подставляет
|
||||||
`init`, `adopt` или `upgrade`, и берётся оно из `docs.py version`, а не из
|
`init`, `adopt` или `upgrade`, и берётся оно из `docs.py version`, а не из
|
||||||
образца: литерал в образце протухает на первом же повышении канона.
|
образца: литерал в образце протухает на первом же повышении.
|
||||||
`migrations` — путь каталога миграций, если БД есть; по нему `docs.py` делает
|
`[docs] migrations` — путь каталога миграций, если БД есть; по нему `docs.py`
|
||||||
сверку с `database.md`.
|
делает сверку с `database.md`. `[tasks]` — где лежит каталог задач и как названы
|
||||||
|
его части; состав ключей описывает скилл `task-track`.
|
||||||
|
|
||||||
**Имя файла — имя плагина, который его завёл.** Канон документов ведёт
|
**Формат TOML взят ради комментариев.** Файл лежит в репозитории проекта, и
|
||||||
`av-dev-docs`, поэтому `.docs.json`; у каталога задач по тому же правилу
|
человек, открывший его через полгода, обязан прочитать в нём, что означает
|
||||||
`.tasks.json`, у конвейера — `openspec/config.yaml`. До версии 13 файл звался
|
число. JSON комментариев не знает, и объяснение приходилось держать в другом
|
||||||
`.pm.json` — по плагину `av-dev-pm`, который распался на четыре и которого
|
файле. Отсюда же правило записи: скрипты правят **строку**, а не переписывают
|
||||||
больше нет; имя пережило владельца и указывало в пустоту. Прежнее имя `docs.py`
|
файл — перезапись стёрла бы то, ради чего формат и выбран.
|
||||||
не читает: два дома для одной версии канона расходятся молча, а переименование
|
|
||||||
стоит одну команду (версия 13 журнала, и `check` называет её сам, когда видит
|
|
||||||
старый файл).
|
|
||||||
|
|
||||||
**Ключа `tasks` здесь больше нет.** Настройки каталога задач вернулись в свой
|
**Файл один, и лежит он в корне.** До слияния плагинов их было два —
|
||||||
файл `<каталог задач>/.tasks.json`, потому что ведёт их другой плагин: конфиг,
|
`docs/.docs.json` с версией канона и `<каталог задач>/.tasks.json` с версией
|
||||||
лежащий в `docs/`, был бы домом, которого нет у проекта, поставившего учёт работ
|
формата задач, — и версии двигались порознь, потому что плагины ставились
|
||||||
без канона документов. Состав ключей описывает тот плагин, а не канон. Там же —
|
порознь. Плагин теперь один, версия одна, а корень выбран потому, что он есть и
|
||||||
**версия формата задач**, и она своя: у проекта без `docs/` версии канона нет
|
у проекта без `docs/`, и у проекта без каталога задач. Прежние имена не
|
||||||
вовсе, сверять её было бы не с чем. Прежний ключ читается, пока живы
|
читаются: два дома для одной версии расходятся молча. Увидев их, `docs.py` и
|
||||||
непереехавшие проекты, и `tasks.py` говорит о нём замечанием на каждом
|
`tasks.py` говорят «прежняя раскладка» и зовут `upgrade` — версия 1 журнала.
|
||||||
прогоне — версия 8 журнала просит его убрать.
|
|
||||||
|
|
||||||
Ключей будет больше по мере роста проверок; неизвестный ключ `docs.py`
|
Ключей будет больше по мере роста проверок; неизвестный ключ `docs.py`
|
||||||
игнорирует, отсутствующий — считает «проверка неприменима» и говорит об этом
|
игнорирует, отсутствующий — считает «проверка неприменима» и говорит об этом
|
||||||
+12
-18
@@ -1,22 +1,16 @@
|
|||||||
# Журнал версий канона
|
# Журнал версий канона до слияния плагинов
|
||||||
|
|
||||||
Одна запись на версию. Проект знает свою версию из `docs/.docs.json`; `canon
|
**Журнал закрыт.** Он описывает версии канона документов 1–14 — время, когда
|
||||||
upgrade` идёт по записям снизу вверх от версии проекта до текущей и делает то,
|
плагинов было три и у канона была своя нумерация. Действующий журнал —
|
||||||
что в них названо. Записи ниже версии 13 зовут этот файл прежним именем,
|
[changelog.md](changelog.md), и его версия 1 идёт **после** записи 14 отсюда.
|
||||||
`docs/.pm.json`, — так и было на день записи, и переписывать историю мы не
|
|
||||||
станем; переименование делает запись 13.
|
|
||||||
|
|
||||||
**Каталог задач этим журналом не повышается.** У него своя версия формата и свой
|
Записи не переписаны под нынешние имена: адрес и имя скилла, верные на день
|
||||||
журнал — `references/changelog.md` скилла `av-dev-tasks:tasks`. Записи 8, 11 и 12
|
записи, остаются там как свидетельство. Записи ниже версии 13 зовут служебный
|
||||||
трогали его в те времена, когда своего числа у него не было; впредь запись канона
|
файл `docs/.pm.json` — так и было; переименование делает запись 13, а переезд в
|
||||||
вправе позвать соседа, но не двигать его версию.
|
`.av-dev.toml` — запись 1 действующего журнала.
|
||||||
|
|
||||||
Правило записи: **что добавилось, что переехало, что удалено, что сделать
|
Проект, отставший от канона 14, идёт сперва по этим записям снизу вверх от своей
|
||||||
проекту**. Без последнего пункта запись бесполезна — по ней и работает
|
версии до 14, и только потом переходит в действующий журнал.
|
||||||
`upgrade`.
|
|
||||||
|
|
||||||
Версия — целое число. Обратной совместимости у канона нет: есть «приведён» и «не
|
|
||||||
приведён».
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -652,7 +646,7 @@ ADR объясняет прошлое решение, а не предъявля
|
|||||||
5. **Заголовки секций — с прописной, после заголовка пустая строка**, во всех
|
5. **Заголовки секций — с прописной, после заголовка пустая строка**, во всех
|
||||||
индексах. Написание канонических секций и отбивку правит `check --fix`; он
|
индексах. Написание канонических секций и отбивку правит `check --fix`; он
|
||||||
же сводит написание секции в мете файла с заголовком индекса.
|
же сводит написание секции в мете файла с заголовком индекса.
|
||||||
6. **Язык проектных текстов** — [language.md](language.md), общий дом для
|
6. **Язык проектных текстов** — [language.md](../../../shared/language.md), общий дом для
|
||||||
документов канона, задач, решений ADR и записок разведки: информационный
|
документов канона, задач, решений ADR и записок разведки: информационный
|
||||||
стиль (глагол вместо отглагольного существительного, активный залог, факт
|
стиль (глагол вместо отглагольного существительного, активный залог, факт
|
||||||
вместо оценки, стоп-слова, параллельность), таблицы англицизмов и жаргона и
|
вместо оценки, стоп-слова, параллельность), таблицы англицизмов и жаргона и
|
||||||
@@ -717,7 +711,7 @@ ADR объясняет прошлое решение, а не предъявля
|
|||||||
11. Переписать заголовки задач в форму действия — по мере того, как задача
|
11. Переписать заголовки задач в форму действия — по мере того, как задача
|
||||||
попадает в работу, а не «заодно»: `check` печатает их число, а `task-form`
|
попадает в работу, а не «заодно»: `check` печатает их число, а `task-form`
|
||||||
предложит формулировки на замену пачкой.
|
предложит формулировки на замену пачкой.
|
||||||
12. Прочитать [language.md](language.md) — и **ничего не переписывать задним
|
12. Прочитать [language.md](../../../shared/language.md) — и **ничего не переписывать задним
|
||||||
числом**. Правила языка применяются к тому, что пишется и правится сейчас;
|
числом**. Правила языка применяются к тому, что пишется и правится сейчас;
|
||||||
сплошная вычитка старых документов стоит дороже, чем даёт.
|
сплошная вычитка старых документов стоит дороже, чем даёт.
|
||||||
13. `docs/.pm.json`: `"canon": 3`.
|
13. `docs/.pm.json`: `"canon": 3`.
|
||||||
+7
-16
@@ -1,21 +1,12 @@
|
|||||||
# Журнал версий формата задач
|
# Журнал версий формата задач до слияния плагинов
|
||||||
|
|
||||||
Одна запись на версию. Проект знает свою версию из ключа `tasks` в `<каталог
|
**Журнал закрыт.** У каталога задач была своя версия, пока его вёл отдельный
|
||||||
задач>/.tasks.json`; повышение (`upgrade` в [SKILL.md](../SKILL.md), раздел
|
плагин `av-dev-tasks`. Версия теперь одна на всю раскладку —
|
||||||
«Версия формата») идёт по записям снизу вверх от версии проекта до текущей и
|
действующий журнал [changelog.md](changelog.md), и переезд числа описан его
|
||||||
делает то, что в них названо.
|
записью 1.
|
||||||
|
|
||||||
Правило записи: **что добавилось, что переехало, что удалено, что сделать
|
Запись ниже не переписана под нынешние имена: она описывает состояние, которое
|
||||||
проекту**. Без последнего пункта запись бесполезна — по ней и работает
|
было.
|
||||||
повышение.
|
|
||||||
|
|
||||||
Версия — целое число. Обратной совместимости у формата нет: есть «приведён» и «не
|
|
||||||
приведён».
|
|
||||||
|
|
||||||
**Это журнал формата задач, а не канона документов.** Числа у них разные и
|
|
||||||
двигаются порознь: плагин `av-dev-tasks` ставится в одиночку, и у проекта без
|
|
||||||
`av-dev-docs` версии канона нет вовсе. Журнал канона —
|
|
||||||
`references/changelog.md` скилла `av-dev-docs:canon`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Журнал версий раскладки
|
||||||
|
|
||||||
|
Одна запись на версию. Проект знает свою версию из ключа `version` в
|
||||||
|
`.av-dev.toml`; операция `upgrade` скилла `av-dev:doc-canon` идёт по записям
|
||||||
|
снизу вверх от версии проекта до текущей и делает то, что в них названо.
|
||||||
|
|
||||||
|
Правило записи: **что добавилось, что переехало, что удалено, что сделать
|
||||||
|
проекту**. Без последнего пункта запись бесполезна — по ней и работает
|
||||||
|
`upgrade`.
|
||||||
|
|
||||||
|
Версия — целое число. Обратной совместимости нет: есть «приведён» и «не
|
||||||
|
приведён». Версия **одна на всю раскладку** — и на документы канона, и на
|
||||||
|
каталог задач: ведёт их один плагин, и второе число означало бы только вопрос,
|
||||||
|
по какому журналу повышать.
|
||||||
|
|
||||||
|
**До слияния журналов было два**, и нумерация в них своя:
|
||||||
|
[changelog-before-merge.md](changelog-before-merge.md) — канон документов,
|
||||||
|
версии 1–14; [changelog-tasks-before-merge.md](changelog-tasks-before-merge.md) —
|
||||||
|
формат задач, версия 1. Оба **закрыты и не переписаны**: адрес, верный на день
|
||||||
|
записи, там и остался. Проект, отставший от канона 14, идёт сперва по ним, а
|
||||||
|
потом по этому журналу — порядок назван в записи 1.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Версия 1 — 2026-08-13
|
||||||
|
|
||||||
|
Три плагина — `av-dev-docs`, `av-dev-tasks` и `av-dev-code` — слились в один,
|
||||||
|
`av-dev`. Раскол делался под раздельную установку: проект мог взять учёт работ
|
||||||
|
без документов канона или конвейер без обоих. Практика посылку не подтвердила —
|
||||||
|
подмножество не понадобилось ни разу, — а платился раскол помеченными копиями
|
||||||
|
общих правил и веткой «плагина нет» на каждый вызов соседа.
|
||||||
|
|
||||||
|
**Что переехало в проекте.** Служебных файла было два, стал один:
|
||||||
|
|
||||||
|
| Было | Стало |
|
||||||
|
| --- | --- |
|
||||||
|
| `docs/.docs.json`, ключ `canon` | `.av-dev.toml` в корне, ключ `version` |
|
||||||
|
| `docs/.docs.json`, ключ `migrations` | `.av-dev.toml`, секция `[docs]` |
|
||||||
|
| `<каталог задач>/.tasks.json`, ключ `tasks` | тот же `version`: версия теперь одна |
|
||||||
|
| `<каталог задач>/.tasks.json`, имена частей | `.av-dev.toml`, секция `[tasks]` |
|
||||||
|
|
||||||
|
Корень выбран потому, что он есть у обоих: и у проекта без `docs/`, и у проекта
|
||||||
|
без каталога задач. Формат TOML — ради комментариев: файл лежит в репозитории
|
||||||
|
проекта, и назначение числа читают из него самого, а не из документации плагина.
|
||||||
|
|
||||||
|
**Что переехало в вызовах.** Имена скиллов сменили пространство имён и получили
|
||||||
|
префикс по прежнему плагину: `av-dev-docs:canon` → `av-dev:doc-canon`,
|
||||||
|
`av-dev-docs:init` → `av-dev:doc-init`, `av-dev-docs:docs` → `av-dev:doc-sync`,
|
||||||
|
`av-dev-docs:healthcheck` → `av-dev:doc-healthcheck`, `av-dev-tasks:tasks` →
|
||||||
|
`av-dev:task-track`, `av-dev-tasks:groom` → `av-dev:task-groom`,
|
||||||
|
`av-dev-code:openspec` → `av-dev:code-openspec`, `av-dev-code:resolve` →
|
||||||
|
`av-dev:code-resolve`, `av-dev-code:review` → `av-dev:code-review`.
|
||||||
|
|
||||||
|
**Что сделать проекту.**
|
||||||
|
|
||||||
|
1. **Отставшим сперва прежние журналы.** Версия канона в `docs/.docs.json`
|
||||||
|
меньше 14 — пройди записи до 14 по
|
||||||
|
[changelog-before-merge.md](changelog-before-merge.md), и только потом эту.
|
||||||
|
Иначе повышение объявит приведённым то, чего никто не делал.
|
||||||
|
2. **Завести `.av-dev.toml`** в корне репозитория: `version = 1`, секция
|
||||||
|
`[docs]` с `migrations`, если ключ был, секция `[tasks]` с `dir` и теми
|
||||||
|
именами частей, которые в `.tasks.json` отличались от умолчаний. Комментарии
|
||||||
|
пиши свои — файл читает человек.
|
||||||
|
3. **Удалить `docs/.docs.json` и `<каталог задач>/.tasks.json`.** Прежние имена
|
||||||
|
не читаются: два дома для одной версии расходятся молча. Пока старые файлы на
|
||||||
|
месте, `docs.py check` и `tasks.py check` называют это прежней раскладкой.
|
||||||
|
4. **Переставить плагины.** `av-dev-docs`, `av-dev-tasks` и `av-dev-code`
|
||||||
|
удалить, `av-dev` поставить — команды в README репозитория плагинов.
|
||||||
|
5. **Поправить гейт проекта.** Пути к `docs.py`, `tasks.py` и `openspec.py`
|
||||||
|
сменились вместе с именами каталогов скиллов: `skills/canon/` →
|
||||||
|
`skills/doc-canon/`, `skills/tasks/` → `skills/task-track/`,
|
||||||
|
`skills/openspec/` → `skills/code-openspec/`. Шаг, который не нашёл скрипт,
|
||||||
|
обязан краснеть, а не пропускаться, — проверь, что он краснеет.
|
||||||
|
6. **Поправить свои вызовы скиллов** — в `CLAUDE.md`, в `Taskfile`, в записях
|
||||||
|
задач: короткое имя разрешится в проектную копию, а прежнее полное не
|
||||||
|
разрешится вовсе.
|
||||||
|
7. **Поднять версию** — `docs.py bump`. Последним шагом: число объявляет
|
||||||
|
пройденными шаги журнала, и раньше времени поднятое врёт.
|
||||||
|
8. `docs.py check` и `tasks.py check --dir <каталог задач>` — до отсутствия
|
||||||
|
дрейфа.
|
||||||
|
|
||||||
|
**Чего делать не надо.** Переписывать прошлые записи журналов под новые имена.
|
||||||
|
Они описывают состояния, которые были, и адрес, верный на день записи, остаётся
|
||||||
|
верным как свидетельство.
|
||||||
+32
-23
@@ -1,6 +1,6 @@
|
|||||||
# Скелеты документов канона
|
# Скелеты документов канона
|
||||||
|
|
||||||
Что кладут `init` и `canon adopt` в незаполненный слот. Правило одно:
|
Что кладут `init` и `doc-canon adopt` в незаполненный слот. Правило одно:
|
||||||
**честная информативная строка вместо заглушки**. Проход читает строку как факт;
|
**честная информативная строка вместо заглушки**. Проход читает строку как факт;
|
||||||
`<!-- заполнить: … -->` он читает как пробел, и `docs.py check` о таком
|
`<!-- заполнить: … -->` он читает как пробел, и `docs.py check` о таком
|
||||||
плейсхолдере напоминает.
|
плейсхолдере напоминает.
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
со строкой «запись лежит сжатой и распаковывается целиком».
|
со строкой «запись лежит сжатой и распаковывается целиком».
|
||||||
```
|
```
|
||||||
|
|
||||||
Нет БД — файла нет, и в `docs/.docs.json` нет ключа `migrations`.
|
Нет БД — файла нет, и в `.av-dev.toml` нет ключа `[docs] migrations`.
|
||||||
|
|
||||||
## `docs/security.md`
|
## `docs/security.md`
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
|
|
||||||
Верно одно из трёх:
|
Верно одно из трёх:
|
||||||
|
|
||||||
<!-- копия: adr-когда-заводить из av-dev-docs/skills/canon/references/canon.md -->
|
<!-- копия: adr-когда-заводить из av-dev/skills/doc-canon/references/canon.md -->
|
||||||
- **дорогой откат** — переделка стоит дороже переписывания одного файла;
|
- **дорогой откат** — переделка стоит дороже переписывания одного файла;
|
||||||
- **намеренный отказ** от очевидного подхода;
|
- **намеренный отказ** от очевидного подхода;
|
||||||
- **пересмотр прежнего решения** — тогда у старой записи обязателен статус
|
- **пересмотр прежнего решения** — тогда у старой записи обязателен статус
|
||||||
@@ -345,7 +345,7 @@
|
|||||||
|
|
||||||
Форма:
|
Форма:
|
||||||
|
|
||||||
<!-- копия: журнал-дефектов-форма из av-dev-code/skills/review/references/review-journal.md -->
|
<!-- копия: журнал-дефектов-форма из av-dev/skills/code-review/references/review-journal.md -->
|
||||||
## ГГГГ-ММ-ДД — <краткое последствие> [проскочил|пойман]
|
## ГГГГ-ММ-ДД — <краткое последствие> [проскочил|пойман]
|
||||||
|
|
||||||
- **Где:** путь:строка либо «конвейер, а не код»
|
- **Где:** путь:строка либо «конвейер, а не код»
|
||||||
@@ -428,36 +428,45 @@ severity стоит здесь, а не выводится каждым прох
|
|||||||
|
|
||||||
## `openspec/config.yaml`
|
## `openspec/config.yaml`
|
||||||
|
|
||||||
**Образец переехал.** Файл заводит и заполняет плагин конвейера — скилл
|
**Образец переехал.** Файл заводит и заполняет скилл
|
||||||
`av-dev-code:openspec`, — потому что по OpenSpec работает он, а не канон
|
`av-dev:code-openspec`, — потому что по OpenSpec работает он, а не канон
|
||||||
документов. Проект без конвейера каталога `openspec/` не имеет вовсе, и образец
|
документов. Проект, не ведущий задачи циклом SDD, каталога `openspec/` не имеет
|
||||||
|
вовсе, и образец
|
||||||
файла, которого у него нет, в скелетах канона лежал бы мёртвым грузом.
|
файла, которого у него нет, в скелетах канона лежал бы мёртвым грузом.
|
||||||
|
|
||||||
**Форму не проверяет и `docs.py`** — с канона 10 он о файле молчит вовсе.
|
**Форму не проверяет и `docs.py`** — с канона 10 он о файле молчит вовсе.
|
||||||
Проверяет её тот же владелец: скилл `av-dev-code:openspec`, команда
|
Проверяет её тот же владелец: скилл `av-dev:code-openspec`, команда
|
||||||
`openspec.py check`. Плагина конвейера в проекте может не быть — тогда форму не
|
`openspec.py check`. Плагина конвейера в проекте может не быть — тогда форму не
|
||||||
смотрит никто, и это строка доклада, а не поломка. Что канон о файле всё же
|
смотрит никто, и это строка доклада, а не поломка. Что канон о файле всё же
|
||||||
говорит (единственный дом, а не форма) — [canon.md](canon.md), раздел
|
говорит (единственный дом, а не форма) — [canon.md](canon.md), раздел
|
||||||
`openspec/config.yaml`.
|
`openspec/config.yaml`.
|
||||||
|
|
||||||
## `docs/.docs.json`
|
## `.av-dev.toml`
|
||||||
|
|
||||||
```json
|
```toml
|
||||||
{
|
# Раскладка av-dev в этом проекте: версия и настройки проверок.
|
||||||
"canon": <текущая версия>
|
|
||||||
}
|
version = <текущая версия>
|
||||||
|
|
||||||
|
[docs]
|
||||||
|
# migrations = "<путь>" — появится, когда появится БД
|
||||||
|
|
||||||
|
[tasks]
|
||||||
|
dir = "tasks"
|
||||||
```
|
```
|
||||||
|
|
||||||
`<текущая версия>` подставляет `init` или `adopt`, целым числом; берётся она из
|
`<текущая версия>` подставляет `init` или `adopt`, целым числом; берётся она из
|
||||||
`docs.py version` (строка «канон скрипта»), а не из памяти. Литерал здесь
|
`docs.py version` (строка «версия раскладки, скрипт»), а не из памяти. Литерал здесь
|
||||||
протухает при каждом повышении канона, поэтому его тут и нет: незамещённый
|
протухает при каждом повышении, поэтому его тут и нет: незамещённый плейсхолдер
|
||||||
плейсхолдер ломает разбор JSON громко, а отставшее число дало бы дрейф молча.
|
ломает разбор TOML громко, а отставшее число дало бы дрейф молча.
|
||||||
|
|
||||||
Плюс `"migrations": "<путь>"`, если есть БД. Ключа `"tasks"` здесь **нет**:
|
Комментарии в файле — не украшение, а причина, по которой взят TOML: файл живёт
|
||||||
настройки каталога задач и версия их формата переехали в свой файл `<каталог
|
в чужом репозитории, и назначение числа читают из него самого. Скрипты это
|
||||||
задач>/.tasks.json`, потому что ведёт их другой плагин. Состав ключей —
|
учитывают и правят строку, а не переписывают файл. Состав ключей —
|
||||||
[canon.md](canon.md).
|
[canon.md](canon.md), раздел `.av-dev.toml`.
|
||||||
|
|
||||||
Имя файла — по плагину-владельцу, `av-dev-docs`. До версии 13 он звался
|
Файл лежит **в корне репозитория**, а не в `docs/`: версия одна на всю
|
||||||
`.pm.json`, по распавшемуся `av-dev-pm`; проект с прежним именем `docs.py check`
|
раскладку, и нужна она в том числе проекту, который канон документов ещё не
|
||||||
называет отдельной строкой и зовёт переименовать.
|
завёл. Прежние `docs/.docs.json` и `<каталог задач>/.tasks.json` остались от
|
||||||
|
трёх плагинов, слившихся в один; увидев их, `docs.py check` называет это прежней
|
||||||
|
раскладкой и зовёт `upgrade`.
|
||||||
+141
-61
@@ -17,26 +17,50 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import importlib.util
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from types import ModuleType
|
||||||
from typing import NoReturn
|
from typing import NoReturn
|
||||||
|
|
||||||
CANON_VERSION = 14
|
|
||||||
|
|
||||||
OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4
|
OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4
|
||||||
|
|
||||||
# Дом версии канона и путей, нужных проверкам. Имя — от плагина, который файл
|
|
||||||
# завёл: настройки канона документов ведёт `av-dev-docs`, и файл называется по
|
def _load_shared() -> ModuleType:
|
||||||
# нему. Прежнее имя досталось от `av-dev-pm` — плагина, который распался на
|
"""Общий читатель `.av-dev.toml` — `shared/config.py` этого же плагина.
|
||||||
# четыре и которого больше нет; читать его скрипт не умеет намеренно, потому что
|
|
||||||
# два дома для версии канона расходятся молча, а переименование стоит одну
|
Путь считается от файла скрипта, а не от рабочего каталога: скрипт зовут из
|
||||||
# команду и названо записью 13 журнала.
|
репозитория проекта, где ни плагина, ни его дерева в текущем каталоге нет.
|
||||||
CONFIG = "docs/.docs.json"
|
Своё дерево — единственное, куда ходить можно; в чужое не ходим никогда.
|
||||||
LEGACY_CONFIG = "docs/.pm.json"
|
"""
|
||||||
|
path = Path(__file__).resolve().parents[3] / "shared" / "config.py"
|
||||||
|
# Проверка именно файлом: `spec_from_file_location` на отсутствующем пути
|
||||||
|
# возвращает исправный спек, и падает уже `exec_module` — трейсбеком и кодом
|
||||||
|
# 1, то есть «найден дрейф, чинится». Битая установка дрейфом не является.
|
||||||
|
spec = importlib.util.spec_from_file_location("avdev_config", path)
|
||||||
|
if not path.is_file() or spec is None or spec.loader is None:
|
||||||
|
print(f"ОТКАЗ: не читается {path} — общий читатель настроек;"
|
||||||
|
f" переустанови плагин av-dev", file=sys.stderr)
|
||||||
|
sys.exit(ENV)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
conf = _load_shared()
|
||||||
|
|
||||||
|
# Версия раскладки одна на плагин и живёт в `shared/config.py`: её знают оба
|
||||||
|
# скрипта, и второе число здесь было бы вторым домом.
|
||||||
|
LAYOUT_VERSION = conf.VERSION
|
||||||
|
|
||||||
|
# Дом версии и путей, нужных проверкам, — `.av-dev.toml` в корне репозитория.
|
||||||
|
# До слияния плагинов файлов было два, `docs/.docs.json` и `.tasks.json`, и
|
||||||
|
# версии двигались порознь; теперь дом один, и лежит он в корне, потому что
|
||||||
|
# настройки нужны и проекту без `docs/`.
|
||||||
|
CONFIG = conf.CONFIG_NAME
|
||||||
|
|
||||||
# --- Раскладка канона -------------------------------------------------------
|
# --- Раскладка канона -------------------------------------------------------
|
||||||
|
|
||||||
@@ -77,7 +101,7 @@ CONDITIONAL_DOCS = {
|
|||||||
# Обязательные файлы вне раскладки docs/.
|
# Обязательные файлы вне раскладки docs/.
|
||||||
REQUIRED = {
|
REQUIRED = {
|
||||||
"CLAUDE.md": "памятка агенту: инварианты с severity, команды, семантика гейта",
|
"CLAUDE.md": "памятка агенту: инварианты с severity, команды, семантика гейта",
|
||||||
CONFIG: "версия канона и пути, нужные проверкам",
|
CONFIG: "версия раскладки av-dev и пути, нужные проверкам",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Файлы, которые документ-каталог обязан держать сверх README.md.
|
# Файлы, которые документ-каталог обязан держать сверх README.md.
|
||||||
@@ -86,9 +110,10 @@ DOC_EXTRA = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Служебное в docs/ и каталог задач, оставшийся там от прежней раскладки. Формы
|
# Служебное в docs/ и каталог задач, оставшийся там от прежней раскладки. Формы
|
||||||
# у них скрипт не проверяет, и по разным причинам: `.docs.json` не markdown, а
|
# у них скрипт не проверяет, и по разным причинам: `.docs.json` не markdown (и
|
||||||
# задачи **принадлежат другому плагину** — `av-dev-tasks`, со своим скриптом,
|
# сам он теперь след прежней раскладки, о котором говорит `check_required`), а
|
||||||
# своим конфигом и своей версией формата (её сторожит `tasks.py check`).
|
# задачи ведёт **другой скилл** — `task-track`, со своим скриптом и своими
|
||||||
|
# проверками.
|
||||||
#
|
#
|
||||||
# Дом задач с версии 11 — `tasks/` в корне репозитория, то есть вне `docs/`
|
# Дом задач с версии 11 — `tasks/` в корне репозитория, то есть вне `docs/`
|
||||||
# вовсе. `docs/tasks/` здесь терпится потому, что непереехавший проект не должен
|
# вовсе. `docs/tasks/` здесь терпится потому, что непереехавший проект не должен
|
||||||
@@ -106,11 +131,11 @@ NOT_DOCS = {".docs.json", ".pm.json", "tasks"}
|
|||||||
RETIRED = {
|
RETIRED = {
|
||||||
"review-brief.md": "документы канона и есть бриф; остаток — в review",
|
"review-brief.md": "документы канона и есть бриф; остаток — в review",
|
||||||
"review-journal.md": "→ документ review",
|
"review-journal.md": "→ документ review",
|
||||||
"plan.md": "→ tasks/ROADMAP.md (плагин av-dev-tasks)",
|
"plan.md": "→ tasks/ROADMAP.md (ведёт скилл task-track)",
|
||||||
"local-research.md": "→ документ research",
|
"local-research.md": "→ документ research",
|
||||||
"specs": "поведение → openspec/specs/, обзор → тема architecture",
|
"specs": "поведение → openspec/specs/, обзор → тема architecture",
|
||||||
"drafts": "идея → запись research, отказ → ADR, порядок → ROADMAP.md",
|
"drafts": "идея → запись research, отказ → ADR, порядок → ROADMAP.md",
|
||||||
"backlog": "→ tasks/ в корне репозитория (плагин av-dev-tasks)",
|
"backlog": "→ tasks/ в корне репозитория (ведёт скилл task-track)",
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Слаги в именах файлов --------------------------------------------------
|
# --- Слаги в именах файлов --------------------------------------------------
|
||||||
@@ -264,16 +289,22 @@ def fail(code: int, msg: str) -> NoReturn:
|
|||||||
|
|
||||||
|
|
||||||
def read_config(root: Path, rep: Report) -> dict:
|
def read_config(root: Path, rep: Report) -> dict:
|
||||||
path = root / CONFIG
|
"""Настройки проекта целиком; проверкам канона нужна секция `[docs]`."""
|
||||||
if not path.exists():
|
|
||||||
return {}
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(path.read_text(encoding="utf-8"))
|
cfg = conf.read(root)
|
||||||
except json.JSONDecodeError as exc:
|
conf.check_keys(docs_cfg(cfg), DOCS_KEYS, "в секции [docs]")
|
||||||
fail(ENV, f"{CONFIG} не разбирается: {exc}")
|
except conf.ConfigError as exc:
|
||||||
if not isinstance(data, dict):
|
fail(ENV, str(exc))
|
||||||
fail(ENV, f"{CONFIG} должен быть объектом")
|
return cfg
|
||||||
return data
|
|
||||||
|
|
||||||
|
# Ключи секции `[docs]`. Секцию знает этот скрипт, а не общий читатель: ключ
|
||||||
|
# заводится вместе с проверкой, которая его читает.
|
||||||
|
DOCS_KEYS = ("migrations",)
|
||||||
|
|
||||||
|
|
||||||
|
def docs_cfg(cfg: dict) -> dict:
|
||||||
|
return conf.section(cfg, "docs")
|
||||||
|
|
||||||
|
|
||||||
# --- Проверки ---------------------------------------------------------------
|
# --- Проверки ---------------------------------------------------------------
|
||||||
@@ -282,22 +313,19 @@ def read_config(root: Path, rep: Report) -> dict:
|
|||||||
def check_version(root: Path, cfg: dict, rep: Report) -> None:
|
def check_version(root: Path, cfg: dict, rep: Report) -> None:
|
||||||
if not (root / CONFIG).exists():
|
if not (root / CONFIG).exists():
|
||||||
return # об отсутствии файла скажет check_required, второй раз не нужно
|
return # об отсутствии файла скажет check_required, второй раз не нужно
|
||||||
if "canon" not in cfg:
|
got = conf.version(cfg)
|
||||||
rep.error(f"в {CONFIG} нет ключа canon — версия канона не объявлена")
|
if got is None:
|
||||||
|
rep.error(f"в {CONFIG} нет ключа version — версия раскладки не объявлена")
|
||||||
return
|
return
|
||||||
got = cfg["canon"]
|
if got < LAYOUT_VERSION:
|
||||||
if not isinstance(got, int):
|
|
||||||
rep.error(f"canon в {CONFIG} должен быть целым числом, а не {got!r}")
|
|
||||||
return
|
|
||||||
if got < CANON_VERSION:
|
|
||||||
rep.error(
|
rep.error(
|
||||||
f"проект приведён к канону версии {got}, текущая — {CANON_VERSION}: "
|
f"проект приведён к раскладке версии {got}, текущая — {LAYOUT_VERSION}:"
|
||||||
f"нужен canon upgrade"
|
f" нужно повышение (скилл av-dev:doc-canon, операция upgrade)"
|
||||||
)
|
)
|
||||||
elif got > CANON_VERSION:
|
elif got > LAYOUT_VERSION:
|
||||||
rep.error(
|
rep.error(
|
||||||
f"проект приведён к канону версии {got}, а скрипт знает {CANON_VERSION}: "
|
f"проект приведён к раскладке версии {got}, а скрипт знает"
|
||||||
f"устарел плагин, обнови маркетплейс"
|
f" {LAYOUT_VERSION}: устарел плагин, обнови маркетплейс"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -327,22 +355,40 @@ def doc_home(root: Path, name: str) -> tuple[Path | None, str | None]:
|
|||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def check_legacy(root: Path, rep: Report) -> None:
|
||||||
|
"""Следы прежней раскладки — отдельная проверка, а не ветка отсутствия.
|
||||||
|
|
||||||
|
Пока она жила внутри «нового файла нет», половина переезда проходила молча:
|
||||||
|
завели `.av-dev.toml`, старые файлы удалить забыли — и оба скрипта считали
|
||||||
|
проект здоровым. Это ровно тот второй дом, против которого переезд и
|
||||||
|
делался, и увидеть его можно только тогда, когда новый файл уже есть.
|
||||||
|
"""
|
||||||
|
legacy = conf.legacy_files(root)
|
||||||
|
if not legacy:
|
||||||
|
return
|
||||||
|
if (root / CONFIG).is_file():
|
||||||
|
rep.error(
|
||||||
|
f"прежняя раскладка не убрана: {', '.join(legacy)} рядом с {CONFIG}."
|
||||||
|
f" Эти файлы не читаются, и версия в них своя — второй дом для того"
|
||||||
|
f" же числа. Удали их: переезд не закончен (журнал, версия 1, шаг 3)"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
rep.error(
|
||||||
|
f"нет {CONFIG}, а настройки лежат по прежней раскладке"
|
||||||
|
f" ({', '.join(legacy)}) — она осталась от трёх плагинов, которые"
|
||||||
|
f" слились в один: перенеси значения и удали старые файлы операцией"
|
||||||
|
f" upgrade скилла av-dev:doc-canon (журнал, версия 1). Прежние имена не"
|
||||||
|
f" читаются, поэтому в этом прогоне всё остальное проверено так, будто"
|
||||||
|
f" настроек нет вовсе"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def check_required(root: Path, cfg: dict, rep: Report) -> None:
|
def check_required(root: Path, cfg: dict, rep: Report) -> None:
|
||||||
for rel, what in REQUIRED.items():
|
for rel, what in REQUIRED.items():
|
||||||
if (root / rel).exists():
|
if (root / rel).exists():
|
||||||
continue
|
continue
|
||||||
# Файл под прежним именем — это не «нет файла», а незаконченный переезд,
|
if rel == CONFIG and conf.legacy_files(root):
|
||||||
# и чинится он одной командой. Без этой ветки проект услышал бы «нет
|
continue # об этом уже сказала check_legacy, и подробнее
|
||||||
# версии канона» и пошёл заводить второй файл рядом с первым.
|
|
||||||
if rel == CONFIG and (root / LEGACY_CONFIG).exists():
|
|
||||||
rep.error(
|
|
||||||
f"нет {rel} — {what}. Настройки лежат под прежним именем"
|
|
||||||
f" {LEGACY_CONFIG} (от плагина av-dev-pm, которого больше нет):"
|
|
||||||
f" `git mv {LEGACY_CONFIG} {rel}` — журнал канона, версия 13."
|
|
||||||
f" Прежнее имя не читается, поэтому в этом прогоне всё"
|
|
||||||
f" остальное проверено так, будто настроек нет вовсе"
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
rep.error(f"нет {rel} — {what}")
|
rep.error(f"нет {rel} — {what}")
|
||||||
|
|
||||||
for name, (kind, what) in DOCS.items():
|
for name, (kind, what) in DOCS.items():
|
||||||
@@ -360,18 +406,20 @@ def check_required(root: Path, cfg: dict, rep: Report) -> None:
|
|||||||
if not (home / extra).is_file():
|
if not (home / extra).is_file():
|
||||||
rep.error(f"нет docs/{name}/{extra} — {why}")
|
rep.error(f"нет docs/{name}/{extra} — {why}")
|
||||||
|
|
||||||
|
docs = docs_cfg(cfg)
|
||||||
for name, (key, kind, what) in CONDITIONAL_DOCS.items():
|
for name, (key, kind, what) in CONDITIONAL_DOCS.items():
|
||||||
home, complaint = doc_home(root, name)
|
home, complaint = doc_home(root, name)
|
||||||
if complaint:
|
if complaint:
|
||||||
rep.error(complaint)
|
rep.error(complaint)
|
||||||
if key in cfg and home is None:
|
if key in docs and home is None:
|
||||||
rep.error(
|
rep.error(
|
||||||
f"нет документа {name} (docs/{name}.md или docs/{name}/),"
|
f"нет документа {name} (docs/{name}.md или docs/{name}/),"
|
||||||
f" категория «{kind}» — {what}"
|
f" категория «{kind}» — {what}"
|
||||||
f" (обязателен: в .docs.json объявлен {key})"
|
f" (обязателен: в {CONFIG} объявлен [docs] {key})"
|
||||||
)
|
)
|
||||||
elif key not in cfg and home is None:
|
elif key not in docs and home is None:
|
||||||
rep.skip(f"{name} — в .docs.json нет ключа {key}, проверка неприменима")
|
rep.skip(f"{name} — в {CONFIG} нет ключа [docs] {key},"
|
||||||
|
f" проверка неприменима")
|
||||||
|
|
||||||
|
|
||||||
def check_stray(root: Path, rep: Report) -> None:
|
def check_stray(root: Path, rep: Report) -> None:
|
||||||
@@ -551,9 +599,10 @@ def changed_files(root: Path, base: str, rep: Report) -> list[str] | None:
|
|||||||
|
|
||||||
|
|
||||||
def check_migrations(root: Path, cfg: dict, base: str | None, rep: Report) -> None:
|
def check_migrations(root: Path, cfg: dict, base: str | None, rep: Report) -> None:
|
||||||
migrations = cfg.get("migrations")
|
migrations = docs_cfg(cfg).get("migrations")
|
||||||
if not migrations:
|
if not migrations:
|
||||||
rep.skip("в .docs.json нет ключа migrations — сверка со схемой неприменима")
|
rep.skip(f"в {CONFIG} нет ключа [docs] migrations —"
|
||||||
|
f" сверка со схемой неприменима")
|
||||||
return
|
return
|
||||||
if not base:
|
if not base:
|
||||||
rep.skip("база диффа не названа (--base) — сверка миграций со схемой не гонялась")
|
rep.skip("база диффа не названа (--base) — сверка миграций со схемой не гонялась")
|
||||||
@@ -595,7 +644,7 @@ def report(rep: Report) -> int:
|
|||||||
"\nМашина проверила раскладку, имена файлов, ссылки, версию и две\n"
|
"\nМашина проверила раскладку, имена файлов, ссылки, версию и две\n"
|
||||||
"сверки с кодом. Форму openspec/config.yaml она не проверяет: каталог\n"
|
"сверки с кодом. Форму openspec/config.yaml она не проверяет: каталог\n"
|
||||||
"принадлежит конвейеру, и форму смотрит его скрипт\n"
|
"принадлежит конвейеру, и форму смотрит его скрипт\n"
|
||||||
"(`av-dev-code:openspec`, команда `openspec.py check`). Согласованность\n"
|
"(`av-dev:code-openspec`, команда `openspec.py check`). Согласованность\n"
|
||||||
"документов между собой и с кодом — тоже не её: это суждение агентов\n"
|
"документов между собой и с кодом — тоже не её: это суждение агентов\n"
|
||||||
"`doc-consistency` (документ ↔ документ ↔ openspec) и `doc-code-drift`\n"
|
"`doc-consistency` (документ ↔ документ ↔ openspec) и `doc-code-drift`\n"
|
||||||
"(документ ↔ код)."
|
"(документ ↔ код)."
|
||||||
@@ -617,6 +666,7 @@ def cmd_check(args: argparse.Namespace) -> int:
|
|||||||
rep = Report()
|
rep = Report()
|
||||||
cfg = read_config(root, rep)
|
cfg = read_config(root, rep)
|
||||||
check_version(root, cfg, rep)
|
check_version(root, cfg, rep)
|
||||||
|
check_legacy(root, rep)
|
||||||
check_required(root, cfg, rep)
|
check_required(root, cfg, rep)
|
||||||
check_stray(root, rep)
|
check_stray(root, rep)
|
||||||
check_slugs(root, rep)
|
check_slugs(root, rep)
|
||||||
@@ -629,10 +679,36 @@ def cmd_check(args: argparse.Namespace) -> int:
|
|||||||
|
|
||||||
def cmd_version(args: argparse.Namespace) -> int:
|
def cmd_version(args: argparse.Namespace) -> int:
|
||||||
root = Path(args.dir).resolve()
|
root = Path(args.dir).resolve()
|
||||||
|
if not root.is_dir():
|
||||||
|
fail(ENV, f"нет каталога {root}")
|
||||||
cfg = read_config(root, Report())
|
cfg = read_config(root, Report())
|
||||||
got = cfg.get("canon", "не объявлена")
|
got = conf.version(cfg)
|
||||||
print(f"канон скрипта: {CANON_VERSION}")
|
print(f"версия раскладки, скрипт: {LAYOUT_VERSION}")
|
||||||
print(f"канон проекта: {got}")
|
print(f"версия раскладки, проект: {got if got is not None else 'не объявлена'}")
|
||||||
|
return OK
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_bump(args: argparse.Namespace) -> int:
|
||||||
|
"""Поднять версию проекта до той, что знает скрипт. Последний шаг повышения.
|
||||||
|
|
||||||
|
Двигается **строка**, а не файл: комментарии в нём принадлежат проекту.
|
||||||
|
Поднять раньше времени нельзя не потому, что скрипт не даст, а потому что
|
||||||
|
число объявляет пройденными шаги журнала, которых никто не делал, — поэтому
|
||||||
|
команда отдельная и зовётся руками, а `check --fix` этого не пишет.
|
||||||
|
"""
|
||||||
|
root = Path(args.dir).resolve()
|
||||||
|
if not (root / CONFIG).is_file():
|
||||||
|
fail(ENV, f"нет {root / CONFIG} — сперва заведи раскладку (adopt)")
|
||||||
|
was = conf.version(read_config(root, Report()))
|
||||||
|
if was == LAYOUT_VERSION:
|
||||||
|
print(f"версия уже {LAYOUT_VERSION}, файл не тронут")
|
||||||
|
return OK
|
||||||
|
if was is not None and was > LAYOUT_VERSION:
|
||||||
|
fail(ENV, f"проект на версии {was}, скрипт знает {LAYOUT_VERSION}:"
|
||||||
|
f" устарел плагин, обнови маркетплейс")
|
||||||
|
conf.set_version(root, LAYOUT_VERSION)
|
||||||
|
print(f"версия раскладки: {was if was is not None else 'не была объявлена'}"
|
||||||
|
f" → {LAYOUT_VERSION} в {CONFIG}")
|
||||||
return OK
|
return OK
|
||||||
|
|
||||||
|
|
||||||
@@ -648,10 +724,14 @@ def main() -> int:
|
|||||||
p_check.add_argument("--base", default=None, help="база диффа для сверки миграций")
|
p_check.add_argument("--base", default=None, help="база диффа для сверки миграций")
|
||||||
p_check.set_defaults(func=cmd_check)
|
p_check.set_defaults(func=cmd_check)
|
||||||
|
|
||||||
p_ver = sub.add_parser("version", help="версия канона скрипта и проекта")
|
p_ver = sub.add_parser("version", help="версия раскладки: скрипта и проекта")
|
||||||
p_ver.add_argument("--dir", default=".", help="корень проекта")
|
p_ver.add_argument("--dir", default=".", help="корень проекта")
|
||||||
p_ver.set_defaults(func=cmd_version)
|
p_ver.set_defaults(func=cmd_version)
|
||||||
|
|
||||||
|
p_bump = sub.add_parser("bump", help="поднять версию проекта до версии скрипта")
|
||||||
|
p_bump.add_argument("--dir", default=".", help="корень проекта")
|
||||||
|
p_bump.set_defaults(func=cmd_bump)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
try:
|
try:
|
||||||
return args.func(args)
|
return args.func(args)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: healthcheck
|
name: doc-healthcheck
|
||||||
description: "Проверка здоровья документации проекта судом, а не машиной: не разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом — doc-consistency (один факт в двух домах, прямое противоречие, поведение в architecture.md вместо спек, ADR без парного статуса, число без провенанса) и doc-code-drift (протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки, capability). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию канона проверяет скилл canon, язык документов — агент doc-wording."
|
description: "Проверка здоровья документации проекта судом, а не машиной: не разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом — doc-consistency (один факт в двух домах, прямое противоречие, поведение в architecture.md вместо спек, ADR без парного статуса, число без провенанса) и doc-code-drift (протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки, capability). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию раскладки проверяет скилл av-dev:doc-canon, язык документов — агент doc-wording."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Здоровье документации
|
# Здоровье документации
|
||||||
@@ -23,7 +23,7 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
`architecture.md` и уже живущий в `CLAUDE.md`;
|
`architecture.md` и уже живущий в `CLAUDE.md`;
|
||||||
- **вернулись к проекту после перерыва** — прежде чем опираться на написанное;
|
- **вернулись к проекту после перерыва** — прежде чем опираться на написанное;
|
||||||
- **перед тем как опереться на документ в решении**, если оно дорогое;
|
- **перед тем как опереться на документ в решении**, если оно дорогое;
|
||||||
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `canon` сам.
|
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `doc-canon` сам.
|
||||||
|
|
||||||
**Не на каждой задаче и не на каждом синке документации.** Цена реальная:
|
**Не на каждой задаче и не на каждом синке документации.** Цена реальная:
|
||||||
`doc-consistency` идёт на `opus`, потому что сличение утверждений — суждение;
|
`doc-consistency` идёт на `opus`, потому что сличение утверждений — суждение;
|
||||||
@@ -35,39 +35,45 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
пришлось назвать заново** — иначе их не звал бы никто, кроме разовых `adopt` и
|
пришлось назвать заново** — иначе их не звал бы никто, кроме разовых `adopt` и
|
||||||
`upgrade`, то есть на живом проекте никогда.
|
`upgrade`, то есть на живом проекте никогда.
|
||||||
|
|
||||||
## Обращение к соседним плагинам
|
## Чего может не быть
|
||||||
|
|
||||||
**Копия.** Дом — `shared/plugin-boundary.md` в репозитории плагинов. Правится
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
дом, а не этот файл.
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
месте.
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, `av-dev-tasks:tasks`,
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
`av-dev-code:review`. Короткое имя может разрешиться в устаревшую проектную копию
|
| --- | --- | --- |
|
||||||
из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в поведении.
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт
|
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||||
только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
прочитает его сам.
|
поведении.
|
||||||
|
|
||||||
**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя,
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
Здесь сосед один: `av-dev-tasks:tasks`, когда находка тянет на задачу. Его нет —
|
<!-- /копия: отсутствие -->
|
||||||
|
|
||||||
|
Здесь сосед один: `av-dev:task-track`, когда находка тянет на задачу. Его нет —
|
||||||
находки остаются списком в докладе, и это говорится строкой.
|
находки остаются списком в докладе, и это говорится строкой.
|
||||||
|
|
||||||
## Пачка — весь канон, и это не расточительство
|
## Пачка — весь канон, и это не расточительство
|
||||||
@@ -109,9 +115,9 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
1. **Строка на замену** — правь документ сразу. Формулировка уже готова, спорить
|
1. **Строка на замену** — правь документ сразу. Формулировка уже готова, спорить
|
||||||
не с чем, и откладывание превращает её в задачу дороже самой правки.
|
не с чем, и откладывание превращает её в задачу дороже самой правки.
|
||||||
2. **Работа больше чем на абзац** — задача типа `chore`. Заводит её **не этот
|
2. **Работа больше чем на абзац** — задача типа `chore`. Заводит её **не этот
|
||||||
скилл**: вызови Skill `av-dev-tasks:tasks`, у него свой формат, дедупликация
|
скилл**: вызови Skill `av-dev:task-track`, у него свой формат, дедупликация
|
||||||
против беклога и кладбища. Плагина нет — отдай списком в докладе и скажи это
|
против беклога и кладбища. Каталога задач в проекте нет — отдай списком в
|
||||||
строкой.
|
докладе и скажи это строкой.
|
||||||
3. **Не находка** — агент ошибся, документ прав. Скажи это прямо: неразобранная
|
3. **Не находка** — агент ошибся, документ прав. Скажи это прямо: неразобранная
|
||||||
находка и отклонённая различаются, и вторая экономит время на следующем
|
находка и отклонённая различаются, и вторая экономит время на следующем
|
||||||
прогоне. Класс ошибок, который повторяется, идёт в `docs/review.*`, раздел
|
прогоне. Класс ошибок, который повторяется, идёт в `docs/review.*`, раздел
|
||||||
@@ -126,18 +132,18 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
идёт из его собственного отчёта — перечень фактов у него закрытый, и он
|
идёт из его собственного отчёта — перечень фактов у него закрытый, и он
|
||||||
называет, какие из них проверить было нечем.
|
называет, какие из них проверить было нечем.
|
||||||
- Канона в проекте нет вовсе — это исход, а не пустой прогон: скажи строкой и
|
- Канона в проекте нет вовсе — это исход, а не пустой прогон: скажи строкой и
|
||||||
предложи `av-dev-docs:canon`.
|
предложи `av-dev:doc-canon`.
|
||||||
|
|
||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
- **Не проверяет раскладку, версию и ссылки** — это `canon check`, там машина.
|
- **Не проверяет раскладку, версию и ссылки** — это `doc-canon check`, там машина.
|
||||||
- **Не судит язык** документов: залог, англицизмы, жаргон, термин без дома — это
|
- **Не судит язык** документов: залог, англицизмы, жаргон, термин без дома — это
|
||||||
агент `doc-wording`, и зовут его отдельно, по пачке правленных документов.
|
агент `doc-wording`, и зовут его отдельно, по пачке правленных документов.
|
||||||
Звонящие у него названные — последний шаг синка в `av-dev-docs:docs`, шаг 9
|
Звонящие у него названные — последний шаг синка в `av-dev:doc-sync`, шаг 9
|
||||||
`av-dev-docs:init` и шаг вычитки в обоих режимах `canon`, — просто ни один из
|
`av-dev:doc-init` и шаг вычитки в обоих режимах `doc-canon`, — просто ни один из
|
||||||
них не здесь. У него другой ритм: он нужен там, где текст только что писали, а
|
них не здесь. У него другой ритм: он нужен там, где текст только что писали, а
|
||||||
не там, где он год лежал. Оркестровать его нечем — он один и работает по
|
не там, где он год лежал. Оркестровать его нечем — он один и работает по
|
||||||
названному списку.
|
названному списку.
|
||||||
- **Не правит документы за агентов** — они возвращают формулировки, решение
|
- **Не правит документы за агентов** — они возвращают формулировки, решение
|
||||||
подставить принимает человек или ты по его правилу.
|
подставить принимает человек или ты по его правилу.
|
||||||
- **Не заводит задачи** — этим владеет `av-dev-tasks:tasks`.
|
- **Не заводит задачи** — этим владеет `av-dev:task-track`.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: init
|
name: doc-init
|
||||||
description: "Завести новый проект — сессия вопросов и ответов по свободному описанию замысла, из которой рождается первичная документация по канону av-dev: паспорт, CLAUDE.md с инвариантами и командами, модель угроз с периметром и скелет остальных документов; первые цели собирает интервью, а записывает их вызовом скилла av-dev-tasks:tasks — роадмап принадлежит плагину задач. OpenSpec заводит не сам, а вызовом скилла av-dev-code:openspec — каталог принадлежит конвейеру; плагина конвейера нет — шаг пропускается строкой доклада. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл canon."
|
description: "Завести новый проект — сессия вопросов и ответов по свободному описанию замысла, из которой рождается первичная документация по канону av-dev: паспорт, CLAUDE.md с инвариантами и командами, модель угроз с периметром и скелет остальных документов; первые цели собирает интервью, а записывает их вызовом скилла av-dev:task-track — роадмап принадлежит учёту работ. OpenSpec заводит не сам, а вызовом скилла av-dev:code-openspec — каталог принадлежит конвейеру. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл doc-canon."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Заведение нового проекта
|
# Заведение нового проекта
|
||||||
@@ -8,9 +8,9 @@ description: "Завести новый проект — сессия вопро
|
|||||||
Вход — свободный текст «что мне нужно и почему». Выход — канон документов, с
|
Вход — свободный текст «что мне нужно и почему». Выход — канон документов, с
|
||||||
которого дальше работают все остальные скиллы.
|
которого дальше работают все остальные скиллы.
|
||||||
|
|
||||||
**Определение канона — [канон](../canon/references/canon.md).** Прочитай его до
|
**Определение канона — [канон](../doc-canon/references/canon.md).** Прочитай его до
|
||||||
первого вопроса: интервью идёт по слотам канона, а не по вкусу. Что класть в
|
первого вопроса: интервью идёт по слотам канона, а не по вкусу. Что класть в
|
||||||
каждый файл — [скелеты](../canon/references/skeletons.md); не выдумывай заглушки
|
каждый файл — [скелеты](../doc-canon/references/skeletons.md); не выдумывай заглушки
|
||||||
своей формы, `docs.py` узнаёт только плейсхолдер оттуда.
|
своей формы, `docs.py` узнаёт только плейсхолдер оттуда.
|
||||||
|
|
||||||
## Что `init` физически не может произвести
|
## Что `init` физически не может произвести
|
||||||
@@ -26,7 +26,7 @@ description: "Завести новый проект — сессия вопро
|
|||||||
| `passport.md` | `architecture.md` |
|
| `passport.md` | `architecture.md` |
|
||||||
| `CLAUDE.md` | `database.md` |
|
| `CLAUDE.md` | `database.md` |
|
||||||
| `security.md` | `conventions/` |
|
| `security.md` | `conventions/` |
|
||||||
| `docs/.docs.json` | `research/`, `adr/` |
|
| `.av-dev.toml` | `research/`, `adr/` |
|
||||||
| | `review.md` — журнал пуст, настройка появится с первым ревью |
|
| | `review.md` — журнал пуст, настройка появится с первым ревью |
|
||||||
|
|
||||||
Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет,
|
Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет,
|
||||||
@@ -34,8 +34,9 @@ description: "Завести новый проект — сессия вопро
|
|||||||
|
|
||||||
**`tasks/ROADMAP.md` в таблице нет намеренно.** Первые цели `init` собирает
|
**`tasks/ROADMAP.md` в таблице нет намеренно.** Первые цели `init` собирает
|
||||||
интервью (блок 6), но записывает их не он: каталогом задач и формой целей владеет
|
интервью (блок 6), но записывает их не он: каталогом задач и формой целей владеет
|
||||||
`av-dev-tasks:tasks`, и это шаг 7. Плагина нет — цели остаются списком в докладе,
|
`av-dev:task-track`, и это шаг 7. Человек от каталога задач отказался — цели
|
||||||
роадмапа в проекте не появляется, и это говорится строкой.
|
остаются списком в докладе, роадмапа в проекте не появляется, и это говорится
|
||||||
|
строкой.
|
||||||
|
|
||||||
## Порядок интервью — зависимость, а не удобство
|
## Порядок интервью — зависимость, а не удобство
|
||||||
|
|
||||||
@@ -69,67 +70,75 @@ description: "Завести новый проект — сессия вопро
|
|||||||
- **Развилка замысла — человеку, механика — сама.** Имена файлов, слаги, порядок
|
- **Развилка замысла — человеку, механика — сама.** Имена файлов, слаги, порядок
|
||||||
строк не выноси.
|
строк не выноси.
|
||||||
|
|
||||||
## Обращение к соседним плагинам
|
## Чего может не быть
|
||||||
|
|
||||||
Два шага порядка работы — вызовы чужого: OpenSpec заводит конвейер, каталог задач
|
Два шага порядка работы — вызовы чужого: OpenSpec заводит конвейер, каталог задач
|
||||||
ведёт плагин задач. Ни того, ни другого `init` не делает руками.
|
ведёт скилл задач. Ни того, ни другого `init` не делает руками.
|
||||||
|
|
||||||
**Копия.** Дом правила — `shared/plugin-boundary.md` в репозитории плагинов.
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
Правится дом, а не этот файл.
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
месте.
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, `av-dev-tasks:tasks`,
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
`av-dev-code:review`. Короткое имя может разрешиться в устаревшую проектную копию
|
| --- | --- | --- |
|
||||||
из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в поведении.
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт
|
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||||
только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
прочитает его сам.
|
поведении.
|
||||||
|
|
||||||
**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя,
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
Чем оборачивается отсутствие каждого — на самих шагах 3 и 7. Заведение проекта
|
<!-- /копия: отсутствие -->
|
||||||
из-за этого не останавливается: проект без конвейера и без учёта задач законен.
|
|
||||||
|
Оба скилла в этом же плагине и разрешаются всегда; чем оборачивается отказ от
|
||||||
|
того, что они заводят, — на самих шагах 3 и 7. Заведение проекта из-за этого не
|
||||||
|
останавливается: проект без OpenSpec и без учёта задач законен.
|
||||||
|
|
||||||
## Порядок работы
|
## Порядок работы
|
||||||
|
|
||||||
1. Прочитай бриф целиком. Выпиши, на какие блоки интервью ответ уже есть.
|
1. Прочитай бриф целиком. Выпиши, на какие блоки интервью ответ уже есть.
|
||||||
2. Проведи интервью итерациями по ≤3 вопроса.
|
2. Проведи интервью итерациями по ≤3 вопроса.
|
||||||
3. **OpenSpec — вызови Skill `av-dev-code:openspec`.** Он заводит каталог и
|
3. **OpenSpec — вызови Skill `av-dev:code-openspec`.** Он заводит каталог и
|
||||||
заменяет пример в `config.yaml` настройкой. Делается это **до первого
|
заменяет пример в `config.yaml` настройкой. Делается это **до первого
|
||||||
документа**: без `openspec/` не работают ни `opsx:propose`, ни ревью дизайна,
|
документа**: без `openspec/` не работают ни `opsx:propose`, ни ревью дизайна,
|
||||||
ни сверка требований. Каталог принадлежит конвейеру, а не канону, поэтому
|
ни сверка требований. Каталог принадлежит конвейеру, а не канону, поэтому
|
||||||
здесь только вызов — ни команды, ни формы файла `init` не знает.
|
здесь только вызов — ни команды, ни формы файла `init` не знает.
|
||||||
|
|
||||||
**Вызов не разрешился** — проект без конвейера живёт без OpenSpec законно:
|
**Человек от OpenSpec отказался** — проект живёт без него законно: строка
|
||||||
строка доклада, и дальше; `docs.py check` о каталоге тоже промолчит.
|
доклада, и дальше; `docs.py check` о каталоге тоже промолчит. Цикл SDD в
|
||||||
4. Заведи `docs/.docs.json` с текущей версией канона — число берётся из
|
таком проекте не запускается, и это надо назвать, а не обойти.
|
||||||
|
4. Заведи `.av-dev.toml` в корне с текущей версией раскладки — число берётся из
|
||||||
`docs.py version`, а не из памяти.
|
`docs.py version`, а не из памяти.
|
||||||
5. Напиши заполняемые документы. **Бриф переезжает в `passport.md`** и
|
5. Напиши заполняемые документы. **Бриф переезжает в `passport.md`** и
|
||||||
отдельным файлом не остаётся: два дома для одного замысла разойдутся на
|
отдельным файлом не остаётся: два дома для одного замысла разойдутся на
|
||||||
первом же уточнении.
|
первом же уточнении.
|
||||||
6. Заведи скелет остальных по [скелетам](../canon/references/skeletons.md) —
|
6. Заведи скелет остальных по [скелетам](../doc-canon/references/skeletons.md) —
|
||||||
каждый с честной строкой.
|
каждый с честной строкой.
|
||||||
7. Каталог задач и первые цели — **вызови скилл `av-dev-tasks:tasks`**: он владеет
|
7. Каталог задач и первые цели — **вызови скилл `av-dev:task-track`**: он владеет
|
||||||
форматом целей и задач. Не разрешился — учёт задач остаётся владельцу, и это
|
форматом целей и задач. Не разрешился — учёт задач остаётся владельцу, и это
|
||||||
тоже строка доклада.
|
тоже строка доклада.
|
||||||
8. `docs.py check` из скилла `canon` — до отсутствия дрейфа. Замечания о
|
8. `docs.py check` из скилла `doc-canon` — до отсутствия дрейфа. Замечания о
|
||||||
незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа.
|
незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа.
|
||||||
9. **Вычитай написанное — агент `doc-wording`**, по пачке заполненных документов
|
9. **Вычитай написанное — агент `doc-wording`**, по пачке заполненных документов
|
||||||
(`passport.md`, `CLAUDE.md`, `security.md`). Здесь он нужен сильнее, чем где
|
(`passport.md`, `CLAUDE.md`, `security.md`). Здесь он нужен сильнее, чем где
|
||||||
@@ -142,8 +151,8 @@ description: "Завести новый проект — сессия вопро
|
|||||||
|
|
||||||
## Что дальше
|
## Что дальше
|
||||||
|
|
||||||
- Содержимое канона по ходу разработки ведёт скилл `docs`.
|
- Содержимое канона по ходу разработки ведёт скилл `doc-sync`.
|
||||||
- Раскладку проверяет `canon check`.
|
- Раскладку проверяет `doc-canon check`.
|
||||||
- Первую задачу берёт конвейер проекта; `architecture.md` и `conventions/`
|
- Первую задачу берёт конвейер проекта; `architecture.md` и `conventions/`
|
||||||
наполняются его шагом синка, а не заранее.
|
наполняются его шагом синка, а не заранее.
|
||||||
|
|
||||||
@@ -151,6 +160,6 @@ description: "Завести новый проект — сессия вопро
|
|||||||
|
|
||||||
- **Не проектирует систему.** Архитектура выводится из кода, а не наоборот.
|
- **Не проектирует систему.** Архитектура выводится из кода, а не наоборот.
|
||||||
- **Не пишет код** и не заводит сборку.
|
- **Не пишет код** и не заводит сборку.
|
||||||
- **Не переводит существующий проект** — это `canon adopt`. Признак: в
|
- **Не переводит существующий проект** — это `doc-canon adopt`. Признак: в
|
||||||
репозитории уже есть документация или беклог в какой-то раскладке.
|
репозитории уже есть документация или беклог в какой-то раскладке.
|
||||||
- **Не решает за человека**, что важно: цель, границы и периметр — его ответы.
|
- **Не решает за человека**, что важно: цель, границы и периметр — его ответы.
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
---
|
---
|
||||||
name: docs
|
name: doc-sync
|
||||||
description: Вести содержимое документов канона по ходу разработки — синк после сделанной задачи с построчным отчётом по каждому документу, заведение ADR промоутом из архивного design.md или из записки разведки, запись наблюдения в research, запись дефекта и настройки конвейера в review.md, чистка architecture.md от поведения с маркерами долга. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл canon.
|
description: Вести содержимое документов канона по ходу разработки — синк после сделанной задачи с построчным отчётом по каждому документу, заведение ADR промоутом из архивного design.md или из записки разведки, запись наблюдения в research, запись дефекта и настройки конвейера в review.md, чистка architecture.md от поведения с маркерами долга. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл av-dev:doc-canon.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Ведение содержимого канона
|
# Ведение содержимого канона
|
||||||
|
|
||||||
Скилл владеет **содержимым** документов канона; раскладкой владеет `canon`.
|
Скилл владеет **содержимым** документов канона; раскладкой владеет `doc-canon`.
|
||||||
Определение канона и роли документов — [канон](../canon/references/canon.md),
|
Определение канона и роли документов — [канон](../doc-canon/references/canon.md),
|
||||||
здесь не пересказывается.
|
здесь не пересказывается.
|
||||||
|
|
||||||
Главный вызывающий — **шаг синка документации в конвейере задачи**. Конвейер
|
Главный вызывающий — **шаг синка документации в конвейере задачи**: скилл
|
||||||
живёт в другом плагине и зовёт этот скилл по имени; проект без конвейера ведёт
|
`av-dev:code-resolve` зовёт этот по имени. Задачу ведут не конвейером —
|
||||||
документацию тем же скиллом вручную.
|
документация ведётся тем же скиллом вручную.
|
||||||
|
|
||||||
## Правило, из которого всё следует
|
## Правило, из которого всё следует
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ description: Вести содержимое документов канона
|
|||||||
- passport, security, conventions, review — не требуется: изменение внутреннее
|
- passport, security, conventions, review — не требуется: изменение внутреннее
|
||||||
```
|
```
|
||||||
|
|
||||||
## Сверка — не здесь, а в `av-dev-docs:healthcheck`
|
## Сверка — не здесь, а в `av-dev:doc-healthcheck`
|
||||||
|
|
||||||
Синк правит документы поодиночке, а расходятся они **между собой**: факт,
|
Синк правит документы поодиночке, а расходятся они **между собой**: факт,
|
||||||
дописанный в `architecture.md`, уже живёт в `CLAUDE.md`; периметр в
|
дописанный в `architecture.md`, уже живёт в `CLAUDE.md`; периметр в
|
||||||
@@ -63,7 +63,7 @@ description: Вести содержимое документов канона
|
|||||||
и судит это агент `doc-consistency`.
|
и судит это агент `doc-consistency`.
|
||||||
|
|
||||||
**Но синк его не зовёт.** Обоими судьями документов владеет скилл
|
**Но синк его не зовёт.** Обоими судьями документов владеет скилл
|
||||||
`av-dev-docs:healthcheck`, и зовут их на весь канон разом, а не на пачку,
|
`av-dev:doc-healthcheck`, и зовут их на весь канон разом, а не на пачку,
|
||||||
отобранную работой. Причина в цене: `doc-consistency` на `opus` по каждой
|
отобранную работой. Причина в цене: `doc-consistency` на `opus` по каждой
|
||||||
сделанной задаче — самая дорогая церемония процесса, а `doc-code-drift` хоть и на
|
сделанной задаче — самая дорогая церемония процесса, а `doc-code-drift` хоть и на
|
||||||
`sonnet`, но читает репозиторий целиком. К тому же расхождение между двумя
|
`sonnet`, но читает репозиторий целиком. К тому же расхождение между двумя
|
||||||
@@ -80,7 +80,7 @@ description: Вести содержимое документов канона
|
|||||||
**Язык правленого вычитывается тем же прогоном, который его написал, и зовёшь
|
**Язык правленого вычитывается тем же прогоном, который его написал, и зовёшь
|
||||||
агента `doc-wording` ты.** Довод обратный доводу про судей: он читает **только
|
агента `doc-wording` ты.** Довод обратный доводу про судей: он читает **только
|
||||||
названную пачку**, стоит дёшево и ищет ровно то, что портится в момент письма, —
|
названную пачку**, стоит дёшево и ищет ровно то, что портится в момент письма, —
|
||||||
залог, оценку без факта, жаргон, термин без ввода. Ждать `healthcheck` здесь
|
залог, оценку без факта, жаргон, термин без ввода. Ждать `doc-healthcheck` здесь
|
||||||
нечего: через месяц никто уже не помнит, какую фразу имел в виду автор.
|
нечего: через месяц никто уже не помнит, какую фразу имел в виду автор.
|
||||||
|
|
||||||
Позови его **последним шагом правки, до коммита**, отдав список файлов, которых
|
Позови его **последним шагом правки, до коммита**, отдав список файлов, которых
|
||||||
@@ -88,7 +88,7 @@ description: Вести содержимое документов канона
|
|||||||
термин. Находки он отдаёт готовыми формулировками, подставляешь их ты.
|
термин. Находки он отдаёт готовыми формулировками, подставляешь их ты.
|
||||||
|
|
||||||
**Условие вызова — правка, а не синк.** Синк самый частый вызывающий, но не
|
**Условие вызова — правка, а не синк.** Синк самый частый вызывающий, но не
|
||||||
единственный: разведка (`av-dev-code:resolve`, сценарий разведки) пишет ответ по
|
единственный: разведка (`av-dev:code-resolve`, сценарий разведки) пишет ответ по
|
||||||
одному адресу и синком себя не считает намеренно — вычитка ей нужна ровно та же.
|
одному адресу и синком себя не считает намеренно — вычитка ей нужна ровно та же.
|
||||||
Признак один и читается буквально: **документы правились — зови, ничего не правил
|
Признак один и читается буквально: **документы правились — зови, ничего не правил
|
||||||
— не зови**.
|
— не зови**.
|
||||||
@@ -103,14 +103,14 @@ description: Вести содержимое документов канона
|
|||||||
сочиняет заново.
|
сочиняет заново.
|
||||||
|
|
||||||
**Второй законный источник — записка разведки**, и приходит он от скилла
|
**Второй законный источник — записка разведки**, и приходит он от скилла
|
||||||
`av-dev-code:resolve`, сценарий разведки: решение, принятое разведкой (намеренный отказ, выбор
|
`av-dev:code-resolve`, сценарий разведки: решение, принятое разведкой (намеренный отказ, выбор
|
||||||
подхода, «проверили и не делаем»), `design.md` не имеет по построению — change по
|
подхода, «проверили и не делаем»), `design.md` не имеет по построению — change по
|
||||||
нему не будет никогда. Промоут при этом тот же: цитата и ссылка, но на записку.
|
нему не будет никогда. Промоут при этом тот же: цитата и ссылка, но на записку.
|
||||||
Перечень источников закрыт и живёт в [каноне](../canon/references/canon.md),
|
Перечень источников закрыт и живёт в [каноне](../doc-canon/references/canon.md),
|
||||||
раздел `adr/`.
|
раздел `adr/`.
|
||||||
|
|
||||||
**Триггер заведения, форма имени и правило замены — в
|
**Триггер заведения, форма имени и правило замены — в
|
||||||
[каноне](../canon/references/canon.md), раздел `adr/`.** Здесь они не
|
[каноне](../doc-canon/references/canon.md), раздел `adr/`.** Здесь они не
|
||||||
повторяются: копия правила расходится с оригиналом на первой же смене версии
|
повторяются: копия правила расходится с оригиналом на первой же смене версии
|
||||||
канона, а расходится незаметно.
|
канона, а расходится незаметно.
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ description: Вести содержимое документов канона
|
|||||||
|
|
||||||
Обзор не держит поведение — его нормативный дом `openspec/specs/`. **Форма
|
Обзор не держит поведение — его нормативный дом `openspec/specs/`. **Форма
|
||||||
маркера долга и правило «гейт от них не краснеет» — в
|
маркера долга и правило «гейт от них не краснеет» — в
|
||||||
[каноне](../canon/references/canon.md), раздел `architecture.md`.**
|
[каноне](../doc-canon/references/canon.md), раздел `architecture.md`.**
|
||||||
|
|
||||||
Разбирается порциями: раздел вычищает та задача, которая его касается.
|
Разбирается порциями: раздел вычищает та задача, которая его касается.
|
||||||
Содержимое не выбрасывается, а переезжает — требования в дельта-спеку change,
|
Содержимое не выбрасывается, а переезжает — требования в дельта-спеку change,
|
||||||
@@ -136,61 +136,64 @@ description: Вести содержимое документов канона
|
|||||||
|
|
||||||
Наблюдение о внешнем мире: что реально шлёт источник, чем документация формата
|
Наблюдение о внешнем мире: что реально шлёт источник, чем документация формата
|
||||||
расходится с практикой. **Требование провенанса и правило про расходящееся
|
расходится с практикой. **Требование провенанса и правило про расходящееся
|
||||||
число — в [каноне](../canon/references/canon.md), раздел `research/`.**
|
число — в [каноне](../doc-canon/references/canon.md), раздел `research/`.**
|
||||||
|
|
||||||
Твоя часть — заметить, что по ходу задачи узналось новое о внешних данных, и не
|
Твоя часть — заметить, что по ходу задачи узналось новое о внешних данных, и не
|
||||||
дать этому остаться в контексте. Признак: ты правил разбор, опираясь на то, чего
|
дать этому остаться в контексте. Признак: ты правил разбор, опираясь на то, чего
|
||||||
нет ни в одном документе.
|
нет ни в одном документе.
|
||||||
|
|
||||||
## Обращение к соседним плагинам
|
## Чего может не быть
|
||||||
|
|
||||||
Два раздела ниже — запись в `review.md` и промоут в конвенции — берут форму у
|
Два раздела ниже — запись в `review.md` и промоут в конвенции — берут форму у
|
||||||
конвейера ревью: она принадлежит ему, а не канону. Берут **вызовом скилла**, а не
|
конвейера ревью: она принадлежит ему, а не канону. Берут **вызовом скилла**, а не
|
||||||
чтением файла по пути.
|
чтением файла по пути.
|
||||||
|
|
||||||
**Копия.** Дом правила — `shared/plugin-boundary.md` в репозитории плагинов.
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
Правится дом, а не этот файл.
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
месте.
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, `av-dev-tasks:tasks`,
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
`av-dev-code:review`. Короткое имя может разрешиться в устаревшую проектную копию
|
| --- | --- | --- |
|
||||||
из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в поведении.
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт
|
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||||
только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
прочитает его сам.
|
поведении.
|
||||||
|
|
||||||
**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя,
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
Чем оборачивается отсутствие конвейера — в каждом из двух разделов отдельно: без
|
<!-- /копия: отсутствие -->
|
||||||
него работа не отменяется, отменяется только его процедура.
|
|
||||||
|
Здесь это значит: документов канона может не быть вовсе — тогда синка нет, и
|
||||||
|
это исход, а не повод раскладывать документы по своему усмотрению.
|
||||||
|
|
||||||
## Запись в `review.md`
|
## Запись в `review.md`
|
||||||
|
|
||||||
Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку
|
Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку
|
||||||
конвейера. **Что в каком и в какой форме — в
|
конвейера. **Что в каком и в какой форме — в
|
||||||
[каноне](../canon/references/canon.md), раздел `review.md`**; подробности формы
|
[каноне](../doc-canon/references/canon.md), раздел `review.md`**; подробности формы
|
||||||
записи и выбор адреса, куда она ведёт, — у конвейера ревью проекта (при
|
записи и выбор адреса, куда она ведёт, — у конвейера ревью: `Skill
|
||||||
`av-dev-code` — `Skill av-dev-code:review`, его
|
av-dev:code-review`, его `references/review-journal.md`.
|
||||||
`references/review-journal.md`). **Конвейера в проекте нет** — пиши по форме из
|
|
||||||
скелета `review.md`, которую положил канон, и скажи в докладе, что подробностей
|
|
||||||
формы взять негде.
|
|
||||||
|
|
||||||
Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим».
|
Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим».
|
||||||
Со временем теряется не факт, а то, почему дефект не поймали, — единственное,
|
Со временем теряется не факт, а то, почему дефект не поймали, — единственное,
|
||||||
@@ -200,10 +203,11 @@ description: Вести содержимое документов канона
|
|||||||
## Промоут в конвенции
|
## Промоут в конвенции
|
||||||
|
|
||||||
Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком
|
Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком
|
||||||
принадлежит конвейеру ревью проекта (при `av-dev-code` — его
|
принадлежит конвейеру ревью — его `references/promote.md`, читается через
|
||||||
`references/promote.md`, читается через `Skill av-dev-code:review`);
|
`Skill av-dev:code-review`; роль каталога конвенций — в
|
||||||
роль каталога конвенций — в [каноне](../canon/references/canon.md). **Конвейера
|
[каноне](../doc-canon/references/canon.md). **Прогон идёт вне конвейера**
|
||||||
нет** — три шага всё равно твои, просто без его процедуры: сформулируй правило,
|
(находку принесли руками) — три шага всё равно твои, просто без его процедуры:
|
||||||
|
сформулируй правило,
|
||||||
поищи, чем оно механизируется, и вычеркни прозу, если механизировалось.
|
поищи, чем оно механизируется, и вычеркни прозу, если механизировалось.
|
||||||
|
|
||||||
Твоя часть — **третий шаг, который пропускают чаще всего**: правило заработало,
|
Твоя часть — **третий шаг, который пропускают чаще всего**: правило заработало,
|
||||||
@@ -213,8 +217,8 @@ description: Вести содержимое документов канона
|
|||||||
|
|
||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
- **Не проверяет раскладку** — это `canon`.
|
- **Не проверяет раскладку** — это `doc-canon`.
|
||||||
- **Не заводит недостающие документы** — их скелет кладёт `canon adopt` или
|
- **Не заводит недостающие документы** — их скелет кладёт `doc-canon adopt` или
|
||||||
`init`.
|
`doc-init`.
|
||||||
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
|
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
|
||||||
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.
|
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: groom
|
name: task-groom
|
||||||
description: "Груминг беклога — интерактивный разбор, отвечающий на два вопроса: что сейчас самое важное и что перестало быть важным. Ответ записывается порядком строк в беклоге: первая строка — то, что делают следующим. Разбирает накопившиеся вопросы, переоценивает задачи порциями по 5–8 (сделано попутно, отменено решением, слилось с соседней, подешевело, разрослось, стало сырьём), закрывает отжившее с причиной и расставляет очередь. Использовать, когда просят разобрать беклог, расставить приоритеты, решить «что делать дальше», провести груминг или переоценку, а также когда вернулись к проекту после перерыва и надо понять, где остановились. Формат и содержимое записей — скилл tasks; выполнение задачи — конвейер проекта."
|
description: "Груминг беклога — интерактивный разбор, отвечающий на два вопроса: что сейчас самое важное и что перестало быть важным. Ответ записывается порядком строк в беклоге: первая строка — то, что делают следующим. Разбирает накопившиеся вопросы, переоценивает задачи порциями по 5–8 (сделано попутно, отменено решением, слилось с соседней, подешевело, разрослось, стало сырьём), закрывает отжившее с причиной и расставляет очередь. Использовать, когда просят разобрать беклог, расставить приоритеты, решить «что делать дальше», провести груминг или переоценку, а также когда вернулись к проекту после перерыва и надо понять, где остановились. Формат и содержимое записей — скилл av-dev:task-track; выполнение задачи — конвейер проекта."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Груминг: что важно, что перестало
|
# Груминг: что важно, что перестало
|
||||||
@@ -13,7 +13,7 @@ description: "Груминг беклога — интерактивный ра
|
|||||||
Ответ на оба **записывается порядком строк в беклоге**: первая строка секции —
|
Ответ на оба **записывается порядком строк в беклоге**: первая строка секции —
|
||||||
то, что делают следующим; то, что перестало быть важным, из беклога уходит с
|
то, что делают следующим; то, что перестало быть важным, из беклога уходит с
|
||||||
причиной. Приоритет — свойство очереди, а не задачи, и живёт он в индексе
|
причиной. Приоритет — свойство очереди, а не задачи, и живёт он в индексе
|
||||||
(правило 4 скилла `tasks`). Груминг — единственное место, где очередь
|
(правило 4 скилла `task-track`). Груминг — единственное место, где очередь
|
||||||
назначается человеком.
|
назначается человеком.
|
||||||
|
|
||||||
**Скилл интерактивный.** Он не «приводит беклог в порядок» сам: суждение о
|
**Скилл интерактивный.** Он не «приводит беклог в порядок» сам: суждение о
|
||||||
@@ -21,7 +21,7 @@ description: "Груминг беклога — интерактивный ра
|
|||||||
рекомендацией. Что решается фактом (сделано, отменено, дублируется), решается
|
рекомендацией. Что решается фактом (сделано, отменено, дублируется), решается
|
||||||
без вопросов и показывается списком.
|
без вопросов и показывается списком.
|
||||||
|
|
||||||
Форматом и содержимым записей владеет скилл `tasks` — груминг зовёт его
|
Форматом и содержимым записей владеет скилл `task-track` — груминг зовёт его
|
||||||
операции, а не правит файлы руками. Выполнением задачи — конвейер проекта.
|
операции, а не правит файлы руками. Выполнением задачи — конвейер проекта.
|
||||||
|
|
||||||
## Три правила, из которых всё следует
|
## Три правила, из которых всё следует
|
||||||
@@ -29,7 +29,7 @@ description: "Груминг беклога — интерактивный ра
|
|||||||
1. **Порядок назначает человек, машина его не выводит.** Ни давность, ни тип, ни
|
1. **Порядок назначает человек, машина его не выводит.** Ни давность, ни тип, ни
|
||||||
число задач под целью приоритетом не являются. Единственное место в очереди,
|
число задач под целью приоритетом не являются. Единственное место в очереди,
|
||||||
назначенное не человеком, — конец секции у сырья, и оно из очереди изъято
|
назначенное не человеком, — конец секции у сырья, и оно из очереди изъято
|
||||||
(`tasks`, правило 4).
|
(`task-track`, правило 4).
|
||||||
2. **Порция важнее охвата.** Тридцать задач за заход — это усталость и
|
2. **Порция важнее охвата.** Тридцать задач за заход — это усталость и
|
||||||
штамповка: последние десять получат «оставить» не потому, что живы, а потому,
|
штамповка: последние десять получат «оставить» не потому, что живы, а потому,
|
||||||
что разбор затянулся. Лучше две честные порции, чем один полный проход.
|
что разбор затянулся. Лучше две честные порции, чем один полный проход.
|
||||||
@@ -159,13 +159,13 @@ flowchart TD
|
|||||||
## Документы устаревают тем же ходом работы
|
## Документы устаревают тем же ходом работы
|
||||||
|
|
||||||
Груминг судит **задачи**, а не документы, и агентов канона не зовёт: они
|
Груминг судит **задачи**, а не документы, и агентов канона не зовёт: они
|
||||||
принадлежат плагину `av-dev-docs`, и когда их звать — решает он.
|
принадлежат скиллам документации, и когда их звать — решают они.
|
||||||
|
|
||||||
Но повод назвать это здесь есть: беклог и документы протухают от одного и того
|
Но повод назвать это здесь есть: беклог и документы протухают от одного и того
|
||||||
же — от сделанной работы. Пришёл на груминг и видишь, что с прошлого раза сделан
|
же — от сделанной работы. Пришёл на груминг и видишь, что с прошлого раза сделан
|
||||||
десяток задач, — скажи строкой, что документы стоит сверить
|
десяток задач, — скажи строкой, что документы стоит сверить
|
||||||
(`av-dev-docs:healthcheck`), и иди дальше. Плагина в проекте нет — сверять нечем,
|
(`av-dev:doc-healthcheck`), и иди дальше. Документов канона в проекте нет —
|
||||||
и это тоже строка.
|
сверять нечем, и это тоже строка.
|
||||||
|
|
||||||
## Интерактив
|
## Интерактив
|
||||||
|
|
||||||
@@ -190,8 +190,8 @@ flowchart TD
|
|||||||
тот же агент, который её и сделал. Груминг приёмкой не занимается — отдельного
|
тот же агент, который её и сделал. Груминг приёмкой не занимается — отдельного
|
||||||
ритуала у неё нет, — и настоящих опор остаётся две:
|
ритуала у неё нет, — и настоящих опор остаётся две:
|
||||||
|
|
||||||
- **независимый отчёт ревью** — артефакт, написанный не исполнителем; при
|
- **независимый отчёт ревью** — артефакт, написанный не исполнителем: отчёт
|
||||||
конвейере `av-dev-code` это отчёт триажа в
|
триажа в
|
||||||
`openspec/changes/archive/<id>/review/` (до архивации — `changes/<id>/review/`);
|
`openspec/changes/archive/<id>/review/` (до архивации — `changes/<id>/review/`);
|
||||||
- **`reopen <слаг> --reason`** — закрытие не окончательно. Заметил на груминге,
|
- **`reopen <слаг> --reason`** — закрытие не окончательно. Заметил на груминге,
|
||||||
что закрытая задача сделана не тем, чем обещала, — возвращай, это штатная
|
что закрытая задача сделана не тем, чем обещала, — возвращай, это штатная
|
||||||
@@ -209,7 +209,7 @@ flowchart TD
|
|||||||
- **Расставить порядок молча**, без доводов: тогда через месяц он неотличим от
|
- **Расставить порядок молча**, без доводов: тогда через месяц он неотличим от
|
||||||
случайного. Защита: причина у каждого движения и строка доклада.
|
случайного. Защита: причина у каждого движения и строка доклада.
|
||||||
- **Разобрать много и мелко** вместо немногого и важного: тридцать полей гигиены
|
- **Разобрать много и мелко** вместо немногого и важного: тридцать полей гигиены
|
||||||
вместо трёх решений о важности. Защита: гигиена — работа скилла `tasks` и
|
вместо трёх решений о важности. Защита: гигиена — работа скилла `task-track` и
|
||||||
побочный продукт здесь; доклад называет **решения**, а не правки.
|
побочный продукт здесь; доклад называет **решения**, а не правки.
|
||||||
|
|
||||||
## Слоты проекта
|
## Слоты проекта
|
||||||
@@ -218,7 +218,7 @@ flowchart TD
|
|||||||
|
|
||||||
1. **Что считается сломанным** — какая красная проверка обгоняет развитие.
|
1. **Что считается сломанным** — какая красная проверка обгоняет развитие.
|
||||||
Не названо — спрашиваем человека, а не решаем сами.
|
Не названо — спрашиваем человека, а не решаем сами.
|
||||||
2. **Необратимое** — что спрашивается всегда (тот же слот, что у скилла `tasks`;
|
2. **Необратимое** — что спрашивается всегда (тот же слот, что у скилла `task-track`;
|
||||||
дом один).
|
дом один).
|
||||||
3. **Ориентир по размеру порции**, если он замерялся. Умолчание — 5–8 задач, и
|
3. **Ориентир по размеру порции**, если он замерялся. Умолчание — 5–8 задач, и
|
||||||
это **ориентир, а не закон**.
|
это **ориентир, а не закон**.
|
||||||
@@ -241,7 +241,7 @@ flowchart TD
|
|||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
Не пишет код и не выполняет задачи. Не заводит и не переоформляет записи сам по
|
Не пишет код и не выполняет задачи. Не заводит и не переоформляет записи сам по
|
||||||
себе — формат и содержимое ведёт `tasks` (груминг зовёт его операции). Не решает
|
себе — формат и содержимое ведёт `task-track` (груминг зовёт его операции). Не решает
|
||||||
за человека, что важно: он готовит развилки и рекомендует. Не принимает
|
за человека, что важно: он готовит развилки и рекомендует. Не принимает
|
||||||
закрытые задачи отдельным ритуалом — `reopen` есть, момента у него нет. Не судит
|
закрытые задачи отдельным ритуалом — `reopen` есть, момента у него нет. Не судит
|
||||||
документы проекта — это плагин `av-dev-docs`.
|
документы проекта — это скиллы `av-dev:doc-canon` и `av-dev:doc-healthcheck`.
|
||||||
+4
-4
@@ -19,8 +19,8 @@
|
|||||||
4. **Запиши ответ в тело задачи, опустоши раздел «Вопросы»**, сними тег
|
4. **Запиши ответ в тело задачи, опустоши раздел «Вопросы»**, сними тег
|
||||||
(`edit <slug> --rm-tag question`), **перепиши «зачем»**: «Решено: …» на вопрос
|
(`edit <slug> --rm-tag question`), **перепиши «зачем»**: «Решено: …» на вопрос
|
||||||
«почему это лежит в беклоге» уже не отвечает. Опустошение раздела — не
|
«почему это лежит в беклоге» уже не отвечает. Опустошение раздела — не
|
||||||
уборка, а условие взятия: правило и причина в скилле `tasks`,
|
уборка, а условие взятия: правило и причина в скилле `task-track`,
|
||||||
[references/task-format.md](../../tasks/references/task-format.md).
|
[references/task-format.md](../../task-track/references/task-format.md).
|
||||||
|
|
||||||
**Вопросы на верхних строках очереди разбираются вне очереди порции** — здесь
|
**Вопросы на верхних строках очереди разбираются вне очереди порции** — здесь
|
||||||
же, даже если сама задача в порцию переоценки не попала. Иначе правило «задача с
|
же, даже если сама задача в порцию переоценки не попала. Иначе правило «задача с
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
5. **Гигиена полей** — протухшее «зачем», вопрос в прозе, снятый ответ, свойство
|
5. **Гигиена полей** — протухшее «зачем», вопрос в прозе, снятый ответ, свойство
|
||||||
репозитория в рамках, предписание процесса в теле, тип, разошедшийся с
|
репозитория в рамках, предписание процесса в теле, тип, разошедшийся с
|
||||||
задачей, границы вместо реализации в разделе «Затрагивает». Список и правила —
|
задачей, границы вместо реализации в разделе «Затрагивает». Список и правила —
|
||||||
в скилле `tasks`. **Груминг — то самое место, где беклог добирает тип и
|
в скилле `task-track`. **Груминг — то самое место, где беклог добирает тип и
|
||||||
разделы его схемы:** требовать их на входе значило бы выгонять в заметки то,
|
разделы его схемы:** требовать их на входе значило бы выгонять в заметки то,
|
||||||
что должно лежать задачей, а к взятию в работу они уже обязательны (`ready`).
|
что должно лежать задачей, а к взятию в работу они уже обязательны (`ready`).
|
||||||
Блок здоровья `check` печатает, сколько записей готово к взятию, — по этому
|
Блок здоровья `check` печатает, сколько записей готово к взятию, — по этому
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую
|
задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую
|
||||||
либо закрыть своей причиной, либо перевесить на другую цель, и только потом
|
либо закрыть своей причиной, либо перевесить на другую цель, и только потом
|
||||||
закрыть цель. Порядок и почему он такой —
|
закрыть цель. Порядок и почему он такой —
|
||||||
[task-goal.md](../../tasks/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель).
|
[task-goal.md](../../task-track/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель).
|
||||||
8. **Задача ли это по-прежнему.** Не проходит `ready` по существу, а не по
|
8. **Задача ли это по-прежнему.** Не проходит `ready` по существу, а не по
|
||||||
недописанным разделам → `edit <slug> --type research` и опустошённый раздел
|
недописанным разделам → `edit <slug> --type research` и опустошённый раздел
|
||||||
«Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач под
|
«Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач под
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: tasks
|
name: task-track
|
||||||
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Он же повышает каталог до текущей версии формата по своему журналу версий, когда tasks.py check говорит, что каталог отстал. Расстановка приоритетов и разбор накопившегося — скилл groom. Не реализует задачи — этим занимается скилл решения задачи.
|
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Каталог отстал от версии раскладки — это скажет tasks.py check, а повышает проект скилл av-dev:doc-canon по общему журналу версий. Расстановка приоритетов и разбор накопившегося — скилл av-dev:task-groom. Не реализует задачи — этим занимается скилл решения задачи.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Задачи
|
# Задачи
|
||||||
@@ -10,7 +10,7 @@ description: Ведение задач и целей как каталога mar
|
|||||||
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
|
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
|
||||||
|
|
||||||
Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть
|
Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть
|
||||||
важным, решает скилл `groom`, а этот скилл лишь даёт ему операции; и выполнением
|
важным, решает скилл `task-groom`, а этот скилл лишь даёт ему операции; и выполнением
|
||||||
задачи — это конвейер проекта.
|
задачи — это конвейер проекта.
|
||||||
|
|
||||||
## Шесть правил, из которых всё следует
|
## Шесть правил, из которых всё следует
|
||||||
@@ -72,8 +72,8 @@ description: Ведение задач и целей как каталога mar
|
|||||||
## Раскладка
|
## Раскладка
|
||||||
|
|
||||||
Каталог задач — **`tasks/` в корне репозитория, жёстко.** Он принадлежит этому
|
Каталог задач — **`tasks/` в корне репозитория, жёстко.** Он принадлежит этому
|
||||||
плагину, а не канону документов: `docs/` ведёт другой плагин (`av-dev-docs`), и
|
скиллу, а не канону документов: учёт работ ведут и в проекте, который к канону
|
||||||
проект, поставивший учёт работ без него, каталога `docs/` не имеет вовсе. Внутри
|
не приведён, и каталога `docs/` там нет вовсе. Внутри
|
||||||
`docs/` задачи лежали до версии канона 11; непереехавший проект скрипт
|
`docs/` задачи лежали до версии канона 11; непереехавший проект скрипт
|
||||||
по-прежнему находит, но новый заводит только в корне.
|
по-прежнему находит, но новый заводит только в корне.
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ stateDiagram-v2
|
|||||||
часть кода мы трогаем».
|
часть кода мы трогаем».
|
||||||
|
|
||||||
**Целью не становится работа, которой держат проект.** Состав перечислен
|
**Целью не становится работа, которой держат проект.** Состав перечислен
|
||||||
[в словаре сопровождения](references/operations.md);
|
[в словаре сопровождения](../../shared/operations.md);
|
||||||
на вопрос «что приложение будет уметь» ничто из него не отвечает. Им отведена отдельная секция роадмапа,
|
на вопрос «что приложение будет уметь» ничто из него не отвечает. Им отведена отдельная секция роадмапа,
|
||||||
чтобы они были видны в том же экране и при этом не читались как возможности
|
чтобы они были видны в том же экране и при этом не читались как возможности
|
||||||
продукта.
|
продукта.
|
||||||
@@ -223,11 +223,10 @@ stateDiagram-v2
|
|||||||
|
|
||||||
**Сопровождение и эксплуатация — целое и часть**, а не синонимы, и та же тема
|
**Сопровождение и эксплуатация — целое и часть**, а не синонимы, и та же тема
|
||||||
живёт ещё в двух местах: разделе «Эксплуатация» в `architecture.md` и теме ревью
|
живёт ещё в двух местах: разделе «Эксплуатация» в `architecture.md` и теме ревью
|
||||||
`operations`. Словарь у всех трёх общий, дом у него один — `shared/operations.md`
|
`operations`. Словарь у всех трёх общий, и дом у него один:
|
||||||
в репозитории плагинов, — а здесь лежит дословная копия:
|
[shared/operations.md](../../shared/operations.md) — читается по ссылке.
|
||||||
[references/operations.md](references/operations.md). Пересказывать его своими
|
Пересказывать его своими словами нельзя: три перечня «чем держат проект» уже
|
||||||
словами нельзя: три перечня «чем держат проект» уже разъезжались на «метриках и
|
разъезжались на «метриках и логах» против «мониторинга».
|
||||||
логах» против «мониторинга».
|
|
||||||
|
|
||||||
Секция выбирается так: очередь значима и обоснована прозой — `Запланировано`;
|
Секция выбирается так: очередь значима и обоснована прозой — `Запланировано`;
|
||||||
тянется долго и очереди не имеет — `Направления`; не про приложение, а про то,
|
тянется долго и очереди не имеет — `Направления`; не про приложение, а про то,
|
||||||
@@ -303,7 +302,7 @@ stateDiagram-v2
|
|||||||
проверять.
|
проверять.
|
||||||
|
|
||||||
**Одно исполнителю тип всё же говорит — каким сценарием работу вести, и то не
|
**Одно исполнителю тип всё же говорит — каким сценарием работу вести, и то не
|
||||||
один.** В плагине `av-dev-code` скилл `resolve` выбирает сценарий связкой из двух
|
один.** Скилл `av-dev:code-resolve` выбирает сценарий связкой из двух
|
||||||
признаков: тип **предлагает** (`chore` — обслуживание, `research` — разведка),
|
признаков: тип **предлагает** (`chore` — обслуживание, `research` — разведка),
|
||||||
а подтверждает его предмет работы — есть ли что менять в спеках. Признаки
|
а подтверждает его предмет работы — есть ли что менять в спеках. Признаки
|
||||||
разошлись — работа останавливается, и тип меняется здесь, командой `edit --type`,
|
разошлись — работа останавливается, и тип меняется здесь, командой `edit --type`,
|
||||||
@@ -354,11 +353,10 @@ stateDiagram-v2
|
|||||||
**Предметно, но без усложнения.** Текст задачи читает человек, который решает,
|
**Предметно, но без усложнения.** Текст задачи читает человек, который решает,
|
||||||
брать её или нет, и делает это по строке индекса и одному экрану тела.
|
брать её или нет, и делает это по строке индекса и одному экрану тела.
|
||||||
|
|
||||||
Язык — общий для всех проектных текстов, и дом у него один,
|
Язык — общий для всех проектных текстов, и дом у него один:
|
||||||
`shared/language.md` в репозитории плагинов; здесь лежит дословная копия:
|
[shared/language.md](../../shared/language.md) — информационный стиль,
|
||||||
[references/language.md](references/language.md) (информационный стиль,
|
|
||||||
применённый к задачам и документам канона; там же таблицы англицизмов и жаргона
|
применённый к задачам и документам канона; там же таблицы англицизмов и жаргона
|
||||||
и то, что из стиля отброшено намеренно). Задаче он даёт четыре требования,
|
и то, что из стиля отброшено намеренно. Задаче он даёт четыре требования,
|
||||||
которые нарушаются чаще прочих:
|
которые нарушаются чаще прочих:
|
||||||
|
|
||||||
- **глагол вместо отглагольного существительного**: «обработчик не проверяет
|
- **глагол вместо отглагольного существительного**: «обработчик не проверяет
|
||||||
@@ -381,7 +379,7 @@ stateDiagram-v2
|
|||||||
|
|
||||||
## Инструмент (`tasks.py`)
|
## Инструмент (`tasks.py`)
|
||||||
|
|
||||||
Пусть `tk="$CLAUDE_PLUGIN_ROOT/skills/tasks/scripts/tasks.py"`, а `D` —
|
Пусть `tk="$CLAUDE_PLUGIN_ROOT/skills/task-track/scripts/tasks.py"`, а `D` —
|
||||||
`tasks` от корня проекта. `--dir` стоит в примерах намеренно: вызов из
|
`tasks` от корня проекта. `--dir` стоит в примерах намеренно: вызов из
|
||||||
подкаталога — обычное дело.
|
подкаталога — обычное дело.
|
||||||
|
|
||||||
@@ -407,7 +405,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
| 0 | сошлось / сделано | дальше по сценарию |
|
| 0 | сошлось / сделано | дальше по сценарию |
|
||||||
| 1 | **только `check`:** найден дрейф индексов и файлов | `check --fix`, остаток разобрать |
|
| 1 | **только `check`:** найден дрейф индексов и файлов | `check --fix`, остаток разобрать |
|
||||||
| 2 | ошибка употребления: аргументы или нарушенное правило | читать сообщение, это отказ по существу |
|
| 2 | ошибка употребления: аргументы или нарушенное правило | читать сообщение, это отказ по существу |
|
||||||
| 3 | окружение: каталог не найден, конфиг битый или мимо диска | чинится путём или `<каталог задач>/.tasks.json`, повтор не поможет |
|
| 3 | окружение: каталог не найден, конфиг битый или мимо диска | чинится путём или `.av-dev.toml` в корне, повтор не поможет |
|
||||||
| 4 | внутренний сбой | дефект скрипта, доложить |
|
| 4 | внутренний сбой | дефект скрипта, доложить |
|
||||||
|
|
||||||
Различать 1 и 3 обязательно: «дрейф в беклоге» — рабочая ситуация, «каталога
|
Различать 1 и 3 обязательно: «дрейф в беклоге» — рабочая ситуация, «каталога
|
||||||
@@ -494,38 +492,24 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
[fix](references/task-fix.md) · [chore](references/task-chore.md) ·
|
[fix](references/task-fix.md) · [chore](references/task-chore.md) ·
|
||||||
[research](references/task-research.md).
|
[research](references/task-research.md).
|
||||||
|
|
||||||
## Версия формата
|
## Версия раскладки
|
||||||
|
|
||||||
Формат каталога задач меняется, и проект должен знать, к какой его версии
|
Формат каталога задач меняется, и проект должен знать, к какой версии он
|
||||||
приведён. Число живёт ключом `tasks` в `<каталог задач>/.tasks.json`, журнал
|
приведён. Число живёт ключом `version` в `.av-dev.toml` в корне репозитория,
|
||||||
версий — [references/changelog.md](references/changelog.md), сверяет их
|
журнал версий — [журнал скилла `doc-canon`](../doc-canon/references/changelog.md),
|
||||||
`tasks.py check`: отстало — строка расхождения, ушло вперёд — устарел плагин.
|
сверяет их `tasks.py check`: отстало — строка расхождения, ушло вперёд — устарел
|
||||||
|
плагин. Обратной совместимости нет: есть «приведён» и «не приведён».
|
||||||
|
|
||||||
**Версия своя, а не канона документов.** Плагин ставится в одиночку: проект,
|
**Версия одна на всю раскладку — и на документы, и на задачи.** Своя у каталога
|
||||||
взявший учёт работ без `av-dev-docs`, каталога `docs/` не имеет вовсе, а значит
|
задач была, пока плагинов было три и ставились они порознь: проект мог взять
|
||||||
не имеет и версии канона — сверять было бы не с чем. Обратной совместимости у
|
учёт работ без канона документов, и общее число оказалось бы домом, которого у
|
||||||
формата нет: есть «приведён» и «не приведён».
|
половины проектов нет. Плагин один — довод ушёл, а два числа оставляли бы
|
||||||
|
вопрос, по какому журналу повышать.
|
||||||
|
|
||||||
**`upgrade` — повысить каталог до текущего формата:**
|
**Повышает проект скилл `av-dev:doc-canon`, операция `upgrade`** — он идёт по
|
||||||
|
журналу, двигает число и зовёт этот скилл там, где запись касается задач. Здесь
|
||||||
1. `python3 $tk check --dir D` — первая же строка расхождений называет версию
|
повышения нет намеренно: две операции, двигающие одно число, разъезжаются на
|
||||||
проекта и версию скрипта. Проект новее скрипта — **обнови маркетплейс**, а не
|
первом же проекте, где прошла только одна из них.
|
||||||
проект: это отстал плагин.
|
|
||||||
2. Иди по [журналу](references/changelog.md) снизу вверх от версии проекта до
|
|
||||||
текущей и делай названное в каждой записи. Записи независимы и применяются по
|
|
||||||
порядку.
|
|
||||||
3. Подними `tasks` в `.tasks.json` до текущей — руками, последним шагом. Раньше
|
|
||||||
времени поднятое число объявляет каталог приведённым к формату, шагов
|
|
||||||
которого никто не делал; `check --fix` этого не пишет намеренно.
|
|
||||||
4. `check --dir D` ещё раз — до отсутствия расхождений.
|
|
||||||
|
|
||||||
Записи журнала описывают **что сделать проекту**. Если запись этого не говорит —
|
|
||||||
это дефект журнала, и о нём надо сказать, а не догадываться.
|
|
||||||
|
|
||||||
**Канон документов сюда не вмешивается.** Его журнал двигает своё число в
|
|
||||||
`docs/.docs.json` и вправе сказать «позови этот скилл», но не двигать версию
|
|
||||||
формата задач: две версии, ходящие по одному журналу, разъедутся на первом же
|
|
||||||
проекте, где стоит один плагин без другого.
|
|
||||||
|
|
||||||
## Сценарии
|
## Сценарии
|
||||||
|
|
||||||
@@ -581,7 +565,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
**одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу.
|
**одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу.
|
||||||
|
|
||||||
Если переводить надо не только задачи, а весь `docs/` — это скилл
|
Если переводить надо не только задачи, а весь `docs/` — это скилл
|
||||||
`av-dev-docs:canon`, и он зовёт этот сценарий сам на своём шаге.
|
`av-dev:doc-canon`, и он зовёт этот сценарий сам на своём шаге.
|
||||||
|
|
||||||
### Декомпозиция и штурм сырья
|
### Декомпозиция и штурм сырья
|
||||||
|
|
||||||
@@ -626,7 +610,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
Зовутся они **пачкой, а не на каждую запись**: после заведения нескольких задач,
|
Зовутся они **пачкой, а не на каждую запись**: после заведения нескольких задач,
|
||||||
после разбора находок ревью, после того как чужая работа уточнила записи (так
|
после разбора находок ревью, после того как чужая работа уточнила записи (так
|
||||||
делает разведка в `av-dev-code:resolve`), и на переоценке. Передаётся список файлов и — если
|
делает разведка в `av-dev:code-resolve`), и на переоценке. Передаётся список файлов и — если
|
||||||
есть — паспорт, архитектура и конвенции проекта: по ним отличается неизвестный
|
есть — паспорт, архитектура и конвенции проекта: по ним отличается неизвестный
|
||||||
термин от известного.
|
термин от известного.
|
||||||
|
|
||||||
@@ -686,22 +670,20 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
- **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда:
|
- **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда:
|
||||||
раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога
|
раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога
|
||||||
нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект
|
нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект
|
||||||
действительно новый, а перевод чужой раскладки делает `av-dev-docs:canon`.
|
действительно новый, а перевод чужой раскладки делает `av-dev:doc-canon`.
|
||||||
У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и
|
У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и
|
||||||
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
|
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
|
||||||
- **Версия формата и настройки живут в `<каталог задач>/.tasks.json`** — свой
|
- **Версия и настройки живут в `.av-dev.toml` в корне репозитория** — версия
|
||||||
файл у своего плагина: ключ `tasks` с версией формата плюс **имена** файлов и
|
ключом `version`, настройки каталога секцией `[tasks]`: `dir` — где каталог
|
||||||
заголовков, и последние — только если отличаются от умолчания. Неизвестный
|
лежит, плюс **имена** файлов и заголовков, и последние только если отличаются
|
||||||
ключ — код 3 на любой команде, так что лишнее слово в этом объекте
|
от умолчания. Неизвестный ключ в секции — код 3 на любой команде, так что
|
||||||
останавливает работу с задачами целиком.
|
лишнее слово останавливает работу с задачами целиком.
|
||||||
|
|
||||||
Дом именно свой, а не `docs/.docs.json`, потому что `docs/` принадлежит
|
Дом в корне, а не внутри каталога задач, по двум причинам: настройка, лежащая
|
||||||
плагину канона: проект, поставивший учёт работ без него, каталога `docs/` не
|
внутри настраиваемого каталога, не смогла бы сказать, **где он**; и версия
|
||||||
имеет вовсе. Прежний ключ `tasks` в `docs/.pm.json` читается, **только когда
|
одна на весь плагин, а корень есть и у проекта без `docs/`. Прежние
|
||||||
своего файла нет** — для проектов, заведённых до раскола плагинов; скрипт при
|
`<каталог задач>/.tasks.json` и `docs/.docs.json` не читаются — увидев их,
|
||||||
этом говорит замечанием, куда его перенести. Есть оба — побеждает свой, и об
|
скрипт говорит «прежняя раскладка» и зовёт `upgrade`.
|
||||||
этом тоже говорится вслух: молча выбранный из двух конфиг это дрейф. Версию
|
|
||||||
прежний дом не знает и знать не может — она читается только из своего файла.
|
|
||||||
- **Секции беклога** берутся из заголовков `##` индекса как есть; их количество
|
- **Секции беклога** берутся из заголовков `##` индекса как есть; их количество
|
||||||
и названия — дело проекта (умолчание `Ядро` / `Инфра`). **В конфиге их нет** —
|
и названия — дело проекта (умолчание `Ядро` / `Инфра`). **В конфиге их нет** —
|
||||||
второй список разошёлся бы с заголовками молча.
|
второй список разошёлся бы с заголовками молча.
|
||||||
@@ -713,13 +695,13 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
переменной не дотянутся. Мост — **вызов скилла через пространство имён**, а не
|
переменной не дотянутся. Мост — **вызов скилла через пространство имён**, а не
|
||||||
путь:
|
путь:
|
||||||
|
|
||||||
> Чужой контекст зовёт `Skill av-dev-tasks:tasks` и называет, что нужно сделать
|
> Чужой контекст зовёт `Skill av-dev:task-track` и называет, что нужно сделать
|
||||||
> («закрой задачу `<слаг>`, реализована»). Скилл разрешает свой
|
> («закрой задачу `<слаг>`, реализована»). Скилл разрешает свой
|
||||||
> `$CLAUDE_PLUGIN_ROOT` сам. Путь наружу не выносится вовсе.
|
> `$CLAUDE_PLUGIN_ROOT` сам. Путь наружу не выносится вовсе.
|
||||||
|
|
||||||
Плагина в проекте нет — вызов не разрешится, и вызывающий **не выдумывает путь и
|
Каталога задач в проекте нет — вызывающий **не выдумывает путь и не правит
|
||||||
не правит индекс руками**, а сообщает в докладе, что учёт задач остаётся за
|
индекс руками**, а сообщает в докладе, что учёт остаётся за владельцем. Скилл
|
||||||
владельцем.
|
при этом разрешится: он в том же плагине, что и вызывающий.
|
||||||
|
|
||||||
## Слоты проекта
|
## Слоты проекта
|
||||||
|
|
||||||
@@ -758,6 +740,6 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в
|
Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в
|
||||||
работу — этим занимается конвейер проекта. **Не ведёт очередь:** что делать
|
работу — этим занимается конвейер проекта. **Не ведёт очередь:** что делать
|
||||||
следующим и что перестало быть важным — скилл `groom`, а этот даёт ему операции.
|
следующим и что перестало быть важным — скилл `task-groom`, а этот даёт ему операции.
|
||||||
Не решает за пользователя, что важно. Не
|
Не решает за пользователя, что важно. Не
|
||||||
переоформляет существующие задачи «заодно»: правится то, чего касается операция.
|
переоформляет существующие задачи «заодно»: правится то, чего касается операция.
|
||||||
+5
-5
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
Проект, где задачи уже как-то ведутся, и из имеющегося материала **выводится**
|
Проект, где задачи уже как-то ведутся, и из имеющегося материала **выводится**
|
||||||
заполненный каталог задач: цели, задачи, кладбище, индексы. Операция разовая —
|
заполненный каталог задач: цели, задачи, кладбище, индексы. Операция разовая —
|
||||||
после неё проект живёт скиллами `tasks` и `groom`.
|
после неё проект живёт скиллами `task-track` и `task-groom`.
|
||||||
|
|
||||||
**Это часть приведения проекта к канону.** Раскладку `docs/` целиком ведёт скилл
|
**Это часть приведения проекта к канону.** Раскладку `docs/` целиком ведёт скилл
|
||||||
`av-dev-docs:canon`; он же зовёт этот сценарий на шаге «каталог задач», потому что
|
`av-dev:doc-canon`; он же зовёт этот сценарий на шаге «каталог задач», потому что
|
||||||
форматом задач владеет `tasks`, а не `canon`. Отдельно сценарий вызывается,
|
форматом задач владеет `task-track`, а не `doc-canon`. Отдельно сценарий вызывается,
|
||||||
когда переводить надо **только** задачи.
|
когда переводить надо **только** задачи.
|
||||||
|
|
||||||
Вход какой угодно: старая раскладка `av-dev-backlog` (индекс `README.md`,
|
Вход какой угодно: старая раскладка `av-dev-backlog` (индекс `README.md`,
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
«машина умеет / не умеет»:
|
«машина умеет / не умеет»:
|
||||||
|
|
||||||
```
|
```
|
||||||
tk="$CLAUDE_PLUGIN_ROOT/skills/tasks/scripts/tasks.py"
|
tk="$CLAUDE_PLUGIN_ROOT/skills/task-track/scripts/tasks.py"
|
||||||
|
|
||||||
python3 $tk adopt scan --from docs/backlog docs/plan.md TODO.md \
|
python3 $tk adopt scan --from docs/backlog docs/plan.md TODO.md \
|
||||||
--target tasks --out tasks-adopt-plan.json # только чтение
|
--target tasks --out tasks-adopt-plan.json # только чтение
|
||||||
@@ -66,7 +66,7 @@ python3 $tk adopt apply --plan tasks-adopt-plan.json \
|
|||||||
всегда `tasks`. Секции беклога (`--sections`) — по умолчанию
|
всегда `tasks`. Секции беклога (`--sections`) — по умолчанию
|
||||||
`Ядро,Инфра`; если у проекта деление другое по существу, оно называется
|
`Ядро,Инфра`; если у проекта деление другое по существу, оно называется
|
||||||
здесь, а не подгоняется под умолчание, и становится **заголовками `##`
|
здесь, а не подгоняется под умолчание, и становится **заголовками `##`
|
||||||
индекса** — их единственным домом. В `.tasks.json` секции не пишутся: там
|
индекса** — их единственным домом. В `.av-dev.toml` секции не пишутся: там
|
||||||
версия формата и имена частей, а второй список секций разошёлся бы с
|
версия формата и имена частей, а второй список секций разошёлся бы с
|
||||||
заголовками молча.
|
заголовками молча.
|
||||||
2. **`adopt scan`** по всем источникам разом. Один прогон, одна карта: два
|
2. **`adopt scan`** по всем источникам разом. Один прогон, одна карта: два
|
||||||
+2
-2
@@ -12,7 +12,7 @@
|
|||||||
не файл-на-находку.** Если у ревью был триаж — половина работы уже сделана, бери
|
не файл-на-находку.** Если у ревью был триаж — половина работы уже сделана, бери
|
||||||
его выход. Если нет — триажируй сам, прежде чем заводить.
|
его выход. Если нет — триажируй сам, прежде чем заводить.
|
||||||
|
|
||||||
**Штатный отправитель — `av-dev-code:review`** (и `av-dev-code:resolve`, который
|
**Штатный отправитель — `av-dev:code-review`** (и `av-dev:code-resolve`, который
|
||||||
его вызывает): задач он не заводит сам, а отдаёт отложенные находки **списком
|
его вызывает): задач он не заводит сам, а отдаёт отложенные находки **списком
|
||||||
урожая** — формулировка, оракул, провенанс — и хранит отчёт триажа вместе с
|
урожая** — формулировка, оракул, провенанс — и хранит отчёт триажа вместе с
|
||||||
изменением. Приходит и любой другой разбор, вплоть до пересказа человеком; тогда
|
изменением. Приходит и любой другой разбор, вплоть до пересказа человеком; тогда
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
отображается **в позицию в очереди**, потому что приоритет и есть порядок строк
|
отображается **в позицию в очереди**, потому что приоритет и есть порядок строк
|
||||||
в беклоге (правило 4 [SKILL.md](../SKILL.md)). Отображается через довод, а не
|
в беклоге (правило 4 [SKILL.md](../SKILL.md)). Отображается через довод, а не
|
||||||
напрямую: своей шкалы у интейка нет, доводы расстановки перечислены в
|
напрямую: своей шкалы у интейка нет, доводы расстановки перечислены в
|
||||||
[скилле груминга](../../groom/SKILL.md#приоритет-как-его-расставляют), и
|
[скилле груминга](../../task-groom/SKILL.md#приоритет-как-его-расставляют), и
|
||||||
серьёзность попадает ровно в один из них.
|
серьёзность попадает ровно в один из них.
|
||||||
|
|
||||||
- **тяжёлая находка со свидетельством о сломанном сейчас** → задача под ту цель,
|
- **тяжёлая находка со свидетельством о сломанном сейчас** → задача под ту цель,
|
||||||
+3
-3
@@ -61,13 +61,13 @@
|
|||||||
|
|
||||||
## Кто такую задачу решает
|
## Кто такую задачу решает
|
||||||
|
|
||||||
Решает её конвейер проекта — в плагине `av-dev-code` это скилл `resolve`,
|
Решает её конвейер проекта — скилл `av-dev:code-resolve`,
|
||||||
**сценарий обслуживания**: он не заводит change и не пишет требований, потому что
|
**сценарий обслуживания**: он не заводит change и не пишет требований, потому что
|
||||||
у работы, не меняющей поведения, дельта-спек нет по построению. Тип записи там
|
у работы, не меняющей поведения, дельта-спек нет по построению. Тип записи там
|
||||||
только **предлагает** сценарий, а подтверждает его отсутствие дельт: разошлись —
|
только **предлагает** сценарий, а подтверждает его отсутствие дельт: разошлись —
|
||||||
работа останавливается, и это тот самый случай, когда тип, оставшийся от первой
|
работа останавливается, и это тот самый случай, когда тип, оставшийся от первой
|
||||||
формулировки, врёт. Плагина нет — задача решается как проект привык, а этот скилл
|
формулировки, врёт. Задачу ведут не этим процессом — она решается как проект
|
||||||
её только заводит и закрывает.
|
привык, а этот скилл её только заводит и закрывает.
|
||||||
|
|
||||||
## Что видит машина, а что человек
|
## Что видит машина, а что человек
|
||||||
|
|
||||||
+2
-2
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
1. **Проверить, что это возможность, а не работа.** Работа, которой держат
|
1. **Проверить, что это возможность, а не работа.** Работа, которой держат
|
||||||
проект, на вопрос «что приложение будет уметь» не отвечает; состав перечислен
|
проект, на вопрос «что приложение будет уметь» не отвечает; состав перечислен
|
||||||
[в словаре сопровождения](operations.md). Ей отведена секция
|
[в словаре сопровождения](../../../shared/operations.md). Ей отведена секция
|
||||||
`Сопровождение` — там она видна в том же
|
`Сопровождение` — там она видна в том же
|
||||||
экране и не читается как обещание продукта. Граница проходит по тому,
|
экране и не читается как обещание продукта. Граница проходит по тому,
|
||||||
**кто наблюдает**:
|
**кто наблюдает**:
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
|
|
||||||
**Место этому — груминг, а не отдельный заход.** Отмена цели значит
|
**Место этому — груминг, а не отдельный заход.** Отмена цели значит
|
||||||
разбор всех её задач, а разбор задач и есть шаг 3 груминга
|
разбор всех её задач, а разбор задач и есть шаг 3 груминга
|
||||||
(скилл `groom`, «что перестало быть важным»). Отменять на ходу,
|
(скилл `task-groom`, «что перестало быть важным»). Отменять на ходу,
|
||||||
между делом, — верный способ закрыть скопом то, что стоило перевесить.
|
между делом, — верный способ закрыть скопом то, что стоило перевесить.
|
||||||
|
|
||||||
## Что видит машина, а что человек
|
## Что видит машина, а что человек
|
||||||
+2
-3
@@ -66,9 +66,8 @@
|
|||||||
4. **Провести разведку** и **записать ответ по названному адресу**. Числа — с
|
4. **Провести разведку** и **записать ответ по названному адресу**. Числа — с
|
||||||
провенансом: с командой или условиями, которыми получены. Число без источника
|
провенансом: с командой или условиями, которыми получены. Число без источника
|
||||||
проход ревью обязан читать как условие, а не как замер. Проводит её конвейер
|
проход ревью обязан читать как условие, а не как замер. Проводит её конвейер
|
||||||
проекта — в плагине `av-dev-code` это скилл `resolve`, сценарий разведки;
|
проекта — скилл `av-dev:code-resolve`, сценарий разведки; этот скилл её
|
||||||
плагина нет — разведка ведётся как проект привык, а этот скилл её только
|
только заводит и закрывает.
|
||||||
заводит и закрывает.
|
|
||||||
5. **Разложить исход на задачи** — если он их родил. Разведка кончается одним из
|
5. **Разложить исход на задачи** — если он их родил. Разведка кончается одним из
|
||||||
трёх: заведены задачи, записано знание, отказ. **Отказ — полноправный исход**:
|
трёх: заведены задачи, записано знание, отказ. **Отказ — полноправный исход**:
|
||||||
«проверили, не проблема» экономит работу.
|
«проверили, не проблема» экономит работу.
|
||||||
+248
-159
@@ -7,11 +7,11 @@
|
|||||||
ровно в одном из них за раз. `REJECTED.md` индексом не считается: он не говорит,
|
ровно в одном из них за раз. `REJECTED.md` индексом не считается: он не говорит,
|
||||||
где запись числится, он кладбище ушедшего.
|
где запись числится, он кладбище ушедшего.
|
||||||
|
|
||||||
Раскладка. Путь каталога — `tasks/` в корне репозитория, жёстко. Каталог
|
Раскладка. Путь каталога — `tasks/` в корне репозитория по умолчанию; другой
|
||||||
принадлежит этому плагину, а не канону документов: `docs/` ведёт другой плагин, и
|
называется ключом `[tasks] dir`. Каталог принадлежит этому скиллу, а не канону
|
||||||
проект, поставивший учёт работ без него, каталога `docs/` не имеет вовсе. Имена
|
документов: учёт работ ведут и в проекте, который к канону не приведён. Имена
|
||||||
внутри и **версия формата** живут в `tasks/.tasks.json`; журнал версий —
|
частей и **версия раскладки** живут в `.av-dev.toml` в корне; журнал версий —
|
||||||
references/changelog.md рядом со скриптом.
|
references/changelog.md скилла doc-canon.
|
||||||
|
|
||||||
tasks/
|
tasks/
|
||||||
items/ задачи и цели файлами, <slug>.md
|
items/ задачи и цели файлами, <slug>.md
|
||||||
@@ -102,32 +102,50 @@ goal | feature | fix | chore | research, по-английски, как и пр
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import datetime
|
||||||
|
import importlib.util
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
CONFIG_NAME = ".tasks.json" # дом настроек и версии: свой файл в каталоге
|
|
||||||
PM_CONFIG_REL = "../.pm.json" # прежний дом настроек: docs/.pm.json, ключ "tasks"
|
|
||||||
|
|
||||||
# Версия формата задач — **своя, а не канона документов**. Число живёт ключом
|
def _load_shared() -> ModuleType:
|
||||||
# `tasks` в `.tasks.json`, журнал версий — references/changelog.md рядом со
|
"""Общий читатель `.av-dev.toml` — `shared/config.py` этого же плагина.
|
||||||
# скриптом, повышает его операция `upgrade` скилла `av-dev-tasks:tasks`.
|
|
||||||
|
Путь считается от файла скрипта: зовут его из репозитория проекта, где
|
||||||
|
дерева плагина в текущем каталоге нет.
|
||||||
|
"""
|
||||||
|
path = Path(__file__).resolve().parents[3] / "shared" / "config.py"
|
||||||
|
# Проверка именно файлом: `spec_from_file_location` на отсутствующем пути
|
||||||
|
# возвращает исправный спек, и падает уже `exec_module` — трейсбеком и кодом
|
||||||
|
# 1, то есть «найден дрейф, чинится». Битая установка дрейфом не является.
|
||||||
|
spec = importlib.util.spec_from_file_location("avdev_config", path)
|
||||||
|
if not path.is_file() or spec is None or spec.loader is None:
|
||||||
|
print(f"ОТКАЗ: не читается {path} — общий читатель настроек;"
|
||||||
|
f" переустанови плагин av-dev", file=sys.stderr)
|
||||||
|
sys.exit(3)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
conf = _load_shared()
|
||||||
|
|
||||||
|
CONFIG_NAME = conf.CONFIG_NAME # дом настроек и версии: `.av-dev.toml` в корне
|
||||||
|
|
||||||
|
# Версия раскладки — **одна на плагин**, и живёт она в `shared/config.py`.
|
||||||
|
# Своей у каталога задач больше нет: пока плагинов было три и ставились они
|
||||||
|
# порознь, проект мог иметь учёт работ без канона документов, и общее число
|
||||||
|
# было бы домом, которого у половины проектов нет. Плагин один — довод ушёл, а
|
||||||
|
# два числа вместо одного оставляли бы вопрос «по какому журналу повышать».
|
||||||
#
|
#
|
||||||
# Число именно своё, потому что плагин ставится в одиночку: проект, взявший учёт
|
# Переезды каталога, случившиеся до слияния (в корень, отмена спринтов), задним
|
||||||
# работ без канона документов, каталога `docs/` не имеет вовсе, а значит не имеет
|
# числом в журнал не переписаны: они названы прежними журналами, и второй
|
||||||
# и версии канона — сверять было бы не с чем. Копия чужого числа в этом скрипте
|
# перечень тех же шагов разошёлся бы с первым.
|
||||||
# была бы вторым домом для одной версии и разъехалась бы молча при обновлении
|
LAYOUT_VERSION = conf.VERSION
|
||||||
# одного плагина без другого.
|
VERSION_KEY = conf.VERSION_KEY
|
||||||
#
|
|
||||||
# Переезды каталога задач, случившиеся до появления этого числа (в корень —
|
|
||||||
# канон 11, отмена спринтов — канон 12), задним числом сюда не переписаны: они
|
|
||||||
# уже названы журналом канона, и второй перечень тех же шагов разошёлся бы с
|
|
||||||
# первым. Версия 1 — формат на день её появления, что бы проекту ни пришлось
|
|
||||||
# пройти до неё.
|
|
||||||
FORMAT_VERSION = 1
|
|
||||||
VERSION_KEY = "tasks"
|
|
||||||
|
|
||||||
EXIT_OK = 0
|
EXIT_OK = 0
|
||||||
EXIT_DRIFT = 1
|
EXIT_DRIFT = 1
|
||||||
@@ -135,6 +153,11 @@ EXIT_USAGE = 2
|
|||||||
EXIT_ENV = 3
|
EXIT_ENV = 3
|
||||||
EXIT_INTERNAL = 4
|
EXIT_INTERNAL = 4
|
||||||
|
|
||||||
|
# Ключ `dir` в DEFAULTS не входит намеренно: он говорит, **где** каталог, а не
|
||||||
|
# как названы его части, и в `Layout` (тот про имена внутри) ему делать нечего.
|
||||||
|
DIR_KEY = "dir"
|
||||||
|
DEFAULT_DIR = "tasks"
|
||||||
|
|
||||||
DEFAULTS = {
|
DEFAULTS = {
|
||||||
"items": "items",
|
"items": "items",
|
||||||
"backlog": "BACKLOG.md",
|
"backlog": "BACKLOG.md",
|
||||||
@@ -435,9 +458,14 @@ class Layout:
|
|||||||
"""Каталог задач и имена его частей. Всё настраивается: у соседнего проекта
|
"""Каталог задач и имена его частей. Всё настраивается: у соседнего проекта
|
||||||
может быть другой подкаталог и другие имена индексов, а семантика та же."""
|
может быть другой подкаталог и другие имена индексов, а семантика та же."""
|
||||||
|
|
||||||
def __init__(self, root: Path, cfg: dict):
|
def __init__(self, root: Path, cfg: dict, project: Path | None = None,
|
||||||
|
full: dict | None = None):
|
||||||
self.root = root
|
self.root = root
|
||||||
self.cfg = {**DEFAULTS, **cfg}
|
# Корень проекта — там, где лежит `.av-dev.toml`. Он нужен отдельно от
|
||||||
|
# каталога задач: версия объявлена в корне, а имена частей — внутри.
|
||||||
|
self.project = project or root
|
||||||
|
self.full = full or {}
|
||||||
|
self.cfg = {**DEFAULTS, **{k: v for k, v in cfg.items() if k != DIR_KEY}}
|
||||||
self.items = root / self.cfg["items"]
|
self.items = root / self.cfg["items"]
|
||||||
|
|
||||||
def index(self, kind: str) -> Path:
|
def index(self, kind: str) -> Path:
|
||||||
@@ -451,104 +479,66 @@ class Layout:
|
|||||||
return ("backlog", "roadmap")
|
return ("backlog", "roadmap")
|
||||||
|
|
||||||
|
|
||||||
def load_config(root: Path) -> dict:
|
def load_config(project: Path) -> dict:
|
||||||
"""Настройки каталога задач и версия его формата.
|
"""Весь `.av-dev.toml` проекта. Секция задач берётся из него отдельно.
|
||||||
|
|
||||||
Дом — `<каталог задач>/.tasks.json`: **свой файл у своего плагина**. Ключ
|
Дом настроек — **корень репозитория**, а не каталог задач: файл держит
|
||||||
`tasks` в `docs/.pm.json` читается, пока живы проекты, заведённые до раскола
|
версию раскладки, которая одна на плагин, и ключ `[tasks] dir`, который
|
||||||
плагинов, и только когда своего файла нет; когда есть оба, побеждает свой, и
|
говорит, где каталог лежит. Настройка внутри настраиваемого каталога не
|
||||||
об этом говорится вслух — молча выбранный из двух конфиг это дрейф, который
|
смогла бы сказать, где он.
|
||||||
потом никто не объяснит.
|
|
||||||
|
|
||||||
Порядок именно такой, а не наоборот, потому что `docs/` принадлежит другому
|
|
||||||
плагину. Проект, поставивший учёт задач без канона документов, каталога
|
|
||||||
`docs/` не имеет вовсе, и дом настроек, лежащий в чужом дереве, был бы домом,
|
|
||||||
которого у половины проектов нет.
|
|
||||||
|
|
||||||
Версия формата (ключ `tasks`) читается **только из своего файла**: прежний
|
|
||||||
дом её не знал и знать не может, и молча выведенная из его отсутствия версия
|
|
||||||
была бы догадкой о том, что чинится одной строкой.
|
|
||||||
"""
|
"""
|
||||||
path = root / CONFIG_NAME
|
|
||||||
pm = (root / PM_CONFIG_REL).resolve()
|
|
||||||
if path.is_file():
|
|
||||||
# Чужой конфиг здесь только повод для замечания, поэтому его поломка не
|
|
||||||
# наша: битый `docs/.pm.json` не должен ронять задачи, у которых свой
|
|
||||||
# файл на месте и читается.
|
|
||||||
try:
|
|
||||||
stale = pm.is_file() and isinstance(_read_json(pm).get("tasks"), dict)
|
|
||||||
except Env:
|
|
||||||
stale = False
|
|
||||||
if stale:
|
|
||||||
print(f"ЗАМЕЧАНИЕ настройки взяты из {path}; ключ «tasks» в {pm}"
|
|
||||||
f" остался от прежней раскладки и не читается — убери его",
|
|
||||||
file=sys.stderr)
|
|
||||||
return _validate_config(_read_json(path), path)
|
|
||||||
if pm.is_file():
|
|
||||||
data = _read_json(pm)
|
|
||||||
section = data.get("tasks", {})
|
|
||||||
if not isinstance(section, dict):
|
|
||||||
raise Env(f"{pm}: ключ «tasks» — ожидался объект с настройками")
|
|
||||||
if section:
|
|
||||||
print(f"ЗАМЕЧАНИЕ настройки взяты из ключа «tasks» в {pm} — это"
|
|
||||||
f" прежний дом. Перенеси их в {path}: каталог docs/ ведёт"
|
|
||||||
f" другой плагин, и его может не быть", file=sys.stderr)
|
|
||||||
return _validate_config(section, pm)
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def _read_json(path: Path) -> dict:
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(path.read_text(encoding="utf-8"))
|
data = conf.read(project)
|
||||||
except json.JSONDecodeError as e:
|
except conf.ConfigError as e:
|
||||||
raise Env(f"{path}: не разбирается как JSON — {e}") from e
|
raise Env(str(e)) from e
|
||||||
if not isinstance(data, dict):
|
_validate_config(conf.section(data, "tasks"), project / CONFIG_NAME)
|
||||||
raise Env(f"{path}: ожидался объект с настройками")
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def tasks_section(full: dict) -> dict:
|
||||||
|
return conf.section(full, "tasks")
|
||||||
|
|
||||||
|
|
||||||
def _validate_config(data: dict, path: Path) -> dict:
|
def _validate_config(data: dict, path: Path) -> dict:
|
||||||
unknown = set(data) - set(DEFAULTS) - {VERSION_KEY}
|
unknown = set(data) - set(DEFAULTS) - {DIR_KEY}
|
||||||
# Ключ «plan» был домом оглавления целей до того, как файл стал ROADMAP.md.
|
# Ключ «plan» был домом оглавления целей до того, как файл стал ROADMAP.md.
|
||||||
# Без этой ветки проект со старым конфигом получал бы «неизвестный ключ» и
|
# Без этой ветки проект со старым конфигом получал бы «неизвестный ключ» и
|
||||||
# искал опечатку там, где на самом деле переименование канона.
|
# искал опечатку там, где на самом деле переименование канона.
|
||||||
if "plan" in unknown:
|
if "plan" in unknown:
|
||||||
raise Env(f"{path}: ключ «plan» переименован в «roadmap»,"
|
raise Env(f"{path}: ключ «plan» переименован в «roadmap»,"
|
||||||
f" а PLAN.md — в ROADMAP.md. Повысь проект скиллом"
|
f" а PLAN.md — в ROADMAP.md. Повысь проект скиллом"
|
||||||
f" av-dev-docs:canon (upgrade), а не правь ключ в одиночку:"
|
f" av-dev:doc-canon (upgrade), а не правь ключ в одиночку:"
|
||||||
f" файл и ссылки на него переезжают вместе с ним")
|
f" файл и ссылки на него переезжают вместе с ним")
|
||||||
if unknown:
|
if unknown:
|
||||||
known = sorted({*DEFAULTS, VERSION_KEY})
|
known = sorted({*DEFAULTS, DIR_KEY})
|
||||||
raise Env(f"{path}: неизвестные ключи: {', '.join(sorted(unknown))}"
|
raise Env(f"{path}: неизвестные ключи в секции [tasks]:"
|
||||||
f" (известны: {', '.join(known)})")
|
f" {', '.join(sorted(unknown))} (известны: {', '.join(known)})")
|
||||||
# Версия — единственный ключ-число: остальные это имена файлов и заголовков.
|
# Версия раскладки лежит ключом верхнего уровня и проверяется общим
|
||||||
# Битое число тут останавливает работу целиком (код 3), а не идёт дрейфом,
|
# читателем: здесь судится только секция задач, и все её ключи — строки.
|
||||||
# потому что «на какой версии формата каталог» решает, чему верить дальше.
|
|
||||||
got = data.get(VERSION_KEY)
|
|
||||||
if VERSION_KEY in data and (isinstance(got, bool) or not isinstance(got, int)):
|
|
||||||
raise Env(f"{path}: ключ «{VERSION_KEY}» — версия формата задач,"
|
|
||||||
f" ожидалось целое число, а не {got!r}")
|
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if key == VERSION_KEY:
|
|
||||||
continue
|
|
||||||
if not isinstance(value, str) or not value.strip():
|
if not isinstance(value, str) or not value.strip():
|
||||||
raise Env(f"{path}: ключ «{key}» — ожидалась непустая строка")
|
raise Env(f"{path}: ключ «{key}» — ожидалась непустая строка")
|
||||||
if key in PATH_KEYS and (value.startswith("/") or ".." in Path(value).parts):
|
if key in PATH_KEYS and (value.startswith("/") or ".." in Path(value).parts):
|
||||||
raise Env(f"{path}: ключ «{key}» = «{value}» — только имя внутри каталога задач")
|
raise Env(f"{path}: ключ «{key}» = «{value}» — только имя внутри каталога задач")
|
||||||
|
# `dir` судится строже прочих: он указывает каталог, а не имя внутри
|
||||||
|
# него, и без этой проверки «../соседний» уводит запись за пределы
|
||||||
|
# репозитория молча — с зелёным кодом и путём, который в докладе
|
||||||
|
# выглядит своим.
|
||||||
|
if key == DIR_KEY and (Path(value).is_absolute() or ".." in Path(value).parts):
|
||||||
|
raise Env(f"{path}: ключ «{DIR_KEY}» = «{value}» — только путь внутри"
|
||||||
|
f" репозитория, без «..» и без корня")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def config_home(root: Path) -> Path | None:
|
def config_home(lay: Layout) -> Path | None:
|
||||||
"""Откуда настройки читаются на самом деле — и куда, значит, слать чинить.
|
"""Откуда настройки читаются на самом деле — и куда, значит, слать чинить.
|
||||||
|
|
||||||
Порядок тот же, что в `load_config`: свой `.tasks.json` побеждает. Без этой
|
Дом один — `.av-dev.toml` в корне проекта; None значит «файла нет, работаем
|
||||||
функции сообщения об ошибке звали бы править файл, который не читается.
|
на умолчаниях». Без этой функции сообщения об ошибке звали бы править файл,
|
||||||
|
которого нет.
|
||||||
"""
|
"""
|
||||||
path = root / CONFIG_NAME
|
path = lay.project / CONFIG_NAME
|
||||||
if path.is_file():
|
return path if path.is_file() else None
|
||||||
return path
|
|
||||||
pm = (root / PM_CONFIG_REL).resolve()
|
|
||||||
return pm if pm.is_file() else None
|
|
||||||
|
|
||||||
|
|
||||||
def config_problems(lay: Layout) -> list[str]:
|
def config_problems(lay: Layout) -> list[str]:
|
||||||
@@ -558,7 +548,7 @@ def config_problems(lay: Layout) -> list[str]:
|
|||||||
check обвинять невиновных: «ссылка на несуществующий файл», хотя файл на
|
check обвинять невиновных: «ссылка на несуществующий файл», хотя файл на
|
||||||
месте, а мимо смотрит конфиг.
|
месте, а мимо смотрит конфиг.
|
||||||
"""
|
"""
|
||||||
where = str(config_home(lay.root) or "умолчания (конфига нет)")
|
where = str(config_home(lay) or "умолчания (конфига нет)")
|
||||||
out = []
|
out = []
|
||||||
if not lay.items.is_dir():
|
if not lay.items.is_dir():
|
||||||
out.append(f"{where}: items = «{lay.cfg['items']}» → {lay.items} — каталога нет")
|
out.append(f"{where}: items = «{lay.cfg['items']}» → {lay.items} — каталога нет")
|
||||||
@@ -582,36 +572,51 @@ def version_problems(lay: Layout) -> list[str]:
|
|||||||
бы объявить каталог приведённым к формату, шагов которого никто не делал.
|
бы объявить каталог приведённым к формату, шагов которого никто не делал.
|
||||||
Заводит число `init`, двигает — операция `upgrade` скилла.
|
Заводит число `init`, двигает — операция `upgrade` скилла.
|
||||||
"""
|
"""
|
||||||
path = lay.root / CONFIG_NAME
|
path = lay.project / CONFIG_NAME
|
||||||
# Прежний дом (`docs/.pm.json`) версии не знает, поэтому спрашиваем строго
|
legacy = conf.legacy_files(lay.project, lay.root)
|
||||||
# свой файл: «конфиг нашёлся» и «версия объявлена» это разные события.
|
out = []
|
||||||
|
# Прежние файлы называются всегда, а не только когда нового нет: половина
|
||||||
|
# переезда — заведён новый, старые остались — иначе проходит молча, и второй
|
||||||
|
# дом для той же версии живёт дальше.
|
||||||
|
if legacy and path.is_file():
|
||||||
|
out.append(f"прежняя раскладка не убрана: {', '.join(legacy)} рядом с"
|
||||||
|
f" {CONFIG_NAME}. Эти файлы не читаются, а версия в них своя —"
|
||||||
|
f" удали их: переезд не закончен (журнал, версия 1, шаг 3)")
|
||||||
if not path.is_file():
|
if not path.is_file():
|
||||||
return [f"нет {path} — версия формата задач не объявлена."
|
if legacy:
|
||||||
f" Заведи файл с «{VERSION_KEY}»: {FORMAT_VERSION} (журнал версий —"
|
return [f"нет {path}, а прежняя раскладка на месте"
|
||||||
f" references/changelog.md скилла av-dev-tasks:tasks)"]
|
f" ({', '.join(legacy)}): перенеси настройки и удали старые"
|
||||||
# Что число целое, уже проверил `_validate_config` — иначе сюда не дошли бы
|
f" файлы операцией upgrade скилла av-dev:doc-canon"]
|
||||||
|
return [f"нет {path} — версия раскладки не объявлена."
|
||||||
|
f" Заведи файл с «{VERSION_KEY} = {LAYOUT_VERSION}» (журнал"
|
||||||
|
f" версий — references/changelog.md скилла av-dev:doc-canon)"]
|
||||||
|
# Что число целое, уже проверил общий читатель — иначе сюда не дошли бы
|
||||||
# вовсе (код 3). Здесь `isinstance` значит ровно «ключ есть».
|
# вовсе (код 3). Здесь `isinstance` значит ровно «ключ есть».
|
||||||
got = lay.cfg.get(VERSION_KEY)
|
got = conf.version(lay.full)
|
||||||
if not isinstance(got, int):
|
if got is None:
|
||||||
return [f"{path}: нет ключа «{VERSION_KEY}» — версия формата не объявлена,"
|
out.append(f"{path}: нет ключа «{VERSION_KEY}» — версия раскладки не"
|
||||||
f" текущая {FORMAT_VERSION}"]
|
f" объявлена, текущая {LAYOUT_VERSION}")
|
||||||
if got < FORMAT_VERSION:
|
elif got < LAYOUT_VERSION:
|
||||||
return [f"каталог приведён к формату версии {got}, текущая —"
|
out.append(f"проект приведён к раскладке версии {got}, текущая —"
|
||||||
f" {FORMAT_VERSION}: нужно повышение по журналу"
|
f" {LAYOUT_VERSION}: нужно повышение по журналу"
|
||||||
f" (скилл av-dev-tasks:tasks, операция upgrade)"]
|
f" (скилл av-dev:doc-canon, операция upgrade)")
|
||||||
if got > FORMAT_VERSION:
|
elif got > LAYOUT_VERSION:
|
||||||
return [f"каталог приведён к формату версии {got}, а скрипт знает"
|
out.append(f"проект приведён к раскладке версии {got}, а скрипт знает"
|
||||||
f" {FORMAT_VERSION}: устарел плагин, обнови маркетплейс"]
|
f" {LAYOUT_VERSION}: устарел плагин, обнови маркетплейс")
|
||||||
return []
|
return out
|
||||||
|
|
||||||
|
|
||||||
def looks_like_tasks(p: Path) -> bool:
|
def looks_like_tasks(p: Path, names: dict | None = None) -> bool:
|
||||||
if (p / CONFIG_NAME).is_file():
|
"""Каталог задач узнаётся индексом, а не служебным файлом.
|
||||||
return True
|
|
||||||
try: # индекс мог быть переименован через конфиг
|
Служебный файл теперь лежит в корне проекта и о каталоге говорит ключом
|
||||||
name = load_config(p).get("backlog", DEFAULTS["backlog"])
|
`[tasks] dir`; узнавать каталог по нему значило бы объявить его задачами
|
||||||
except Env:
|
ровно там, куда указывает ключ, — даже если по этому пути пусто.
|
||||||
name = DEFAULTS["backlog"]
|
|
||||||
|
Имя индекса берётся из настроек: проект вправе назвать его по-своему, и
|
||||||
|
поиск по умолчанию не нашёл бы переименованного каталога вовсе.
|
||||||
|
"""
|
||||||
|
name = (names or {}).get("backlog") or DEFAULTS["backlog"]
|
||||||
return (p / name).is_file()
|
return (p / name).is_file()
|
||||||
|
|
||||||
|
|
||||||
@@ -619,33 +624,63 @@ def resolve_layout(explicit: str | None) -> Layout:
|
|||||||
"""Каталог задач для команд, кроме init.
|
"""Каталог задач для команд, кроме init.
|
||||||
|
|
||||||
Цепочка разрешения: явный `--dir` (обязан быть внутри рабочего каталога) →
|
Цепочка разрешения: явный `--dir` (обязан быть внутри рабочего каталога) →
|
||||||
`.tasks.json` или умолчания вверх от текущего каталога. Указатель в
|
ключ `[tasks] dir` из `.av-dev.toml` в корне → умолчание `tasks/` вверх от
|
||||||
`CLAUDE.md` проекта — звено между ними, но читает его агент и передаёт
|
текущего каталога. Указатель в `CLAUDE.md` проекта — звено между первым и
|
||||||
сюда `--dir`: скрипт не разбирает чужую документацию.
|
вторым, но читает его агент и передаёт сюда `--dir`: скрипт не разбирает
|
||||||
|
чужую документацию.
|
||||||
"""
|
"""
|
||||||
|
here = Path.cwd().resolve()
|
||||||
|
project = conf.find_root(here)
|
||||||
|
full = load_config(project) if project else {}
|
||||||
|
names = tasks_section(full)
|
||||||
|
|
||||||
if explicit:
|
if explicit:
|
||||||
root = Path(explicit)
|
root = Path(explicit)
|
||||||
if not dir_within_cwd(root):
|
if not dir_within_cwd(root):
|
||||||
raise Env(f"--dir вне рабочего каталога: {explicit}")
|
raise Env(f"--dir вне рабочего каталога: {explicit}")
|
||||||
if not looks_like_tasks(root):
|
if not looks_like_tasks(root, names):
|
||||||
raise Env(f"задач нет в «{explicit}»;"
|
raise Env(f"задач нет в «{explicit}»;"
|
||||||
f" новый проект — tasks.py init --dir {explicit}")
|
f" новый проект — tasks.py init --dir {explicit}")
|
||||||
return Layout(root, load_config(root))
|
return Layout(root, names, project or root.resolve(), full)
|
||||||
here = Path.cwd().resolve()
|
|
||||||
|
if DIR_KEY in names:
|
||||||
|
# Ключ назван — значит ответ на «где каталог» уже дан. Не нашли по нему
|
||||||
|
# — это отказ, а не повод искать дальше: молчаливый уход на умолчание
|
||||||
|
# означал бы работу в другом каталоге, о котором никто не просил.
|
||||||
|
candidate = (project or here) / names[DIR_KEY]
|
||||||
|
if not looks_like_tasks(candidate, names):
|
||||||
|
raise Env(f"каталог задач не найден по ключу [tasks] {DIR_KEY} ="
|
||||||
|
f" «{names[DIR_KEY]}» → {candidate}: индекса"
|
||||||
|
f" {names.get('backlog') or DEFAULTS['backlog']} там нет."
|
||||||
|
f" Поправь ключ в {CONFIG_NAME} или заведи каталог")
|
||||||
|
return Layout(relative_if_inside(candidate, here), names, project, full)
|
||||||
|
|
||||||
|
if project:
|
||||||
|
candidate = project / DEFAULT_DIR
|
||||||
|
if looks_like_tasks(candidate, names):
|
||||||
|
return Layout(relative_if_inside(candidate, here), names, project, full)
|
||||||
|
|
||||||
|
# Проект без `.av-dev.toml` — учёт работ ведут и до того, как канон заведён.
|
||||||
|
# Тогда каталог ищется умолчанием вверх, а версия объявится на `adopt`.
|
||||||
for base in (here, *here.parents):
|
for base in (here, *here.parents):
|
||||||
for candidate in (base, base / "tasks", base / "docs/tasks", base / "doc/tasks"):
|
for candidate in (base, base / DEFAULT_DIR, base / "docs/tasks", base / "doc/tasks"):
|
||||||
if looks_like_tasks(candidate):
|
if looks_like_tasks(candidate, names):
|
||||||
try:
|
return Layout(relative_if_inside(candidate, here), names,
|
||||||
rel = candidate.relative_to(here)
|
project or base, full)
|
||||||
except ValueError:
|
|
||||||
rel = candidate
|
|
||||||
return Layout(rel if str(rel) != "." else candidate, load_config(candidate))
|
|
||||||
if (base / ".git").exists():
|
if (base / ".git").exists():
|
||||||
break # выше корня репозитория не ищем
|
break # выше корня репозитория не ищем
|
||||||
raise Env("каталог задач не найден: ни --dir, ни tasks/ вверх от"
|
raise Env(f"каталог задач не найден: ни --dir, ни ключ [tasks] {DIR_KEY} в"
|
||||||
f" {here}. Путь всегда tasks/ в корне репозитория; прежний"
|
f" {CONFIG_NAME}, ни {DEFAULT_DIR}/ вверх от {here}."
|
||||||
" docs/tasks переезжает по записи 11 журнала версий канона,"
|
f" Новый проект — tasks.py init --dir {DEFAULT_DIR}")
|
||||||
" новый проект — tasks.py init --dir tasks")
|
|
||||||
|
|
||||||
|
def relative_if_inside(path: Path, here: Path) -> Path:
|
||||||
|
"""Путь покороче для сообщений, если каталог лежит под текущим."""
|
||||||
|
try:
|
||||||
|
rel = path.relative_to(here)
|
||||||
|
except ValueError:
|
||||||
|
return path
|
||||||
|
return path if str(rel) == "." else rel
|
||||||
|
|
||||||
|
|
||||||
# --- Чтение индексов ---
|
# --- Чтение индексов ---
|
||||||
@@ -1079,7 +1114,7 @@ def check(lay: Layout, fix: bool = False) -> int:
|
|||||||
for p in problems:
|
for p in problems:
|
||||||
print(f"КОНФИГ {p}")
|
print(f"КОНФИГ {p}")
|
||||||
print("\nсперва конфиг: пока он мимо, всё остальное диагностируется ложно"
|
print("\nсперва конфиг: пока он мимо, всё остальное диагностируется ложно"
|
||||||
f" (правь {config_home(lay.root) or lay.root / CONFIG_NAME}"
|
f" (правь {config_home(lay) or lay.project / CONFIG_NAME}"
|
||||||
f" или переименуй файлы)")
|
f" или переименуй файлы)")
|
||||||
return EXIT_ENV
|
return EXIT_ENV
|
||||||
|
|
||||||
@@ -2596,15 +2631,10 @@ def apply_fixes(lay: Layout) -> tuple[list[str], list[str]]:
|
|||||||
def init_files(lay: Layout, sections: list[str], roadmap_sections: list[str],
|
def init_files(lay: Layout, sections: list[str], roadmap_sections: list[str],
|
||||||
cfg: dict) -> dict[Path, str]:
|
cfg: dict) -> dict[Path, str]:
|
||||||
out: dict[Path, str] = {}
|
out: dict[Path, str] = {}
|
||||||
# Файл заводится всегда, даже когда все имена умолчательные: в нём живёт
|
# Служебный файл здесь не заводится: его пишет `write_config` по живому
|
||||||
# версия формата, а версия — не настройка, от которой можно отказаться.
|
# файлу — версию двигает построчно, ключи дописывает, чужого не затирает.
|
||||||
#
|
# Планом это сделать нельзя, потому что план перезаписывает целиком, а
|
||||||
# Пишем всегда в свой `.tasks.json`, даже когда рядом живёт `docs/.pm.json`:
|
# перезапись стёрла бы комментарии — то, ради чего взят TOML.
|
||||||
# дом настроек принадлежит этому плагину, а `docs/` — другому, и его в
|
|
||||||
# проекте может не быть. load_config читает свой файл первым, так что
|
|
||||||
# записанное сюда и прочитается отсюда.
|
|
||||||
out[lay.root / CONFIG_NAME] = json.dumps(cfg, ensure_ascii=False,
|
|
||||||
indent=2) + "\n"
|
|
||||||
out[lay.index("backlog")] = (
|
out[lay.index("backlog")] = (
|
||||||
"# Беклог\n\n"
|
"# Беклог\n\n"
|
||||||
f"Что **можно взять**. Одна задача = один файл `{lay.cfg['items']}/<slug>.md`\n"
|
f"Что **можно взять**. Одна задача = один файл `{lay.cfg['items']}/<slug>.md`\n"
|
||||||
@@ -2660,6 +2690,49 @@ def uniq_sections(raw: str) -> list[str]:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def adopt_cfg(lay: Layout) -> dict:
|
||||||
|
"""Что адаптация обязана записать о себе: где встал каталог.
|
||||||
|
|
||||||
|
Имён частей здесь нет — адаптация раскладывает всё по умолчаниям, — а путь
|
||||||
|
есть всегда, даже умолчательный: `--target` задаёт его свободно, и молча
|
||||||
|
записанное «tasks» указывало бы в пустоту.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
rel = lay.root.resolve().relative_to(lay.project.resolve()).as_posix()
|
||||||
|
except ValueError:
|
||||||
|
return {}
|
||||||
|
return {DIR_KEY: rel}
|
||||||
|
|
||||||
|
|
||||||
|
def write_config(project: Path, cfg: dict) -> list[str]:
|
||||||
|
"""Записать версию и настройки каталога; вернуть строки доклада.
|
||||||
|
|
||||||
|
Файла нет — он заводится целиком скелетом, с комментариями. Файл есть — в
|
||||||
|
нём двигается версия и дописываются недостающие ключи секции; чужое
|
||||||
|
значение не затирается, но и не замалчивается: разошедшийся ключ уезжает в
|
||||||
|
доклад строкой, потому что `dir`, указывающий не туда, куда только что
|
||||||
|
заведён каталог, оставляет каталог недостижимым.
|
||||||
|
"""
|
||||||
|
path = project / CONFIG_NAME
|
||||||
|
if not path.is_file():
|
||||||
|
path.write_text(conf.skeleton(LAYOUT_VERSION, tasks=cfg), encoding="utf-8")
|
||||||
|
return [f"{CONFIG_NAME} заведён: версия {LAYOUT_VERSION}"
|
||||||
|
f"{', ' + ', '.join(sorted(cfg)) if cfg else ''}"]
|
||||||
|
out = []
|
||||||
|
if conf.version(conf.read(project)) != LAYOUT_VERSION:
|
||||||
|
conf.set_version(project, LAYOUT_VERSION)
|
||||||
|
out.append(f"версия раскладки в {CONFIG_NAME}: {LAYOUT_VERSION}")
|
||||||
|
clash = conf.missing_keys(project, "tasks", cfg)
|
||||||
|
added = conf.merge_section(project, "tasks", cfg)
|
||||||
|
if added:
|
||||||
|
out.append(f"дописано в [tasks]: {', '.join(added)}")
|
||||||
|
for key, had in sorted(clash.items()):
|
||||||
|
out.append(f"ВНИМАНИЕ [tasks] {key} = «{had}» оставлен как был, а каталог"
|
||||||
|
f" заведён под «{cfg[key]}» — поправь {CONFIG_NAME} руками,"
|
||||||
|
f" иначе скрипт пойдёт не туда")
|
||||||
|
return out or [f"{CONFIG_NAME} уже описывает эту раскладку"]
|
||||||
|
|
||||||
|
|
||||||
def cmd_init(root: Path, a: argparse.Namespace) -> int:
|
def cmd_init(root: Path, a: argparse.Namespace) -> int:
|
||||||
if not dir_within_cwd(root):
|
if not dir_within_cwd(root):
|
||||||
raise Usage(f"--dir вне рабочего каталога: {root}")
|
raise Usage(f"--dir вне рабочего каталога: {root}")
|
||||||
@@ -2670,8 +2743,18 @@ def cmd_init(root: Path, a: argparse.Namespace) -> int:
|
|||||||
# Имена частей — следом и только те, что названы явно: умолчание, записанное
|
# Имена частей — следом и только те, что названы явно: умолчание, записанное
|
||||||
# в файл, стало бы вторым домом для того же имени. В раскладку версия не
|
# в файл, стало бы вторым домом для того же имени. В раскладку версия не
|
||||||
# идёт — `Layout` про имена, и число среди имён там ничего не значит.
|
# идёт — `Layout` про имена, и число среди имён там ничего не значит.
|
||||||
cfg = {VERSION_KEY: FORMAT_VERSION, **names}
|
# Каталог задач называется ключом `dir`, если он не умолчательный: без него
|
||||||
lay = Layout(root, names)
|
# `.av-dev.toml` не сможет сказать, где искать, и разрешение уедет на
|
||||||
|
# умолчание — молча и в другой каталог.
|
||||||
|
project = conf.find_root() or Path.cwd().resolve()
|
||||||
|
cfg = dict(names)
|
||||||
|
try:
|
||||||
|
rel = root.resolve().relative_to(project).as_posix()
|
||||||
|
except ValueError:
|
||||||
|
raise Usage(f"каталог задач {root} вне проекта {project}") from None
|
||||||
|
if rel != DEFAULT_DIR:
|
||||||
|
cfg[DIR_KEY] = rel
|
||||||
|
lay = Layout(root, names, project, load_config(project))
|
||||||
if lay.index("backlog").exists():
|
if lay.index("backlog").exists():
|
||||||
raise Usage(f"{lay.index('backlog')} уже есть — каталог задач заведён")
|
raise Usage(f"{lay.index('backlog')} уже есть — каталог задач заведён")
|
||||||
|
|
||||||
@@ -2692,12 +2775,12 @@ def cmd_init(root: Path, a: argparse.Namespace) -> int:
|
|||||||
for path, text in init_files(lay, sections, roadmap_sections, cfg).items():
|
for path, text in init_files(lay, sections, roadmap_sections, cfg).items():
|
||||||
plan.file(path, text)
|
plan.file(path, text)
|
||||||
plan.commit()
|
plan.commit()
|
||||||
|
said = write_config(project, cfg)
|
||||||
print(f"каталог задач заведён: {root}")
|
print(f"каталог задач заведён: {root}")
|
||||||
print(f" секции беклога: {', '.join(sections)};"
|
print(f" секции беклога: {', '.join(sections)};"
|
||||||
f" секции роадмапа канонические: {', '.join(roadmap_sections)}")
|
f" секции роадмапа канонические: {', '.join(roadmap_sections)}")
|
||||||
what = ("версия формата и имена частей записаны" if names
|
for line in said:
|
||||||
else "версия формата записана")
|
print(f" {line}")
|
||||||
print(f" {what} в {root / CONFIG_NAME}: формат {FORMAT_VERSION}")
|
|
||||||
return EXIT_OK
|
return EXIT_OK
|
||||||
|
|
||||||
|
|
||||||
@@ -2999,7 +3082,8 @@ def cmd_adopt_apply(a: argparse.Namespace) -> int:
|
|||||||
root = Path(pl["target"])
|
root = Path(pl["target"])
|
||||||
if not dir_within_cwd(root):
|
if not dir_within_cwd(root):
|
||||||
raise Usage(f"target вне рабочего каталога: {root}")
|
raise Usage(f"target вне рабочего каталога: {root}")
|
||||||
lay = Layout(root, {})
|
project = conf.find_root() or Path.cwd().resolve()
|
||||||
|
lay = Layout(root, {}, project, load_config(project))
|
||||||
sections = pl.get("sections_backlog") or uniq_sections(DEFAULT_SECTIONS)
|
sections = pl.get("sections_backlog") or uniq_sections(DEFAULT_SECTIONS)
|
||||||
roadmap_sections = pl.get("sections_roadmap") or uniq_sections(DEFAULT_ROADMAP_SECTIONS)
|
roadmap_sections = pl.get("sections_roadmap") or uniq_sections(DEFAULT_ROADMAP_SECTIONS)
|
||||||
known_sections = {s.lower() for s in sections}
|
known_sections = {s.lower() for s in sections}
|
||||||
@@ -3058,8 +3142,7 @@ def cmd_adopt_apply(a: argparse.Namespace) -> int:
|
|||||||
# Версия та же, что у `init`: каталог выводится из чужой раскладки сегодня и
|
# Версия та же, что у `init`: каталог выводится из чужой раскладки сегодня и
|
||||||
# сегодняшним форматом, сколько бы лет ни было тому, из чего он выведен.
|
# сегодняшним форматом, сколько бы лет ни было тому, из чего он выведен.
|
||||||
# Имён частей здесь нет — адаптация раскладывает всё по умолчаниям.
|
# Имён частей здесь нет — адаптация раскладывает всё по умолчаниям.
|
||||||
for path, text in init_files(lay, sections, roadmap_sections,
|
for path, text in init_files(lay, sections, roadmap_sections, {}).items():
|
||||||
{VERSION_KEY: FORMAT_VERSION}).items():
|
|
||||||
wr.file(path, text)
|
wr.file(path, text)
|
||||||
backlog_lines = init_files(lay, sections, roadmap_sections, {})[lay.index("backlog")].splitlines()
|
backlog_lines = init_files(lay, sections, roadmap_sections, {})[lay.index("backlog")].splitlines()
|
||||||
roadmap_lines = init_files(lay, sections, roadmap_sections, {})[lay.index("roadmap")].splitlines()
|
roadmap_lines = init_files(lay, sections, roadmap_sections, {})[lay.index("roadmap")].splitlines()
|
||||||
@@ -3128,6 +3211,10 @@ def cmd_adopt_apply(a: argparse.Namespace) -> int:
|
|||||||
return EXIT_OK
|
return EXIT_OK
|
||||||
lay.items.mkdir(parents=True, exist_ok=True)
|
lay.items.mkdir(parents=True, exist_ok=True)
|
||||||
wr.commit()
|
wr.commit()
|
||||||
|
# Настройки — тем же проходом, что и у `init`, и по той же причине: каталог,
|
||||||
|
# собранный здесь, обязан быть назван в `.av-dev.toml`, иначе следующая же
|
||||||
|
# команда не найдёт его и уйдёт искать умолчание.
|
||||||
|
said = write_config(lay.project, adopt_cfg(lay))
|
||||||
|
|
||||||
# --- перекрёстные ссылки: тем же проходом, иначе они останутся битыми ---
|
# --- перекрёстные ссылки: тем же проходом, иначе они останутся битыми ---
|
||||||
ref_paths: list[Path] = [*lay.items.glob("*.md"), lay.index("rejected")]
|
ref_paths: list[Path] = [*lay.items.glob("*.md"), lay.index("rejected")]
|
||||||
@@ -3138,6 +3225,8 @@ def cmd_adopt_apply(a: argparse.Namespace) -> int:
|
|||||||
[tuple(pair) for pair in pl.get("path_map", [])], False)
|
[tuple(pair) for pair in pl.get("path_map", [])], False)
|
||||||
|
|
||||||
print(f"каталог задач собран: {root}")
|
print(f"каталог задач собран: {root}")
|
||||||
|
for line in said:
|
||||||
|
print(f" {line}")
|
||||||
print(f" целей {len(pl.get('goals', []))}, задач {len(pl.get('items', []))},"
|
print(f" целей {len(pl.get('goals', []))}, задач {len(pl.get('items', []))},"
|
||||||
f" строк кладбища {len(pl.get('rejected', []))}")
|
f" строк кладбища {len(pl.get('rejected', []))}")
|
||||||
print(f" переименовано слагов: {len(renames)};"
|
print(f" переименовано слагов: {len(renames)};"
|
||||||
+3
-3
@@ -56,9 +56,9 @@ quote-style = "double"
|
|||||||
|
|
||||||
[tool.pyrefly]
|
[tool.pyrefly]
|
||||||
project-includes = [
|
project-includes = [
|
||||||
"av-dev-tasks/skills/tasks/scripts/tasks.py",
|
"av-dev/skills/task-track/scripts/tasks.py",
|
||||||
"av-dev-docs/skills/canon/scripts/docs.py",
|
"av-dev/skills/doc-canon/scripts/docs.py",
|
||||||
"av-dev-code/skills/openspec/scripts/openspec.py",
|
"av-dev/skills/code-openspec/scripts/openspec.py",
|
||||||
"scripts/addresses.py",
|
"scripts/addresses.py",
|
||||||
"scripts/copies.py",
|
"scripts/copies.py",
|
||||||
"scripts/diagrams.py",
|
"scripts/diagrams.py",
|
||||||
|
|||||||
+17
-13
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Сверка чужих адресов в прозе плагинов с перечнем их владельца.
|
"""Сверка чужих адресов в прозе скиллов с перечнем их владельца.
|
||||||
|
|
||||||
Судится **упразднённое, а не незнакомое**, и это следует из канона, а не из
|
Судится **упразднённое, а не незнакомое**, и это следует из канона, а не из
|
||||||
осторожности: список тем открытый — всё, что проект кладёт в `docs/` сверх
|
осторожности: список тем открытый — всё, что проект кладёт в `docs/` сверх
|
||||||
@@ -9,9 +9,9 @@
|
|||||||
единственная догадка — имя, **почти** совпавшее с каноническим: это опечатка с
|
единственная догадка — имя, **почти** совпавшее с каноническим: это опечатка с
|
||||||
куда большей вероятностью, чем новая тема.
|
куда большей вероятностью, чем новая тема.
|
||||||
|
|
||||||
Адрес документа принадлежит одному плагину, а называют его все: `docs/*` стоит
|
Адрес документа принадлежит одному скиллу, а называют его все: `docs/*` стоит
|
||||||
примерно в сорока местах `av-dev-code`, `tasks/ROADMAP.md` — в четырёх местах
|
примерно в сорока местах конвейера, `tasks/ROADMAP.md` — в четырёх местах
|
||||||
`av-dev-docs`. Переименование в каноне до этих мест не доходит.
|
канона. Переименование в каноне до этих мест не доходит.
|
||||||
|
|
||||||
**Почему тут нужна машина, а не аккуратность.** Прогон ревью умеет честно
|
**Почему тут нужна машина, а не аккуратность.** Прогон ревью умеет честно
|
||||||
деградировать: дома темы нет — в границах покрытия появляется строка «документа в
|
деградировать: дома темы нет — в границах покрытия появляется строка «документа в
|
||||||
@@ -49,15 +49,18 @@ SKIP_DIRS = {".git", ".venv", "node_modules", "__pycache__", "tmp"}
|
|||||||
|
|
||||||
# Владельцы: префикс адреса → скрипт, который этим каталогом и владеет.
|
# Владельцы: префикс адреса → скрипт, который этим каталогом и владеет.
|
||||||
OWNERS = {
|
OWNERS = {
|
||||||
"docs": "av-dev-docs/skills/canon/scripts/docs.py",
|
"docs": "av-dev/skills/doc-canon/scripts/docs.py",
|
||||||
"tasks": "av-dev-tasks/skills/tasks/scripts/tasks.py",
|
"tasks": "av-dev/skills/task-track/scripts/tasks.py",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Журналы: описывают прошлые состояния и задним числом не переписываются.
|
# Журналы: описывают прошлые состояния и задним числом не переписываются.
|
||||||
# Адрес, верный на момент записи, здесь останется навсегда, и это не дрейф.
|
# Адрес, верный на момент записи, здесь останется навсегда, и это не дрейф.
|
||||||
JOURNALS = {
|
JOURNALS = {
|
||||||
"av-dev-docs/skills/canon/references/changelog.md": "журнал версий канона",
|
"av-dev/skills/doc-canon/references/changelog.md": "журнал версий раскладки",
|
||||||
"av-dev-tasks/skills/tasks/references/changelog.md": "журнал версий формата задач",
|
"av-dev/skills/doc-canon/references/changelog-before-merge.md":
|
||||||
|
"журнал версий канона до слияния",
|
||||||
|
"av-dev/skills/doc-canon/references/changelog-tasks-before-merge.md":
|
||||||
|
"журнал версий формата задач до слияния",
|
||||||
"DECISIONS.md": "журнал решений",
|
"DECISIONS.md": "журнал решений",
|
||||||
"HISTORY.md": "журнал работ",
|
"HISTORY.md": "журнал работ",
|
||||||
"NOTES.md": "рабочие заметки",
|
"NOTES.md": "рабочие заметки",
|
||||||
@@ -66,9 +69,9 @@ JOURNALS = {
|
|||||||
# Файлы, где упразднённый адрес назван по делу: карта переездов и сценарии
|
# Файлы, где упразднённый адрес назван по делу: карта переездов и сценарии
|
||||||
# перевода чужой раскладки. Неизвестные адреса в них проверяются как везде.
|
# перевода чужой раскладки. Неизвестные адреса в них проверяются как везде.
|
||||||
RETIRED_OK = {
|
RETIRED_OK = {
|
||||||
"av-dev-docs/skills/canon/references/canon.md": "карта упразднённых слотов",
|
"av-dev/skills/doc-canon/references/canon.md": "карта упразднённых слотов",
|
||||||
"av-dev-docs/skills/canon/SKILL.md": "adopt: что где искать в чужой раскладке",
|
"av-dev/skills/doc-canon/SKILL.md": "adopt: что где искать в чужой раскладке",
|
||||||
"av-dev-tasks/skills/tasks/references/adopt.md": "перевод чужого каталога задач",
|
"av-dev/skills/task-track/references/adopt.md": "перевод чужого каталога задач",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Адрес в прозе: начало токена, префикс владельца, остаток пути. Отрицательный
|
# Адрес в прозе: начало токена, префикс владельца, остаток пути. Отрицательный
|
||||||
@@ -120,8 +123,9 @@ def vocabularies(root: Path) -> tuple[dict[str, set[str]], dict[str, str]]:
|
|||||||
docs_names = {stem(n) for n in docs.DOCS}
|
docs_names = {stem(n) for n in docs.DOCS}
|
||||||
docs_names |= {stem(n) for n in docs.CONDITIONAL_DOCS}
|
docs_names |= {stem(n) for n in docs.CONDITIONAL_DOCS}
|
||||||
docs_names |= {stem(n) for n in docs.NOT_DOCS}
|
docs_names |= {stem(n) for n in docs.NOT_DOCS}
|
||||||
# `docs/.docs.json` объявлен обязательным файлом вне раскладки.
|
# Обязательные файлы канона живут вне `docs/` (`CLAUDE.md`, `.av-dev.toml`),
|
||||||
docs_names |= {stem(Path(p).name) for p in docs.REQUIRED if p.startswith("docs/")}
|
# и в перечень имён внутри каталога не идут вовсе. Ветка осталась бы мёртвой
|
||||||
|
# молча, поэтому её тут нет: имя служебного файла добавляется ниже поимённо.
|
||||||
|
|
||||||
tasks_names = {stem(tasks.DEFAULTS[k]) for k in tasks.PATH_KEYS}
|
tasks_names = {stem(tasks.DEFAULTS[k]) for k in tasks.PATH_KEYS}
|
||||||
tasks_names |= {stem(tasks.CONFIG_NAME)}
|
tasks_names |= {stem(tasks.CONFIG_NAME)}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
Диаграммы заведены там, где структура — граф или автомат: порядок проходов
|
Диаграммы заведены там, где структура — граф или автомат: порядок проходов
|
||||||
ревью, жизненный цикл записи по индексам, исходы задачи в спринте, храповик
|
ревью, жизненный цикл записи по индексам, исходы задачи в спринте, храповик
|
||||||
промоута, счётчик калибровки, граф вызовов между плагинами.
|
промоута, счётчик калибровки, граф вызовов между скиллами.
|
||||||
|
|
||||||
Проверка нужна по одной причине: **синтаксическая ошибка в блоке не видна при
|
Проверка нужна по одной причине: **синтаксическая ошибка в блоке не видна при
|
||||||
чтении**. Текст диаграммы выглядит правдоподобно, `git diff` показывает разумную
|
чтении**. Текст диаграммы выглядит правдоподобно, `git diff` показывает разумную
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
**Цвет, не отвечающий модели.** Цвет charter'а кодирует **модель**, на которой
|
**Цвет, не отвечающий модели.** Цвет charter'а кодирует **модель**, на которой
|
||||||
идёт проход, а не его роль: раскладка — в
|
идёт проход, а не его роль: раскладка — в
|
||||||
`av-dev-code/skills/review/SKILL.md`, раздел «Модель по проходу».
|
`av-dev/skills/code-review/SKILL.md`, раздел «Модель по проходу».
|
||||||
Правило существует ровно затем, чтобы стоимость прогона читалась взглядом по
|
Правило существует ровно затем, чтобы стоимость прогона читалась взглядом по
|
||||||
списку агентов, и держаться вниманием оно не может: цвет ставится один раз при
|
списку агентов, и держаться вниманием оно не может: цвет ставится один раз при
|
||||||
заведении charter'а, а модель потом меняется калибровкой.
|
заведении charter'а, а модель потом меняется калибровкой.
|
||||||
@@ -49,7 +49,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4
|
OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4
|
||||||
|
|
||||||
# Дом раскладки — «Модель по проходу» в av-dev-code/skills/review/SKILL.md; здесь её
|
# Дом раскладки — «Модель по проходу» в av-dev/skills/code-review/SKILL.md; здесь её
|
||||||
# механизация. Порядок цветов — порядок стоимости прогона.
|
# механизация. Порядок цветов — порядок стоимости прогона.
|
||||||
PALETTE = {"sonnet": "green", "opus": "yellow"}
|
PALETTE = {"sonnet": "green", "opus": "yellow"}
|
||||||
|
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
# Граница между плагинами
|
|
||||||
|
|
||||||
**Это дом.** Правило обращения к соседнему плагину нужно всем, кто зовёт чужой
|
|
||||||
скилл, — а таких скиллов больше половины всех, и ни один плагин правилом не
|
|
||||||
владеет. (Числа здесь нет намеренно: оно уже дважды протухало за один день.) Поэтому дом стоит снаружи, а плагины везут **копии**, помеченные
|
|
||||||
разметкой `copies.py`.
|
|
||||||
|
|
||||||
Правится **здесь**. Копия, поправленная у себя, — расхождение, а не правка.
|
|
||||||
|
|
||||||
Дом заведён по замеру, а не на всякий случай. К моменту раскола правило стояло в
|
|
||||||
пяти местах в пяти редакциях:
|
|
||||||
|
|
||||||
| Где стояло | Довод | Ветка «не разрешился» |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `task-pipeline` | устаревшая проектная копия | нет |
|
|
||||||
| `task-batch` | то же | нет |
|
|
||||||
| `review-pipeline` | вшито в пункт про удаление проектных копий | нет |
|
|
||||||
| `openspec` | путём в чужое дерево — никогда | есть |
|
|
||||||
| `canon` | — | есть |
|
|
||||||
|
|
||||||
Имена с тех пор изменились — `task-pipeline` стал `resolve`, `review-pipeline` —
|
|
||||||
`review`, `task-batch` удалён, — но замер относится к местам, а не к названиям.
|
|
||||||
|
|
||||||
Два разных довода, и ни в одном месте не было обоих. Три места из пяти молчали о
|
|
||||||
том, что делать, когда вызов не разрешился, — то есть о единственном, ради чего
|
|
||||||
правило и написано.
|
|
||||||
|
|
||||||
**Что в дом не идёт: чем оборачивается отсутствие конкретного соседа.** «Нет
|
|
||||||
`av-dev-tasks` — учёт остаётся владельцу» знает только конвейер; «нет конвейера —
|
|
||||||
`docs.py` о каталоге `openspec/` молчит» знает только канон. Правило общее,
|
|
||||||
последствие местное, и держать последствия здесь значило бы завести дом, который
|
|
||||||
знает про всех своих потребителей.
|
|
||||||
|
|
||||||
<!-- дом: граница-плагинов -->
|
|
||||||
|
|
||||||
Плагины `av-dev` ставятся порознь, и ни один не вправе считать, что сосед на
|
|
||||||
месте.
|
|
||||||
|
|
||||||
**Чужой скилл зовётся полным именем** — `av-dev-docs:canon`, `av-dev-tasks:tasks`,
|
|
||||||
`av-dev-code:review`. Короткое имя может разрешиться в устаревшую проектную копию
|
|
||||||
из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в поведении.
|
|
||||||
|
|
||||||
**Путь в дерево чужого плагина не пишется никогда.** `$CLAUDE_PLUGIN_ROOT` ведёт
|
|
||||||
только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо
|
|
||||||
откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он
|
|
||||||
прочитает его сам.
|
|
||||||
|
|
||||||
**Вызов не разрешился — плагина в проекте нет.** Это исход, а не поломка: назови
|
|
||||||
строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя,
|
|
||||||
пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.
|
|
||||||
|
|
||||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
|
||||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
|
||||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
|
||||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /дом: граница-плагинов -->
|
|
||||||
Reference in New Issue
Block a user