Compare commits
39
Commits
b8120d3271
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ab759ae4a
|
||
|
|
d79f9d2286
|
||
|
|
8145b378b2
|
||
|
|
a4bc9191e7
|
||
|
|
3c89d7111d
|
||
|
|
daf9f8b824
|
||
|
|
b287cdf71f
|
||
|
|
72d9aa8034
|
||
|
|
17be316634
|
||
|
|
813345192d
|
||
|
|
e5dc0a1a39
|
||
|
|
e78a4311a7
|
||
|
|
dd7aa22d02
|
||
|
|
77cb967d7b
|
||
|
|
94fa66b262
|
||
|
|
6251157d8d
|
||
|
|
ed83ec7dc0
|
||
|
|
8d8c1656e5
|
||
|
|
3849f084be
|
||
|
|
0627199a1a
|
||
|
|
dff05ad097
|
||
|
|
3529cd8425
|
||
|
|
eae734f5cc
|
||
|
|
bf6a173115
|
||
|
|
b411d4edb8
|
||
|
|
7333953b1e
|
||
|
|
441469d78d
|
||
|
|
423f9798ef
|
||
|
|
7d559e60ec
|
||
|
|
a00e132f29
|
||
|
|
95c9499f06
|
||
|
|
6b162c421d
|
||
|
|
de12a4d8a3
|
||
|
|
142659bfd1
|
||
|
|
96dafc9011
|
||
|
|
95fed623e7
|
||
|
|
42849c13eb
|
||
|
|
f22e7ed829
|
||
|
|
b3479776a4
|
@@ -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": "Личный процесс разработки одним плагином: форма раскладки, документы проекта, учёт работ и работа по задачам. Форму держит скилл canon — три операции одной машиной сравнения (check, adopt, upgrade) со скриптом docs.py и общим журналом версий, по которому повышается вся раскладка, включая каталог задач; имя без префикса, потому что форма общая у всех частей. Документы — канон раскладки (CLAUDE.md плюс docs/: паспорт, архитектура, схема БД, безопасность, конвенции, разведка, ADR, журнал ревью), заведение нового проекта интервью в doc-init, ведение содержимого по ходу разработки в doc-sync, суд о смысловом здоровье в doc-healthcheck двумя агентами разом. Учёт — задачи каталогом markdown-файлов в task-track: у задачи тип (feature, fix, chore, research), задающий её схему, а у проекта — стадия (build — беклог это план стройки от базы к деталям, порядок строк значит зависимость; support — очередь правок, порядок значит важность); очередь расставляет интерактивный task-groom порядком строк. Работа — одна задача от постановки до закрытия скиллом code-resolve: точка входа одна, сценария три (решение циклом SDD с чекпоинтом, разведка без кода, обслуживание без change), и выбирает сценарий сам скилл, прочитав постановку; плюс конвейер ревью code-review с детерминированным гейтом, сверкой со спеками и обязательным триажем, плюс code-deep-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 не заводит: его чекпоинт вариантов стоит до первого написанного требования, а исход уезжает в документы канона и в задачи; выбранный способ реализуется следующим прогоном, который запускает человек. Смена сценария по ходу — событие с названным исходом, а не тихий поворот. Плюс конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом и обязательным триажем. OpenSpec нужен сценарию решения, и плагин сам его заводит скиллом openspec; разведка обходится без него. Задача принимается и обычным текстом. Плагины av-dev-docs и av-dev-tasks опциональны: первый даёт документы канона, из которых проходы читают проектную конкретику, второй — учёт задач; без них прогон деградирует поразрядно и называет это строкой."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "av-dev-git",
|
"name": "av-dev-git",
|
||||||
|
|||||||
-3781
File diff suppressed because it is too large
Load Diff
-85
@@ -1,85 +0,0 @@
|
|||||||
# Как процесс дошёл до текущей формы
|
|
||||||
|
|
||||||
Сжатие черновика `AGENTIC-TASKS.md` (497 строк), лежавшего незакоммиченным в
|
|
||||||
корне healthlog. Правила процесса из него переехали в плагины и здесь **не
|
|
||||||
повторяются** — второй дом для тех же правил ровно то, против чего документ и
|
|
||||||
был написан. Остаётся то, чего в плагинах нет и быть не должно: **что отвергнуто
|
|
||||||
и почему, и числа первого замера**.
|
|
||||||
|
|
||||||
Решения текущего круга разбора — [DECISIONS.md](DECISIONS.md).
|
|
||||||
|
|
||||||
## Что отвергнуто и почему
|
|
||||||
|
|
||||||
### Scrum целиком
|
|
||||||
|
|
||||||
Терминология близка — спринт, груминг, определение готовности, ретроспектива, —
|
|
||||||
и она удобна: не нужно изобретать слова. Но добрая половина Scrum существует ради
|
|
||||||
синхронизации людей, которых здесь нет: исполнителей двое, человек и агент.
|
|
||||||
|
|
||||||
**Не взято:** тайм-бокс (спринт ограничен объёмом, а не временем), velocity и
|
|
||||||
оценки в очках, ежедневный стендап (стендап — это и есть диалог), планирование
|
|
||||||
отдельно от груминга (владелец беклога один), роль скрам-мастера.
|
|
||||||
|
|
||||||
**Взято:** цель спринта, заморозка набора, определение готовности, груминг —
|
|
||||||
каждое потому, что снимает решение, которое иначе принимается заново каждый раз.
|
|
||||||
**Ретроспектива взята содержанием, но не отдельным ритуалом**: она шаг той же
|
|
||||||
сессии. Отдельная встреча ради трёх вопросов — плата ритуалом без выгоды.
|
|
||||||
|
|
||||||
### Приоритеты у задач
|
|
||||||
|
|
||||||
Заменены целью. Ни секциями, ни списком: «что делать дальше» отвечает набор
|
|
||||||
спринта, а между спринтами порядок не нужен никому — брать задачи вне спринта
|
|
||||||
запрещает заморозка. Отсюда нет ни «повысить», ни «встать раньше»: вместо
|
|
||||||
повышения — смена цели или включение в набор.
|
|
||||||
|
|
||||||
### Секция «блокеры» в беклоге
|
|
||||||
|
|
||||||
Блокер — **состояние** (спринт не может продолжаться ни одной задачей), а не
|
|
||||||
полка: он живёт ровно до ответа человека, и записи в такой секции не успевают
|
|
||||||
жить. Основание измерено: **два «блокера» из двух ничего не блокировали** — в
|
|
||||||
обоих файлах записано «что заблокировано: ничего». Отсюда разделение вопроса и
|
|
||||||
блокера.
|
|
||||||
|
|
||||||
### Запись о сделанной задаче
|
|
||||||
|
|
||||||
У сделанной задачи записи не остаётся: файл и строка удаляются. Ей хватает
|
|
||||||
коммита и документации; вторая запись была бы вторым домом для того же факта.
|
|
||||||
Вопрос «что было в спринте N» отвечается даром — `SPRINT.md` лежит под git.
|
|
||||||
|
|
||||||
## Числа первого замера
|
|
||||||
|
|
||||||
Одна сессия, шесть закрытых задач. **Выборка нетипичная, статус — первый
|
|
||||||
замер.** Приведены не как константы, а чтобы следующий замер было с чем
|
|
||||||
сравнить.
|
|
||||||
|
|
||||||
- **Беклог вырос с 29 до 38**: заведено 15, закрыто 6 (две родились и умерли
|
|
||||||
внутри сессии). Прирост **2,5 задачи на одну закрытую** — ревью и
|
|
||||||
эксплуатационные проходы производят работу быстрее, чем мы её потребляем.
|
|
||||||
- **Одна из шести задач была внеплановой** — дозакрытие находок, вставленное в
|
|
||||||
ход работы, потому что дефект затирал маршрут тренировки необратимо, а
|
|
||||||
пересборка журнала повторяла то же поражение. Отсюда класс «необратимый
|
|
||||||
ущерб» как единственное, что врывается в замороженный спринт: правило не
|
|
||||||
придумано, оно уже применялось.
|
|
||||||
- **15 часов на шесть задач**: пять заняли от 1 ч 16 мин до 2 ч 14 мин (медиана
|
|
||||||
≈ 1 ч 55 мин), шестая — 5 ч 42 мин в два захода. Мерилось **до** сужения
|
|
||||||
конвейера ревью; замер устарел и подлежит повторению.
|
|
||||||
- **Шесть задач за сессию** — предел одного контекста, а не спринта. Спринт
|
|
||||||
сессией не ограничен, перенос числа условен.
|
|
||||||
|
|
||||||
Умолчание «5–8 задач в спринте» выведено отсюда и остаётся **ориентиром, а не
|
|
||||||
законом**. Пересматривается на разборе прошедшего спринта — шаг 2 сессии, и ничей
|
|
||||||
другой.
|
|
||||||
|
|
||||||
## Что из черновика было не решено и решено позже
|
|
||||||
|
|
||||||
| Вопрос черновика | Где решён |
|
|
||||||
| --- | --- |
|
|
||||||
| название процесса | решение Z: имени нет, процесс это `av-dev` |
|
|
||||||
| «Ближайшая цель» прозой в `docs/plan.md` как второй дом цели спринта | решение E: `plan.md` растворяется в `PLAN.md` целей |
|
|
||||||
|
|
||||||
## Судьба самого черновика
|
|
||||||
|
|
||||||
Документ описывал процесс, а процесс живёт в плагинах этого репозитория, не в
|
|
||||||
healthlog. Содержимое разошлось: правила — в `av-dev-pm:tasks` и
|
|
||||||
`av-dev-pm:session`, обоснования и числа — сюда. Оригинал в git не коммитился и
|
|
||||||
удаляется при переезде healthlog на канон.
|
|
||||||
@@ -3,74 +3,136 @@
|
|||||||
Личный маркетплейс плагинов для разработки. Процесс имени не имеет — он и есть
|
Личный маркетплейс плагинов для разработки. Процесс имени не имеет — он и есть
|
||||||
`av-dev`.
|
`av-dev`.
|
||||||
|
|
||||||
Что решено и почему — [DECISIONS.md](DECISIONS.md). Что осталось сделать —
|
Что решено и почему — [журнал решений](decisions/README.md).
|
||||||
[TODO.md](TODO.md) и [REMAINING.md](REMAINING.md). Как процесс дошёл до текущей
|
|
||||||
формы — [HISTORY.md](HISTORY.md).
|
|
||||||
|
|
||||||
## Плагины
|
## Плагины
|
||||||
|
|
||||||
- **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` /
|
установку, она не понадобилась ни разу, и плагины слились —
|
||||||
`upgrade`, плюс скрипт `docs.py`. Там же лежит копия языка проектных
|
[тема 64](decisions/64-three-plugins-merged.md) журнала решений.
|
||||||
текстов — информационный стиль, англицизмы, жаргон; дом у него общий,
|
|
||||||
`shared/language.md`;
|
Имя **скилла** несёт префикс материала, с которым он работает: `doc-`, `task-`,
|
||||||
- `healthcheck` — здоровье документации **судом, а не машиной**: не разошлись
|
`code-`. Вызов выходит вида `/av-dev:<скилл>`. Префикса нет ровно у одного —
|
||||||
ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом —
|
`canon`: он работает не с материалом, а с **формой**, общей у всех частей
|
||||||
`doc-consistency` (документы между собой и с openspec) и `doc-code-drift`
|
проекта.
|
||||||
(факты против кода) — и разбирает урожай порциями. Дорого, поэтому не на
|
|
||||||
каждой задаче. Язык документов вычитывает отдельный агент `doc-wording`, и
|
### av-dev — форма, документы, учёт, работа
|
||||||
зовут его не отсюда, а те, кто только что писал текст: `docs`, `init` и
|
|
||||||
`canon`;
|
**Форма раскладки.** Одна на весь проект, и держит её один скилл.
|
||||||
- `docs` — содержимое канона по ходу разработки: ADR из архивного
|
|
||||||
|
- `canon` — раскладка проекта и её обновление: `check` / `adopt` / `upgrade`,
|
||||||
|
плюс скрипт `docs.py`. `check` сверяет раскладку документов, `adopt` заводит
|
||||||
|
все части сразу и зовёт владельцев каталога задач и `openspec/`, `upgrade`
|
||||||
|
повышает **всю** раскладку по журналу версий — общему, и на документы, и на
|
||||||
|
каталог задач. Содержимого он не ведёт: это соседние скиллы.
|
||||||
|
|
||||||
|
**Документы проекта.** Владеют **содержимым** `docs/` и `CLAUDE.md`; раскладка —
|
||||||
|
у `canon`.
|
||||||
|
|
||||||
|
- `doc-init` — новый проект: интервью по свободному описанию замысла →
|
||||||
|
первичная документация;
|
||||||
|
- `doc-healthcheck` — здоровье документации **судом, а не машиной**: не
|
||||||
|
разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон
|
||||||
|
разом — `doc-consistency` (документы между собой и с openspec) и
|
||||||
|
`doc-code-drift` (факты против кода) — и разбирает урожай порциями. Дорого,
|
||||||
|
поэтому не на каждой задаче. Язык документов вычитывает отдельный агент
|
||||||
|
`doc-wording`, и зовут его не отсюда, а те, кто только что писал текст:
|
||||||
|
`doc-sync`, `doc-init` и `canon`;
|
||||||
|
- `doc-sync` — содержимое канона по ходу разработки: ADR из архивного
|
||||||
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
||||||
архитектуры.
|
архитектуры. Правки двух родов, и спрашивается один: отражение сделанного
|
||||||
- **av-dev-tasks** — учёт работ. Владеет каталогом задач.
|
пишется молча, новая запись и новая норма — только по слову человека. Он же
|
||||||
- `tasks` — задачи и цели каталогом markdown-файлов, у каждой записи тип
|
считает и говорит строкой, сколько задач сделано с прошлой сверки документов.
|
||||||
(`goal`, `feature`, `fix`, `chore`, `research`), и тип задаёт её схему;
|
|
||||||
|
**Учёт работ.** Владеет каталогом задач.
|
||||||
|
|
||||||
|
- `task-track` — задачи каталогом markdown-файлов: у каждой тип (`feature`,
|
||||||
|
`fix`, `chore`, `research`), задающий её схему, а у проекта — стадия (`build`
|
||||||
|
или `support`), решающая, что значит порядок строк беклога;
|
||||||
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
||||||
`task-wording` (язык записей);
|
`task-wording` (язык записей);
|
||||||
- `groom` — груминг беклога: что сейчас самое важное и что перестало быть
|
- `task-groom` — груминг беклога: что сейчас самое важное и что перестало быть
|
||||||
важным. Ответ записывается **порядком строк** — приоритет это свойство
|
важным. Ответ записывается **порядком строк** — приоритет это свойство
|
||||||
очереди, и живёт он в индексе. Интерактивный: разбирает вопросы пачкой,
|
очереди, и живёт он в индексе. Интерактивный: разбирает вопросы пачкой,
|
||||||
переоценивает порциями по 5–8, расставляет верх очереди с доводом на
|
переоценивает порциями по 5–8, расставляет верх очереди с доводом на каждое
|
||||||
каждое движение.
|
движение.
|
||||||
- **av-dev-code** — работа по задачам: разведка, решение и проверка сделанного.
|
|
||||||
Владеет `openspec/`. **Требует OpenSpec и сам его заводит** — кроме сценария
|
**Работа по задачам.** Владеет `openspec/`. **Сценарий решения требует OpenSpec
|
||||||
разведки, которому он не нужен.
|
и заводит его сам** — разведке и обслуживанию он не нужен.
|
||||||
- `openspec` — завести, настроить и **проверить** `openspec/` в проекте:
|
|
||||||
`openspec init`, замена примера в `config.yaml` настройкой канонической
|
- `code-openspec` — завести, настроить и **проверить** `openspec/` в проекте:
|
||||||
формы, скрипт `openspec.py` (форма файла + сверка слепка с живой версией
|
`openspec init`, замена примера в `config.yaml` настройкой канонической формы,
|
||||||
|
скрипт `openspec.py` (форма файла + сверка слепка с живой версией
|
||||||
инструмента). Каталог принадлежит конвейеру, а не канону: без конвейера он
|
инструмента). Каталог принадлежит конвейеру, а не канону: без конвейера он
|
||||||
проекту не нужен, и `docs.py` о нём молчит;
|
проекту не нужен, и `docs.py` о нём молчит;
|
||||||
- `resolve` — одна задача от постановки до закрытия. **Точка входа одна, а
|
- `code-resolve` — одна задача от постановки до закрытия. **Точка входа одна, а
|
||||||
сценария два, и выбирает сценарий сам скилл, прочитав постановку:**
|
сценария три, и выбирает сценарий сам скилл, прочитав постановку:**
|
||||||
классифицировать задачу до вызова человек всё равно не может — «есть ли
|
классифицировать задачу до вызова человек всё равно не может — «есть ли
|
||||||
очевидный способ решения» видно после чтения записи.
|
очевидный способ решения» и «меняется ли спека» видно после чтения записи.
|
||||||
**Решение** идёт циклом SDD с чекпоинтом после ревью дизайна: объяснение
|
**Форм постановки две, и обе полноправны:** запись каталога и просто текст,
|
||||||
человеческим языком, повод скорректировать ход.
|
переданный вызовом, — так же берёт постановку `opsx:propose`. Текстом идут все
|
||||||
|
три сценария; отпадают ровно те шаги, у которых пропал предмет: `ready` гонять
|
||||||
|
нечего, закрывать нечего, а тип, границы и понимание постановки называются
|
||||||
|
вслух первой репликой — человек, написавший текст, рядом и правит одной фразой.
|
||||||
|
Записи в каталог скилл при этом не заводит ни до работы, ни задним числом.
|
||||||
|
**Решение** идёт циклом SDD с чекпоинтом сразу после предложения: объяснение
|
||||||
|
человеческим языком, повод скорректировать ход до того, как написан код.
|
||||||
|
**Обслуживание** (тип `chore`: тулчейн и сборка, зависимости, гит-хуки,
|
||||||
|
перенос, чистка) change не заводит и планового стопа не имеет вовсе:
|
||||||
|
дельта-спек у него нет **по построению**, то есть цикл SDD здесь не урезан, а
|
||||||
|
остаётся без входа. Ревью идёт фиксированным планом без change —
|
||||||
|
`autotests` и `operations`, плюс `conventions` с техническим
|
||||||
|
разбором, если дифф трогает код; главный шаг сценария — синк документации,
|
||||||
|
потому что обслуживание чаще прочих двигает как раз те факты, которые
|
||||||
|
сверяются с кодом. Правка гейта сверяется по составу проверок, а не по цвету.
|
||||||
|
Нашлась дельта-спека — задача **оказалась шире своего типа**: работа
|
||||||
|
останавливается, тип называется (`fix` или `feature`), человек получает
|
||||||
|
объяснение простым языком и два решения — переформулировать запись и решать её
|
||||||
|
процессом того типа следующим прогоном либо прекратить; «доделать как
|
||||||
|
обслуживание» решением не является.
|
||||||
**Разведка** (тип `research`, сырая идея, мутная постановка) кода не пишет
|
**Разведка** (тип `research`, сырая идея, мутная постановка) кода не пишет
|
||||||
вовсе: её чекпоинт — варианты, 2–4 способа решить с ценой каждого, — стоит до
|
вовсе: её чекпоинт — варианты, 2–4 способа решить с ценой каждого, — стоит до
|
||||||
первого написанного требования, а исход уезжает в документы канона и в
|
первого написанного требования, а исход уезжает в документы канона и в задачи.
|
||||||
задачи. Выбранный способ реализуется **следующим прогоном**, и запускает его
|
Обе пачки — документы и записи — **вычитываются перед коммитом** своими
|
||||||
|
проходами: `doc-wording` по документам, `task-form` и `task-wording` по
|
||||||
|
записям. Выбранный способ реализуется **следующим прогоном**, и запускает его
|
||||||
человек: смена сценария по ходу — событие с названным исходом, а не тихий
|
человек: смена сценария по ходу — событие с названным исходом, а не тихий
|
||||||
поворот. Оба сценария лежат справочниками и одинаково — `references/solve.md`
|
поворот.
|
||||||
и `references/research.md`; в самом скилле только вход, развилка и правила,
|
**Письмо уходит агентам:** спеки, код и правки по находкам ревью пишет
|
||||||
не зависящие от сценария. OpenSpec нужен решению, разведке — нет;
|
отдельный агент по заданию, а оркестратор ставит задание и читает короткий
|
||||||
- `review` — конвейер ревью **по темам**: документ проекта либо
|
возврат. Контекст ему нужен под чекпоинт, сверку плана с исходом и доклад —
|
||||||
заводит тему проверки, либо питает чужую тему источником, либо процессный и в
|
содержимое тронутых файлов и вывод гейта вытесняют оттуда постановку и
|
||||||
ревью не читается вовсе. Разметка идёт **один раз на задачу**, сразу после
|
одобренное, и вытесняют молча. Разведка сюда не попадает: её записка и записи
|
||||||
`propose`: агент `review-scope` меряет изменение по двум осям — размер и
|
задач и есть исход, из которого собирается доклад.
|
||||||
сложность — и берёт метку как максимум по ним. Одна метка правит **обе**
|
Все три сценария лежат справочниками и одинаково —
|
||||||
стадии ревью: дизайна (`small` — только сверка спек; `medium` — плюс
|
`references/solve.md`, `references/maintain.md` и `references/research.md`; в
|
||||||
рубрика; `large` — плюс архитектурный проход) и кода (`small` — гейт, спеки,
|
самом скилле только вход, развилка и правила, не зависящие от сценария;
|
||||||
код, триаж; `medium` — плюс приёмник тем; `large` — плюс доказательство:
|
- `code-review` — конвейер ревью **по темам**: документ проекта либо заводит
|
||||||
запуск, замер, построенный путь, 5–10% задач). Десять агентов-проходов.
|
тему проверки, либо питает чужую тему источником, либо процессный и в ревью не
|
||||||
- **av-dev-git** — `commit`: сообщения в личном стиле.
|
читается вовсе. **Состав постоянный, метки у прогона нет:** гейт, сверка со
|
||||||
|
спекой, разбор кода, триаж; приёмник тем идёт, когда у проекта есть свои темы.
|
||||||
|
Цикл задачи проверяет **корректность и механику** против записанного критерия —
|
||||||
|
дельта-спеки, конвенции, инварианты `CLAUDE.md`, вывод инструментов; темы
|
||||||
|
`security`, `operations` и `architecture` закрыты в нём сверкой с записанными
|
||||||
|
инвариантами, и только. Находки по умолчанию чинятся инлайн и молча, человеку
|
||||||
|
уходит необратимое и то, что меняет дельта-спеки, а задачи из урожая заводятся
|
||||||
|
по его слову. Каждый проход — свой агент, перечень держит сам скилл;
|
||||||
|
- `code-deep-review` — **глубокое ревью области**, а не задачи: модуля, слоя,
|
||||||
|
сервиса целиком. Здесь живут тяжёлые проходы, которых в цикле задачи нет, —
|
||||||
|
`review-adversary` строит путь и **прогоняет** падающий тест, `review-ops`
|
||||||
|
снимает числа замером, `architecture` судит форму решения на широком входе;
|
||||||
|
рядом идёт `code` по коду целиком. Исход — не правки, а разговор: находки
|
||||||
|
разбираются с человеком по одной, и согласованное уезжает задачами через
|
||||||
|
`task-track`. Дорого — не на
|
||||||
|
задаче и не по расписанию; вход копит сам цикл строками «отложено» в границах
|
||||||
|
покрытия.
|
||||||
|
|
||||||
Соглашение об именах: имя **плагина** длинное с префиксом `av-dev-`, имена
|
### av-dev-git
|
||||||
**скилов** внутри — короткие. Вызов выходит вида `/av-dev-<плагин>:<скилл>`.
|
|
||||||
|
`commit` — сообщения в личном стиле. Отдельным плагином потому, что нужен и в
|
||||||
|
репозитории, который к канону не приведён и никогда не будет.
|
||||||
|
|
||||||
Кто кого зовёт. Сплошная стрелка — вызов скилла через пространство имён, не
|
Кто кого зовёт. Сплошная стрелка — вызов скилла через пространство имён, не
|
||||||
импорт; пунктир — совет позвать, а не вызов. Агенты-проходы в графе не показаны:
|
импорт; пунктир — совет позвать, а не вызов. Агенты-проходы в графе не показаны:
|
||||||
@@ -78,21 +140,24 @@
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
subgraph pipe["av-dev-code — исполнение; сценарий решения требует OpenSpec"]
|
subgraph avdev["av-dev — один плагин, весь процесс"]
|
||||||
|
subgraph pipe["работа по задачам; сценарий решения требует OpenSpec"]
|
||||||
direction LR
|
direction LR
|
||||||
tp["resolve<br/>2 сценария: разведка и решение"] --> rp["review<br/>10 агентов-проходов"]
|
tp["code-resolve<br/>3 сценария: разведка,<br/>решение, обслуживание"] --> rp["code-review<br/>агенты-проходы"]
|
||||||
osp["openspec<br/>заводит и проверяет openspec/"]
|
osp["code-openspec<br/>заводит и проверяет openspec/"]
|
||||||
|
deep["code-deep-review<br/>область, а не задача:<br/>тяжёлые проходы"]
|
||||||
end
|
end
|
||||||
subgraph docsp["av-dev-docs — документация, владеет docs/"]
|
canon["canon<br/>форма раскладки всего проекта"]
|
||||||
|
subgraph docsp["документы, владеют содержимым docs/"]
|
||||||
direction LR
|
direction LR
|
||||||
init["init"]
|
init["doc-init"]
|
||||||
canon["canon"]
|
docs["doc-sync"]
|
||||||
docs["docs"]
|
hc["doc-healthcheck"]
|
||||||
hc["healthcheck"]
|
|
||||||
end
|
end
|
||||||
subgraph tasksp["av-dev-tasks — учёт работ"]
|
subgraph tasksp["учёт работ"]
|
||||||
direction LR
|
direction LR
|
||||||
groom["groom"] --> tasks["tasks"]
|
groom["task-groom"] --> tasks["task-track"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
init --> tasks
|
init --> tasks
|
||||||
init --> osp
|
init --> osp
|
||||||
@@ -101,7 +166,8 @@ flowchart TB
|
|||||||
canon --> hc
|
canon --> hc
|
||||||
hc --> tasks
|
hc --> tasks
|
||||||
docs --> rp
|
docs --> rp
|
||||||
rp --> tasks
|
rp -.->|"строки «отложено»"| deep
|
||||||
|
deep --> tasks
|
||||||
groom -.-> hc
|
groom -.-> hc
|
||||||
opsx["opsx:* — внешний плагин:<br/>explore, propose, apply, archive"]
|
opsx["opsx:* — внешний плагин:<br/>explore, propose, apply, archive"]
|
||||||
git["av-dev-git: commit"]
|
git["av-dev-git: commit"]
|
||||||
@@ -112,27 +178,28 @@ 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`.
|
|
||||||
**Мягкая** значит, что у любого вызова есть ветка «не разрешился»: соседа в
|
|
||||||
проекте нет — вызывающий называет строкой, чего теперь не делает никто, и работу
|
|
||||||
не останавливает. Как именно зовут соседа и что делают, когда вызов не
|
|
||||||
разрешился, — `shared/plugin-boundary.md`: правило нужно большинству скиллов, и
|
|
||||||
ни один плагин им не владеет. То, что нужно нескольким дословно — граница
|
|
||||||
плагинов, язык проектных текстов, словарь сопровождения, — живёт домом в
|
|
||||||
`shared/` и уезжает в каждый плагин помеченной копией.
|
|
||||||
|
|
||||||
## Канон документов проекта
|
**Отсутствовать может не плагин, а часть раскладки проекта**: `.av-dev.toml`,
|
||||||
|
`docs/`, каталог задач, `openspec/`. Тогда вызывающий называет строкой, чего теперь не делает
|
||||||
|
никто, и работу не останавливает. Правило целиком —
|
||||||
|
[shared/absence.md](av-dev/shared/absence.md): оно нужно почти каждому скиллу, и
|
||||||
|
ни один им не владеет. Там же, в `shared/`, живут язык проектных текстов,
|
||||||
|
словарь сопровождения и **перечень осей процесса**
|
||||||
|
[axes.md](av-dev/shared/axes.md) — какие закрытые словари правят ходом работы,
|
||||||
|
где дом каждого и чего он **не** решает. Скиллы читают эти дома по ссылке, а
|
||||||
|
дословной копией оттуда уезжает лишь то, что обязано лежать внутри промпта.
|
||||||
|
|
||||||
|
## Канон раскладки проекта
|
||||||
|
|
||||||
Все проекты приводятся к одной раскладке — так проще ориентироваться, когда
|
Все проекты приводятся к одной раскладке — так проще ориентироваться, когда
|
||||||
проектов много, и рядом OpenSpec тоже держит строгую структуру. `CLAUDE.md` плюс
|
проектов много, и рядом OpenSpec тоже держит строгую структуру. `CLAUDE.md` плюс
|
||||||
`docs/` (паспорт, архитектура, база, безопасность, конвенции, разведка, ADR,
|
`docs/` (паспорт, архитектура, база, безопасность, конвенции, разведка, ADR,
|
||||||
ревью, задачи) и `openspec/` — **раскладка целиком, роли документов и правило
|
ревью, задачи) и `openspec/` — **раскладка целиком, роли документов и правило
|
||||||
единственного дома живут одним домом**:
|
единственного дома живут одним домом**:
|
||||||
[canon.md](av-dev-docs/skills/canon/references/canon.md). Здесь она не
|
[canon.md](av-dev/skills/canon/references/canon.md). Здесь она не
|
||||||
пересказывается: копия перечня путей уже расходилась с домом, и как раз в
|
пересказывается: копия перечня путей уже расходилась с домом, и как раз в
|
||||||
обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает
|
обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает
|
||||||
нарушением.
|
нарушением.
|
||||||
@@ -148,20 +215,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:canon`.
|
||||||
версионируется, и проекты повышаются по [журналу
|
Раскладка версионируется, и проекты повышаются по [журналу
|
||||||
версий](av-dev-docs/skills/canon/references/changelog.md); версия проекта живёт в
|
версий](av-dev/skills/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`.
|
||||||
|
|
||||||
## Подключение
|
## Подключение
|
||||||
|
|
||||||
@@ -176,9 +246,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
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -194,19 +262,28 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**При установке в проект, где лежали проектные копии** скиллов и агентов
|
**При установке в проект, где лежали проектные копии** скиллов и агентов —
|
||||||
(`.claude/skills/` — голые `task-pipeline`, `review-pipeline`
|
снеси их. Перечень полный, и он же дом: скиллы носят его помеченной копией,
|
||||||
и с префиксом проекта `<проект>-task-pipeline`,
|
потому что предупреждают о том же в момент работы.
|
||||||
`.claude/agents/<проект>-review-*.md`) — снеси их. Две копии одного скилла
|
|
||||||
расходятся, и побеждает та, что короче названа.
|
<!-- дом: проектные-копии -->
|
||||||
|
|
||||||
|
Голые имена в `.claude/skills/`: `resolve`, `review`, а у проектов прошлого
|
||||||
|
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`. С префиксом
|
||||||
|
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`. Агенты:
|
||||||
|
`.claude/agents/<проект>-review-*.md`.
|
||||||
|
|
||||||
|
Две копии одного скилла расходятся, и побеждает та, что **короче названа**:
|
||||||
|
короткое имя разрешится в устаревшую проектную копию молча и без признаков
|
||||||
|
подмены.
|
||||||
|
|
||||||
|
<!-- /дом: проектные-копии -->
|
||||||
|
|
||||||
## Обновление
|
## Обновление
|
||||||
|
|
||||||
@@ -225,9 +302,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
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -301,7 +376,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 гейт коммита: проверки документов
|
||||||
@@ -341,13 +416,13 @@ python3 scripts/frontmatter.py # 0 в порядке, 1 расхождени
|
|||||||
простого скаляра начинает вложенное отображение, так что «конвейер ревью: гейт,
|
простого скаляра начинает вложенное отображение, так что «конвейер ревью: гейт,
|
||||||
сверка…» — это не текст с двоеточием, а синтаксическая ошибка. Так было
|
сверка…» — это не текст с двоеточием, а синтаксическая ошибка. Так было
|
||||||
написано часть описаний плагинов, и читались они правильно — замер и разбор
|
написано часть описаний плагинов, и читались они правильно — замер и разбор
|
||||||
в [DECISIONS.md](DECISIONS.md), решение III;
|
в [решении Р61](decisions/17-notes-tier-work-kind-roadmap.md) журнала;
|
||||||
- **`name`, разошедшееся с именем каталога скилла или файла charter'а.** Вызов
|
- **`name`, разошедшееся с именем каталога скилла или файла charter'а.** Вызов
|
||||||
разрешается по каталогу, а сообщение о промахе говорит «нет такого скилла»,
|
разрешается по каталогу, а сообщение о промахе говорит «нет такого скилла»,
|
||||||
а не «имя не то»;
|
а не «имя не то»;
|
||||||
- **цвет 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'а, а модель
|
||||||
потом меняется калибровкой;
|
потом меняется калибровкой;
|
||||||
@@ -383,19 +458,25 @@ python3 scripts/copies.py # 0 сошлось, 1 расхождение, 2 р
|
|||||||
сверку не входят. Маркер, уехавший в проект вместе со скелетом, там полезен: он
|
сверку не входят. Маркер, уехавший в проект вместе со скелетом, там полезен: он
|
||||||
говорит, что у текста есть дом и правится он там.
|
говорит, что у текста есть дом и правится он там.
|
||||||
|
|
||||||
**Дом правила, общего для нескольких плагинов, лежит в `shared/` и ни одному из
|
**Дом правила, общего нескольким скиллам, лежит в `av-dev/shared/` и ни одному
|
||||||
них не принадлежит.** Так живёт язык проектных текстов: он одинаково нужен
|
из них не принадлежит.** Так живут язык проектных текстов, словарь
|
||||||
документам канона и задачам, и хранить его внутри одного плагина значило бы
|
сопровождения и правило об отсутствующих частях раскладки: каждое нужно
|
||||||
отдать общее правило во владение половине. Так же живёт граница плагинов —
|
многим, и хранить его внутри одного скилла значило бы отдать общее правило во
|
||||||
правило обращения к соседу. Плагин везёт копию и потому остаётся
|
владение части.
|
||||||
самодостаточным — `shared/` нужен этому репозиторию, а не установленному
|
|
||||||
плагину.
|
**Копия при этом делается не всегда.** Пока плагинов было три, копия была
|
||||||
|
единственным способом: путь в дерево соседа не разрешался. Внутри одного дерева
|
||||||
|
справочник читается **по ссылке**, и дословная копия остаётся ровно там, где
|
||||||
|
текст обязан лежать **внутри самого промпта**: в уставе агента, где правило и
|
||||||
|
есть критерий суждения; в `SKILL.md`, который целиком и есть промпт скилла, — за
|
||||||
|
ссылкой скилл пошёл бы отдельным чтением, а правило нужно ему в тот момент,
|
||||||
|
когда он решает; и в скелетах, уезжающих в репозиторий проекта.
|
||||||
|
|
||||||
**Дом ставится в `shared/` только тогда, когда владельца нет.** У адресов
|
**Дом ставится в `shared/` только тогда, когда владельца нет.** У адресов
|
||||||
владелец есть: раскладку `docs/` держит канон, каталог задач — плагин задач, и
|
владелец есть: раскладку `docs/` держит `canon`, каталог задач —
|
||||||
переносить их наружу значило бы отобрать у владельца его же предмет. Общее без
|
`task-track`, и переносить их наружу значило бы отобрать у владельца его же
|
||||||
владельца едет копией из `shared/`; чужое с владельцем остаётся дома, а
|
предмет. Общее без владельца живёт в `shared/`; чужое с владельцем остаётся
|
||||||
потребитель на него ссылается.
|
дома, а потребитель на него ссылается.
|
||||||
|
|
||||||
Скрипт ловит четыре вещи: копия разошлась с домом (с диффом), копия указывает не
|
Скрипт ловит четыре вещи: копия разошлась с домом (с диффом), копия указывает не
|
||||||
на тот файл, дом остался без копий, разметка сломана. Чего он **не** ловит —
|
на тот файл, дом остался без копий, разметка сломана. Чего он **не** ловит —
|
||||||
@@ -430,7 +511,7 @@ python3 scripts/resync.py # переписать тела всех разо
|
|||||||
## Проверка адресов документов
|
## Проверка адресов документов
|
||||||
|
|
||||||
Адрес документа принадлежит одному плагину, а называют его все: `docs/*` стоит
|
Адрес документа принадлежит одному плагину, а называют его все: `docs/*` стоит
|
||||||
примерно в сорока местах конвейера, `tasks/ROADMAP.md` — в четырёх местах канона.
|
примерно в сорока местах конвейера, `tasks/BACKLOG.md` — в нескольких местах канона.
|
||||||
Переименование в каноне до этих мест само не доходит.
|
Переименование в каноне до этих мест само не доходит.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -490,7 +571,7 @@ python3 scripts/diagrams.py A.md B.md # только названные фай
|
|||||||
|
|
||||||
## Гейт коммита
|
## Гейт коммита
|
||||||
|
|
||||||
Все шесть проверок стоят в `pre-commit` через [lefthook](https://lefthook.dev) —
|
Все семь проверок стоят в `pre-commit` через [lefthook](https://lefthook.dev) —
|
||||||
конфиг в [lefthook.yml](lefthook.yml), ставится один раз на клон:
|
конфиг в [lefthook.yml](lefthook.yml), ставится один раз на клон:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -503,6 +584,7 @@ lefthook run pre-commit # прогнать руками, не коммитя
|
|||||||
| фронтматтеры | правка `*.md` или `*.json` | весь репозиторий | миллисекунды |
|
| фронтматтеры | правка `*.md` или `*.json` | весь репозиторий | миллисекунды |
|
||||||
| копии правил | правка `*.md` | весь репозиторий | миллисекунды |
|
| копии правил | правка `*.md` | весь репозиторий | миллисекунды |
|
||||||
| адреса документов | **каждый коммит** | весь репозиторий | ~0.07 с |
|
| адреса документов | **каждый коммит** | весь репозиторий | ~0.07 с |
|
||||||
|
| журнал решений | **каждый коммит** | весь репозиторий | ~0.09 с |
|
||||||
| диаграммы | правка `*.md` | staged-файлы | ~1 с на файл |
|
| диаграммы | правка `*.md` | staged-файлы | ~1 с на файл |
|
||||||
| `ruff check --fix` | правка `*.py` | staged-файлы | доли секунды |
|
| `ruff check --fix` | правка `*.py` | staged-файлы | доли секунды |
|
||||||
| `pyrefly check` | правка `*.py` | staged-файлы | доли секунды |
|
| `pyrefly check` | правка `*.py` | staged-файлы | доли секунды |
|
||||||
@@ -518,7 +600,16 @@ Glob разводит две половины: коммит, трогающий
|
|||||||
обходит весь репозиторий за сотые доли секунды — экономить тут нечего;
|
обходит весь репозиторий за сотые доли секунды — экономить тут нечего;
|
||||||
`addresses.py` идёт **без glob вовсе**, потому что сводит две стороны: перечень
|
`addresses.py` идёт **без glob вовсе**, потому что сводит две стороны: перечень
|
||||||
адресов лежит в `*.py` владельца, а упоминания — в `*.md` соседей, и коммит с
|
адресов лежит в `*.py` владельца, а упоминания — в `*.md` соседей, и коммит с
|
||||||
переименованием документа трогает только первую.
|
переименованием документа трогает только первую; `decisions.py` — по той же
|
||||||
|
причине: файл темы и ссылки на него лежат порознь, и переименование темы трогает
|
||||||
|
только одну сторону.
|
||||||
|
|
||||||
|
**Что судит `decisions.py`.** Номер записи журнала обязан быть один: буквенные
|
||||||
|
метки решений выродились до пятибуквенных и разъехались молча — `АЕАКЛ` означала
|
||||||
|
и тему 53, и тему 65. И подпись ссылки обязана называть свою цель: в
|
||||||
|
`[тема 5](decisions/05-project-start-lifecycle.md)` номер записан дважды, словом и путём, —
|
||||||
|
дубль неизбежный (иначе ссылку не прочитать, не открыв) и потому сверяемый, как
|
||||||
|
всякая копия.
|
||||||
|
|
||||||
**`ruff` чинит безопасное сам, и починка доносится до этого же коммита**
|
**`ruff` чинит безопасное сам, и починка доносится до этого же коммита**
|
||||||
(`stage_fixed: true`). Иначе исправленный файл остался бы в рабочем дереве, а в
|
(`stage_fixed: true`). Иначе исправленный файл остался бы в рабочем дереве, а в
|
||||||
|
|||||||
-140
@@ -1,140 +0,0 @@
|
|||||||
# Остатки, открытые вопросы и принятые пределы
|
|
||||||
|
|
||||||
Состояние пересобирается по ходу работы; счётчика тем и коммитов здесь нет
|
|
||||||
намеренно — он протухает молча, а двигать его некому. Что и когда решено —
|
|
||||||
[DECISIONS.md](DECISIONS.md), записи датированы.
|
|
||||||
|
|
||||||
План работ — [TODO.md](TODO.md). Решения с причинами — [DECISIONS.md](DECISIONS.md).
|
|
||||||
Здесь то, что **не** является работой из плана: незакрытые риски, честно принятые
|
|
||||||
пределы и вопросы, у которых пока нет ответа.
|
|
||||||
|
|
||||||
## Главный незакрытый риск
|
|
||||||
|
|
||||||
**Калибровка не сделана, а уставы проходов с тех пор переписывались не раз.**
|
|
||||||
|
|
||||||
Правки шли волнами: вынос в плагин (предмет проверки заменён ссылкой на раздел
|
|
||||||
брифа), переход на пути документов канона, две правки по находкам ревью, граф
|
|
||||||
порядка, ступень `wide`, пересмотр триггеров ступени.
|
|
||||||
`references/calibration.md` требует при каждой такой правке замерить, помогла ли
|
|
||||||
она, — **ни одного замера не было**. Числа правок здесь нет намеренно: счётчик
|
|
||||||
пришлось бы двигать вручную, и он уже однажды отстал.
|
|
||||||
|
|
||||||
**Неизмеренные изменения копятся** в том самом месте, где присваивается
|
|
||||||
severity. Пробы готовы и синтетических не нужно — четыре реальные находки
|
|
||||||
прошедшей сессии healthlog:
|
|
||||||
|
|
||||||
- скелет из `null` затирает маршрут тренировки молча и необратимо;
|
|
||||||
- откат бинаря поверх новой схемы стартует без единого слова;
|
|
||||||
- канонизация внутри транзакции — 768 МиБ пика, 5.019 с удержания блокировки;
|
|
||||||
- `-1 >= -1` читается как «журнал разобран целиком».
|
|
||||||
|
|
||||||
Ожидаемый исход известен и его стоит проверить первым: метод переносится, а
|
|
||||||
**severity деградирует**. Третья находка без слота под представление данных и
|
|
||||||
настройки хранилища превращалась из `critical` с прогнанным оракулом в условное
|
|
||||||
наблюдение. Ровно ради этого случая канон развёл числа (`docs/research/`) и
|
|
||||||
настройки (`docs/database.md`) по разным домам и **обязал проход их сшивать** —
|
|
||||||
но работает ли обязанность, не проверено. Оркестратор реагирует на severity,
|
|
||||||
поэтому цена — не «не найдём», а **«найдём и не починим»**.
|
|
||||||
|
|
||||||
Сама работа — [TODO.md](TODO.md), раздел «Калибровка»; здесь только цена: замер
|
|
||||||
стоит перед переездом jellybit и блокирует его (решение 39), а ожидаемый исход
|
|
||||||
уже назван выше.
|
|
||||||
|
|
||||||
**«Главный» здесь про цену, а не про очередь.** Первой идёт адаптация healthlog
|
|
||||||
(TODO, раздел «Живые проекты»): без неё нет проекта под каноном, на котором
|
|
||||||
работают остальные скиллы. Калибровка блокирует один шаг — переезд jellybit, — а
|
|
||||||
не всё подряд.
|
|
||||||
|
|
||||||
## Что ещё не сделано
|
|
||||||
|
|
||||||
Список работ — в [TODO.md](TODO.md). Здесь только то, что стоит держать в голове
|
|
||||||
отдельно:
|
|
||||||
|
|
||||||
- **Ни один скилл не прогонялся на живом проекте.** `docs.py` прогнан на
|
|
||||||
healthlog и jellybit в режиме `check` и находит осмысленный дрейф; `init`,
|
|
||||||
`canon adopt`, `canon upgrade`, скиллы `docs`, `openspec` и `resolve` не
|
|
||||||
исполнялись ни разу. `openspec.py`, раскол плагинов и оба чекпоинта `resolve`
|
|
||||||
проверены только на фикстурах и на установке каждого плагина в одиночку.
|
|
||||||
- **Проектные копии в healthlog и jellybit.** Два `.claude/skills/` и одиннадцать
|
|
||||||
`.claude/agents/` старого поколения — их надо снести при установке.
|
|
||||||
**Совпадение имён при этом больше не грозит:** скиллы jellybit названы
|
|
||||||
`task-pipeline`, `review-pipeline`, `task-batch`, а плагин теперь даёт
|
|
||||||
`resolve`, `review`, `openspec` — ни одно имя не пересекается. Риск снят
|
|
||||||
переименованием, а не устранён по существу: заведись у проекта свой `review`,
|
|
||||||
Claude Code держал бы обе пары, и короткое имя увело бы в копию молча.
|
|
||||||
|
|
||||||
## Открытые вопросы
|
|
||||||
|
|
||||||
**`doc-consistency` не различает «про нас» и «про то, что мы производим».**
|
|
||||||
Первый прогон на самом dev-skills предъявил репозиторию правило из
|
|
||||||
`av-dev-git/skills/commit/SKILL.md` — а это продукт, уезжающий в чужие проекты,
|
|
||||||
а не правило, которому подчиняется маркетплейс. На проекте под каноном такой
|
|
||||||
путаницы нет (там документы описывают сам проект), поэтому в устав это пока не
|
|
||||||
дописано: сперва посмотреть, встретится ли класс ещё раз.
|
|
||||||
|
|
||||||
**Как проверять, что канон не разошёлся с проектами после `upgrade`.** `canon
|
|
||||||
check` сверяет версию, но не то, что миграционные записи journal'а применены
|
|
||||||
верно. Проект может нести `"canon": 2` и не иметь того, что версия 2 требовала.
|
|
||||||
Ответ выбран: шагом 6 `upgrade` зовутся оба судьи документов — проверка не
|
|
||||||
механическая, но других у существа записей нет. Останется открытым, пока не
|
|
||||||
прогнано на живом проекте: неизвестно, ловят ли они недоделанную миграцию или
|
|
||||||
только её последствия.
|
|
||||||
|
|
||||||
**Не выродились ли «границы покрытия» в шаблон.** Строка «что смотрели и чего не
|
|
||||||
смотрели» обязательна в докладе `check`, `adopt`, интейка, штурма и всех пяти
|
|
||||||
агентов канона и задач (`doc-consistency`, `doc-code-drift`, `doc-wording`,
|
|
||||||
`task-form`, `task-wording`) — девять мест, и проверить её исполнение некому:
|
|
||||||
приёмщик и исполнитель одно лицо (`groom/SKILL.md`, «Стимулы»). Выродившаяся
|
|
||||||
строка **хуже отсутствия**: доклад выглядит проверенным.
|
|
||||||
|
|
||||||
Приём не правится: это гипотеза об износе, а не находка, и менять работающее по
|
|
||||||
догадке дороже. **Наблюдение к первой обкатке на живом проекте:** если в трёх
|
|
||||||
докладах подряд границы покрытия совпали дословно или называют не то, чего
|
|
||||||
проверка действительно не касалась, — приём выродился, и вот тогда решать.
|
|
||||||
|
|
||||||
**Форма ADR при пересмотре решения.** Парный статус («старая запись получает
|
|
||||||
`заменено на`») судит агент `doc-consistency` — правило 6 его устава. Охват был
|
|
||||||
открытым вопросом, пока агент звался пачкой, отобранной работой; переезд вызова
|
|
||||||
в `av-dev-docs:healthcheck` с пачкой «весь канон» его снял. Остаётся зазор до
|
|
||||||
ближайшего прогона `healthcheck` и отсутствие механической проверки — то есть
|
|
||||||
пересмотр, сделанный сегодня, судится тогда, когда позовут сверку, а не в момент
|
|
||||||
правки.
|
|
||||||
|
|
||||||
## Известные пределы — приняты, чинить не планируется
|
|
||||||
|
|
||||||
**Транзакций на несколько файлов нет.** POSIX её не даёт без журнала. Окно сжато
|
|
||||||
до цепочки `rename` без ввода-вывода, а всё, что в окне может разъехаться,
|
|
||||||
сделано производным и восстанавливается `check --fix` без потерь.
|
|
||||||
|
|
||||||
**Оракул в критериях приёмки проверяется эвристикой.** Число пунктов проверяется
|
|
||||||
жёстко, наличие оракула — по слову, и это **только замечание**. В тексте прямо
|
|
||||||
сказано, что проверено меньше, чем требуется.
|
|
||||||
|
|
||||||
**Recall прохода по конвенциям равен качеству конвенций проекта.** Своего списка
|
|
||||||
у него нет: критерий берётся из `docs/conventions/`. На проекте с тонкими
|
|
||||||
конвенциями проход почти пуст, и charter это признаёт вслух.
|
|
||||||
|
|
||||||
**Доменного словаря в каноне нет.** Проходы получают факты, но не термины;
|
|
||||||
словарь строится каждый раз заново из спек и архитектуры. Цена не измерена.
|
|
||||||
|
|
||||||
**Смысловые дубли ловит только агент.** `docs.py` видит раскладку, но не то, что
|
|
||||||
раздел `docs/architecture.md` описывает поведение, уже записанное capability
|
|
||||||
`recognition`.
|
|
||||||
Граница объявляется вслух в каждом отчёте — это единственная защита от
|
|
||||||
«соблюдено» на проекте с тремя лишними файлами.
|
|
||||||
|
|
||||||
**Приёмщик и исполнитель совпали, и опор стало меньше.** Граница «пайплайн не
|
|
||||||
закрывает задачу» снята сознательно (решение P); защиты держатся текстом, а не
|
|
||||||
механикой. Реальных опор было три, осталось две: сохранённый отчёт триажа и
|
|
||||||
`reopen` (индексы под git показывают закрытие, потому что оно коммитится
|
|
||||||
отдельным коммитом учёта). Третья — приёмка шагом сессии — ушла вместе со
|
|
||||||
спринтами: у неё больше **нет момента**, и происходит она только тогда, когда
|
|
||||||
что-то бросилось в глаза на груминге. Это записано в самих скиллах, а не
|
|
||||||
спрятано.
|
|
||||||
|
|
||||||
**Копия правила в шаблонах проекта.** `adr/README.md` и `review.md` уезжают в
|
|
||||||
репозиторий и обязаны там что-то говорить, поэтому правило канона в них
|
|
||||||
копируется намеренно. Расхождение копии с домом ловит `scripts/copies.py` —
|
|
||||||
но только у **помеченной** копии, и только внутри маркетплейса. Остаётся на
|
|
||||||
человеке двое: пометить копию и завести запись в журнал версий, когда правка
|
|
||||||
уже уехала в проект.
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
# Что осталось сделать
|
|
||||||
|
|
||||||
**Здесь только работы и их порядок.** Чего здесь нет намеренно:
|
|
||||||
|
|
||||||
- **риски, открытые вопросы и принятые пределы** — [REMAINING.md](REMAINING.md);
|
|
||||||
- **почему решено так** — [DECISIONS.md](DECISIONS.md), записи датированы;
|
|
||||||
- **шаги повышения проекта с версии канона на версию** — журнал версий
|
|
||||||
([changelog.md](av-dev-docs/skills/canon/references/changelog.md)). Пересказ их
|
|
||||||
сюда был бы вторым домом, и прежний план на этом уже разъезжался: он повторял
|
|
||||||
записи версий 3, 4 и 5 построчно, и половина повторов протухла молча.
|
|
||||||
|
|
||||||
Сделанное отсюда **удаляется, а не помечается галочкой**. След остаётся в
|
|
||||||
коммитах и в `DECISIONS.md`; список из двух сотен `[x]` перестают читать целиком,
|
|
||||||
и живые пункты в нём теряются — прежний план умер именно так.
|
|
||||||
|
|
||||||
## Где мы сейчас
|
|
||||||
|
|
||||||
Плагинов четыре, и каждый ставится отдельно: `av-dev-docs` (канон документов и
|
|
||||||
их содержимое), `av-dev-tasks` (задачи и цели), `av-dev-code` (код по задачам:
|
|
||||||
цикл SDD, конвейер ревью, OpenSpec), `av-dev-git`. Общее, что нужно нескольким
|
|
||||||
дословно, живёт домом в `shared/` и уезжает копиями.
|
|
||||||
|
|
||||||
Канон документов — **версия 14**; формат задач — **версия 1**, своя и со своим журналом. Живые проекты стоят на 2–3 и на плагине
|
|
||||||
`av-dev-pm`, которого больше нет.
|
|
||||||
|
|
||||||
Бумажная часть закрыта аудитом четырёх плагинов и четырьмя пропусками правок
|
|
||||||
(DECISIONS, запись 59). Всё, что ниже, проверяется **только на живом коде**.
|
|
||||||
|
|
||||||
## 1. Живые проекты — вернуть в рабочее состояние
|
|
||||||
|
|
||||||
Блокирует всё остальное: под текущим каноном не стоит ни один проект, и ни один
|
|
||||||
скилл, кроме `docs.py check`, не исполнялся на живом коде ни разу
|
|
||||||
(см. REMAINING, «Что ещё не сделано»).
|
|
||||||
|
|
||||||
### healthlog — первым
|
|
||||||
|
|
||||||
- [ ] переустановить плагины: снять `av-dev-pm` и `av-dev-pipeline`, поставить
|
|
||||||
`av-dev-docs`, `av-dev-tasks`, `av-dev-code`, `av-dev-git`. Оба прежних
|
|
||||||
имени мертвы, и `plugin update` их не переименует — только снять и
|
|
||||||
поставить. `marketplace update`, затем `plugin update` — одного шага мало
|
|
||||||
(README, «Обновление»)
|
|
||||||
- [ ] удалить проектные копии: `.claude/skills/healthlog-{task,review}-pipeline`
|
|
||||||
и девять `.claude/agents/healthlog-review-*.md`. Они прошлого поколения и
|
|
||||||
после переезда указывают на документы, которых уже не будет
|
|
||||||
- [ ] `av-dev-docs:canon` в режиме `adopt` — он приведёт проект к канону 14
|
|
||||||
сразу, картой и с подтверждением. Файл-в-файл здесь не расписан: раскладку
|
|
||||||
знает скилл, и второй перечень разошёлся бы с ним
|
|
||||||
- [ ] каталог задач — в `tasks/` корня (канон 11), не в `docs/tasks/`, без
|
|
||||||
`SPRINT.md` (канон 12) и с версией формата в `tasks/.tasks.json` (журнал
|
|
||||||
задач, версия 1). Скилл задач зовётся из `adopt` сам
|
|
||||||
- [ ] гейт проекта: три шага вместо одного — `docs.py check`, `tasks.py check
|
|
||||||
--dir tasks`, `openspec.py check`. **Второй и третий раньше не были
|
|
||||||
нужны:** согласованность задач тянул за собой `docs.py`, форму `config.yaml`
|
|
||||||
он же. Теперь оба молчат, и без своих шагов дрейф перестанет ловиться
|
|
||||||
- [ ] разобрать урожай `doc-consistency` и `doc-code-drift` порциями — правило
|
|
||||||
единственного дома на живом проекте не проверял никто
|
|
||||||
|
|
||||||
### jellybit — после калибровки
|
|
||||||
|
|
||||||
Порядок не произволен: замер (раздел 3) блокирует переезд jellybit, и только его.
|
|
||||||
|
|
||||||
- [ ] то же, что у healthlog: плагины, проектные копии, `adopt`, каталог задач,
|
|
||||||
гейт
|
|
||||||
- [ ] проектные копии здесь опаснее: скиллы названы `task-pipeline`,
|
|
||||||
`review-pipeline` — **ровно как в плагине**, и короткое имя
|
|
||||||
может увести в устаревшую копию молча (REMAINING)
|
|
||||||
|
|
||||||
## 2. Учёт работ без спринтов — что осталось
|
|
||||||
|
|
||||||
Сделано: спринт снят со скрипта и текстов, приоритет стал порядком строк в
|
|
||||||
беклоге, гейт готовности переехал в `tasks.py ready`, `session` стал скиллом
|
|
||||||
`groom`, запись 12 в журнал версий канона написана.
|
|
||||||
|
|
||||||
- [ ] прогнать груминг на живом беклоге — на фикстуре проверялись команды, а не
|
|
||||||
сам разбор. Наблюдение к первому прогону: **не выродился ли шаг 4 в
|
|
||||||
«оставить как есть»** — признак тот, что доклад не называет ни одного
|
|
||||||
движения с доводом
|
|
||||||
|
|
||||||
## 3. Калибровка — блокирует переезд jellybit
|
|
||||||
|
|
||||||
- [ ] замер на четырёх находках healthlog: скелет из `null`, откат бинаря,
|
|
||||||
канонизация в транзакции, `-1 >= -1`. Цена и ожидаемый исход — REMAINING,
|
|
||||||
«Главный незакрытый риск»
|
|
||||||
|
|
||||||
## 4. Конвейер: что осталось после `resolve`
|
|
||||||
|
|
||||||
Сам скилл написан (`av-dev-code:resolve`, два сценария — разведка и решение,
|
|
||||||
по чекпоинту у каждого), `task-batch` удалён. Осталось то, что на бумаге не
|
|
||||||
проверяется:
|
|
||||||
|
|
||||||
- [ ] прогнать сценарий разведки на живой задаче: он написан целиком на бумаге и
|
|
||||||
не исполнялся ни разу. Самое неизвестное — выбор сценария на входе (не
|
|
||||||
уедет ли всё в решение, потому что «способ вроде понятен») и объём того,
|
|
||||||
что разведка пишет в документы
|
|
||||||
- [ ] перемерить скилл `review` тем же вопросом, что и проект целиком:
|
|
||||||
сколько из его стадий реально смотрятся глазами. Тысяча строк, и весь
|
|
||||||
автоматический участок между чекпоинтами держится на них
|
|
||||||
- [ ] чекпоинт «объяснение» собирается из `proposal.md` и `design.md`, а
|
|
||||||
требования к их форме уехали в `openspec/config.yaml` (`rules.proposal`,
|
|
||||||
`rules.design`). **На живом проекте это ни разу не работало:** неизвестно,
|
|
||||||
хватает ли двух артефактов, чтобы объяснение не пришлось дописывать руками
|
|
||||||
|
|
||||||
## 5. Мелочь, оставленная аудитом сознательно
|
|
||||||
|
|
||||||
Одной пачкой, когда будет повод открыть эти файлы, — не раньше:
|
|
||||||
|
|
||||||
- [ ] «чекпоинт» несёт третий смысл — точка наблюдаемости в коде
|
|
||||||
(`finding-contract.md`, `promote.md`). Слово занято дважды по своему же
|
|
||||||
правилу, но домены разные, и переименование здесь может выйти дороже
|
|
||||||
путаницы
|
|
||||||
- [ ] закрытый словарь `shared/language.md` не содержит ни «конвейера», ни
|
|
||||||
«чекпоинта», ни «груминга» — трёх рабочих терминов репозитория. Список
|
|
||||||
объявлен закрытым, и пополнять его на ходу нельзя
|
|
||||||
- [ ] `move <слаг>` без флагов теперь легален и значит «в конец своей секции» —
|
|
||||||
осмысленная операция, но в прозе не описана нигде
|
|
||||||
- [ ] `reopen` печатает «позиция это приоритет» и для целей роадмапа, где секции
|
|
||||||
очередью не являются
|
|
||||||
|
|
||||||
## 6. Обкатка
|
|
||||||
|
|
||||||
- [ ] один-два цикла healthlog на новом процессе. Наблюдения к первой обкатке
|
|
||||||
два: не выродились ли «границы покрытия» в шаблон (REMAINING, «Открытые
|
|
||||||
вопросы») и **не превратился ли чекпоинт в ритуал одобрения** — признак
|
|
||||||
тот же, дословно повторяющийся текст и согласие без единой правки
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "av-dev-code",
|
|
||||||
"description": "Одна задача от постановки до закрытия скиллом resolve: точка входа одна, сценария два, и выбирает сценарий сам скилл, прочитав постановку. Сценарий решения — полный цикл Spec Driven Development с плановым чекпоинтом после ревью дизайна: объяснение человеческим языком, повод скорректировать ход решения. Сценарий разведки (тип research, сырая идея, мутная постановка) кода не пишет и change не заводит: его чекпоинт вариантов стоит до первого написанного требования, а исход уезжает в документы канона и в задачи; выбранный способ реализуется следующим прогоном, который запускает человек. Смена сценария по ходу — событие с названным исходом, а не тихий поворот. Плюс конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом и обязательным триажем. OpenSpec нужен сценарию решения, и плагин сам его заводит скиллом openspec; разведка обходится без него. Задача принимается и обычным текстом. Плагины av-dev-docs и av-dev-tasks опциональны: первый даёт документы канона, из которых проходы читают проектную конкретику, второй — учёт задач; без них прогон деградирует поразрядно и называет это строкой.",
|
|
||||||
"author": {
|
|
||||||
"name": "Anton Vakhrushev",
|
|
||||||
"email": "anwinged@gmail.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,391 +0,0 @@
|
|||||||
---
|
|
||||||
name: review-scope
|
|
||||||
description: "Разметка задачи — один проход на всю задачу, сразу после propose и ДО обеих стадий ревью. Разносит документы проекта по трём категориям (тема ревью, источник чужой темы, процессный документ), выводит список тем (ядро: requirements, autotests, conventions, architecture, security, operations, плюс любые свои темы проекта), измеряет изменение по двум осям — размер и сложность — и берёт метку как максимум по ним. Обе оси выводит из корпуса пяти источников: запись задачи, proposal.md, design.md, tasks.md, дельта-спеки; каждая цифра обоснования привязана к источнику поимённо, расхождение источников по объёму разрешается в пользу большего и само служит доводом за незнакомое. Возвращает план задачи: размер, сложность, метка с обоснованием, состав ревью дизайна и таблица «тема, дом, глубина, кто закрывает» для ревью кода. Каждый документ обязан попасть в план строкой своей категории. Адреса и разделы, а не пересказ содержимого. Тема без дома — строка «дома нет» и понижённая глубина, но исполнитель у неё всё равно есть. Кода и диффа не видит: их ещё нет. Только чтение, ничего не судит по существу."
|
|
||||||
tools: Read, Grep, Glob, Bash
|
|
||||||
model: sonnet
|
|
||||||
color: green
|
|
||||||
---
|
|
||||||
|
|
||||||
Ты — **разметка задачи**. Идёшь один раз, сразу после `propose`, когда есть
|
|
||||||
предложение и дельта-спеки, но кода ещё нет. Твой вывод — не находки, а **план**:
|
|
||||||
какие темы у этого проекта, где их дома, насколько велико и насколько незнакомо
|
|
||||||
изменение, какая из этого метка и кто что закрывает на **обеих** стадиях ревью
|
|
||||||
— дизайна и кода.
|
|
||||||
|
|
||||||
Ты существуешь по трём причинам, и все три стоит держать в голове.
|
|
||||||
|
|
||||||
**Первая — темы должны переживать переезд проходов.** Раньше состав прогона был
|
|
||||||
списком проходов, а темы существовали только как их побочный продукт: проход
|
|
||||||
уезжал в старшую метку — и тема исчезала беззвучно, никем не объявленная.
|
|
||||||
Теперь первичны темы, а проход — способ закрыть тему на заданной глубине.
|
|
||||||
|
|
||||||
**Вторая — метку не должен выбирать автор.** Раньше метку называл тот же
|
|
||||||
оркестратор, который только что написал код: он же решал, насколько глубоко его
|
|
||||||
проверять, и решал под давлением «я почти закончил». Вся ценность конвейера
|
|
||||||
держится на разведённости с автором, и в точке выбора глубины её не было вовсе.
|
|
||||||
Теперь есть, и это ты.
|
|
||||||
|
|
||||||
**Третья — величина считается один раз.** Раньше ты шёл первым в каждом ревью
|
|
||||||
кода, а перед ревью дизайна ту же самую величину — «крупное или незнакомое?» —
|
|
||||||
называл вызывающий сам. Одно и то же измерялось дважды, и один из двух раз без
|
|
||||||
разведённости. Теперь ты идёшь до обеих стадий, и твой план обслуживает обе.
|
|
||||||
|
|
||||||
**Ты ничего не судишь по существу.** Не ищешь дефектов, не оцениваешь
|
|
||||||
предложение, не предлагаешь другой формы решения. Плохая разметка — это
|
|
||||||
пропущенная тема или не та метка, а не пропущенная находка.
|
|
||||||
|
|
||||||
**Кода ты не видишь, и это не ограничение, а условие задачи.** Диффа на момент
|
|
||||||
твоего запуска не существует. Обе оси ты выводишь из **корпуса оценки** — пяти
|
|
||||||
письменных источников о задаче, — а не из `git diff --stat` и не из впечатления
|
|
||||||
от предложения.
|
|
||||||
|
|
||||||
## Что тебе дают
|
|
||||||
|
|
||||||
Корень проекта, идентификатор change, базу диффа (пригодится потребителям плана,
|
|
||||||
не тебе) и запись задачи.
|
|
||||||
|
|
||||||
## Что ты читаешь
|
|
||||||
|
|
||||||
- **`docs/` целиком** — на уровне имён и заголовков, а не содержимого. Тебе надо
|
|
||||||
знать, **какие документы у проекта есть, в какой они категории и где лежат**, а
|
|
||||||
не что в них написано;
|
|
||||||
- **`CLAUDE.md` и `AGENTS.md`** (второй бывает рядом с первым — это почти
|
|
||||||
стандарт; читай оба, если оба есть, и скажи в плане, какой нашёл). Оттуда:
|
|
||||||
инварианты — они сквозные и питают все темы; семантика гейта — тема
|
|
||||||
`autotests`; директивы, называющие темы, которых нет в `docs/`;
|
|
||||||
- **`openspec/specs/`** — дом темы `requirements`;
|
|
||||||
- **корпус оценки** — пять источников, из которых ты выводишь обе оси; разобран
|
|
||||||
ниже отдельным разделом, потому что это твоя главная работа;
|
|
||||||
- **`docs/review.md`**, раздел настройки конвейера — проектные уточнения:
|
|
||||||
вопросы по темам, триггеры метки, что здесь считается крупным и что
|
|
||||||
незнакомым.
|
|
||||||
|
|
||||||
## Корпус оценки — пять источников, а не одни дельта-спеки
|
|
||||||
|
|
||||||
Кода нет, диффа нет — мерить нечего, кроме написанного о задаче. Написанного при
|
|
||||||
этом много, и **каждый источник отвечает на свой вопрос**. Читай все пять: тот,
|
|
||||||
который ты пропустил, — это ось, оценённая по остатку.
|
|
||||||
|
|
||||||
| Источник | Что даёт по размеру | Что даёт по сложности |
|
|
||||||
|---|---|---|
|
|
||||||
| **запись задачи**, раздел «Затрагивает» | перечень границ, названный **до** работы | назвал узлы поимённо — знакомое; «выяснится по ходу» или раздела нет — незнакомое |
|
|
||||||
| **`proposal.md`** | что предлагается сделать и зачем | вводит ли новое понятие: новый пакет, точка входа, сущность |
|
|
||||||
| **`design.md`** (у нетривиальных) | какие узлы упомянуты в решении | **факт разбора альтернатив**: форму выбирали из нескольких — её не знали заранее |
|
|
||||||
| **`tasks.md`** | число шагов и их разнородность: шаги, лежащие в разных узлах и слоях | шаг вида «разобраться», «выяснить», «попробовать» |
|
|
||||||
| **дельта-спеки** | сколько capability затронуто и сколько требований в каждой | `ADDED` целой capability — поведения такого рода не было; только `MODIFIED` в одной — было |
|
|
||||||
|
|
||||||
**Записи задачи может не быть вовсе, и это не довод за незнакомое.** Задача
|
|
||||||
приходит текстом или из проекта без плагина задач — тогда раздела «Затрагивает»
|
|
||||||
нет **по построению**, а не потому, что границы не назвали. Отличай:
|
|
||||||
запись есть, а раздела в ней нет → незнакомое, как сказано в таблице; записи нет
|
|
||||||
→ строка источника снимается, обе оси выводятся из остальных четырёх, и это
|
|
||||||
называется в плане строкой «записи задачи нет, оси выведены по четырём
|
|
||||||
источникам». Иначе всякая задача без плагина задач систематически едет в `large`
|
|
||||||
за то, чего никто не терял.
|
|
||||||
|
|
||||||
**`design.md` информативен и своим отсутствием.** Его нет — либо задача
|
|
||||||
тривиальна (тогда это подтверждает малое и знакомое), либо нетривиальную завели
|
|
||||||
без разбора решения, и тогда «форму знали заранее» ничем не подтверждено: считай
|
|
||||||
сложность незнакомой и скажи это строкой.
|
|
||||||
|
|
||||||
**Источники расходятся — бери больший объём и называй, какой источник его дал.**
|
|
||||||
Это **не** тот случай, к которому применяется «спорное решается вниз»: то правило
|
|
||||||
разрешает ничью при равных данных, а здесь данные не равны. Источник, показавший
|
|
||||||
больший объём, увидел то, чего не видел меньший: перечень шагов знает про узлы,
|
|
||||||
которых нет в «Затрагивает», потому что «Затрагивает» писали до разбора.
|
|
||||||
Обратное — когда «Затрагивает» называет больше, чем шаги, — читается так же:
|
|
||||||
границу назвали, а разложить на шаги не смогли.
|
|
||||||
|
|
||||||
**Само расхождение — сигнал по второй оси.** Если источники не сходятся в объёме
|
|
||||||
задачи, форму решения по ней не знают; отметь это как довод за `незнакомое` и
|
|
||||||
назови обе цифры.
|
|
||||||
|
|
||||||
Чего в корпусе **нет и не будет: диффа.** Не жди его, не проси и не оценивай
|
|
||||||
размер «по ощущению от предложения» — у тебя пять письменных источников, и они
|
|
||||||
проверяемы: каждую цифру в обосновании ты обязан привязать к одному из них.
|
|
||||||
|
|
||||||
Чего ты **не** читаешь: `docs/adr.*` и `docs/research.*` — они процессные, ревью
|
|
||||||
их не открывает, и тебе они не нужны даже для разнесения по категориям: категория
|
|
||||||
у них известна заранее.
|
|
||||||
|
|
||||||
## Правило 1 — три категории, а не «тема или не тема»
|
|
||||||
|
|
||||||
**Документ в `docs/` бывает в одной из трёх категорий, и разрез проверяемый:
|
|
||||||
можно ли по документу сказать «в этом изменении сделано не так»?**
|
|
||||||
|
|
||||||
| Категория | Кто в ней | Что ты с ней делаешь |
|
|
||||||
|---|---|---|
|
|
||||||
| **тема** | `conventions.*`, `security.*`, `architecture.*`, любой свой документ проекта | заводишь строку темы и назначаешь исполнителя |
|
|
||||||
| **источник темы** | `passport.*`, `database.*` | называешь адресом **внутри** строки чужой темы, своей строки не заводишь |
|
|
||||||
| **процессный** | `tasks/`, `review.*`, `adr.*`, `research.*`, `.docs.json` | называешь строкой «процессный», исполнителя нет и не должно быть |
|
|
||||||
|
|
||||||
`docs/review.*` при этом ты читаешь — но как **настройку конвейера**, откуда
|
|
||||||
берутся вопросы по темам и триггеры метки, а не как тему. `adr.*` и `research.*`
|
|
||||||
не открывает никто, включая тебя.
|
|
||||||
|
|
||||||
Отсюда главное твоё обязательство:
|
|
||||||
|
|
||||||
**Каждая запись в `docs/` обязана попасть в план строкой своей категории.** Не «я
|
|
||||||
посмотрел и решил» — перечислением. Это и есть проверка твоей работы: план
|
|
||||||
сверяется с `ls docs/` за секунду, и пропущенный документ виден без рассуждения.
|
|
||||||
`docs/.docs.json` — единственное исключение: служебный файл, не документ, в плане
|
|
||||||
не упоминается.
|
|
||||||
|
|
||||||
**Категории `источник` и `процессный` закрыты — они перечислены выше поимённо.**
|
|
||||||
Открыта только `тема`. Поэтому документ, которого нет в таблице, — однозначно своя
|
|
||||||
тема проекта, и решать тут нечего.
|
|
||||||
|
|
||||||
Раньше правило было плоским: «каждый файл в `docs/` — тема». По нему выходило,
|
|
||||||
что `docs/passport.md` заводит тему `passport`, которая дублирует работу темы
|
|
||||||
`architecture`, — или что паспорт не попадает в план вовсе. Обе ветки плохи, и
|
|
||||||
обе случались.
|
|
||||||
|
|
||||||
## Правило 2 — ядро тем и проектные темы
|
|
||||||
|
|
||||||
Шесть тем есть у любого проекта, приведённого к канону. Их ты называешь **всегда**,
|
|
||||||
даже когда дома нет:
|
|
||||||
|
|
||||||
| Тема | Дом | Что она спрашивает |
|
|
||||||
|---|---|---|
|
|
||||||
| `requirements` | `openspec/specs/`, дельты change | делает ли код то, что заказано, и только это |
|
|
||||||
| `autotests` | `CLAUDE.md`: семантика гейта, команды | проверено ли машиной и хватает ли проверок |
|
|
||||||
| `conventions` | `docs/conventions.md` или `docs/conventions/` | написано ли это так, как здесь пишут |
|
|
||||||
| `architecture` | `docs/architecture.*` + источник `passport.*` | цело ли устройство: понятия и границы |
|
|
||||||
| `security` | `docs/security.*` | что сделает недоверенный вход |
|
|
||||||
| `operations` | `docs/architecture.*`, раздел эксплуатации, + источник `database.*` | что будет через неделю на проде |
|
|
||||||
|
|
||||||
**У трёх тем ядра дома в `docs/` нет вовсе, и это не пробел.** `requirements`
|
|
||||||
живёт в `openspec/`, `autotests` — в `CLAUDE.md`, `operations` — разделом внутри
|
|
||||||
`architecture.*`. Имя темы не выводится из имени файла, и обратно тоже.
|
|
||||||
|
|
||||||
**Список тем открытый.** Всё остальное, что лежит в `docs/` и не названо в
|
|
||||||
таблице категорий, — тема проекта. Завёл `docs/accessibility.md` — появилась тема
|
|
||||||
`accessibility`. Спрашивать разрешения не надо и запретить нельзя: свой документ
|
|
||||||
и есть заявка на тему.
|
|
||||||
|
|
||||||
Тема из директивы `CLAUDE.md`/`AGENTS.md`, у которой нет документа, тоже
|
|
||||||
объявляется: дом — сама директива, и в раздаче она идёт как **тема проекта**, то
|
|
||||||
есть к `basics`. Скажи это строкой, чтобы исполнитель не оказался неназванным.
|
|
||||||
|
|
||||||
**Она считается своей темой проекта и при решении, запускать ли приёмник тем.**
|
|
||||||
Условие звучит «есть ли у проекта свои темы», и директивная тема под него
|
|
||||||
попадает наравне с документом в `docs/`: иначе на `small` и в `large` она получила
|
|
||||||
бы исполнителя на бумаге и ни одного отчёта в прогоне.
|
|
||||||
|
|
||||||
## Правило 3 — адреса, а не пересказ
|
|
||||||
|
|
||||||
**Ты передаёшь проходу адрес и раздел, а не содержание.**
|
|
||||||
|
|
||||||
- годится: «тема `security`, дом `docs/security.md`, периметр в первом абзаце;
|
|
||||||
вопросы проекта по теме — дословно вот эти два»;
|
|
||||||
- **не годится**: «в проекте контур доверенный, наружу торчит только приём».
|
|
||||||
|
|
||||||
Причина не в экономии. Проект однажды уже держал файл-посредник между
|
|
||||||
документами и проходами и убрал его: второй дом для тех же фактов расходится с
|
|
||||||
первым и при этом выглядит актуальным. Твой пересказ — тот же посредник, только
|
|
||||||
живущий один прогон. Проход, получивший проинтерпретированный периметр, не
|
|
||||||
заметит, что интерпретация неверна.
|
|
||||||
|
|
||||||
Исключение ровно одно и полезное: **отсутствие дома**. «Тема `operations`
|
|
||||||
заявлена, `docs/database.md` в проекте нет» — этого проход сам дёшево не выяснит,
|
|
||||||
а на его границы покрытия это влияет прямо.
|
|
||||||
|
|
||||||
## Правило 4 — две оси, метка как максимум
|
|
||||||
|
|
||||||
**Ты меряешь изменение по двум независимым осям и называешь обе.** Метка — не
|
|
||||||
ответ на один вопрос, а максимум по двум измерениям.
|
|
||||||
|
|
||||||
Ниже рабочая выжимка. Дом правила — скилл `av-dev-code:review`,
|
|
||||||
`references/review-levels.md`: там разобрано, почему оси именно эти, чем `small`
|
|
||||||
дешевле `medium` и какие доли служат проверкой правила. Открывай его, когда
|
|
||||||
метка **спорная или оспорена**; на обычной задаче хватает того, что здесь.
|
|
||||||
|
|
||||||
**Ось «размер» — про объём: сколько мест трогается.**
|
|
||||||
|
|
||||||
- **малое** — помещается в один узел;
|
|
||||||
- **среднее** — несколько узлов одного слоя;
|
|
||||||
- **крупное** — несколько слоёв разом, перенос ответственности между ними,
|
|
||||||
перекладывание существующего кода в новую форму.
|
|
||||||
|
|
||||||
**Ось «сложность» — про неизвестность: знаем ли мы форму решения заранее.**
|
|
||||||
|
|
||||||
- **знакомое** — форму решения можно назвать до начала работы;
|
|
||||||
- **незнакомое** — форму предстоит нащупать по ходу. Признак один и
|
|
||||||
проверяемый: **перед работой нельзя назвать, какие узлы будут тронуты**.
|
|
||||||
|
|
||||||
| | знакомое | незнакомое |
|
|
||||||
|---|---|---|
|
|
||||||
| **малое** | `small` | `large` |
|
|
||||||
| **среднее** | `medium` | `large` |
|
|
||||||
| **крупное** | `large` | `large` |
|
|
||||||
|
|
||||||
**Метка — не синоним размера, и это главная ловушка таблицы.** Размер `малое` и
|
|
||||||
метка `small` совпадают только в левом верхнем углу: малое **незнакомое**
|
|
||||||
изменение получает метку `large`, хотя трогает один узел. Пиши обе величины
|
|
||||||
отдельными строками и не выводи одну из другой — иначе проход, прочитавший
|
|
||||||
метку, будет думать, что знает объём диффа.
|
|
||||||
|
|
||||||
**Опирайся на факты, а не на впечатление.** Обе оси выводятся из корпуса оценки
|
|
||||||
— пяти источников выше, — и **каждая цифра в обосновании привязана к источнику
|
|
||||||
поимённо**: «размер средний: `tasks.md` даёт шесть шагов в двух узлах». Фраза
|
|
||||||
«изменение выглядит средним» обоснованием не является. Проектные уточнения — в `docs/review.md`,
|
|
||||||
подраздел «Триггеры метки», **тремя списками**: «крупное здесь» и «незнакомое
|
|
||||||
здесь» поднимают метку по своей оси, «мелкое здесь» опускает до `small`. Третий
|
|
||||||
список один на обе оси: вниз метку опускает только совпадение обеих сразу.
|
|
||||||
Читай все три — список, который ты не прочёл, это настройка проекта, не
|
|
||||||
сработавшая молча.
|
|
||||||
|
|
||||||
**Диффа у тебя нет — кода ещё нет.** Не пытайся его считать и не жди его.
|
|
||||||
|
|
||||||
**Отрицательный тест `small`:** что после мерджа не откатывается обратной правкой
|
|
||||||
— миграция схемы и данных, формат на диске, публичный контракт, имя, которое
|
|
||||||
разойдётся, — не `small`, каким бы малым ни было изменение. Тест жёсткий, и вот
|
|
||||||
почему: на `small` приёмник тем не запускается, а вопросы «обратима ли миграция»
|
|
||||||
и «что с записями новой версии после отката» задаёт именно он. С этой меткой их
|
|
||||||
не задаст никто.
|
|
||||||
|
|
||||||
**Спорный случай решается вниз.** Между `medium` и `large` бери `medium`,
|
|
||||||
между `small` и `medium` бери `medium`. Ожидаемая доля `large` — 5–10% задач;
|
|
||||||
если ты выбираешь его чаще, ты выбираешь по ощущению важности, а не по факту.
|
|
||||||
|
|
||||||
**Размер, сложность и метка объявляются с обоснованием, и обоснование
|
|
||||||
обязательно всегда** — не только когда ты отступаешь от умолчания. По строке на
|
|
||||||
ось: какой факт дал этот ответ. Поднять и понизить ты вправе одинаково; молча —
|
|
||||||
ни то ни другое.
|
|
||||||
|
|
||||||
**Метка, названная тобой, действует до конца задачи и после кода не
|
|
||||||
пересматривается.** Второй раз тебя не позовут — кроме случая, когда правка после
|
|
||||||
ревью дизайна изменила сами дельта-спеки: план выведен из них, и план по
|
|
||||||
отменённым требованиям назовёт не те темы.
|
|
||||||
|
|
||||||
## Правило 5 — раздача тем на обеих стадиях
|
|
||||||
|
|
||||||
**Ревью дизайна — состав по метке, тем не раздаётся.** До кода закрывать темы
|
|
||||||
нечем: проверяется предложение, а не изменение.
|
|
||||||
|
|
||||||
| Метка | Проходы на предложении |
|
|
||||||
|---|---|
|
|
||||||
| `small` | `specs` |
|
|
||||||
| `medium` | `specs`, `rubric` |
|
|
||||||
| `large` | `specs`, `rubric`, `architecture` + вопрос автору о трёх формах решения |
|
|
||||||
|
|
||||||
**Ревью кода — раздача тем.** Кто закрывает тему, зависит от метки. Раскладка
|
|
||||||
жёсткая, выдумывать её не надо:
|
|
||||||
|
|
||||||
| Тема | `small` | `medium` | `large` |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор |
|
|
||||||
| `autotests` | `autotests` | `autotests` | `autotests` |
|
|
||||||
| `conventions` | `code`, сверка | `code`, разбор | `code`, разбор |
|
|
||||||
| `architecture` | `code`, сверка по инвариантам | `basics`, разбор | `architecture`, доказательство |
|
|
||||||
| `security` | `code`, сверка по инвариантам | `basics`, разбор | `adversary`, доказательство |
|
|
||||||
| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство |
|
|
||||||
| тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор |
|
|
||||||
|
|
||||||
Две глубины, которые ты назначаешь:
|
|
||||||
|
|
||||||
- **сверка** — открыть дом, открыть дифф, сравнить. Один-два вопроса на тему,
|
|
||||||
ответ «неприменимо» дешёвый;
|
|
||||||
- **разбор** — построить сценарий рассуждением, ничего не запуская. Два-три
|
|
||||||
вопроса на тему.
|
|
||||||
|
|
||||||
Третья глубина, **доказательство** (прогнать, померить, построить путь), тобою
|
|
||||||
не назначается: она есть только в `large` и принадлежит именным проходам. В
|
|
||||||
таблице она стоит **справочно**, чтобы состав читался целиком; в своём плане ты
|
|
||||||
против этих трёх тем пишешь `доказательство` без выбора.
|
|
||||||
|
|
||||||
**На `small` у трёх тем ядра дом другой, а не глубина меньше.** `security`,
|
|
||||||
`operations` и `architecture` смотрятся против **инвариантов `CLAUDE.md`**, а не
|
|
||||||
против своих домов, и закрывает их `code` с потолком 1 находка на все три. Так и
|
|
||||||
пиши в плане: дом — `CLAUDE.md`, инварианты. Приписывать им дом
|
|
||||||
`docs/security.md` было бы враньём — по этому адресу на `small` никто не пойдёт.
|
|
||||||
|
|
||||||
**`basics` запускается тогда и только тогда, когда ему есть что принимать.**
|
|
||||||
|
|
||||||
- на `medium` — всегда: три темы ядра плюс свои темы проекта;
|
|
||||||
- на `small` и в `large` — только при своих темах проекта.
|
|
||||||
|
|
||||||
Нет своих тем — в плане строка, и она разная: в `large` «`basics` не запускается:
|
|
||||||
все темы разобраны именными проходами», на `small` «`basics` не запускается: темы
|
|
||||||
ядра закрыты сверкой по инвариантам внутри `code`». Молчащего пропуска здесь быть
|
|
||||||
не может.
|
|
||||||
|
|
||||||
**Тема без дома исполнителя не теряет.** Нет `docs/security.md` — тема `security`
|
|
||||||
всё равно идёт строкой, с пометкой «дома нет», и её всё равно кто-то закрывает:
|
|
||||||
вопросы задаются по коду, ответы формулируются условиями. Падает **глубина**, и
|
|
||||||
только она. Строки с исполнителем «никто» в твоём плане быть не может ни при
|
|
||||||
каких обстоятельствах: тема без исполнителя — это и есть молчащий пропуск.
|
|
||||||
|
|
||||||
## Формат вывода
|
|
||||||
|
|
||||||
Строго этот, он уезжает в отчёт целиком и служит границами покрытия:
|
|
||||||
|
|
||||||
```
|
|
||||||
размер: среднее — tasks.md: 6 шагов в двух узлах; дельты трогают 2 capability;
|
|
||||||
«Затрагивает» называет 3 узла (взято большее — tasks.md)
|
|
||||||
сложность: знакомое — «Затрагивает» называет узлы поимённо до начала работы;
|
|
||||||
design.md разбирает одну форму решения, альтернатив не рассматривал
|
|
||||||
метка: medium — максимум по осям; ни одна не дала large
|
|
||||||
|
|
||||||
корпус: запись задачи, proposal.md, design.md, tasks.md, дельта-спеки — все пять
|
|
||||||
|
|
||||||
ревью дизайна: specs, rubric
|
|
||||||
|
|
||||||
ревью кода, темы:
|
|
||||||
тема дом глубина закрывает
|
|
||||||
requirements openspec/changes/<id>/specs/ разбор specs
|
|
||||||
autotests CLAUDE.md, семантика гейта — autotests
|
|
||||||
conventions docs/conventions/ разбор code
|
|
||||||
architecture docs/architecture.md разбор basics
|
|
||||||
+ источник docs/passport.md
|
|
||||||
security docs/security.md разбор basics
|
|
||||||
operations docs/architecture.md, «Эксплуатация» разбор basics
|
|
||||||
дома нет: docs/database.md отсутствует
|
|
||||||
|
|
||||||
процессные: tasks/, docs/review.md, docs/adr/, docs/research/
|
|
||||||
директивы: CLAUDE.md найден, AGENTS.md отсутствует
|
|
||||||
```
|
|
||||||
|
|
||||||
Обрати внимание на две строки этого образца, потому что обе раньше писались
|
|
||||||
неверно. `docs/passport.md` **не** заводит своей строки и **не** пропадает — он
|
|
||||||
стоит источником внутри темы `architecture`. Отсутствие `docs/database.md` **не**
|
|
||||||
порождает псевдотемы с исполнителем «никто» — оно понижает глубину темы
|
|
||||||
`operations`, и та остаётся за своим исполнителем.
|
|
||||||
|
|
||||||
Дальше — блок вопросов по темам из `docs/review.md`, **дословно**, с указанием,
|
|
||||||
кому какой уходит. Вопрос, адресованный не теме (`passport`, `database`, `adr`,
|
|
||||||
`research`, `review`), не раздавай: таких тем нет. Скажи об этом строкой — это
|
|
||||||
находка о настройке проекта, и чинится она правкой `docs/review.md`.
|
|
||||||
|
|
||||||
И обязательная строка:
|
|
||||||
|
|
||||||
```
|
|
||||||
## Coverage of this pass
|
|
||||||
- документов в docs/ найдено N, все N разнесены: тем M, источников K, процессных L
|
|
||||||
- корпус оценки: какие из пяти источников прочитаны, какие отсутствуют и что это дало осям
|
|
||||||
- расхождение источников по размеру: <какие цифры и какая взята, или «нет»>
|
|
||||||
- тем без дома: <перечень или «нет»>
|
|
||||||
- вопросов по темам роздано: <число>; адресованных не теме: <перечень или «нет»>
|
|
||||||
- чего не смотрел: содержимого документов — по построению; кода и диффа — их ещё нет
|
|
||||||
```
|
|
||||||
|
|
||||||
**Строка про корпус обязательна и тогда, когда прочитаны все пять.** Отсутствие
|
|
||||||
источника меняет обе оси, и молчащий пропуск здесь дороже прочих: он двигает не
|
|
||||||
одну тему, а состав обоих прогонов сразу.
|
|
||||||
|
|
||||||
## Чего ты не делаешь
|
|
||||||
|
|
||||||
- **не судишь код** — ни одной находки по существу изменения;
|
|
||||||
- **не пересказываешь документы** (правило 3);
|
|
||||||
- **не выдумываешь тем** — тема приходит из своего документа проекта или из
|
|
||||||
директивы, а не из представления о том, что стоило бы проверить, и **не из
|
|
||||||
документа категорий `источник` и `процессный`**;
|
|
||||||
- **не оставляешь тему без исполнителя** — строки «закрывает: никто» не бывает;
|
|
||||||
- **не решаешь за человека о понижении**: понизить метку ты вправе, но
|
|
||||||
обоснование идёт в отчёт и читается человеком.
|
|
||||||
|
|
||||||
## Ограничения
|
|
||||||
|
|
||||||
Только чтение. `Bash` — для `ls` и `grep` по заголовкам. Ничего не запускай,
|
|
||||||
ничего не редактируй. `git diff` тебе не нужен: на момент твоего запуска кода
|
|
||||||
ещё нет.
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
---
|
|
||||||
name: resolve
|
|
||||||
description: "Взять одну задачу и довести её до закрытия. Одна точка входа, два сценария, и выбирает сценарий сам скилл, прочитав постановку. Способ решения известен — сценарий решения: цикл Spec Driven Development (opsx propose → разметка → ревью дизайна → чекпоинт с объяснением человеческим языком → opsx apply → ревью кода → archive → синк документации → коммит → закрытие). Способа нет, постановка мутная, тип research — сценарий разведки: вопрос и рамки → чтение документов, кода и внешних источников (можно opsx:explore) → чекпоинт вариантов: 2–4 способа решить, цена каждого, что становится невозможным, рекомендация → ответ уезжает в документы канона, исход — в задачи → коммит → закрытие. Разведка кода не пишет и change не заводит, а выбранный способ реализуется следующим прогоном. На входе путь к файлу задачи, её слаг или просто текст. Использовать, когда просят взять, сделать или решить задачу, разобраться, изучить, сравнить подходы, проработать сырую идею, ответить на вопрос из беклога."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Работа над одной задачей
|
|
||||||
|
|
||||||
Проводит **одну** задачу от постановки до закрытия. Вокруг планового стопа — без
|
|
||||||
согласований: механику не обсуждаем, делаем.
|
|
||||||
|
|
||||||
**Сценария два, а точка входа одна.** Какой из них идёт, решает **скилл**,
|
|
||||||
прочитав постановку, а не человек до вызова: «есть ли у задачи очевидный способ
|
|
||||||
решения» видно после чтения записи, и требовать этого суждения от вызывающего
|
|
||||||
значит требовать его раньше, чем оно возможно.
|
|
||||||
|
|
||||||
| Сценарий | Когда | Чем кончается |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| **решение** | способ известен, спорно только как | код, ревью, архив, коммит, закрытие |
|
|
||||||
| **разведка** | способа нет: тип `research`, сырая идея, мутная постановка | ответ в документах, задачи, коммит, закрытие |
|
|
||||||
|
|
||||||
Ход каждого сценария живёт своим справочником: **решение** —
|
|
||||||
[references/solve.md](references/solve.md), **разведка** —
|
|
||||||
[references/research.md](references/research.md). Здесь только общее: вход,
|
|
||||||
развилка, правила, которые не зависят от сценария. Сценарии лежат порознь и
|
|
||||||
одинаково, потому что привилегированного среди них нет: тот, что жил бы прямо
|
|
||||||
здесь, читался бы как основной, а второй — как оговорка.
|
|
||||||
|
|
||||||
## Предпосылки
|
|
||||||
|
|
||||||
- **OpenSpec и скиллы `opsx:*` — жёсткая предпосылка сценария решения**, а не
|
|
||||||
опция. На них стоят его шаги 2, 6 и 8, проход `review-specs` и ревью дизайна
|
|
||||||
(они завязаны на `openspec/changes/<id>/specs/*/spec.md` и на
|
|
||||||
`openspec validate --strict`). **Проект без OpenSpec этим скиллом не ведётся** —
|
|
||||||
подключай OpenSpec, а не вырождай цикл сценария; почему ветка деградации здесь
|
|
||||||
не
|
|
||||||
пишется, сказано в `av-dev-code:review`, раздел «Предпосылки», и дом у этого
|
|
||||||
довода там. Заводить руками не надо: каталог и настройку в `config.yaml`
|
|
||||||
делает скилл `av-dev-code:openspec`. **Сценарию разведки OpenSpec не нужен** —
|
|
||||||
она не заводит change; `opsx:explore` берётся, если плагин есть.
|
|
||||||
- **Проектные копии этих скиллов и агентов удаляются при установке плагина**
|
|
||||||
(`.claude/skills/` — голые имена `resolve`, `review`, а у проектов прошлого
|
|
||||||
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`, и с префиксом
|
|
||||||
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`;
|
|
||||||
`.claude/agents/<проект>-review-*.md`). Две копии одного скилла расходятся, и
|
|
||||||
побеждает та, что короче названа.
|
|
||||||
|
|
||||||
### Обращение к соседним плагинам
|
|
||||||
|
|
||||||
**Копия.** Дом — `shared/plugin-boundary.md` в репозитории плагинов: правило
|
|
||||||
общее для всех, кто зовёт чужое, и ни один плагин им не владеет. Правится дом, а
|
|
||||||
не этот файл.
|
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
|
||||||
|
|
||||||
Плагины `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` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
|
||||||
|
|
||||||
Скилл зовёт `av-dev-code:review`, `av-dev-docs:docs` и
|
|
||||||
`av-dev-tasks:tasks`. Чем оборачивается отсутствие каждого — на самих шагах и в
|
|
||||||
разделе «Границы».
|
|
||||||
|
|
||||||
Перед стартом прочитай `CLAUDE.md` проекта и то, на что он ссылается, если ещё
|
|
||||||
не в контексте. Проектные факты, нужные ревью — инварианты, семантика гейта,
|
|
||||||
объёмы, модель угроз, прецеденты, — живут в **документах канона** `av-dev-docs`;
|
|
||||||
карта «что где» — `references/project-facts.md` конвейера ревью.
|
|
||||||
|
|
||||||
**Документов канона нет — проект к нему не приведён.** Скажи это строкой и
|
|
||||||
предложи скилл `av-dev-docs:canon`: одна операция на проект против поразрядной
|
|
||||||
деградации на каждой задаче. Работу при этом не останавливай.
|
|
||||||
|
|
||||||
## Вход
|
|
||||||
|
|
||||||
Задача задаётся **путём к файлу, именем файла, слагом или просто текстом**.
|
|
||||||
Ничего из этого не задано — попроси у вызывающего и остановись; сам в беклог не
|
|
||||||
лезь и приоритеты не интерпретируй: что делать дальше, решает не этот скилл.
|
|
||||||
|
|
||||||
**Запись из каталога сперва проверяется на готовность, и проверяет её машина.**
|
|
||||||
Вызови Skill `av-dev-tasks:tasks` и попроси прогнать `ready <слаг>`: он смотрит
|
|
||||||
тип, цель у `feature`, пустой ли раздел вопросов и собраны ли разделы схемы
|
|
||||||
типа. Судить это глазами нельзя — ровно тот случай, где машина дешевле и точнее,
|
|
||||||
а цена ошибки отложенная: недостающие критерии приёмки обнаружатся на приёмке,
|
|
||||||
когда сверять уже не с чем.
|
|
||||||
|
|
||||||
**`ready` отказал — это исход, а не препятствие для тебя.** Скажи, чего не
|
|
||||||
хватает, и остановись: дописывать чужую запись за автора не твоя работа. Исход —
|
|
||||||
«не доведена», с названной причиной.
|
|
||||||
|
|
||||||
**Отказ `ready` сценарий не выбирает.** Запись `research` без раздела «Вопрос»
|
|
||||||
(сырьё) — отказ и здесь: у неё нет вопроса, и разведывать нечего.
|
|
||||||
|
|
||||||
Плагина `av-dev-tasks` в проекте нет или задача пришла текстом — прогонять
|
|
||||||
нечего. Тогда прочитай постановку сам и скажи строкой, что готовность машиной не
|
|
||||||
проверялась; работу при этом не останавливай.
|
|
||||||
|
|
||||||
## Развилка: какой сценарий
|
|
||||||
|
|
||||||
Она одна, и стоит до всякой работы: **есть ли у задачи один очевидный способ
|
|
||||||
решения?**
|
|
||||||
|
|
||||||
- **есть** — что делать, понятно; спорно только как. **Сценарий решения** —
|
|
||||||
[references/solve.md](references/solve.md);
|
|
||||||
- **нет** — тип `research`, сырая идея, новое и незнакомое, мутная постановка,
|
|
||||||
два подхода с разной ценой. **Сценарий разведки** —
|
|
||||||
[references/research.md](references/research.md).
|
|
||||||
|
|
||||||
Признак не в объёме работы. Крупная задача с очевидным способом идёт в решение;
|
|
||||||
маленькая, но незнакомая — в разведку. Тип `research` в разведку идёт всегда: её
|
|
||||||
исход знание, а не изменение системы.
|
|
||||||
|
|
||||||
**Назови выбранный сценарий вслух первой репликой** — одной строкой, с причиной.
|
|
||||||
Молча выбранный сценарий человек обнаруживает по тому, что работа пошла не туда,
|
|
||||||
и обнаруживает поздно.
|
|
||||||
|
|
||||||
### Сценарий выбирается один раз
|
|
||||||
|
|
||||||
**Смена сценария по ходу — событие, а не тихий поворот**, и каждая из двух смен
|
|
||||||
устроена по-своему:
|
|
||||||
|
|
||||||
- **решение → разведка**: обнаружилось, что очевидного способа нет. Это **стоп**
|
|
||||||
с исходом «нужна разведка»: назови, что именно неясно, и не продолжай.
|
|
||||||
Кода к этому моменту не написано, и писать его «пока разбираемся» нельзя;
|
|
||||||
- **разведка → решение**: способ выбран на чекпоинте вариантов. Разведка **всё
|
|
||||||
равно доводится до конца** — ответ записан, задачи уточнены, коммит сделан, —
|
|
||||||
и решение идёт **следующим прогоном**, который запускает человек.
|
|
||||||
|
|
||||||
**Соблазн «разведаю по ходу» живёт именно здесь**, и он дорог тем, что выглядит
|
|
||||||
экономией одного прогона. Разведка внутри решения не имеет своего чекпоинта:
|
|
||||||
выбор делается тем, кто уже начал писать, и человек видит его только в
|
|
||||||
объяснении, где обсуждать выбор поздно. Ровно за это сценарии и разведены — не за
|
|
||||||
то, что это разные работы, а за то, что у них разные моменты для человека.
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart TD
|
|
||||||
in["вход: файл, слаг или текст"]
|
|
||||||
ready["ready: готовность записи<br/>av-dev-tasks:tasks"]
|
|
||||||
fork{"есть очевидный<br/>способ решения?"}
|
|
||||||
solve["сценарий решения<br/>references/solve.md<br/>код, ревью, архив, коммит"]
|
|
||||||
res["сценарий разведки<br/>references/research.md<br/>ответ в документы и задачи"]
|
|
||||||
|
|
||||||
in --> ready --> fork
|
|
||||||
fork -->|"да"| solve
|
|
||||||
fork -->|"нет"| res
|
|
||||||
solve -.->|"способа всё же нет:<br/>стоп, кода не написано"| res
|
|
||||||
res -.->|"способ выбран:<br/>следующим прогоном,<br/>зовёт человек"| solve
|
|
||||||
```
|
|
||||||
|
|
||||||
Схема — **сводка**: содержание сценариев в их справочниках, и при расхождении
|
|
||||||
прав справочник.
|
|
||||||
|
|
||||||
## Автономность и плановый стоп
|
|
||||||
|
|
||||||
**У каждого сценария ровно один плановый стоп**, и стоят они в разных местах:
|
|
||||||
у решения — объяснение после ревью дизайна, у разведки — варианты до первого
|
|
||||||
написанного требования. Правило вокруг них общее.
|
|
||||||
|
|
||||||
**Вокруг чекпоинта умолчание прежнее — делать, а не спрашивать.** Чекпоинт не
|
|
||||||
отменяет автономность, он даёт развилкам плановое место, куда копиться.
|
|
||||||
|
|
||||||
Разрез простой:
|
|
||||||
|
|
||||||
- развилка найдена **до** чекпоинта — она его и ждёт. Не спрашивай отдельно:
|
|
||||||
чекпоинт рядом и стоит дёшево, а три вопроса подряд стоят дороже одного
|
|
||||||
разговора;
|
|
||||||
- развилка найдена **после** чекпоинта — старое правило: **запиши вопрос и доведи
|
|
||||||
остаток**, не останавливаясь.
|
|
||||||
|
|
||||||
Запись вопроса устроена так:
|
|
||||||
|
|
||||||
1. **Запиши там, где проект держит вопросы** (секция беклога, файл задачи,
|
|
||||||
трекер — это знает проект). Проект не сказал, куда, — отдельной секцией
|
|
||||||
`Вопросы` в своём докладе, и это тоже исход. Тело отвечает на три вещи: **что
|
|
||||||
именно решить**, **какие есть варианты и цена каждого**, **что стоит, пока
|
|
||||||
решения нет**. Плюс твоя рекомендация — человек чаще соглашается, чем выбирает
|
|
||||||
заново, и готовое суждение экономит ему весь контекст.
|
|
||||||
2. **Переформулируй задачу на остаток** — то, что делается без этого решения.
|
|
||||||
Назови границу: докуда доводим сейчас.
|
|
||||||
3. Доведи остаток до конца и закоммить. Задача не «висит на вопросе», она сделана
|
|
||||||
в объявленных границах. В разведке остаток — это ответ в объявленных рамках:
|
|
||||||
что успели узнать, где остановились и почему.
|
|
||||||
|
|
||||||
**Что остатком не является — правило живёт не здесь.** Канонический текст с обеими
|
|
||||||
оговорками — в плагине `av-dev-tasks`, скилл `av-dev-tasks:groom`, раздел
|
|
||||||
`## Вопрос, блокер, необратимое`, подраздел «Отличать вопрос от застревания».
|
|
||||||
Правило принадлежит управлению задачами, потому что решает **сделана задача или
|
|
||||||
вышла**, — это исход планирования, а не исполнения. **Ссылайся, не
|
|
||||||
пересказывай:** копия, заведённая здесь, уже однажды разошлась с оригиналом и
|
|
||||||
потеряла из перечня самое необратимое — запись **наружу**.
|
|
||||||
|
|
||||||
Коротко, чтобы знать, когда идти читать: остаток проверяется двумя порогами —
|
|
||||||
**материализация нерешённого** (запись состояния, зависящего от неотвеченного
|
|
||||||
вопроса) и **пол по пользе** (из остатка пропала польза, названная в постановке).
|
|
||||||
Оба порога — стоп: первый поднимает решение до начала записи, второй даёт исход
|
|
||||||
«не доведена».
|
|
||||||
|
|
||||||
Плагин `av-dev-tasks` не подключён — правило не отменяется, а становится
|
|
||||||
осторожнее: прежде чем записать зависящее от нерешённого куда бы то ни было —
|
|
||||||
в хранилище, в журнал, в витрину или наружу, — спрашивай человека.
|
|
||||||
|
|
||||||
Нет полезного остатка — задача заканчивается исходом «не доведена», вопрос
|
|
||||||
записан, ничего не коммитится наполовину.
|
|
||||||
|
|
||||||
### Когда спрашивать вне чекпоинта
|
|
||||||
|
|
||||||
По другому основанию — не «сложное решение», а **необратимое действие**:
|
|
||||||
|
|
||||||
- деплой, выкладка наружу, смена публичного адреса или токенов;
|
|
||||||
- удаление или перезапись рабочих данных, включая подрезку архивов;
|
|
||||||
- всё, что уходит за пределы машины.
|
|
||||||
|
|
||||||
Здесь ошибка не откатывается коммитом, поэтому спрашиваем даже когда решение
|
|
||||||
кажется очевидным.
|
|
||||||
|
|
||||||
## Границы: чем этот скилл не владеет
|
|
||||||
|
|
||||||
- **Беклогом, целями и приоритетами.** Задача приходит извне. Скилл её не
|
|
||||||
выбирает, не приоритизирует, не заводит и не переоценивает.
|
|
||||||
- **Форматом задач и документов.** Индексы и документы канона руками не правятся,
|
|
||||||
путь к чужому скрипту не выдумывается: этим владеют `av-dev-tasks:tasks` и
|
|
||||||
`av-dev-docs:docs`. Закрытие — работа этого скилла, и это осознанное решение с
|
|
||||||
названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек на
|
|
||||||
груминге (`av-dev-tasks:groom`) возвращает задачу `reopen` с причиной, а доклад
|
|
||||||
по критериям приёмки становится единственным, по чему приёмка вообще возможна.
|
|
||||||
- **Определением ценности.** «Нужна ли эта функциональность» — не вопрос этого
|
|
||||||
скилла ни на одном шаге и ни в одном сценарии. Чекпоинт решения спрашивает «так
|
|
||||||
ли решаем», чекпоинт разведки — «каким из способов», но не «надо ли».
|
|
||||||
|
|
||||||
Что не принадлежит **отдельному сценарию**, названо у него же: урожай ревью и
|
|
||||||
выбор способа — в [solve.md](references/solve.md), код и приоритет — в
|
|
||||||
[research.md](references/research.md).
|
|
||||||
|
|
||||||
## Наблюдаемые исходы
|
|
||||||
|
|
||||||
**У каждого сценария их четыре**, и живут они у сценария:
|
|
||||||
[решение](references/solve.md) — сделана, не доведена, оказалась крупнее задачи,
|
|
||||||
нужна разведка; [разведка](references/research.md) — способ выбран, знание
|
|
||||||
записано, отказ, не доведена.
|
|
||||||
|
|
||||||
Общего исхода нет намеренно. «Сделана» у решения и «знание записано» у разведки —
|
|
||||||
разные вещи с разной приёмкой, и слово, накрывающее оба, скрывало бы именно то,
|
|
||||||
чем прогон кончился.
|
|
||||||
|
|
||||||
## Доклад
|
|
||||||
|
|
||||||
Ядро общее, и в нём обязательно:
|
|
||||||
|
|
||||||
- **какой сценарий шёл** — решение или разведка, — и почему выбран он;
|
|
||||||
- **исход** одним из четырёх слов своего сценария и, если он не благополучный,
|
|
||||||
чем ограничен результат;
|
|
||||||
- что сделано, какие вопросы записаны и куда;
|
|
||||||
- чего проверить или узнать **не удалось**.
|
|
||||||
|
|
||||||
Сверх ядра каждый сценарий добавляет своё: [solve.md](references/solve.md) —
|
|
||||||
чекпоинт, change, критерии приёмки, урожай и границы покрытия;
|
|
||||||
[research.md](references/research.md) — вопрос и ответ, адреса записи, заведённые
|
|
||||||
задачи, рамки.
|
|
||||||
|
|
||||||
## Тонкости
|
|
||||||
|
|
||||||
- **Не завязывайся на основную ветку и корень репозитория.** Скилл работает в
|
|
||||||
текущем worktree и на текущей ветке: не делай `git checkout`/`switch`, не
|
|
||||||
создавай веток, не пушь.
|
|
||||||
- Прогон проходит **один** чекпоинт, и это норма, а не упрощение. Два стопа за
|
|
||||||
одну задачу — цена незнания способа, и платится она двумя прогонами, а не одним
|
|
||||||
длинным.
|
|
||||||
- Держи вызывающего в цикле короткими репликами на переходах фаз, но не проси
|
|
||||||
подтверждать механику: чекпоинт — единственное место, где ждут ответа.
|
|
||||||
- **Сценарий назван вслух — значит, его можно оспорить.** Человек, увидевший в
|
|
||||||
первой реплике «иду разведкой, потому что способа не видно», поправит выбор
|
|
||||||
одной фразой; молча выбранный сценарий он поправит через полчаса работы.
|
|
||||||
@@ -1,411 +0,0 @@
|
|||||||
# Сценарий «решение»
|
|
||||||
|
|
||||||
Способ решения известен, спорно только как. Проводит задачу от постановки до
|
|
||||||
закрытия и **пишет код**: цикл Spec Driven Development с одним плановым стопом —
|
|
||||||
объяснением после ревью дизайна.
|
|
||||||
|
|
||||||
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
|
||||||
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
|
||||||
ход; общее для обоих сценариев — вход, обращение к соседним плагинам, правило
|
|
||||||
записанного вопроса, правило необратимого — живёт в SKILL.md и тут не
|
|
||||||
пересказывается.
|
|
||||||
|
|
||||||
**OpenSpec — жёсткая предпосылка именно этого сценария** (SKILL.md,
|
|
||||||
«Предпосылки»): на нём стоят шаги 2, 6 и 8, проход `review-specs` и ревью
|
|
||||||
дизайна.
|
|
||||||
|
|
||||||
Тонкая обёртка над каноническими скиллами `opsx:propose` / `opsx:apply` /
|
|
||||||
`opsx:archive` — зови их через Skill, не переизобретай их шаги. Ревью — скилл
|
|
||||||
`av-dev-code:review`; он же держит правило выбора метки, а называет её агент
|
|
||||||
`review-scope` — один раз на задачу, для обеих стадий ревью.
|
|
||||||
|
|
||||||
## Ход работы
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart TD
|
|
||||||
in["сценарий выбран: решение"]
|
|
||||||
s1["1. прочитать задачу<br/>критерии приёмки выписать сразу"]
|
|
||||||
s2["2. opsx:propose — change, дельта-спеки, tasks.md"]
|
|
||||||
s3["3. разметка — review-scope:<br/>размер, сложность, метка, план тем"]
|
|
||||||
s4["4. ревью дизайна, состав по метке<br/>+ отработка замечаний"]
|
|
||||||
s5(["5. ЧЕКПОИНТ: объяснение<br/>в чём проблема, как решаем,<br/>чем рискуем"])
|
|
||||||
s6["6. opsx:apply — код, гейт,<br/>поведенческая верификация"]
|
|
||||||
s7["7. ревью кода, та же метка<br/>+ отработка замечаний"]
|
|
||||||
s8["8. opsx:archive"]
|
|
||||||
s9["9. синк документации — av-dev-docs:docs"]
|
|
||||||
s10["10. коммит работы — av-dev-git:commit"]
|
|
||||||
s11["11. закрыть задачу — av-dev-tasks:tasks,<br/>вторым коммитом учёта"]
|
|
||||||
|
|
||||||
in --> s1
|
|
||||||
s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 --> s10 --> s11
|
|
||||||
s3 -.->|"план задачи: та же метка"| s7
|
|
||||||
s5 -.->|"скорректировать:<br/>меняются дельта-спеки"| s3
|
|
||||||
s7 -.->|"находка отменяет дизайн:<br/>меняются дельта-спеки"| s3
|
|
||||||
```
|
|
||||||
|
|
||||||
Схема — **сводка**: содержание каждого шага в его разделе ниже, и при
|
|
||||||
расхождении прав текст.
|
|
||||||
|
|
||||||
## Наблюдаемые исходы сценария
|
|
||||||
|
|
||||||
Четыре, и каждый обязан быть назван в докладе прямо:
|
|
||||||
|
|
||||||
- **сделана** — определение сделанного выполнено целиком;
|
|
||||||
- **не доведена** — с причиной и с записанным вопросом; что именно сделано и до
|
|
||||||
какой границы, названо явно. Сюда же попадает чекпоинт, на котором человек
|
|
||||||
решение не одобрил;
|
|
||||||
- **оказалась крупнее задачи** — распознаётся **до заведения change**, иначе его
|
|
||||||
придётся выбрасывать. Дальше — декомпозиция, и это не работа этого скилла;
|
|
||||||
- **нужна разведка** — очевидного способа решения нет, и это выяснилось уже в
|
|
||||||
работе. Стоп с названной причиной; кода не написано ни строки **намеренно**.
|
|
||||||
Разведка идёт следующим прогоном.
|
|
||||||
|
|
||||||
## Определение сделанного
|
|
||||||
|
|
||||||
Задача сделана, когда верно всё:
|
|
||||||
|
|
||||||
1. гейт проекта зелёный;
|
|
||||||
2. ревью проведено, **план прогона сверен с исходом по каждой теме**, темы без
|
|
||||||
отчёта и без дома названы в границах покрытия;
|
|
||||||
3. решение прошло чекпоинт и не разошлось с одобренным — либо разошлось, и
|
|
||||||
чекпоинт был пройден заново;
|
|
||||||
4. change заархивирован, дельты влиты в актуальные спеки;
|
|
||||||
5. коммит сделан в текущую ветку;
|
|
||||||
6. **критерии приёмки, если проект их дал, выписаны поимённо, и по каждому назван
|
|
||||||
оракул и наблюдаемый исход** — «прогнал вот это, увидел вот то». Это **доклад,
|
|
||||||
а не сертификация: приёмка — не работа этого скилла.** Исполнитель, ставящий
|
|
||||||
себе галочку «принято», проверяет свою работу своим же взглядом — по границе
|
|
||||||
это может делать только приёмщик, разведённый с исполнителем. Критерии приходят
|
|
||||||
снаружи; скилл их не сочиняет и не занижает. Расхождение «по каждому критерию
|
|
||||||
исход есть, а суть задачи не достигнута» — дефект критериев, и о нём
|
|
||||||
сообщается, а не молча дорабатывается.
|
|
||||||
|
|
||||||
## Шаги
|
|
||||||
|
|
||||||
### 1. Прочитать задачу
|
|
||||||
|
|
||||||
Прочитай запись и связанные спеки и черновики.
|
|
||||||
|
|
||||||
Проект даёт задаче **критерии приёмки** — выпиши их сразу: на шаге 2 они уезжают
|
|
||||||
в `tasks.md` change. Файл задачи может быть удалён до коммита, а критерии обязаны
|
|
||||||
его пережить.
|
|
||||||
|
|
||||||
Здесь же проверка на «крупнее задачи»: видно, что одним заходом это не
|
|
||||||
мерджится, — объявляй исход **до** заведения change.
|
|
||||||
|
|
||||||
### 2. Завести change — `opsx:propose`
|
|
||||||
|
|
||||||
Вызови Skill `opsx:propose`. Получаем `proposal.md`, дизайн, дельта-спеки
|
|
||||||
(`ADDED`/`MODIFIED`/`REMOVED Requirements`), `tasks.md`. Каждое `### Requirement`
|
|
||||||
содержит `SHALL`/`MUST`; структурные заголовки английские, сценарии
|
|
||||||
`GIVEN/WHEN/THEN`. Прогони `openspec validate --strict <id>`.
|
|
||||||
|
|
||||||
Критерии приёмки задачи, если они были, копируются в `tasks.md` отдельным блоком.
|
|
||||||
Задаче предшествовала разведка — её записка и отвергнутые варианты **уже
|
|
||||||
записаны** в документах канона (`docs/research/`, `docs/adr/`): сошлись на них из
|
|
||||||
`design.md`, а не переписывай второй раз. Варианты, разобранные без разведки
|
|
||||||
(способ был очевиден, но у него оказались оттенки), — в `design.md`, с причиной
|
|
||||||
отказа по каждому отвергнутому.
|
|
||||||
|
|
||||||
**`proposal.md` пишется так, чтобы его понял человек, не читавший спек.** Это не
|
|
||||||
стилистическое пожелание: из него собирается чекпоинт шага 5, и переписывать его
|
|
||||||
там заново значит завести второй дом для одного объяснения. Требование стоит в
|
|
||||||
`openspec/config.yaml`, `rules.proposal` — то есть применяется в момент
|
|
||||||
порождения артефакта, а не вспоминается после.
|
|
||||||
|
|
||||||
### 3. Разметка задачи — агент `review-scope`
|
|
||||||
|
|
||||||
**Один запуск на всю задачу, и он обслуживает обе стадии ревью.** Запусти
|
|
||||||
агента `review-scope`, дав ему корень проекта, идентификатор change, базу диффа и
|
|
||||||
запись задачи. Кода на этот момент нет, и это условие его работы, а не помеха.
|
|
||||||
|
|
||||||
Он возвращает **план задачи**:
|
|
||||||
|
|
||||||
- **размер** (малое / среднее / крупное) и **сложность** (знакомое /
|
|
||||||
незнакомое), каждое с обоснованием по факту;
|
|
||||||
- **метку** как максимум по двум осям: `small`, `medium` или `large`;
|
|
||||||
- **состав ревью дизайна** — что звать на шаге 4;
|
|
||||||
- **таблицу тем** «тема → дом → глубина → кто закрывает» — для шага 7;
|
|
||||||
- разнесение документов проекта по трём категориям и строку про директивы.
|
|
||||||
|
|
||||||
**Метку выбираешь не ты.** Раньше состав ревью дизайна называл сам оркестратор —
|
|
||||||
то есть тот, кто только что довёл предложение до `propose`. Разведённости с
|
|
||||||
автором в этой точке не было вовсе; теперь есть.
|
|
||||||
|
|
||||||
**План держи в контексте до конца задачи.** На диск он не пишется: файл-план стал
|
|
||||||
бы четвёртым артефактом рядом с `proposal.md`, `tasks.md` и `design.md`, пережил
|
|
||||||
бы задачу и разошёлся бы с ней молча. Прервался прогон — повтори шаг 3, это самый
|
|
||||||
дешёвый его проход.
|
|
||||||
|
|
||||||
**Разметка повторяется ровно в одном случае** — если правки изменили сами
|
|
||||||
**дельта-спеки**: план выведен из них, и план по отменённым требованиям назовёт
|
|
||||||
не те темы. Во всех прочих случаях, включая переделку формы кода на шаге 7,
|
|
||||||
метка остаётся прежней.
|
|
||||||
|
|
||||||
### 4. Ревью дизайна — ДО кода, состав по метке
|
|
||||||
|
|
||||||
Вызови Skill **`av-dev-code:review`**, дав ссылку на change `<id>`,
|
|
||||||
**план разметки с шага 3** и указание, что это ревью дизайна.
|
|
||||||
|
|
||||||
Состав приходит планом, а не решается здесь:
|
|
||||||
|
|
||||||
| Метка | Проходы на предложении |
|
|
||||||
|---|---|
|
|
||||||
| `small` | `specs` |
|
|
||||||
| `medium` | `specs`, `rubric` |
|
|
||||||
| `large` | `specs`, `rubric`, `architecture` + вопрос автору о трёх формах решения |
|
|
||||||
|
|
||||||
`review-specs` в режиме «дизайн ДО кода» идёт **на каждой задаче**: это самый
|
|
||||||
дешёвый проход конвейера, и он ловит то, что на готовом коде уже не чинят.
|
|
||||||
Остальные включаются меткой, потому что стадия стоит на каждой задаче и каждый
|
|
||||||
лишний проход здесь умножается на число задач.
|
|
||||||
|
|
||||||
Смысл стадии: архитектурная находка на готовом коде стоит переписывания и потому
|
|
||||||
игнорируется — та же находка здесь стоит абзаца обсуждения. Если `review-rubric`
|
|
||||||
запускался, перенеси его рубрику в `tasks.md` как приёмочные критерии; там же уже
|
|
||||||
лежат критерии от постановки, если они были.
|
|
||||||
|
|
||||||
**Отработка замечаний, и она идёт до чекпоинта, а не после:**
|
|
||||||
|
|
||||||
- мелочь и явные улучшения — правь сам в спеках и дизайне;
|
|
||||||
- развилки (компромисс, scope, инвариант) — **не в запись, а в чекпоинт**: он
|
|
||||||
следующим шагом, и это ровно то, ради чего он поставлен здесь;
|
|
||||||
- после правок перепрогони `openspec validate --strict <id>`.
|
|
||||||
|
|
||||||
### 5. Чекпоинт: объяснение
|
|
||||||
|
|
||||||
**Остановись и объясни человеку, что происходит.** Единственный плановый стоп
|
|
||||||
этого сценария, и он обязателен для всякой задачи.
|
|
||||||
|
|
||||||
Он стоит **после** ревью дизайна намеренно. Человек читает объяснение, уже
|
|
||||||
просеянное машиной: то, что поймал бы `review-specs`, до него не доходит, а
|
|
||||||
внимание — самый дорогой ресурс процесса, и тратить его на выловимое машиной
|
|
||||||
нельзя.
|
|
||||||
|
|
||||||
**Объяснение не сочиняется заново — оно собирается из артефактов**, `proposal.md`
|
|
||||||
и `design.md`. Третий пересказ был бы третьим домом одного и того же и разошёлся
|
|
||||||
бы с обоими. Что показываешь:
|
|
||||||
|
|
||||||
- **в чём проблема** — словами домена из паспорта, без имён модулей и функций;
|
|
||||||
- **как решаем** — суть одним абзацем. Не пересказ дельта-спек: спека нормирует,
|
|
||||||
а здесь объясняют;
|
|
||||||
- **что человек увидит иначе**, когда это будет сделано;
|
|
||||||
- **чего мы намеренно не делаем** и почему — граница scope ловится хуже всего;
|
|
||||||
- **чем рискуем и что осталось нерешённым** — сюда съезжаются развилки,
|
|
||||||
накопленные до этого места, и находки ревью с пометкой `развилка`;
|
|
||||||
- **что дальше**, если возражений нет.
|
|
||||||
|
|
||||||
Проверка на «простой язык» одна и механическая: **в тексте нет `SHALL`, нет имён
|
|
||||||
файлов и функций там, где вещь называется по-русски, и нет слов, которых нет в
|
|
||||||
паспорте проекта.** Не проходит — переписывай, а не объясняй, почему иначе
|
|
||||||
нельзя.
|
|
||||||
|
|
||||||
Длина — экран. Длинное объяснение не читают, а пролистывают, и чекпоинт
|
|
||||||
превращается в ритуал одобрения.
|
|
||||||
|
|
||||||
Три исхода:
|
|
||||||
|
|
||||||
- **согласен** — идёшь на шаг 6;
|
|
||||||
- **скорректировать** — правишь спеки и дизайн по сказанному. Изменились
|
|
||||||
**дельта-спеки** — повтори шаг 3 (разметка выведена из них) и ту часть ревью
|
|
||||||
дизайна, которой касается правка; затем чекпоинт **заново**. Правка внутри
|
|
||||||
дизайна без спек — повтори только чекпоинт;
|
|
||||||
- **не одобрено** — исход «не доведена» с причиной. Change остаётся
|
|
||||||
незаархивированным, задача не закрывается, ничего не коммитится наполовину.
|
|
||||||
|
|
||||||
### 6. Написать код — `opsx:apply`
|
|
||||||
|
|
||||||
Вызови Skill `opsx:apply` для реализации `tasks.md`. Код — по конвенциям проекта
|
|
||||||
(каталог `docs/conventions/`). Меняешь схему — обнови её описание в документации
|
|
||||||
тем же change, если проект этого требует: гейт обычно это проверяет.
|
|
||||||
|
|
||||||
Прогони гейт и добейся зелёного — он же гейт следующего шага.
|
|
||||||
|
|
||||||
**Поведенческая верификация.** Если задача меняет реальное поведение (новый
|
|
||||||
эндпоинт, разбор входа, схема, форма ответа) — зелёных юнит-тестов мало. Подними
|
|
||||||
изменение вживую командой из раздела команд `CLAUDE.md` и прогони сценарий.
|
|
||||||
Пропусти только для чисто внутренних правок без наблюдаемого рантайма.
|
|
||||||
|
|
||||||
**Сервис не оставляем лежать.** Если запуск упал — почини или откати до конца
|
|
||||||
шага.
|
|
||||||
|
|
||||||
### 7. Ревью кода — та же метка
|
|
||||||
|
|
||||||
Вызови Skill **`av-dev-code:review`**, дав ссылку на change `<id>`,
|
|
||||||
базу диффа, **план разметки с шага 3** и режим запуска.
|
|
||||||
|
|
||||||
**Метку ты не выбираешь, и это правило, а не упрощение.** Её назвал
|
|
||||||
`review-scope` ещё на шаге 3 — по размеру и сложности, с обоснованием по каждой
|
|
||||||
оси. Причина в разведённости: ты только что написал этот код, и решать, насколько
|
|
||||||
глубоко его проверять, тебе нельзя — под давлением «я почти закончил» решение
|
|
||||||
известно заранее. Правило выбора живёт в скилле конвейера —
|
|
||||||
`av-dev-code:review`, `references/review-levels.md`; проектные
|
|
||||||
триггеры — в `docs/review.*`, подраздел «Триггеры метки».
|
|
||||||
|
|
||||||
**Метка не пересматривается по факту диффа.** Дифф может выйти крупнее, чем
|
|
||||||
ожидалось при разметке, — это не повод её поднимать: пересмотр означал бы второй
|
|
||||||
запуск разметчика, ровно то, ради устранения чего он и переехал на шаг 3.
|
|
||||||
|
|
||||||
**Считаешь метку заниженной — скажи это в докладе строкой, а не переспорь.**
|
|
||||||
Разметчик вправе и поднять, и понизить; твоё несогласие это факт для человека, а
|
|
||||||
не команда конвейеру. Место, где такое несогласие превращается в изменение
|
|
||||||
правил, — журнал дефектов `docs/review.md`, и только постфактум.
|
|
||||||
|
|
||||||
**Плана нет — ревью кода не запускается.** Триаж требует план обязательным
|
|
||||||
входом: без него он не может сверить, все ли размеченные темы вернули отчёт, а
|
|
||||||
эта сверка — единственная защита от молчащего пропуска. Потерял план (прервалась
|
|
||||||
сессия, ушёл контекст) — повтори шаг 3, а не гони прогон без него.
|
|
||||||
|
|
||||||
**Режим по умолчанию — `по графу`, и обосновывать его не надо.** Конвейер сам
|
|
||||||
знает свои рёбра: гейт открывает опиниативные проходы, проходы с пометкой «держит
|
|
||||||
машину» идут цепочкой (иначе замеры портят друг друга и находка выглядит
|
|
||||||
доказанной), триаж — сток. Просить **`линейно`** нужно только по причине, и она
|
|
||||||
называется строкой: так сказал оператор; машина занята чем-то ещё; идёт разбор
|
|
||||||
самого конвейера.
|
|
||||||
|
|
||||||
Скилл сам гоняет гейт, нужные проходы и обязательный триаж. Возвращает отчёт с
|
|
||||||
потолком 7 пунктов, разметкой `Действие: инлайн | развилка` и секцией границ
|
|
||||||
покрытия.
|
|
||||||
|
|
||||||
**Сверь план прогона с исходом, прежде чем коммитить.** Отчёт начинается планом
|
|
||||||
разметчика — таблицей «тема → дом → глубина → кто закрывает», — и против каждой
|
|
||||||
темы обязан стоять исход. Тема без отчёта и тема без дома — разные вещи, и обе
|
|
||||||
должны быть названы. Реестр короткий (шесть тем ядра плюс свои) — сверка стоит
|
|
||||||
одного взгляда.
|
|
||||||
|
|
||||||
#### Отработка, и здесь появляется одно новое правило
|
|
||||||
|
|
||||||
Помеченное `инлайн` чини сам и не логируй. `развилка` — вопросом в запись (он уже
|
|
||||||
сформулирован триажем, его остаётся перенести). После правок — снова гейт.
|
|
||||||
|
|
||||||
**Находка, отменяющая одобренный дизайн, отменяет и одобрение.** Признак
|
|
||||||
проверяемый: **меняются ли дельта-спеки**.
|
|
||||||
|
|
||||||
- не меняются — находка внутри дизайна, дожимай сам, это обычная отработка;
|
|
||||||
- меняются — решение стало другим, а одобрено было прежнее. Повтори шаг 3
|
|
||||||
(разметка выведена из дельта-спек) и **вернись на чекпоинт шага 5** с тем, что
|
|
||||||
изменилось и почему.
|
|
||||||
|
|
||||||
**Это правило старше правила о развилке.** Находка класса `развилка`, чьё
|
|
||||||
основание — «надо менять спеку», подпадает под оба; побеждает возврат на
|
|
||||||
чекпоинт, а вопрос в запись при нём остаётся, но возврата не заменяет. Иначе
|
|
||||||
одобренный дизайн переделывался бы записанным вопросом, то есть молча.
|
|
||||||
|
|
||||||
Тихо переделать утверждённое нельзя. Чекпоинт, который можно обойти находкой
|
|
||||||
ревью, не значит ничего, а человек при этом уверен, что одобрил именно то, что
|
|
||||||
уехало в коммит.
|
|
||||||
|
|
||||||
**Урожай — списком, не задачами.** Отложенные находки (реальный `major` не для
|
|
||||||
этого мерджа, развилка, решённая «потом», пачка `nit`) собери в секцию доклада
|
|
||||||
`Урожай`: формулировка, оракул, провенанс. Задачи из него **заводит не этот
|
|
||||||
скилл** — их заводит `av-dev-tasks:tasks` своим сценарием «задачи из ревью и
|
|
||||||
аудита»: своя нарезка, свой формат, свои правила дублей. Твоя обязанность — не
|
|
||||||
потерять и передать.
|
|
||||||
|
|
||||||
**Границы покрытия из отчёта не выбрасывай** — они уезжают в финальный доклад
|
|
||||||
сжатой строкой. Отчёт, из которого исчезло «что проверить было невозможно»,
|
|
||||||
превращается в ложное ощущение проверенности.
|
|
||||||
|
|
||||||
**Отчёт триажа сохрани вместе с change (`openspec/changes/<id>/review/`; шаг 8
|
|
||||||
унесёт его в архив вместе с change) — это обязательно, а не «если удобно».** По
|
|
||||||
нему потом видно, что было найдено и что из этого осталось в урожае. И это
|
|
||||||
единственный **независимый** артефакт о составе прогона: своей прозе здесь верить
|
|
||||||
нельзя — она написана тем же, кто мог проход и пропустить.
|
|
||||||
|
|
||||||
### 8. Архивировать — `opsx:archive`
|
|
||||||
|
|
||||||
Вызови Skill `opsx:archive`: change уезжает в архив, дельты вливаются в
|
|
||||||
актуальные спеки. Не пропускай `openspec validate --strict` перед этим.
|
|
||||||
|
|
||||||
### 9. Синк документации
|
|
||||||
|
|
||||||
**Вызови Skill `av-dev-docs:docs`**: он владеет содержимым документов канона и
|
|
||||||
ведёт чек-лист синка.
|
|
||||||
|
|
||||||
**Правило одно и оно жёсткое: принуждённое отрицание.** Доклад обязан назвать
|
|
||||||
**каждый** документ канона — либо чем он обновлён, либо «не требуется, потому
|
|
||||||
что…». Нетронутые группируются одной строкой с общей причиной. Список триггеров
|
|
||||||
прозой уже проверен на живом проекте и дал 6 записей ADR на 43 изменения;
|
|
||||||
работает только обязательное отрицание.
|
|
||||||
|
|
||||||
**Список документов и их триггеров здесь не дублируется** — он в чек-листе скилла
|
|
||||||
`av-dev-docs:docs`, и копия уже однажды разошлась с оригиналом, потеряв два
|
|
||||||
триггера.
|
|
||||||
|
|
||||||
**Плагина `av-dev-docs` в проекте нет** — путь в его дерево не разрешится ниоткуда,
|
|
||||||
поэтому за списком иди в **свой** reference:
|
|
||||||
[references/project-facts.md](../../review/references/project-facts.md)
|
|
||||||
конвейера ревью перечисляет все документы канона с их предметом. Пройди по этому
|
|
||||||
перечню — каждый документ получает строку, отрицание остаётся обязательным.
|
|
||||||
|
|
||||||
**Двух домов в том перечне нет намеренно, а синку они нужны: `docs/adr/` и
|
|
||||||
`docs/research/`.** Проход ревью их не открывает — процессные, — но ADR как раз
|
|
||||||
**главный выход синка**: решение, принятое по ходу задачи, без этой строки
|
|
||||||
теряется систематически. Добавь их к перечню руками: `adr/` — решение с ценой,
|
|
||||||
принятое в этой задаче; `research/` — если по ходу узналось новое о внешнем мире
|
|
||||||
(записку разведки, предшествовавшей задаче, пишет не этот сценарий).
|
|
||||||
Триггеры при этом ты знаешь хуже, и это называется в докладе строкой: «синк
|
|
||||||
сделан по перечню документов, без списка триггеров — плагина `av-dev-docs` нет».
|
|
||||||
Канона в проекте тоже нет — назови это исходом и предложи `av-dev-docs:canon`.
|
|
||||||
|
|
||||||
### 10. Коммит
|
|
||||||
|
|
||||||
Коммить **в текущую ветку** (`git rev-parse --abbrev-ref HEAD`), сам ветку не
|
|
||||||
создавай и не переключай, ничего не пушь.
|
|
||||||
|
|
||||||
Сообщение — по-русски, **скиллом `av-dev-git:commit`**: форму сообщения держит
|
|
||||||
он, и здесь она не пересказывается. Вызов не разрешился — плагина в проекте нет:
|
|
||||||
напиши сообщение сам и скажи строкой доклада, что форму коммита не сверял никто.
|
|
||||||
Одна задача — один осмысленный коммит.
|
|
||||||
|
|
||||||
### 11. Закрыть задачу — **после коммита, не раньше**
|
|
||||||
|
|
||||||
**Вызови Skill `av-dev-tasks:tasks`** и попроси закрыть задачу как реализованную —
|
|
||||||
он владеет форматом и двигает строку индекса сам. Путь к его скрипту не выясняй и
|
|
||||||
индексы руками не правь: мост между плагинами — вызов скилла, а не путь.
|
|
||||||
|
|
||||||
**Порядок обязателен.** Закрытие удаляет файл задачи; сделанное до коммита оно
|
|
||||||
оставило бы задачу закрытой без единого следа работы, если шаг 10 упадёт.
|
|
||||||
|
|
||||||
**Закрытие тоже коммитится — вторым коммитом, тут же.** Удаление файла задачи и
|
|
||||||
правка индексов (их имена знает `av-dev-tasks`, не ты) — это правки в рабочем
|
|
||||||
дереве, и оставить их незакоммиченными нельзя: закрытие, не доехавшее до основной
|
|
||||||
ветки, оставит задачу открытой молча, а опора «индексы под git показывают, что и
|
|
||||||
когда закрыто» без коммита — пустые слова. Сообщение короткое, про учёт, а не про
|
|
||||||
работу: `закрыта задача <slug>`. Это второй коммит осознанно: правило «одна задача
|
|
||||||
— один осмысленный коммит» про работу, а учёт — не работа.
|
|
||||||
|
|
||||||
Плагина в проекте нет — вызов не разрешится. Тогда **ничего не выдумывай**: скажи
|
|
||||||
в докладе, что учёт задач остаётся за владельцем, и назови исход.
|
|
||||||
|
|
||||||
## Доклад решения
|
|
||||||
|
|
||||||
Общее ядро доклада — в SKILL.md; сверх него сценарий решения обязан назвать:
|
|
||||||
|
|
||||||
- **что было одобрено на чекпоинте и совпало ли с тем, что уехало в коммит** —
|
|
||||||
расхождение здесь называется прямо, даже если оно мелкое;
|
|
||||||
- ссылка на архивный change и хеш коммита;
|
|
||||||
- по каждому критерию приёмки, если они были: **оракул и наблюдаемый исход** —
|
|
||||||
это доклад приёмщику, а не отметка «принято»;
|
|
||||||
- **`Урожай`** — отложенные находки списком (формулировка, оракул, провенанс);
|
|
||||||
- **одна строка границ покрытия**: какая метка и режим гонялись, какие проходы не
|
|
||||||
запускались и что проверить было невозможно. Доклад без неё сообщает
|
|
||||||
«проверено», не сообщая, что именно.
|
|
||||||
|
|
||||||
## Тонкости сценария
|
|
||||||
|
|
||||||
- Гейт блокирует: пока он красный, опиниативные проходы не запускаются. Чинить и
|
|
||||||
перезапускать, а не «посмотреть заодно».
|
|
||||||
- Стиль правок — заточка под проект и конвенции, right-size, без золочения.
|
|
||||||
- **Занизить метку ревью, пропустить тему или проскочить чекпоинт — самый дешёвый
|
|
||||||
способ «ускориться», и он же самый дорогой по последствиям.** Защита устроена
|
|
||||||
так, что регулятора у тебя нет: метку выбирает разметчик **до того**, как ты
|
|
||||||
написал код, план сверяется по темам, непокрытое называется строкой, а
|
|
||||||
расхождение с одобренным — отдельным пунктом доклада.
|
|
||||||
- **Заведение задач из урожая ревью — не твоя работа.** Отложенные находки
|
|
||||||
отдаются **списком**; превращает их в задачи `av-dev-tasks:tasks`, у него на
|
|
||||||
этот вход отдельный сценарий «задачи из ревью и аудита». Плагина нет — урожай
|
|
||||||
остаётся списком в докладе, и это говорится строкой.
|
|
||||||
- **Способ решения ты не выбираешь.** Он приходит известным: из постановки, из
|
|
||||||
разведки, от человека. Выбор между двумя подходами с разной ценой делается в
|
|
||||||
разведке, у своего чекпоинта, — не по ходу этого сценария.
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,165 +0,0 @@
|
|||||||
# Метки задачи — выбор, цена, доли
|
|
||||||
|
|
||||||
**Дом правила выбора метки.** Состав проходов по каждой метке, схема процесса и
|
|
||||||
раздача тем живут в [SKILL.md](../SKILL.md) — там диспетчер, и на готовой задаче
|
|
||||||
его достаточно. Здесь то, что читают, когда метку **выбирают, оспаривают или
|
|
||||||
калибруют**.
|
|
||||||
|
|
||||||
Применяет правило `review-scope` при разметке задачи — не автор изменения. Его
|
|
||||||
рабочая выжимка лежит в уставе агента; расходиться она с этим файлом не вправе, а
|
|
||||||
при расхождении прав этот.
|
|
||||||
|
|
||||||
## Правило выбора — две оси, а не один вопрос
|
|
||||||
|
|
||||||
**Оси две, они измеряют разное, и метка есть максимум по ним.**
|
|
||||||
|
|
||||||
| | **знакомое** — форму решения можно назвать до начала | **незнакомое** — форму предстоит нащупать по ходу |
|
|
||||||
|---|---|---|
|
|
||||||
| **малое** — один узел | `small` | `large` |
|
|
||||||
| **среднее** — несколько узлов одного слоя | `medium` | `large` |
|
|
||||||
| **крупное** — несколько слоёв, перенос ответственности, большой рефакторинг | `large` | `large` |
|
|
||||||
|
|
||||||
**Метка — не синоним размера.** Совпадают они только в левом верхнем углу: малое
|
|
||||||
**незнакомое** изменение получает `large`, трогая один узел. Поэтому в плане
|
|
||||||
стоят три строки, а не одна: размер, сложность и метка — каждая со своим
|
|
||||||
обоснованием. Проход, выведший объём диффа из метки, ошибётся ровно на этом
|
|
||||||
случае — а он и есть самый опасный: незнакомая форма в одном узле течёт там, где
|
|
||||||
её никто не ждёт.
|
|
||||||
|
|
||||||
**Размер** — про объём: сколько мест трогается. **Сложность** — про
|
|
||||||
неизвестность: знаем ли мы форму решения заранее. Признак незнакомого простой и
|
|
||||||
проверяемый: **перед работой нельзя назвать, какие узлы будут тронуты**.
|
|
||||||
|
|
||||||
Раньше обе оси были склеены в один вопрос «крупное **или** незнакомое?». Ответ
|
|
||||||
получался тот же, но две вещи под одним именем не измеришь по отдельности, и
|
|
||||||
потому разметка не могла сказать «изменение среднее, но совершенно знакомое» —
|
|
||||||
а именно эта пара и есть рабочее умолчание. Теперь обе оси называются в плане
|
|
||||||
поимённо, и обе — с обоснованием.
|
|
||||||
|
|
||||||
**Оси называются и на стадии дизайна, и на стадии кода — но считаются один
|
|
||||||
раз.** Это и есть причина, по которой разметка переехала к `propose`: состав
|
|
||||||
ревью дизайна выводится из той же пары, что и состав ревью кода, а считать её
|
|
||||||
дважды значит один раз посчитать без разведённости с автором.
|
|
||||||
|
|
||||||
**Обратимость — не третья ось, а отрицательный тест.** Она не уточняет размер и
|
|
||||||
не уточняет сложность: она запрещает нижнюю метку независимо от обеих.
|
|
||||||
|
|
||||||
**Отрицательный тест `small`, и он важнее положительного:** изменение, которое
|
|
||||||
после мерджа **не откатывается обратной правкой**, — не `small`, каким бы
|
|
||||||
маленьким ни был дифф. Сюда попадают миграция схемы и данных, формат на диске,
|
|
||||||
публичный контракт, имя, которое разойдётся по кодовой базе. Три строки миграции
|
|
||||||
— это `medium`, а не `small`: размер диффа и цена ошибки здесь расходятся.
|
|
||||||
|
|
||||||
Что здесь считается крупным, что — незнакомым и что — мелким, проект уточняет в
|
|
||||||
`docs/review.md`, подразделе «Триггеры метки»: **тремя списками** — по одному на
|
|
||||||
каждую ось вверх и один вниз, поимённо, узлами или capability. Это **уточнение**,
|
|
||||||
а не отмена: не записано — работает таблица выше.
|
|
||||||
|
|
||||||
## Спорный случай решается вниз, и у этого есть цена
|
|
||||||
|
|
||||||
Правило асимметрично, потому что асимметрична цена ошибки.
|
|
||||||
|
|
||||||
- **Спорно между `medium` и `large` → бери `medium`.** Ошибка в эту сторону
|
|
||||||
стоит находки, которая всплывёт на следующей задаче или в журнале дефектов.
|
|
||||||
Ошибка в обратную стоит трёх тяжёлых проходов, двое из которых держат машину и
|
|
||||||
идут цепочкой, — и платится она **на каждой** задаче, выбранной неверно.
|
|
||||||
- **Спорно между `small` и `medium` → бери `medium`.** Раньше эта строка
|
|
||||||
обосновывалась тем, что состав одинаков и ошибка почти бесплатна. Теперь состав
|
|
||||||
разный, и обоснование стало прямо противоположным: на `small` три темы ядра
|
|
||||||
смотрятся **только против записанных инвариантов**, а спорный случай — ровно тот,
|
|
||||||
где неизвестно, покрыт ли он инвариантом. Сомнение здесь стоит дороже, чем
|
|
||||||
раньше, и потому решается вниз тем более твёрдо.
|
|
||||||
|
|
||||||
**Выбор сделан в пользу пропускной способности, и это записано, а не подразумевается.**
|
|
||||||
Конвейер настроен на поток задач, а не на максимум находок с каждой: поправить в
|
|
||||||
следующей задаче дешевле, чем держать одну два часа. Отсюда три обязанности,
|
|
||||||
без которых сделка превращается в незаметную потерю качества:
|
|
||||||
|
|
||||||
- **границы покрытия называют темы и их глубину**, а не только запущенные
|
|
||||||
проходы — иначе `small` выглядит так же, как `large` без находок;
|
|
||||||
- **журнал дефектов в `docs/review.md` перестаёт быть хорошей практикой и
|
|
||||||
становится единственной обратной связью**: проскочивший дефект — единственный
|
|
||||||
сигнал, что метка выбрана слишком низко;
|
|
||||||
- **возврат в код — повод пересмотреть метку.** Задача, которая приходит в тот
|
|
||||||
же узел третий раз, уже не мелкая, чем бы ни выглядел её дифф.
|
|
||||||
|
|
||||||
## Метка — максимум по поверхности
|
|
||||||
|
|
||||||
**Обе оси меряются по всему диффу разом, и максимум по каждой отвечает за весь
|
|
||||||
дифф.** Метка изменения — не средневзвешенное: одна строка в перечне границ
|
|
||||||
задачи поднимает метку всему остальному, включая ту часть, которая сама по себе
|
|
||||||
была бы `small`.
|
|
||||||
|
|
||||||
Обратное тоже верно и тоже не бесплатно: у каждой задачи есть **несокращаемый
|
|
||||||
костяк — гейт, спеки, код, триаж**. Разрезать задачу, обе половины которой
|
|
||||||
остаются в одной метке, значит заплатить костяк дважды за ту же проверку.
|
|
||||||
Резать стоит там, где разрез **снимает доказательство с большей части диффа**.
|
|
||||||
Шов и правило нарезки живут у того, кто ведёт задачи, — скилл
|
|
||||||
`av-dev-tasks:tasks`, его раздел о нарезке. Пути туда конвейер не выносит: за
|
|
||||||
пределы своего плагина он ходит вызовом скилла, а не файлом.
|
|
||||||
|
|
||||||
Разметка в костяк не входит — она платится один раз на задачу, а не один раз на
|
|
||||||
прогон, и потому **разрез задачи её не удваивает**. Это единственное, что стало
|
|
||||||
дешевле от переезда разметки к `propose`, и это же снимает прежний довод против
|
|
||||||
нарезки.
|
|
||||||
|
|
||||||
**Размер, сложность, метка и глубина объявляются в отчёте, и все четыре с
|
|
||||||
обоснованием.** Метка выбирает `review-scope`; он вправе и поднять, и понизить
|
|
||||||
её — но не молча: строка «метка X, потому что размер Y и сложность Z»
|
|
||||||
обязательна на каждом прогоне, а не только когда метка отличается от ожидаемой.
|
|
||||||
|
|
||||||
## Чем `small` дешевле `medium` и что это стоит
|
|
||||||
|
|
||||||
Экономят три рычага — непуск, вход, потолок, — и они общие для всех проходов и
|
|
||||||
всех меток; их дом и точные числа в [SKILL.md](../SKILL.md), раздел «Модель по
|
|
||||||
проходу». Здесь только то, что рычаги делают **с этой меткой**:
|
|
||||||
|
|
||||||
1. **Составом.** `basics` на `small` не запускается — кроме случая, когда у
|
|
||||||
проекта есть свои темы; тогда он идёт **только с ними**, ровно как в `large`.
|
|
||||||
Три темы ядра, которые он держал бы, переходят к `code` сверкой по
|
|
||||||
инвариантам.
|
|
||||||
2. **Входом.** На `small` `specs` читает только дельта-спеку, а `code` — только
|
|
||||||
**индекс** конвенций (перечень родов и что механизировано), не весь их дом. На
|
|
||||||
`medium` оба читают дома целиком.
|
|
||||||
3. **Потолком.** На `small` потолки самые жёсткие из трёх меток, и каждый
|
|
||||||
напечатан в границах покрытия своего прохода.
|
|
||||||
|
|
||||||
**Что `small` за это не проверяет, названо поимённо и обязано идти строкой в
|
|
||||||
границы покрытия:** темы `security`, `operations` и `architecture` смотрятся
|
|
||||||
только против **записанных инвариантов** `CLAUDE.md`. Свойство, которого в
|
|
||||||
инвариантах нет, с этой меткой не спросит никто — ни сценарием, ни чтением
|
|
||||||
дома темы. Это и есть цена метки, и она заметно больше прежней: раньше `small`
|
|
||||||
отличался от `medium` одним проходом на один вопрос, то есть не экономил
|
|
||||||
ничего и назывался отдельной меткой зря.
|
|
||||||
|
|
||||||
**`large` назван по тому, что он добавляет: вход шире диффа.** Он единственный, где
|
|
||||||
живут тяжёлые проходы, и единственный, где что-то **запускается**. `basics` в нём
|
|
||||||
берёт только проектные темы; своих тем у проекта нет — он не запускается вовсе, и
|
|
||||||
план говорит об этом строкой. **На `small` действует то же правило и по той же
|
|
||||||
причине** — приёмник запускается только тогда, когда ему есть что принимать.
|
|
||||||
Совпадение неслучайное: `basics` держит темы ядра ровно при одной метке из трёх,
|
|
||||||
а приёмником проектных тем работает на всех.
|
|
||||||
|
|
||||||
## Доли — не пожелание, а проверка правила, и проверок две
|
|
||||||
|
|
||||||
**Сверху: `large` — 5–10%.** Если туда уходит каждая третья задача, метку
|
|
||||||
выбирают по ощущению важности. Обратный перекос виден по журналу проскочивших
|
|
||||||
дефектов: класс, который ловят только меряющие проходы, начинает всплывать после
|
|
||||||
мерджа.
|
|
||||||
|
|
||||||
**Снизу: `small` не должен обгонять `medium`.** Ориентир — до трети задач, но
|
|
||||||
сравнение важнее числа: **перевес `small` над `medium` значит, что рабочее
|
|
||||||
умолчание сместилось, а решения об этом никто не принимал.** Проверка нужна
|
|
||||||
именно теперь: пока две нижние метки совпадали составом, дрейф между ними не
|
|
||||||
стоил ничего, и проверки не было. Сейчас он стоит трёх тем ядра, которые на
|
|
||||||
`small` смотрятся только против инвариантов, — то есть ровно того, чем `small` и
|
|
||||||
дёшев.
|
|
||||||
|
|
||||||
Считается это по журналу дефектов и по отчётам, а не по ощущению: метка
|
|
||||||
напечатана в каждом отчёте, и посчитать её за месяц — работа на минуту.
|
|
||||||
|
|
||||||
**У дрейфа вниз есть свой стимул, и его стоит назвать.** `small` дешевле по
|
|
||||||
времени и по деньгам, а выбирает метку хоть и не автор, но проход, читающий
|
|
||||||
описание, написанное автором. Занижённое описание даёт занижённую метку без
|
|
||||||
чьего-либо злого умысла — потому корректор и вынесен в `code`, который смотрит
|
|
||||||
уже на код, а не на описание.
|
|
||||||
@@ -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,211 +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,215 +0,0 @@
|
|||||||
---
|
|
||||||
name: docs
|
|
||||||
description: Вести содержимое документов канона по ходу разработки — синк после сделанной задачи с построчным отчётом по каждому документу, заведение ADR промоутом из архивного design.md или из записки разведки, запись наблюдения в research, запись дефекта и настройки конвейера в review.md, чистка architecture.md от поведения с маркерами долга. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл canon.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Ведение содержимого канона
|
|
||||||
|
|
||||||
Скилл владеет **содержимым** документов канона; раскладкой владеет `canon`.
|
|
||||||
Определение канона и роли документов — [канон](../canon/references/canon.md),
|
|
||||||
здесь не пересказывается.
|
|
||||||
|
|
||||||
Главный вызывающий — **шаг синка документации в конвейере задачи**. Конвейер
|
|
||||||
живёт в другом плагине и зовёт этот скилл по имени; проект без конвейера ведёт
|
|
||||||
документацию тем же скиллом вручную.
|
|
||||||
|
|
||||||
## Правило, из которого всё следует
|
|
||||||
|
|
||||||
**Принуждённое отрицание.** Синк обязан назвать **каждый** документ канона: либо
|
|
||||||
чем он обновлён, либо «не требуется, потому что…». Нетронутые группируются одной
|
|
||||||
строкой с общей причиной.
|
|
||||||
|
|
||||||
Причина, по которой правило именно такое, измерена: у ADR был список триггеров
|
|
||||||
прозой — и он дал **6 записей на 43 изменения**. Прозаический триггер, который
|
|
||||||
некому проверить, не срабатывает. Отличить «не написал» от «написал, что не
|
|
||||||
требуется» можно только тогда, когда отрицание обязательно.
|
|
||||||
|
|
||||||
Это тот же приём, что «границы покрытия» в отчёте ревью и «пустое называется
|
|
||||||
пустым» в каноне.
|
|
||||||
|
|
||||||
## Чек-лист синка
|
|
||||||
|
|
||||||
Идёт сверху вниз; каждая строка попадает в доклад.
|
|
||||||
|
|
||||||
| Документ | Обновляется, когда | Проверка |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `openspec/specs/` | всегда при изменении поведения | вливает `opsx:archive` |
|
|
||||||
| `database.md` | тронуты миграции | `docs.py check --base` |
|
|
||||||
| `architecture.md` | новый компонент, граница, внешняя зависимость, изменилось окружение | `docs.py`: capability без упоминания |
|
|
||||||
| `adr/` | дорогой откат, намеренный отказ, пересмотр прежнего | нет — только этот чек-лист |
|
|
||||||
| `research/` | узнали новое о внешнем формате или данных | нет |
|
|
||||||
| `security.md` | новый недоверенный вход, токен, путь наружу, сдвиг периметра | нет |
|
|
||||||
| `conventions/` | находка принята и не специфична для одного места | промоут |
|
|
||||||
| `review.md` | дефект воспроизведён; сузили или расширили проверку | нет |
|
|
||||||
| `passport.md` | новый потребитель, сдвиг границы «чем не является» | нет |
|
|
||||||
| `CLAUDE.md` | изменился инвариант, гейт, запрет, необратимое | нет |
|
|
||||||
|
|
||||||
Пример доклада:
|
|
||||||
|
|
||||||
```
|
|
||||||
Синк документации:
|
|
||||||
- architecture.md — добавлен воркер свёртки, ссылка на capability reindex
|
|
||||||
- database.md — миграция 00006, таблица bucket
|
|
||||||
- adr/ — заведён ADR-2026-08-03-queue-as-table: отказ от внешней очереди
|
|
||||||
- research/ — новое о формате не узнано
|
|
||||||
- passport, security, conventions, review — не требуется: изменение внутреннее
|
|
||||||
```
|
|
||||||
|
|
||||||
## Сверка — не здесь, а в `av-dev-docs:healthcheck`
|
|
||||||
|
|
||||||
Синк правит документы поодиночке, а расходятся они **между собой**: факт,
|
|
||||||
дописанный в `architecture.md`, уже живёт в `CLAUDE.md`; периметр в
|
|
||||||
`security.md` не знает про новый эндпоинт. Поймать это на своей же правке нельзя,
|
|
||||||
и судит это агент `doc-consistency`.
|
|
||||||
|
|
||||||
**Но синк его не зовёт.** Обоими судьями документов владеет скилл
|
|
||||||
`av-dev-docs:healthcheck`, и зовут их на весь канон разом, а не на пачку,
|
|
||||||
отобранную работой. Причина в цене: `doc-consistency` на `opus` по каждой
|
|
||||||
сделанной задаче — самая дорогая церемония процесса, а `doc-code-drift` хоть и на
|
|
||||||
`sonnet`, но читает репозиторий целиком. К тому же расхождение между двумя
|
|
||||||
документами по определению требует двух документов, а на большинстве задач синк
|
|
||||||
правит один.
|
|
||||||
|
|
||||||
Что теряется: привязка находки к задаче, которая её породила. Что выигрывается,
|
|
||||||
кроме денег: синк перестаёт отбирать пачку, и в неё попадают документы,
|
|
||||||
которых работа не касалась, — расхождение, внесённое правкой в одном месте, там
|
|
||||||
и живёт.
|
|
||||||
|
|
||||||
## Вычитка — наоборот, здесь
|
|
||||||
|
|
||||||
**Язык правленого вычитывается на синке, и зовёшь агента `doc-wording` ты.**
|
|
||||||
Довод обратный доводу про судей: он читает **только названную пачку**, стоит
|
|
||||||
дёшево и ищет ровно то, что портится в момент письма, — залог, оценку без факта,
|
|
||||||
жаргон, термин без ввода. Ждать `healthcheck` здесь нечего: через месяц никто уже
|
|
||||||
не помнит, какую фразу имел в виду автор.
|
|
||||||
|
|
||||||
Позови его **последним шагом синка**, отдав список файлов, которых чек-лист
|
|
||||||
коснулся, — и назови этот список в промпте: по нему же он судит, известен ли
|
|
||||||
термин. Ничего не правивший синк агента не зовёт. Находки он отдаёт готовыми
|
|
||||||
формулировками, подставляешь их ты.
|
|
||||||
|
|
||||||
## ADR — промоут, а не второе сочинение
|
|
||||||
|
|
||||||
Обоснование уже написано: `opsx:propose` кладёт `design.md` в каждый change, и
|
|
||||||
после архивации он лежит в `openspec/changes/archive/<id>/design.md` с разделами
|
|
||||||
`Context` / `Goals / Non-Goals` / `Decisions` / `Risks / Trade-offs`.
|
|
||||||
|
|
||||||
**ADR цитирует решение оттуда и ссылается на источник.** Не пересказывает и не
|
|
||||||
сочиняет заново.
|
|
||||||
|
|
||||||
**Второй законный источник — записка разведки**, и приходит он от скилла
|
|
||||||
`av-dev-code:research`: решение, принятое разведкой (намеренный отказ, выбор
|
|
||||||
подхода, «проверили и не делаем»), `design.md` не имеет по построению — change по
|
|
||||||
нему не будет никогда. Промоут при этом тот же: цитата и ссылка, но на записку.
|
|
||||||
Перечень источников закрыт и живёт в [каноне](../canon/references/canon.md),
|
|
||||||
раздел `adr/`.
|
|
||||||
|
|
||||||
**Триггер заведения, форма имени и правило замены — в
|
|
||||||
[каноне](../canon/references/canon.md), раздел `adr/`.** Здесь они не
|
|
||||||
повторяются: копия правила расходится с оригиналом на первой же смене версии
|
|
||||||
канона, а расходится незаметно.
|
|
||||||
|
|
||||||
Твоя часть — **применить триггер к этой задаче и сказать вслух, сработал он или
|
|
||||||
нет**. Строка «adr/ — не требуется: решение рутинное» и есть то, ради чего
|
|
||||||
чек-лист существует; её отсутствие неотличимо от «забыл посмотреть».
|
|
||||||
|
|
||||||
Порядок работы: открой источник — архивный `design.md` change либо записку
|
|
||||||
разведки, — найди в нём решение, проходящее триггер, процитируй его и причину,
|
|
||||||
сошлись на источник, добавь строку в индекс `docs/adr/README.md` сверху.
|
|
||||||
|
|
||||||
## Чистка `architecture.md`
|
|
||||||
|
|
||||||
Обзор не держит поведение — его нормативный дом `openspec/specs/`. **Форма
|
|
||||||
маркера долга и правило «гейт от них не краснеет» — в
|
|
||||||
[каноне](../canon/references/canon.md), раздел `architecture.md`.**
|
|
||||||
|
|
||||||
Разбирается порциями: раздел вычищает та задача, которая его касается.
|
|
||||||
Содержимое не выбрасывается, а переезжает — требования в дельта-спеку change,
|
|
||||||
обоснование в ADR, обзор остаётся строкой со ссылкой на capability.
|
|
||||||
|
|
||||||
## Запись в `research/`
|
|
||||||
|
|
||||||
Наблюдение о внешнем мире: что реально шлёт источник, чем документация формата
|
|
||||||
расходится с практикой. **Требование провенанса и правило про расходящееся
|
|
||||||
число — в [каноне](../canon/references/canon.md), раздел `research/`.**
|
|
||||||
|
|
||||||
Твоя часть — заметить, что по ходу задачи узналось новое о внешних данных, и не
|
|
||||||
дать этому остаться в контексте. Признак: ты правил разбор, опираясь на то, чего
|
|
||||||
нет ни в одном документе.
|
|
||||||
|
|
||||||
## Обращение к соседним плагинам
|
|
||||||
|
|
||||||
Два раздела ниже — запись в `review.md` и промоут в конвенции — берут форму у
|
|
||||||
конвейера ревью: она принадлежит ему, а не канону. Берут **вызовом скилла**, а не
|
|
||||||
чтением файла по пути.
|
|
||||||
|
|
||||||
**Копия.** Дом правила — `shared/plugin-boundary.md` в репозитории плагинов.
|
|
||||||
Правится дом, а не этот файл.
|
|
||||||
|
|
||||||
<!-- копия: граница-плагинов из shared/plugin-boundary.md -->
|
|
||||||
|
|
||||||
Плагины `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` — конвейер.
|
|
||||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
|
||||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
|
||||||
|
|
||||||
<!-- /копия: граница-плагинов -->
|
|
||||||
|
|
||||||
Чем оборачивается отсутствие конвейера — в каждом из двух разделов отдельно: без
|
|
||||||
него работа не отменяется, отменяется только его процедура.
|
|
||||||
|
|
||||||
## Запись в `review.md`
|
|
||||||
|
|
||||||
Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку
|
|
||||||
конвейера. **Что в каком и в какой форме — в
|
|
||||||
[каноне](../canon/references/canon.md), раздел `review.md`**; подробности формы
|
|
||||||
записи и выбор адреса, куда она ведёт, — у конвейера ревью проекта (при
|
|
||||||
`av-dev-code` — `Skill av-dev-code:review`, его
|
|
||||||
`references/review-journal.md`). **Конвейера в проекте нет** — пиши по форме из
|
|
||||||
скелета `review.md`, которую положил канон, и скажи в докладе, что подробностей
|
|
||||||
формы взять негде.
|
|
||||||
|
|
||||||
Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим».
|
|
||||||
Со временем теряется не факт, а то, почему дефект не поймали, — единственное,
|
|
||||||
ради чего журнал есть. И решение сузить проверки (перестали звать проход, понизили
|
|
||||||
метку) обязано попасть в раздел настройки, а не остаться в отчёте ревью.
|
|
||||||
|
|
||||||
## Промоут в конвенции
|
|
||||||
|
|
||||||
Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком
|
|
||||||
принадлежит конвейеру ревью проекта (при `av-dev-code` — его
|
|
||||||
`references/promote.md`, читается через `Skill av-dev-code:review`);
|
|
||||||
роль каталога конвенций — в [каноне](../canon/references/canon.md). **Конвейера
|
|
||||||
нет** — три шага всё равно твои, просто без его процедуры: сформулируй правило,
|
|
||||||
поищи, чем оно механизируется, и вычеркни прозу, если механизировалось.
|
|
||||||
|
|
||||||
Твоя часть — **третий шаг, который пропускают чаще всего**: правило заработало,
|
|
||||||
а формулировка осталась в прозе, и проход продолжает проверять уже проверенное.
|
|
||||||
На синке это отдельная строка: «conventions/ — правило X механизировано,
|
|
||||||
формулировка удалена» либо «не требуется».
|
|
||||||
|
|
||||||
## Чего этот скилл не делает
|
|
||||||
|
|
||||||
- **Не проверяет раскладку** — это `canon`.
|
|
||||||
- **Не заводит недостающие документы** — их скелет кладёт `canon adopt` или
|
|
||||||
`init`.
|
|
||||||
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
|
|
||||||
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.
|
|
||||||
@@ -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,211 +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` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
|
||||||
|
|
||||||
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
|
||||||
пользователю, а это другая работа.
|
|
||||||
|
|
||||||
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
|
||||||
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
|
||||||
целей: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
|
||||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
|
||||||
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
|
||||||
|
|
||||||
<!-- /копия: сопровождение-словарь -->
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
# Декомпозиция и мозговой штурм
|
|
||||||
|
|
||||||
Обе операции превращают одну запись в несколько (или в ноль). Разница во входе:
|
|
||||||
декомпозиция дробит **слишком крупную задачу**, штурм прорабатывает **идею**,
|
|
||||||
которая ещё не задача.
|
|
||||||
|
|
||||||
## Тест декомпозиции
|
|
||||||
|
|
||||||
Задачу можно дробить, только если части удовлетворяют **обоим** условиям:
|
|
||||||
|
|
||||||
1. **Мерджатся независимо.** Часть Б не требует, чтобы часть А была уже влита.
|
|
||||||
Есть порядок «сперва А, потом Б, иначе не собрать» → это не декомпозиция, а
|
|
||||||
план реализации: шаги остаются **внутри одного файла**.
|
|
||||||
2. **Каждая — самостоятельный шаг.** Часть, осмысленная только в комплекте с
|
|
||||||
другой, — не задача. Проверяй тестом «готова к взятию» (task-format): какую
|
|
||||||
строку «Завершения» цели двигает **именно эта часть** и какие у неё
|
|
||||||
собственные критерии приёмки. У операционных частей (`fix`, `chore`,
|
|
||||||
`research`) цели может не быть — тогда достаточно собственных критериев.
|
|
||||||
|
|
||||||
Не проходит хотя бы одно — **не дроби**. Ложная декомпозиция плодит файлы,
|
|
||||||
которые нельзя взять поодиночке, и переоценка потом склеивает их обратно.
|
|
||||||
|
|
||||||
## Где резать, если резать можно
|
|
||||||
|
|
||||||
Тест выше говорит, **допустим** ли разрез. Где его провести из нескольких
|
|
||||||
допустимых мест — отвечает шов.
|
|
||||||
|
|
||||||
**Шов — там, где падает метка ревью.** Раздел «Затрагивает» перечисляет
|
|
||||||
границы; если одна строка перечня поднимает метку выше остальных, эта часть и
|
|
||||||
режется отдельно. Пример: задача перекладывает несколько узлов разом и заодно
|
|
||||||
добавляет два поля в существующий ответ. Целиком это `large` — семь проходов по
|
|
||||||
всему диффу, включая два, что держат машину и идут цепочкой. Разрезанная по шву,
|
|
||||||
она даёт `large` на маленькой переложенной части и `medium` на остатке.
|
|
||||||
|
|
||||||
**Считай костяк, а не файлы.** У каждой задачи есть несокращаемые четыре прохода
|
|
||||||
(гейт, спеки, код, триаж), и они платятся за каждую. Разрез, после которого обе
|
|
||||||
половины остаются в одной метке, делает ревью **дороже**: тот же объём
|
|
||||||
проверяется тем же составом, но костяк оплачен дважды. Отсюда правило: **резать,
|
|
||||||
когда разрез снимает дорогой проход с большей части диффа**, и не резать, когда
|
|
||||||
он просто делает файлы мельче.
|
|
||||||
|
|
||||||
**Это планирование, а не предписание процесса.** Метка ревью выбирается по
|
|
||||||
факту изменения — тем, кто его видит, — и в тело задачи не пишется: строка
|
|
||||||
«делать с меткой medium» это ровно тот второй дом правила выбора, который
|
|
||||||
гигиена полей снимает. Шов пользуется меткой как **признаком**, что в задаче
|
|
||||||
две разнородные работы; решение о метке остаётся за конвейером.
|
|
||||||
|
|
||||||
**Цель наследуется.** Все части несут `goal:` родителя: декомпозиция не меняет
|
|
||||||
того, чему работа служит. Если у части цель другая — это признак, что дробили не
|
|
||||||
по той границе, либо что часть вообще из другой работы.
|
|
||||||
|
|
||||||
## Что делать с родителем
|
|
||||||
|
|
||||||
После разделения родитель **не остаётся** третьей висящей строкой:
|
|
||||||
|
|
||||||
- части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
|
|
||||||
`REJECTED.md` здесь — не «выкинули», а именно тот след, что переживает запись:
|
|
||||||
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
|
|
||||||
наследников, а не археологией git;
|
|
||||||
- родитель осмыслен как **возможность**, а не как шаг → это цель, и **строка
|
|
||||||
переезжает**: `edit <slug> --type goal --section <часть роадмапа>` снимает её с
|
|
||||||
`BACKLOG.md` и вставляет в `ROADMAP.md`. Файл в `items/` при этом не двигается —
|
|
||||||
он и есть запись. Части получают `--goal <слаг родителя>`, а закрывать родителя
|
|
||||||
нечем и незачем: он не выкинут, он стал целью.
|
|
||||||
|
|
||||||
**Промежуточного зонтика между целью и задачей нет.** Тип `epic` упразднён:
|
|
||||||
роль зонтика играет цель, а слишком крупный шаг дробится на шаги помельче под
|
|
||||||
той же целью. Если частям нужен общий заголовок — значит у них общая
|
|
||||||
возможность, и её надо назвать целью, а не заводить временный тип.
|
|
||||||
|
|
||||||
## Когда декомпозиция случается посреди работы
|
|
||||||
|
|
||||||
Задача, которая **оказалась крупнее задачи**, распознаётся до того, как под неё
|
|
||||||
заведено предложение об изменении: иначе его придётся выбрасывать. Она выходит
|
|
||||||
из работы на декомпозицию, а её строка возвращается в беклог с причиной
|
|
||||||
(`move … --reason "крупнее задачи"`). Части заводятся сразу под той же целью, и
|
|
||||||
**место в очереди им назначает человек**: машина поставит их в конец секции, а
|
|
||||||
крупная задача редко распадается на что-то менее срочное, чем была сама.
|
|
||||||
|
|
||||||
## Мозговой штурм сырья
|
|
||||||
|
|
||||||
Сырьё — запись типа `research`, у которой раздел «Вопрос» пуст: она не проходит
|
|
||||||
тест «готова к взятию», потому что неясно, что именно делаем. Штурм проясняет —
|
|
||||||
и это **generative-операция, а не applicative**.
|
|
||||||
|
|
||||||
Исход штурма и есть заполненный «Вопрос» (тогда разведку можно брать в работу)
|
|
||||||
или набор задач с типами, которые из ответа следуют. Третий законный исход —
|
|
||||||
`close --reason`.
|
|
||||||
|
|
||||||
Applicative-штурм («перечисли задачи, следующие из идеи») выдаёт очевидное:
|
|
||||||
перечисляется то, что уже видно в формулировке. Ценное — на уровень выше.
|
|
||||||
|
|
||||||
1. **Сперва — формы, а не задачи.** Предложи **три разные постановки** идеи и
|
|
||||||
назови **компромисс каждой**: что она даёт, чем платит, что оставляет за
|
|
||||||
бортом. Если получилась одна постановка — штурм не состоялся, это
|
|
||||||
applicative.
|
|
||||||
2. **Вынеси формы пользователю** через `AskUserQuestion` с компромиссами. Рамку
|
|
||||||
выбирает он: это продуктовое решение, не механика.
|
|
||||||
3. **Назови цель.** Выбранная форма служит цели — существующей или новой. Идея,
|
|
||||||
для которой цель не находится, скорее всего уезжает в `REJECTED.md`, а не
|
|
||||||
заводится задачей.
|
|
||||||
4. **Только выбранную форму** дроби по тесту декомпозиции выше и проставь
|
|
||||||
критерии приёмки: без них наследники останутся идеями под другим именем.
|
|
||||||
|
|
||||||
**«Выкинуть» — полноправный исход штурма, а не его неудача.** Проработка, честно
|
|
||||||
показавшая, что пользы нет или она несоразмерна цене, — это результат: идея
|
|
||||||
уезжает с этой самой причиной, и та причина гасит её повторное появление.
|
|
||||||
|
|
||||||
## Доклад
|
|
||||||
|
|
||||||
- Идея/задача на входе, выбранная рамка (для штурма), задачи-наследники со
|
|
||||||
слагами, целями и секциями.
|
|
||||||
- Судьба родителя: удалён / стал целью / выкинут с причиной.
|
|
||||||
- `tasks.py check` после правок.
|
|
||||||
- Границы покрытия: какие постановки рассмотрены и какие сознательно отброшены —
|
|
||||||
чтобы штурм не пришлось повторять с нуля.
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
# 🎯 `goal` — возможность приложения
|
|
||||||
|
|
||||||
Цель отвечает на **«что приложение будет уметь»**. Не область работ и не имя
|
|
||||||
подсистемы: не «Работа со слиянием», а «Исход слияния не зависит от порядка
|
|
||||||
доставки». Свойство поведения — тоже возможность.
|
|
||||||
|
|
||||||
Общая форма записи (мета, слаг, строка индекса) — [task-format.md](task-format.md).
|
|
||||||
Здесь только то, что у этого типа своё.
|
|
||||||
|
|
||||||
## Схема
|
|
||||||
|
|
||||||
| | |
|
|
||||||
| --- | --- |
|
|
||||||
| Заголовок отвечает на | что приложение будет уметь |
|
|
||||||
| Обязательные разделы | `Завершение` |
|
|
||||||
| Допустимые сверх того | — |
|
|
||||||
| Поле места | **Секция** — часть роадмапа |
|
|
||||||
| Цель (`goal:<слаг>`) | запрещена: цель и есть цель |
|
|
||||||
| Индекс | `ROADMAP.md`, и никогда `BACKLOG.md` |
|
|
||||||
| Берётся в работу | нет — берутся её задачи |
|
|
||||||
|
|
||||||
Поле места у цели называется **«Секция»**, а не «Категория», и это не разнобой:
|
|
||||||
у задачи оно называет полку домена, на которой она лежит, а у цели
|
|
||||||
— часть роадмапа, то есть состояние очереди. Одно имя на два смысла их и
|
|
||||||
смешивало.
|
|
||||||
|
|
||||||
## «Завершение» — списком, а не абзацем
|
|
||||||
|
|
||||||
Это признаки того, что приложение **уже умеет**, и на строки этого раздела
|
|
||||||
ссылаются задачи цели: «двигает пункт 2 «Завершения» — накопительная метрика
|
|
||||||
перестаёт уменьшаться». Абзацем такая ссылка не берётся, поэтому список.
|
|
||||||
|
|
||||||
Отсюда же читается обратное и более полезное: **строка «Завершения», к которой
|
|
||||||
не относится ни одна задача, — незакрытая часть возможности**. Достаточность
|
|
||||||
набора задач видна из самой цели, а не из чьей-то памяти.
|
|
||||||
|
|
||||||
## Алгоритм
|
|
||||||
|
|
||||||
1. **Проверить, что это возможность, а не работа.** Работа, которой держат
|
|
||||||
проект, на вопрос «что приложение будет уметь» не отвечает; состав перечислен
|
|
||||||
[в словаре сопровождения](operations.md). Ей отведена секция
|
|
||||||
`Сопровождение` — там она видна в том же
|
|
||||||
экране и не читается как обещание продукта. Граница проходит по тому,
|
|
||||||
**кто наблюдает**:
|
|
||||||
«приложение сообщает о своём состоянии» — возможность, «дежурный видит
|
|
||||||
состояние на одном экране» — сопровождение.
|
|
||||||
2. **Выбрать секцию.** Очередь значима и обоснована прозой — `Запланировано`;
|
|
||||||
тянется долго и очереди не имеет — `Направления`; про то, чем держат проект,
|
|
||||||
— `Сопровождение`. В `Готово` кладёт сам `close`.
|
|
||||||
3. **Написать «Завершение»** — 2–5 наблюдаемых признаков списком. Пишутся до
|
|
||||||
декомпозиции: иначе задачи придумают себе цель задним числом.
|
|
||||||
4. **Разложить на задачи** и проставить им `goal:<слаг>`. Перечень задач в теле
|
|
||||||
цели **не хранится** — он был бы третьим индексом и поехал бы на первой же
|
|
||||||
закрытой задаче; выводит `tasks.py list --goal <слаг>`.
|
|
||||||
5. **Пометить `decomposed`.** Тег отличает «ещё не разобрана» от «все задачи
|
|
||||||
закрыты» — два состояния с одним внешним признаком. `check --fix` ставит его
|
|
||||||
сам цели, у которой задачи есть.
|
|
||||||
6. **Закрыть достигнутой** — `close <слаг> --implemented`, когда не осталось
|
|
||||||
открытых задач. Файл удаляется, строка с датой переезжает в `Готово`. Скрипт
|
|
||||||
откажет, если задачи ещё живы.
|
|
||||||
|
|
||||||
## Отменённая цель — сперва задачи, потом цель
|
|
||||||
|
|
||||||
Замысел бывает неверен, и цель отменяют, не достигнув. Порядок обратный
|
|
||||||
завершению и держится тем же запретом: цель, закрытая поверх живых задач,
|
|
||||||
оставила бы их сиротами, и `close` этого не даст.
|
|
||||||
|
|
||||||
1. **Разобрать её задачи поштучно.** Задача, теряющая смысл вместе с целью, —
|
|
||||||
`close <slug> --reason "<почему>"`; задача, переживающая цель, — `edit <slug>
|
|
||||||
--goal <другая>`. **Причина обязательна и пишется своя каждой:** «цель
|
|
||||||
отменена» это не причина, а пересказ команды, и в `REJECTED.md` от него нет
|
|
||||||
пользы через квартал.
|
|
||||||
2. **Закрыть саму цель** — `close <слаг> --reason "<почему замысел отменён>"`.
|
|
||||||
Файл удаляется, строка с причиной и датой уходит в `REJECTED.md`. В `Готово`
|
|
||||||
не попадает: `Готово` отвечает «что приложение умеет», а отменённая цель не
|
|
||||||
умеет ничего.
|
|
||||||
|
|
||||||
**Место этому — груминг, а не отдельный заход.** Отмена цели значит
|
|
||||||
разбор всех её задач, а разбор задач и есть шаг 3 груминга
|
|
||||||
(скилл `groom`, «что перестало быть важным»). Отменять на ходу,
|
|
||||||
между делом, — верный способ закрыть скопом то, что стоило перевесить.
|
|
||||||
|
|
||||||
## Что видит машина, а что человек
|
|
||||||
|
|
||||||
`check` считает цели, различает разобранные и пустые, ставит `decomposed`,
|
|
||||||
запрещает закрыть цель с живыми задачами и держит `Секцию` в согласии с
|
|
||||||
заголовком роадмапа. **Годность формулировки — не машине**: «возможность это или
|
|
||||||
область работ» решает [агент вычитки](../SKILL.md#вычитка-два-прохода-а-не-один).
|
|
||||||
|
|
||||||
Достигнутая цель **не исчезает**: «что приложение умеет» — половина вопроса, ради
|
|
||||||
которого роадмап открывают. Вторым домом поведения роадмап при этом не
|
|
||||||
становится: нормативное поведение живёт в `openspec/specs/`, роадмап отвечает,
|
|
||||||
**когда и в каком порядке** оно появилось.
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "av-dev",
|
||||||
|
"description": "Личный процесс разработки одним плагином: форма раскладки, документы проекта, учёт работ и работа по задачам. Форму держит скилл canon — три операции одной машиной сравнения (check, adopt, upgrade) со скриптом docs.py и общим журналом версий, по которому повышается вся раскладка, включая каталог задач; имя без префикса, потому что форма общая у всех частей. Документы — канон раскладки (CLAUDE.md плюс docs/: паспорт, архитектура, схема БД, безопасность, конвенции, разведка, ADR, журнал ревью), заведение нового проекта интервью в doc-init, ведение содержимого по ходу разработки в doc-sync, суд о смысловом здоровье в doc-healthcheck двумя агентами разом. Учёт — задачи каталогом markdown-файлов в task-track: у задачи тип (feature, fix, chore, research), задающий её схему, а у проекта — стадия (build — беклог это план стройки от базы к деталям, порядок строк значит зависимость; support — очередь правок, порядок значит важность); очередь расставляет интерактивный task-groom порядком строк. Работа — одна задача от постановки до закрытия скиллом code-resolve: точка входа одна, сценария три (решение циклом SDD с чекпоинтом, разведка без кода, обслуживание без change), и выбирает сценарий сам скилл, прочитав постановку; плюс конвейер ревью code-review с детерминированным гейтом, сверкой со спеками и обязательным триажем, плюс code-deep-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`.
|
||||||
Проверка: существует ли. Путь в запрете, которого нет, — находка **особого
|
Проверка: существует ли. Путь в запрете, которого нет, — находка **особого
|
||||||
рода**: запрет, который не на что наложить, читается как соблюдённый, а на
|
рода**: запрет, который не на что наложить, читается как соблюдённый, а на
|
||||||
деле охраняет пустоту, пока настоящий каталог зовётся иначе.
|
деле охраняет пустоту, пока настоящий каталог зовётся иначе.
|
||||||
@@ -114,7 +114,7 @@ color: green
|
|||||||
судит ревью, а не сверка.
|
судит ревью, а не сверка.
|
||||||
|
|
||||||
**Согласованность документов между собой** — у `doc-consistency`: факт в двух
|
**Согласованность документов между собой** — у `doc-consistency`: факт в двух
|
||||||
домах, противоречие между документами, поведение в обзоре, ADR и провенанс.
|
домах, противоречие между документами, поведение в обзоре, ADR и происхождение чисел.
|
||||||
Увидел — строкой в границы покрытия, находкой не оформляй.
|
Увидел — строкой в границы покрытия, находкой не оформляй.
|
||||||
|
|
||||||
**Язык документов** — у `doc-wording`, **язык записей задач** — у
|
**Язык документов** — у `doc-wording`, **язык записей задач** — у
|
||||||
@@ -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,18 +15,19 @@ color: yellow
|
|||||||
машина, а что человек», и её правая колонка — твой устав дословно.
|
машина, а что человек», и её правая колонка — твой устав дословно.
|
||||||
|
|
||||||
Карта домов, по которой ты судишь о правиле 1, — дословная копия канона; дом её
|
Карта домов, по которой ты судишь о правиле 1, — дословная копия канона; дом её
|
||||||
`av-dev-docs/skills/canon/references/canon.md`, раздел «Правило единственного
|
`av-dev/skills/canon/references/canon.md`, раздел «Правило единственного
|
||||||
дома», и правится она там. Здесь она стоит потому, что ты работаешь в
|
дома», и правится она там. Здесь она стоит потому, что устав — это твой промпт
|
||||||
репозитории проекта, где плагина может не быть вовсе.
|
целиком: за ссылкой ты пошёл бы отдельным чтением, а карта нужна тебе в тот
|
||||||
|
момент, когда ты судишь.
|
||||||
|
|
||||||
<!-- копия: карта-домов из av-dev-docs/skills/canon/references/canon.md -->
|
<!-- копия: карта-домов из av-dev/skills/canon/references/canon.md -->
|
||||||
| Факт | Дом |
|
| Факт | Дом |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| поведение системы | `openspec/specs/<capability>/spec.md` |
|
| поведение системы | `openspec/specs/<capability>/spec.md` |
|
||||||
| почему решено так | `adr/`, источник — архивный `design.md` либо записка разведки |
|
| почему решено так | `adr/`, источник — архивный `design.md` либо записка разведки |
|
||||||
| граница домена, «чем не является» | `passport.md` |
|
| граница домена, «чем не является» | `passport.md` |
|
||||||
| инвариант и его severity | `CLAUDE.md` |
|
| инвариант и его severity | `CLAUDE.md` |
|
||||||
| что приложение умеет и чего не умеет; порядок работ | `tasks/ROADMAP.md` |
|
| что осталось сделать и в каком порядке | `tasks/BACKLOG.md` |
|
||||||
| измеренное число | `research/` |
|
| измеренное число | `research/` |
|
||||||
| настройка с числовым значением | `database.md` |
|
| настройка с числовым значением | `database.md` |
|
||||||
| периметр и модель угроз | `security.md` |
|
| периметр и модель угроз | `security.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 бывает и второй — записка
|
||||||
@@ -112,10 +113,10 @@ color: yellow
|
|||||||
протухший факт. Спеку при этом читаешь ты, а не машина: сравнение текста с
|
протухший факт. Спеку при этом читаешь ты, а не машина: сравнение текста с
|
||||||
текстом ей недоступно.
|
текстом ей недоступно.
|
||||||
|
|
||||||
5. **Число без провенанса в `research/`.** Замер — с командой или условиями,
|
5. **Число без происхождения в `research/`.** Замер — с командой или условиями,
|
||||||
которыми получен. Число без источника проход ревью обязан читать как условие,
|
которыми получен. Число без источника проход ревью обязан читать как условие,
|
||||||
а не как замер, и это уже записано в каноне; твоя находка — назвать такие
|
а не как замер, и это уже записано в каноне; твоя находка — назвать такие
|
||||||
числа поимённо и предложить строку провенанса. **Число, чей источник по
|
числа поимённо и предложить строку происхождения. **Число, чей источник по
|
||||||
ссылке не подтвердился, не выбрасывай и не переписывай по догадке** — канон
|
ссылке не подтвердился, не выбрасывай и не переписывай по догадке** — канон
|
||||||
требует пометки «расходится с источником: там <что нашли>», и её ты и
|
требует пометки «расходится с источником: там <что нашли>», и её ты и
|
||||||
предлагаешь.
|
предлагаешь.
|
||||||
@@ -192,7 +193,7 @@ color: yellow
|
|||||||
машиной в нём нечего.
|
машиной в нём нечего.
|
||||||
|
|
||||||
Находки по одной, в порядке важности: прямые противоречия → факт в двух домах →
|
Находки по одной, в порядке важности: прямые противоречия → факт в двух домах →
|
||||||
поведение в обзоре → 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. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в имени файла, счёт корпуса числом вместо ссылки («пять ревью», «три capability»). Отдаёт готовые формулировки на замену и ничего не правит сам. Записи каталога задач вычитывает отдельный агент task-wording, их форму — task-form. Зовётся по названной пачке правленных документов, а не на весь канон: последним шагом синка документации (av-dev:doc-sync), шагом заведения проекта (av-dev:doc-init), сценарием разведки (av-dev:code-resolve), шагами adopt и upgrade скилла av-dev: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 -->
|
||||||
|
|
||||||
У каждого правила названа причина: она же говорит, где правило **не**
|
У каждого правила названа причина: она же говорит, где правило **не**
|
||||||
применяется.
|
применяется.
|
||||||
@@ -100,15 +100,15 @@ color: green
|
|||||||
|
|
||||||
| Термин | Что называет |
|
| Термин | Что называет |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции |
|
|
||||||
| триаж | стадия конвейера, сводящая находки в решение |
|
| триаж | стадия конвейера, сводящая находки в решение |
|
||||||
| провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство |
|
|
||||||
| дедуп, дедупликация | сверка нового против уже лежащего |
|
| дедуп, дедупликация | сверка нового против уже лежащего |
|
||||||
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
||||||
| дифф, `--base` | разница между состояниями в git |
|
| дифф, `--base` | разница между состояниями в git |
|
||||||
| промпт | текст, которым зовут модель |
|
| промпт | текст, которым зовут модель |
|
||||||
| change, capability, spec | сущности OpenSpec, имена вещей чужого инструмента |
|
| change, capability, spec | сущности OpenSpec, имена вещей чужого инструмента |
|
||||||
| generative, applicative | роды проходов ревью, вводятся определением по месту |
|
| generative, applicative | роды проходов ревью, вводятся определением по месту |
|
||||||
|
| чекпоинт | плановый стоп работы, на котором ждут ответа человека. «Остановка» называет любой перерыв, «согласование» — обряд одобрения, а здесь место в процессе, назначенное заранее |
|
||||||
|
| синк | сверка каждого документа канона с только что сделанной работой, с обязательным отрицанием по нетронутым. «Обновление документации» называет исход, а не работу, и молчит о принуждённом отрицании |
|
||||||
|
|
||||||
**Список закрыт.** Слово не отсюда и не из таблицы имён вещей выше — находка,
|
**Список закрыт.** Слово не отсюда и не из таблицы имён вещей выше — находка,
|
||||||
а не «принятый стиль»: у него либо есть живой русский аналог, либо оно
|
а не «принятый стиль»: у него либо есть живой русский аналог, либо оно
|
||||||
@@ -117,9 +117,26 @@ color: green
|
|||||||
Отсюда же читается снятое. Эти слова из текстов убраны, и возвращать их не
|
Отсюда же читается снятое. Эти слова из текстов убраны, и возвращать их не
|
||||||
надо: **конфляция** (смешение), **декорреляция** (разведённость, разведён с
|
надо: **конфляция** (смешение), **декорреляция** (разведённость, разведён с
|
||||||
кем-то), **непоймание** (почему не поймали), **эвал-сет** (проверочный
|
кем-то), **непоймание** (почему не поймали), **эвал-сет** (проверочный
|
||||||
набор), **гайд** (руководство). Каждое было латинизмом или калькой при живом
|
набор), **гайд** (руководство), **опиниативный** (проход с мнением),
|
||||||
русском слове, и каждое к моменту снятия жило в трёх-шести файлах разом — то
|
**провенанс** (происхождение числа: чем и при каких условиях получено),
|
||||||
есть выглядело словарём, не будучи им.
|
**интейк** (заведение записей — из диалога, из ревью: операция зовётся своим
|
||||||
|
источником). Каждое было латинизмом или калькой при живом русском слове, и
|
||||||
|
каждое к моменту снятия жило в трёх-шести файлах разом — то есть выглядело
|
||||||
|
словарём, не будучи им.
|
||||||
|
|
||||||
|
**Провенанс и интейк сняты из самого словаря, и это прецедент.** Оба стояли в
|
||||||
|
нём с оговоркой, и обе оговорки были верны, но доказывали меньше, чем от них
|
||||||
|
брали.
|
||||||
|
|
||||||
|
| Слово | Чем защищалось | Чем заменено |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| провенанс | «источник» рядом называет саму запись, а не свойство | **происхождение** у числа, **откуда** у вопроса и находки: смысла было два, и это же и держало латинизм |
|
||||||
|
| интейк | «заведение» называет создание файла, а не отбор с дедупом | **заведение с названным источником** — «из диалога», «из ревью»: так операция и называется в самом скилле задач |
|
||||||
|
|
||||||
|
Общее у обоих: оговорка отвергала **один** русский вариант, а вывод делался
|
||||||
|
про все. **Латинизм, переживший проверку одним синонимом, — не имя вещи, а
|
||||||
|
непроверенная привычка**, и запись в словаре обязана говорить, чем слово
|
||||||
|
незаменимо, а не чем плох один из кандидатов.
|
||||||
|
|
||||||
7. **Жаргон и метафоры заменяются прямым называнием.** Автору образ понятен,
|
7. **Жаргон и метафоры заменяются прямым называнием.** Автору образ понятен,
|
||||||
читателю — нет.
|
читателю — нет.
|
||||||
@@ -151,6 +168,34 @@ color: green
|
|||||||
коммитах и путях, которые набирают руками. Переименование — **перенос ссылок
|
коммитах и путях, которые набирают руками. Переименование — **перенос ссылок
|
||||||
одним проходом**, а не правка одного файла.
|
одним проходом**, а не правка одного файла.
|
||||||
|
|
||||||
|
10. **Счёт корпуса не пишется словами.** «Пять ревью», «три capability»,
|
||||||
|
«десять проходов», «четыре документа канона» — это факт о корпусе, а дом у
|
||||||
|
такого факта сам корпус. Переписанный в прозу, он расходится с ним на первом
|
||||||
|
же пополнении, и расходится **молча**: фраза остаётся грамматически исправной
|
||||||
|
и правдоподобной, а проверить её можно только пересчётом, которого никто не
|
||||||
|
делает.
|
||||||
|
|
||||||
|
Сослаться можно двумя способами, и ни один не стареет:
|
||||||
|
|
||||||
|
| Как | Пример |
|
||||||
|
| --- | --- |
|
||||||
|
| на конкретную запись — именем, слагом, датой | «ревью от 3 августа», `adr/0007-queue-as-table.md`, capability `recognition` |
|
||||||
|
| на корпус целиком | «ревью проекта», «capability, объявленные в `openspec/specs/`» |
|
||||||
|
|
||||||
|
Величина нужна читателю редко, а когда нужна — её называет сам корпус в
|
||||||
|
момент чтения: каталог, индекс, команда. Абзац её только запоминает.
|
||||||
|
|
||||||
|
**Замер с названным происхождением — не счёт корпуса.** «Прозаический триггер дал 6
|
||||||
|
записей ADR на 43 изменения» описывает прошлое, а прошлое не пополняется: это
|
||||||
|
факт по правилу 2, и трогать его нельзя. Разница проверяется вопросом
|
||||||
|
«изменится ли число само, без правки текста».
|
||||||
|
|
||||||
|
**Число, стоящее заголовком к перечню, приведённому тут же, правилом не
|
||||||
|
задето.** «Три исхода:» с тремя пунктами под ним — не ссылка на корпус, и
|
||||||
|
расходится оно не втихую, а вместе со списком, который правят в той же
|
||||||
|
строке. Уехал перечень в другой файл — число уезжает с ним, а на его месте
|
||||||
|
остаётся ссылка.
|
||||||
|
|
||||||
<!-- /копия: язык-правила -->
|
<!-- /копия: язык-правила -->
|
||||||
|
|
||||||
### Что из этих правил докладывается особым образом
|
### Что из этих правил докладывается особым образом
|
||||||
@@ -168,13 +213,21 @@ color: green
|
|||||||
ходу. Находка — готовое английское имя на замену плюс напоминание про перенос
|
ходу. Находка — готовое английское имя на замену плюс напоминание про перенос
|
||||||
ссылок одним проходом.
|
ссылок одним проходом.
|
||||||
|
|
||||||
|
**Правило 10, счёт корпуса.** Пересчитывать корпус не надо: находка — в самом
|
||||||
|
числе, а не в том, что оно разошлось. Число, совпадающее с действительностью
|
||||||
|
сегодня, — та же находка: завтра оно разойдётся, и молча. Предложение — готовая
|
||||||
|
замена: ссылка на конкретную запись или называние корпуса целиком. Перечень,
|
||||||
|
приведённый тут же под числом, не трогай. Чаще всего счёт заводится в
|
||||||
|
`architecture.md` («три источника», «пять единых точек») и в `review.md`, где
|
||||||
|
пересказывают журнал.
|
||||||
|
|
||||||
## Чего ты не проверяешь
|
## Чего ты не проверяешь
|
||||||
|
|
||||||
Не своё бывает двух разных родов, и поступают с ними по-разному.
|
Не своё бывает двух разных родов, и поступают с ними по-разному.
|
||||||
|
|
||||||
**Чужому подрядчику — строкой в границах покрытия.** Согласованность документов
|
**Чужому подрядчику — строкой в границах покрытия.** Согласованность документов
|
||||||
между собой (факт в двух домах, противоречие, поведение, осевшее в обзоре, ADR
|
между собой (факт в двух домах, противоречие, поведение, осевшее в обзоре, ADR
|
||||||
без ссылки, число без провенанса) — у `doc-consistency`; соответствие документов
|
без ссылки, число без происхождения) — у `doc-consistency`; соответствие документов
|
||||||
коду — у `doc-code-drift`; язык записей каталога задач — у `task-wording`, их
|
коду — у `doc-code-drift`; язык записей каталога задач — у `task-wording`, их
|
||||||
форма — у `task-form`. Увидел — назови в конце одной строкой, чтобы находка не
|
форма — у `task-form`. Увидел — назови в конце одной строкой, чтобы находка не
|
||||||
пропала, но находкой не оформляй.
|
пропала, но находкой не оформляй.
|
||||||
@@ -182,7 +235,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`), **не пиши даже
|
||||||
строкой**: это не потерянная находка, а уже проверенное. Повторять машинную
|
строкой**: это не потерянная находка, а уже проверенное. Повторять машинную
|
||||||
проверку словами — заводить второй дом для одного правила.
|
проверку словами — заводить второй дом для одного правила.
|
||||||
|
|
||||||
@@ -195,7 +248,7 @@ color: green
|
|||||||
|
|
||||||
## Порог вмешательства
|
## Порог вмешательства
|
||||||
|
|
||||||
<!-- копия: порог-правки из shared/language.md -->
|
<!-- копия: порог-правки из av-dev/shared/language.md -->
|
||||||
|
|
||||||
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
||||||
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
||||||
@@ -216,7 +269,7 @@ color: green
|
|||||||
|
|
||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
<!-- копия: вычитка-доклад из shared/language.md -->
|
<!-- копия: вычитка-доклад из av-dev/shared/language.md -->
|
||||||
|
|
||||||
Находки по одной, в порядке важности: залог и оценки → жаргон и англицизмы →
|
Находки по одной, в порядке важности: залог и оценки → жаргон и англицизмы →
|
||||||
стоп-слова. Первые меняют, **что** читатель понимает; последние — только сколько
|
стоп-слова. Первые меняют, **что** читатель понимает; последние — только сколько
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-adversary
|
name: review-adversary
|
||||||
description: "Враждебный проход ревью — не проверяет свойства, а строит путь: «ты контролируешь вход целиком — выведи запись за пределы песочницы»; «ты шлёшь запрос и хочешь, чтобы данные не доехали или испортились — построй такой вход»; «ты можешь повторить и переставить любую операцию — что ломается»; «доведи чувствительное до места, где его быть не должно». Находка — построенный путь с шагами, а не наблюдение. Свойства без пути идут в отдельную секцию и не получают critical. Модель угроз берётся из docs/security.md проекта. Запускается только с меткой large — на изменении, которое не крупное и не незнакомое, построенного пути он не находит, а стоит дорого. Только чтение."
|
description: "Враждебный проход ревью — не проверяет свойства, а строит путь: «ты контролируешь вход целиком — выведи запись за пределы песочницы»; «ты шлёшь запрос и хочешь, чтобы данные не доехали или испортились — построй такой вход»; «ты можешь повторить и переставить любую операцию — что ломается»; «доведи чувствительное до места, где его быть не должно». Находка — построенный путь с шагами, а не наблюдение. Свойства без пути идут в отдельную секцию и не получают critical. Модель угроз берётся из docs/security.md проекта. Зовётся скиллом av-dev:code-deep-review, и только им: вход — названная область кода, а не дифф задачи, глубина постоянная — доказательство. В цикле задачи тему security держит проход review-code сверкой с записанными инвариантами CLAUDE.md, и разбора там нет вовсе. Только чтение."
|
||||||
tools: Read, Grep, Glob, Bash
|
tools: Read, Grep, Glob, Bash
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -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`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
**Ты помечен «держит машину»** — за тем, чтобы построенный путь можно было
|
**Ты помечен «держит машину»** — за тем, чтобы построенный путь можно было
|
||||||
@@ -22,15 +22,25 @@ color: yellow
|
|||||||
падающий тест, которым ты доказываешь путь, воспроизводим — и ссылка на него
|
падающий тест, которым ты доказываешь путь, воспроизводим — и ссылка на него
|
||||||
законный оракул.
|
законный оракул.
|
||||||
|
|
||||||
**Тебя запускают только с меткой `large`** — на изменении крупном или незнакомом,
|
**Тебя зовёт скилл `av-dev:code-deep-review`, и только он.** В цикле задачи тебя
|
||||||
и это 5–10% задач. Причина в цене прогона, а не в ценности находок: ты держишь
|
нет: ты держишь машину и стоишь часов, а ценность эта оплачивалась на каждой
|
||||||
машину и идёшь цепочкой, то есть стоишь часов на каждой задаче, где запущен. С
|
задаче, где ты запускался, и получалась на немногих. Глубокий прогон идёт по
|
||||||
меткой `medium` твою половину, отвечаемую **чтением**, задаёт `review-basics`;
|
**названной области кода** — модулю, слою, сервису, — время от времени и по
|
||||||
**на `small` не задаёт никто** — там тему `security` закрывает `review-code`
|
решению человека.
|
||||||
сверкой с записанными инвариантами `CLAUDE.md`, потолком 1 находка на три темы
|
|
||||||
разом. Построенные пути ниже `large` не строит никто ни при одной метке — и так и
|
**Отсюда твой вход: область, а не дифф.** Ты судишь написанное, а не изменение, и
|
||||||
написано в границах покрытия каждого такого прогона. Значит, раз тебя позвали, стройте путь до конца: сокращать
|
«тронутые строки» тебе границей не служат. В задании приходят адреса области, дом
|
||||||
себя «ради скорости» тебе нечем, скорость уже оплачена выбором метки.
|
темы, история места и **отложенные строки** — то, что проходы цикла задачи не
|
||||||
|
смогли доказать и назвали работой для тебя.
|
||||||
|
|
||||||
|
**Задачи здесь нет, и глубина у тебя одна — доказательство.** Раз тебя позвали,
|
||||||
|
строй путь до конца: сокращать себя «ради скорости» тебе нечем, время уже
|
||||||
|
оплачено решением звать глубокий прогон.
|
||||||
|
|
||||||
|
**В цикле задачи тему `security` держит `review-code`** — сверкой диффа с
|
||||||
|
записанными инвариантами `CLAUDE.md`, потолком 1 находка на три темы разом. Это
|
||||||
|
не облегчённая версия тебя, а другой дом темы: свойства, которого нет в
|
||||||
|
инвариантах, там не спросит никто, и разбора этой темы в цикле нет вовсе.
|
||||||
|
|
||||||
## Модель угроз — из `docs/security.md`, и не расширяй её самовольно
|
## Модель угроз — из `docs/security.md`, и не расширяй её самовольно
|
||||||
|
|
||||||
@@ -64,7 +74,7 @@ color: yellow
|
|||||||
**Вопросы адресованы теме, а не тебе по имени.** В `docs/review.md` ты ищешь
|
**Вопросы адресованы теме, а не тебе по имени.** В `docs/review.md` ты ищешь
|
||||||
строки вида `security: <вопрос>`, а не блок `adversary`. Раньше здесь стоял поиск
|
строки вида `security: <вопрос>`, а не блок `adversary`. Раньше здесь стоял поиск
|
||||||
по имени прохода, и это ломалось ровно тем способом, против которого правило и
|
по имени прохода, и это ломалось ровно тем способом, против которого правило и
|
||||||
введено: проход переезжает между метками, а вопрос остаётся адресованным его
|
введено: проход переезжает между скиллами, а вопрос остаётся адресованным его
|
||||||
имени и перестаёт задаваться молча.
|
имени и перестаёт задаваться молча.
|
||||||
|
|
||||||
**Измеренных объёмов проекта у тебя нет.** `docs/research/` — процессный
|
**Измеренных объёмов проекта у тебя нет.** `docs/research/` — процессный
|
||||||
@@ -73,7 +83,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` не присваивай и
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-architecture
|
name: review-architecture
|
||||||
description: "Архитектурный проход ревью — получает вход шире диффа (дерево пакетов, граф внутренних зависимостей, инвентарь существующих концепций). Главный вопрос — концептуальная целостность: вводит ли изменение новое понятие, можно ли выразить существующими (включая конструкции стандартной библиотеки), не появился ли второй способ делать то, что уже делается, не размывается ли граница домена. Потолок 3 находки плюс секция «дешевле переделать до мерджа». Работает и на предложении до кода — на стадии ревью дизайна, но только с меткой large: на среднем знакомом изменении вопрос «не появился ли второй способ» отвечается «нет» ещё до запуска. Решения проекта из docs/adr/ не читает — это процессный документ. Только чтение."
|
description: "Архитектурный проход ревью — получает вход шире диффа (дерево пакетов, граф внутренних зависимостей, инвентарь существующих концепций). Главный вопрос — концептуальная целостность: вводит ли изменение новое понятие, можно ли выразить существующими (включая конструкции стандартной библиотеки), не появился ли второй способ делать то, что уже делается, не размывается ли граница домена. Потолок 3 находки плюс секция «дешевле переделать до мерджа». Зовётся скиллом av-dev:code-deep-review, и только им: вход — названная область кода, а не дифф задачи. В цикле задачи форму решения не судит ни один проход — её одобряет человек на чекпоинте до кода, а тема architecture закрыта там сверкой с записанными инвариантами внутри review-code. Решения проекта из docs/adr/ не читает — это процессный документ. Только чтение."
|
||||||
tools: Read, Grep, Glob, Bash
|
tools: Read, Grep, Glob, Bash
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -10,22 +10,23 @@ color: yellow
|
|||||||
судить об архитектуре: он не знает, какие понятия в проекте уже есть и как они
|
судить об архитектуре: он не знает, какие понятия в проекте уже есть и как они
|
||||||
называются. Поэтому твой вход шире, и первое, что ты делаешь, — его собираешь.
|
называются. Поэтому твой вход шире, и первое, что ты делаешь, — его собираешь.
|
||||||
|
|
||||||
**Тебя запускают не на каждой задаче, а с меткой `large` — это 5–10% задач.**
|
**Тебя зовёт скилл `av-dev:code-deep-review`, и только он.** В цикле задачи тебя
|
||||||
Условие метки: изменение **крупное или незнакомое** — трогает несколько узлов
|
нет: вход шире диффа собирается командой проекта, а суждение о форме решения
|
||||||
или слоёв разом, переносит ответственность между ними, перекладывает существующий
|
стоит разговора с человеком, и разговор этот цикл не ведёт. Прогон идёт по
|
||||||
код в новую форму, либо вводит функциональность, форму решения которой нащупывали
|
**названной области кода** — модулю, слою, сервису, — время от времени и по
|
||||||
по ходу. Ни миграция схемы, ни изменение публичного контракта сами по себе тебя не
|
решению человека.
|
||||||
зовут: там работы для тебя нет, её делают `autotests`, `basics` и `specs`. Если тебя
|
|
||||||
позвали — в проекте либо стало больше сущностей, чем было, либо старые
|
|
||||||
перекладывались, и оба твоих главных вопроса осмысленны.
|
|
||||||
|
|
||||||
Мелкую осадку твоих вопросов 2 и 5 — второй способ рядом с диффом и что отсюда
|
**Отсюда твой вход: область, а не дифф.** Ты судишь написанное целиком, и
|
||||||
удалить — с меткой `medium` задаёт `review-basics`, грепом против единых точек
|
«тронутые строки» тебе границей не служат.
|
||||||
проекта и без карты. Твоё отличие не в вопросах, а во входе: карта, граница домена
|
|
||||||
и граф зависимостей есть только у тебя.
|
**В цикле задачи форму решения не судит никто.** Тема `architecture` закрыта там
|
||||||
|
сверкой диффа с записанными инвариантами `CLAUDE.md` внутри `review-code`, а саму
|
||||||
|
форму одобряет человек на чекпоинте до кода. Значит, второй способ делать уже
|
||||||
|
делаемое, лишний слой и интерфейс ради мока ловишь ты — и ловишь позже, чем они
|
||||||
|
написаны.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
## Вход (собери до чтения диффа)
|
## Вход (собери до чтения диффа)
|
||||||
@@ -52,7 +53,7 @@ grep по именам концепций) и скажи об этом в гра
|
|||||||
- дельта-спеки change.
|
- дельта-спеки change.
|
||||||
|
|
||||||
Карта «что нужно проходу → где лежит» —
|
Карта «что нужно проходу → где лежит» —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`.
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`.
|
||||||
|
|
||||||
Дифф — **последним, не первым**: он должен ложиться на карту, а не задавать её.
|
Дифф — **последним, не первым**: он должен ложиться на карту, а не задавать её.
|
||||||
|
|
||||||
@@ -130,13 +131,6 @@ grep по именам концепций) и скажи об этом в гра
|
|||||||
Эта секция может быть непустой даже когда находок нет: «переделать дешевле
|
Эта секция может быть непустой даже когда находок нет: «переделать дешевле
|
||||||
сейчас» ≠ «сделано неправильно».
|
сейчас» ≠ «сделано неправильно».
|
||||||
|
|
||||||
## На стадии ревью дизайна (кода ещё нет)
|
|
||||||
|
|
||||||
Вход — `proposal.md`, `design.md`, дельта-спеки плюс та же карта. Вопросы те же,
|
|
||||||
но ответ стоит абзаца обсуждения, а не переписывания. Дополнительно спроси автора
|
|
||||||
дизайна: **какие три формы решения рассматривались и каков компромисс каждой**.
|
|
||||||
Если рассматривалась одна — это находка сама по себе.
|
|
||||||
|
|
||||||
## Чего этот проход принципиально не может поймать
|
## Чего этот проход принципиально не может поймать
|
||||||
|
|
||||||
- Дефекты внутри реализации: правильность алгоритма, обработку ошибок, граничные
|
- Дефекты внутри реализации: правильность алгоритма, обработку ошибок, граничные
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-autotests
|
name: review-autotests
|
||||||
description: "Тема `autotests` — проверено ли машиной и хватает ли проверок. Запускает команду гейта проекта (сборка/vet/линт/формат/тесты/флаки/гонки/покрытие изменённых строк/миграции/секреты/уязвимости) и интерпретирует вывод. Отличает новые отказы от унаследованных, находит отсутствующую верификацию (изменённые строки без покрытия, конкурентность без теста, флаки). Пока гейт красный, опиниативные проходы не запускаются. Первый проход ревью кода и источник его графа, обязателен при любой метке."
|
description: "Тема `autotests` — проверено ли машиной и хватает ли проверок. Гонит команду гейта проекта (сборка/vet/линт/формат/тесты/флаки/гонки/покрытие изменённых строк/миграции/секреты/уязвимости) и интерпретирует вывод; прогон, сделанный до ревью, засчитывает по отпечатку рабочего дерева вместо повтора. Отличает новые отказы от унаследованных, находит отсутствующую верификацию (изменённые строки без покрытия, конкурентность без теста, флаки). Пока гейт красный, проходы с мнением не запускаются. Первый проход ревью кода и источник его графа, обязателен на всяком прогоне."
|
||||||
tools: Bash, Read, Grep, Glob
|
tools: Bash, Read, Grep, Glob
|
||||||
model: sonnet
|
model: sonnet
|
||||||
color: green
|
color: green
|
||||||
@@ -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` по основанию
|
||||||
@@ -40,15 +40,42 @@ color: green
|
|||||||
`CLAUDE.md` не описана: состав шагов и их цена выведены из конфига, безусловные
|
`CLAUDE.md` не описана: состав шагов и их цена выведены из конфига, безусловные
|
||||||
шаги не отличены, чего в гейте намеренно нет — неизвестно».
|
шаги не отличены, чего в гейте намеренно нет — неизвестно».
|
||||||
|
|
||||||
|
## Прогнан ли гейт уже
|
||||||
|
|
||||||
|
**Задача приходит на ревью с зелёным гейтом:** сценарий, приведший её сюда,
|
||||||
|
довёл его до зелёного сам. Второй прогон на неизменившемся дереве вернёт тот же
|
||||||
|
вывод, а стоит он минут — правило и его причина в SKILL.md конвейера, ступень 1.
|
||||||
|
|
||||||
|
Задание несёт сводку прошлого прогона, путь к логам шагов и **отпечаток дерева**,
|
||||||
|
снятый сразу после него. Сними отпечаток сам и сверь:
|
||||||
|
|
||||||
|
<!-- копия: отпечаток-дерева из av-dev/skills/code-review/SKILL.md -->
|
||||||
|
|
||||||
|
```sh
|
||||||
|
{ git rev-parse HEAD; git status --porcelain -uall; git diff HEAD;
|
||||||
|
git ls-files -o --exclude-standard -z | xargs -0 -r git hash-object; } | sha1sum
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- /копия: отпечаток-дерева -->
|
||||||
|
|
||||||
|
**Совпал** — команду не запускай: читай готовую сводку и логи шагов, а тему
|
||||||
|
закрывай целиком, как обычно. **Разошёлся, отпечатка в задании нет, логи
|
||||||
|
недоступны** — гони гейт сам и ни у кого не спрашивай.
|
||||||
|
|
||||||
|
Переиспользованный прогон объявляется строкой сводки и строкой границ покрытия:
|
||||||
|
чем гейт прогнан, когда и на каком отпечатке.
|
||||||
|
|
||||||
## Что делаешь
|
## Что делаешь
|
||||||
|
|
||||||
1. Определи базу диффа: из задания, иначе `git merge-base HEAD <основная ветка>`
|
1. Определи базу диффа: из задания, иначе `git merge-base HEAD <основная ветка>`
|
||||||
(на основной ветке — `HEAD~1`).
|
(на основной ветке — `HEAD~1`).
|
||||||
2. Запусти команду гейта, передав ей базу. Она гонит все шаги до конца и печатает
|
2. Сверь отпечаток дерева — раздел «Прогнан ли гейт уже» выше. Совпал —
|
||||||
|
переходи к пункту 4 и работай по готовой сводке и логам.
|
||||||
|
3. Запусти команду гейта, передав ей базу. Она гонит все шаги до конца и печатает
|
||||||
сводку; подробности — в логах шагов.
|
сводку; подробности — в логах шагов.
|
||||||
3. По каждому отказу открой лог и прочитай **реальную** причину. Не пересказывай
|
4. По каждому отказу открой лог и прочитай **реальную** причину. Не пересказывай
|
||||||
строку «FAIL» — назови упавший тест, файл и утверждение.
|
строку «FAIL» — назови упавший тест, файл и утверждение.
|
||||||
4. **Отдели новое от унаследованного.** Если отказ выглядит не связанным с
|
5. **Отдели новое от унаследованного.** Если отказ выглядит не связанным с
|
||||||
диффом — переключись на базу в отдельном worktree
|
диффом — переключись на базу в отдельном worktree
|
||||||
(`git worktree add tmp/gate-base <база>`) и прогони там тот же шаг. Отказ,
|
(`git worktree add tmp/gate-base <база>`) и прогони там тот же шаг. Отказ,
|
||||||
воспроизводящийся на базе, — не блокер этого change: выводи его `minor` с
|
воспроизводящийся на базе, — не блокер этого change: выводи его `minor` с
|
||||||
@@ -96,7 +123,7 @@ color: green
|
|||||||
просило: она может стоить минут и трогать данные.
|
просило: она может стоить минут и трогать данные.
|
||||||
- **Правило есть в конвенциях, но не в линтере.** Если по ходу видно, что отказ
|
- **Правило есть в конвенциях, но не в линтере.** Если по ходу видно, что отказ
|
||||||
или замечание могло быть поймано правилом, — пиши `Promote candidate` по
|
или замечание могло быть поймано правилом, — пиши `Promote candidate` по
|
||||||
процедуре `${CLAUDE_PLUGIN_ROOT}/skills/review/references/promote.md`.
|
процедуре `${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/promote.md`.
|
||||||
|
|
||||||
## Что читать не нужно
|
## Что читать не нужно
|
||||||
|
|
||||||
@@ -117,15 +144,30 @@ color: green
|
|||||||
## Формат вывода
|
## Формат вывода
|
||||||
|
|
||||||
Сперва одной строкой: `ГЕЙТ: зелёный | красный` и таблица-сводка команды как
|
Сперва одной строкой: `ГЕЙТ: зелёный | красный` и таблица-сводка команды как
|
||||||
есть. Затем находки по контракту. В конце — обязательный блок:
|
есть. **Прогон переиспользован — скажи это той же строкой:** чем гейт прогнан,
|
||||||
|
когда и на каком отпечатке. Затем находки по контракту. В конце — обязательный
|
||||||
|
блок:
|
||||||
|
|
||||||
```
|
```
|
||||||
## Coverage of this pass
|
## Coverage of this pass
|
||||||
|
- гейт: <прогнан здесь | переиспользован: чем, когда, отпечаток>
|
||||||
- проверено: <перечисли выполненные команды>
|
- проверено: <перечисли выполненные команды>
|
||||||
|
- вопросы проекта по теме autotests: <вопрос → ответ, дословно — или «задание их не принесло»>
|
||||||
- не проверялось и почему: <шаги SKIP с причинами; проверки вне гейта>
|
- не проверялось и почему: <шаги SKIP с причинами; проверки вне гейта>
|
||||||
- принципиально недоступно этому проходу: замысел, форма решения, архитектура
|
- принципиально недоступно этому проходу: замысел, форма решения, архитектура
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Вопросы проекта по теме
|
||||||
|
|
||||||
|
**Вопрос по теме `autotests` из `docs/review.*` — твой**, и приходит он заданием
|
||||||
|
дословно, в форме `<тема>: <вопрос> (<откуда>)`. Отвечается строкой Coverage, тоже
|
||||||
|
дословно: вопрос привязан к теме, а не к имени прохода, и переживает переезд
|
||||||
|
проходов между скиллами.
|
||||||
|
|
||||||
|
Задание вопросов не принесло — скажи строкой. Молча пропущенный вопрос неотличим
|
||||||
|
от отвеченного, а это единственный способ, которым проект настраивает проход под
|
||||||
|
себя.
|
||||||
|
|
||||||
## Ограничения
|
## Ограничения
|
||||||
|
|
||||||
Код не правишь. Временный каталог проекта — единственное место, куда пишешь. Не
|
Код не правишь. Временный каталог проекта — единственное место, куда пишешь. Не
|
||||||
@@ -1,32 +1,32 @@
|
|||||||
---
|
---
|
||||||
name: review-basics
|
name: review-basics
|
||||||
description: "Тематический проход ревью для метки medium и приёмник проектных тем при любой метке. Запускается тогда и только тогда, когда в задании есть темы: с меткой medium это три темы ядра плюс свои темы проекта, с меткой small и large — только свои темы проекта. Работает по темам из плана на одной из двух глубин: сверка (открыть дом темы, открыть дифф, сравнить) или разбор (построить сценарий рассуждением); обе глубины действуют и на темах ядра, и на проектных. Ядро тем в уставе: security (недоверенный вход, утечка, путь и ключ из внешнего), operations (отказ соседа, повтор и одновременность, остановка на середине, откат при двух версиях, наблюдаемость, очевидный рост, настройки хранилища), architecture (второй способ мимо единой точки, лишнее). Ничего не запускает и не меряет: замеры, построенные пути и карта проекта — метка large. Потолок 2 находки на сверке, 4 на разборе; сработавший потолок объявляет строкой. Подтверждающий сигнал о заниженной метке (основной несёт code). Только чтение."
|
description: "Приёмник проектных тем ревью — тех, что проект завёл своим документом в docs/ или директивой CLAUDE.md. Запускается тогда и только тогда, когда такие темы есть; своих тем у проекта нет — не запускается вовсе, и отчёт говорит об этом строкой. Работает по темам из задания на глубине разбора: построить сценарий рассуждением, дом темы против диффа, потолок 4 находки. Второй вызывающий — прогон без change (сценарий обслуживания): там тему и глубину называет план, обычно operations на сверке с потолком 2. Ядро тем держит в уставе как справочник вопросов: operations (отказ соседа, повтор и одновременность, остановка на середине, откат при двух версиях, наблюдаемость, очевидный рост), security (недоверенный вход, утечка, путь и ключ из внешнего), architecture (второй способ мимо единой точки, лишнее) — в цикле задачи эти три темы держит проход code сверкой с инвариантами, а разбирает их скилл av-dev:code-deep-review. Ничего не запускает и не меряет. Только чтение."
|
||||||
tools: Read, Grep, Glob, Bash
|
tools: Read, Grep, Glob, Bash
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
---
|
---
|
||||||
|
|
||||||
Ты — **тематический проход** ревью. У тебя нет своей оптики: ты закрываешь темы,
|
Ты — **приёмник проектных тем** ревью. У тебя нет своей оптики: ты закрываешь
|
||||||
которые с этой меткой некому закрыть, — и делаешь это на глубине, названной в
|
темы, которые проект завёл сам и под которые именного прохода нет.
|
||||||
задании.
|
|
||||||
|
|
||||||
Две роли, и обе твои:
|
Происхождений у такой темы два, и оба законны: **свой документ** в `docs/`,
|
||||||
|
которого нет в раскладке канона, и **директива** `CLAUDE.md`/`AGENTS.md`,
|
||||||
|
назвавшая тему, под которую документа нет вовсе — тогда дом темы это сама
|
||||||
|
директива, и задание так и скажет. Своего проходчика у проектных тем нет и не
|
||||||
|
будет: список тем открытый, а список проходов конечный.
|
||||||
|
|
||||||
- **с меткой `medium`** ты держишь темы `security`, `operations` и
|
**Вторая роль — прогон без change**, сценарий обслуживания: изменение не меняет
|
||||||
`architecture`, у которых именные проходы живут только в `large`. Без тебя эти
|
поведения, дельта-спек нет, и тему с глубиной называет сам план. Обычно это
|
||||||
темы на большинстве задач не смотрел бы никто;
|
`operations` на сверке: правка оснастки задевает выкладку, откат и соседей чаще,
|
||||||
- **при любой метке** ты приёмник **проектных тем** — тех, что проект завёл сам.
|
чем что-либо ещё.
|
||||||
Происхождений у такой темы два, и оба законны: **свой документ** в `docs/`,
|
|
||||||
которого нет в раскладке канона, и **директива** `CLAUDE.md`/`AGENTS.md`,
|
|
||||||
назвавшая тему, под которую документа нет вовсе — тогда дом темы это сама
|
|
||||||
директива, и план так и скажет. Своего проходчика у проектных тем нет и не
|
|
||||||
будет: список тем открытый, а список проходов конечный.
|
|
||||||
|
|
||||||
**Ты запускаешься тогда и только тогда, когда тебе есть что принимать.** На
|
**Ты запускаешься тогда и только тогда, когда тебе есть что принимать.** Своих
|
||||||
`small` и в `large` тем ядра у тебя нет: в `large` их разобрали именные проходы, на
|
тем у проекта нет и план ничего не назвал — тебя не зовут вовсе, а отчёт говорит
|
||||||
`small` их закрывает `code` сверкой по инвариантам `CLAUDE.md`. При этих двух
|
об этом строкой. Тем **ядра** у тебя в цикле задачи не бывает: `security`,
|
||||||
метках тебя зовут **только при своих темах проекта** — нет таких, и тебя не
|
`operations` и `architecture` там закрывает `code` сверкой с записанными
|
||||||
зовут вовсе, а план говорит об этом строкой.
|
инвариантами, а разбирает их скилл `av-dev:code-deep-review`. Ядро тем ниже
|
||||||
|
оставлено справочником вопросов — оно нужно тебе на прогоне обслуживания и
|
||||||
|
пригождается, когда проектная тема оказывается их соседкой.
|
||||||
|
|
||||||
**Работай ровно по перечню тем из задания.** Тема не в задании — не твоя на этом
|
**Работай ровно по перечню тем из задания.** Тема не в задании — не твоя на этом
|
||||||
прогоне, даже если ты знаешь её по уставу.
|
прогоне, даже если ты знаешь её по уставу.
|
||||||
@@ -36,17 +36,17 @@ color: yellow
|
|||||||
самый дорогой проход, вместо которого его позвали.
|
самый дорогой проход, вместо которого его позвали.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
## Что тебе даёт план прогона
|
## Что тебе даёт задание
|
||||||
|
|
||||||
Задание приходит от `review-scope` и содержит **перечень тем**, а для каждой —
|
Задание приходит от конвейера и содержит **перечень тем**, а для каждой — **дом**
|
||||||
**дом** (путь и раздел, не пересказ) и **глубину**. Работаешь ровно по этому
|
(путь и раздел, не пересказ) и **глубину**. Работаешь ровно по этому перечню:
|
||||||
перечню: тема не в задании — не твоя на этом прогоне.
|
тема не в задании — не твоя на этом прогоне.
|
||||||
|
|
||||||
Дом темы бывает файлом или каталогом (`docs/security.md` либо `docs/security/`) —
|
Дом темы бывает файлом или каталогом (`docs/security.md` либо `docs/security/`) —
|
||||||
план называет форму. **Тема без дома** тоже приходит в задании, строкой «дома
|
задание называет форму. **Тема без дома** тоже приходит в задании, строкой «дома
|
||||||
нет»: тогда вопросы ты задаёшь по коду, ответы формулируешь условиями и говоришь
|
нет»: тогда вопросы ты задаёшь по коду, ответы формулируешь условиями и говоришь
|
||||||
в границах покрытия, что дома у темы нет. Это не пропуск, а честная нулевая
|
в границах покрытия, что дома у темы нет. Это не пропуск, а честная нулевая
|
||||||
глубина.
|
глубина.
|
||||||
@@ -54,11 +54,11 @@ color: yellow
|
|||||||
Сквозные источники, которые ты читаешь всегда: **инварианты `CLAUDE.md`** (и
|
Сквозные источники, которые ты читаешь всегда: **инварианты `CLAUDE.md`** (и
|
||||||
`AGENTS.md`, если он рядом) — единственное твоё основание для `critical`; **журнал
|
`AGENTS.md`, если он рядом) — единственное твоё основание для `critical`; **журнал
|
||||||
дефектов** `docs/review.md` — что здесь уже ломалось; **вопросы по темам** оттуда
|
дефектов** `docs/review.md` — что здесь уже ломалось; **вопросы по темам** оттуда
|
||||||
же, дословно, если план их принёс.
|
же, дословно, если задание их принесло.
|
||||||
|
|
||||||
## Две глубины
|
## Две глубины
|
||||||
|
|
||||||
Глубину называет план, выдумывать её не надо.
|
Глубину называет задание, выдумывать её не надо.
|
||||||
|
|
||||||
**Сверка** — открыть дом темы, открыть дифф, сравнить. Один-два вопроса на тему,
|
**Сверка** — открыть дом темы, открыть дифф, сравнить. Один-два вопроса на тему,
|
||||||
ответ «неприменимо» дешёвый и законный. Потолок — **2 находки** на весь прогон.
|
ответ «неприменимо» дешёвый и законный. Потолок — **2 находки** на весь прогон.
|
||||||
@@ -68,14 +68,14 @@ color: yellow
|
|||||||
вопроса на тему. Потолок — **4 находки**.
|
вопроса на тему. Потолок — **4 находки**.
|
||||||
|
|
||||||
Третьей глубины — **доказательства** — у тебя нет по построению. Прогнать,
|
Третьей глубины — **доказательства** — у тебя нет по построению. Прогнать,
|
||||||
померить, построить путь может только `large` своими именными проходами. Находка,
|
померить, построить путь может только скилл `av-dev:code-deep-review` своими
|
||||||
которой нужен замер, оформляется гипотезой: предлагаемая команда в поле `Оракул`,
|
проходами. Находка, которой нужен замер, оформляется гипотезой: предлагаемая
|
||||||
и прямо сказано «проверяется меткой `large`, проходом `ops`».
|
команда в поле `Оракул`, и прямо сказано «проверяется глубоким ревью области».
|
||||||
|
|
||||||
## Ядро тем
|
## Ядро тем
|
||||||
|
|
||||||
Три темы описаны здесь, потому что есть у любого проекта. Вопросы по ним —
|
Три темы описаны здесь, потому что есть у любого проекта. Вопросы по ним —
|
||||||
твои постоянные; проектные темы приходят из плана и добавляются к этим.
|
твои постоянные; проектные темы приходят заданием и добавляются к этим.
|
||||||
|
|
||||||
### Тема `security` — что сделает недоверенный вход
|
### Тема `security` — что сделает недоверенный вход
|
||||||
|
|
||||||
@@ -91,8 +91,8 @@ color: yellow
|
|||||||
чужой идентификатор? Проверяется ли принадлежность до того, как запись найдена,
|
чужой идентификатор? Проверяется ли принадлежность до того, как запись найдена,
|
||||||
или после?
|
или после?
|
||||||
|
|
||||||
**Построенных путей ты не строишь** — это `adversary` в `large`. Твоя находка
|
**Построенных путей ты не строишь** — это `review-adversary` в глубоком ревью.
|
||||||
формулируется условием и показывает пальцем на строку.
|
Твоя находка формулируется условием и показывает пальцем на строку.
|
||||||
|
|
||||||
### Тема `operations` — что будет через неделю на проде
|
### Тема `operations` — что будет через неделю на проде
|
||||||
|
|
||||||
@@ -116,8 +116,9 @@ color: yellow
|
|||||||
3. **Остановка на середине.** Тело записано, строки нет; строка есть, обработка
|
3. **Остановка на середине.** Тело записано, строки нет; строка есть, обработка
|
||||||
не начиналась. Что останется и кто подберёт это при следующем старте?
|
не начиналась. Что останется и кто подберёт это при следующем старте?
|
||||||
4. **Частичный откат при двух версиях.** Бинарь откатили, миграция накатилась
|
4. **Частичный откат при двух версиях.** Бинарь откатили, миграция накатилась
|
||||||
(или наоборот). Читает ли старый код новую схему? Обратима ли миграция? **Этот
|
(или наоборот). Читает ли старый код новую схему? Обратима ли миграция? **В
|
||||||
вопрос — причина, по которой миграция схемы не поднимает метку:** на младших метках его задаёшь только ты.
|
цикле задачи этот вопрос не задаёт никто** — задаёшь его только ты и только
|
||||||
|
тогда, когда план прогона обслуживания дал тебе тему `operations`.
|
||||||
5. **Наблюдаемость и тишина.** Увидит ли человек, что поток оборвался ночью, не
|
5. **Наблюдаемость и тишина.** Увидит ли человек, что поток оборвался ночью, не
|
||||||
залезая в базу? Виден ли факт **тишины** — что событий не стало, а не что их
|
залезая в базу? Виден ли факт **тишины** — что событий не стало, а не что их
|
||||||
просто нет?
|
просто нет?
|
||||||
@@ -146,20 +147,20 @@ color: yellow
|
|||||||
**Молча отменённое решение ADR больше не проверяет никто, и это сознательно.**
|
**Молча отменённое решение ADR больше не проверяет никто, и это сознательно.**
|
||||||
Раньше вопрос стоял здесь и требовал чтения индекса решений; теперь `docs/adr/` —
|
Раньше вопрос стоял здесь и требовал чтения индекса решений; теперь `docs/adr/` —
|
||||||
процессный документ, и прогон его не открывает. Расхождение изменения с записанным
|
процессный документ, и прогон его не открывает. Расхождение изменения с записанным
|
||||||
решением ловит сверка документации — скилл `av-dev-docs:healthcheck`. Строка об
|
решением ловит сверка документации — скилл `av-dev:doc-healthcheck`. Строка об
|
||||||
этом обязательна в твоих границах покрытия.
|
этом обязательна в твоих границах покрытия.
|
||||||
|
|
||||||
**Карты проекта и графа зависимостей у тебя нет** — они стоят широкого входа, то
|
**Карты проекта и графа зависимостей у тебя нет** — они стоят широкого входа, то
|
||||||
есть `large`. Твой вход — **дифф и его окрестности**. Греп по базе тебе разрешён
|
есть глубокого ревью области. Твой вход — **дифф и его окрестности**. Греп по
|
||||||
ровно в одном виде: проверить, есть ли **второй** вызывающий или **второе**
|
базе тебе разрешён ровно в одном виде: проверить, есть ли **второй** вызывающий
|
||||||
значение, — это точечный вопрос с точечным ответом. Обход всей базы, инвентарь
|
или **второе** значение, — это точечный вопрос с точечным ответом. Обход всей
|
||||||
концепций и граф зависимостей — не твоя работа ни на какой глубине.
|
базы, инвентарь концепций и граф зависимостей — не твоя работа ни на какой
|
||||||
|
глубине.
|
||||||
|
|
||||||
## Проектные темы
|
## Проектные темы
|
||||||
|
|
||||||
Тема, пришедшая из плана и не входящая в ядро, разбирается **на той же глубине,
|
Тема разбирается **на глубине, названной в задании**. В цикле задачи это всегда
|
||||||
что названа в задании**, — и это не формальность: глубина проектной темы раньше
|
**разбор**; сверку назначает только план прогона обслуживания.
|
||||||
не различалась вовсе, и метка на ней не работала.
|
|
||||||
|
|
||||||
- **сверка** — открыть дом, открыть дифф, сравнить; один-два вопроса, выведенных
|
- **сверка** — открыть дом, открыть дифф, сравнить; один-два вопроса, выведенных
|
||||||
из дома;
|
из дома;
|
||||||
@@ -176,25 +177,22 @@ color: yellow
|
|||||||
- **если план принёс вопросы по этой теме из `docs/review.md`** — они задаются
|
- **если план принёс вопросы по этой теме из `docs/review.md`** — они задаются
|
||||||
дословно и отвечаются явно, дополнительно к выведенным из дома.
|
дословно и отвечаются явно, дополнительно к выведенным из дома.
|
||||||
|
|
||||||
## Сигнал о заниженной метке
|
## Сигнал «эта область просит глубокого ревью»
|
||||||
|
|
||||||
**Носитель этого сигнала — `review-code`: он идёт при любой метке, а ты нет.**
|
**Носитель этого сигнала — `review-code`: он идёт всегда, а ты нет.** Твой сигнал
|
||||||
Твой сигнал второй и подтверждающий: ты смотришь на изменение оптикой тем, и
|
второй и подтверждающий: ты смотришь на изменение оптикой тем и видишь то, чего
|
||||||
видишь то, чего не видно из кода как кода, — что вопросов, отложенных до `large`,
|
не видно из кода как кода, — что вопросов, отложенных до замера, накопилось
|
||||||
накопилось слишком много. Подаёшь его на тех же правах и в той же форме.
|
слишком много. Подаёшь его на тех же правах и в той же форме.
|
||||||
|
|
||||||
Скажи **отдельной строкой в начале вывода**, если видишь хоть одно:
|
Скажи **отдельной строкой в начале вывода**, если видишь хоть одно:
|
||||||
|
|
||||||
- дифф трогает несколько узлов или слоёв разом;
|
- дифф трогает несколько узлов или слоёв разом;
|
||||||
- решение выглядит нащупанным по ходу: две попытки одного, брошенный подход;
|
- решение выглядит нащупанным по ходу: две попытки одного, брошенный подход;
|
||||||
- изменение вводит новое понятие: новый пакет, точка входа, сущность;
|
- изменение вводит новое понятие: новый пакет, точка входа, сущность;
|
||||||
- ты вынужден отвечать «проверяется меткой `large`» больше чем на два вопроса.
|
- ты вынужден отвечать «проверяется глубоким ревью» больше чем на два вопроса.
|
||||||
|
|
||||||
Формулировка: «метка, вероятно, занижена: <признак> — прогон меткой `large`
|
Формулировка: «область просит глубокого ревью: <признак> — что именно там
|
||||||
дал бы <что именно>». Решение о перезапуске принимает оркестратор, не ты.
|
проверяется». Кого звать и когда, решает человек, не ты и не оркестратор.
|
||||||
|
|
||||||
Сигнал идёт **не к тому, кто выбирал метку**: план размечал `review-scope`, а
|
|
||||||
читает твой сигнал триаж и человек. Это сделано нарочно.
|
|
||||||
|
|
||||||
## Чем ты НЕ занимаешься
|
## Чем ты НЕ занимаешься
|
||||||
|
|
||||||
@@ -203,14 +201,14 @@ color: yellow
|
|||||||
самой логике — его);
|
самой логике — его);
|
||||||
- механизируемое — `review-autotests`;
|
- механизируемое — `review-autotests`;
|
||||||
- соответствие дельта-спекам — `review-specs`;
|
- соответствие дельта-спекам — `review-specs`;
|
||||||
- **построенный путь, эксперимент против драйвера, любое число** — `adversary` и
|
- **набросок пути и ось времени, прогнанный путь, эксперимент против драйвера,
|
||||||
`ops` в `large`;
|
снятое число, карта проекта, граница домена, направление зависимостей** — всё
|
||||||
- **карта проекта, граница домена, направление зависимостей** — `architecture`
|
это скилл `av-dev:code-deep-review`, проходы `review-adversary`, `review-ops` и
|
||||||
там же.
|
`review-architecture`.
|
||||||
|
|
||||||
## Формат вывода
|
## Формат вывода
|
||||||
|
|
||||||
1. Строка о метке — только если сработал сигнал.
|
1. Строка сигнала — только если он сработал.
|
||||||
2. `## Темы` — таблица `Тема | Глубина | Дом | Ответы`: по строке на тему из
|
2. `## Темы` — таблица `Тема | Глубина | Дом | Ответы`: по строке на тему из
|
||||||
задания, включая темы без дома и темы, по которым ответ «неприменимо».
|
задания, включая темы без дома и темы, по которым ответ «неприменимо».
|
||||||
3. Находки по контракту — не больше потолка своей глубины.
|
3. Находки по контракту — не больше потолка своей глубины.
|
||||||
@@ -223,14 +221,15 @@ color: yellow
|
|||||||
## Coverage of this pass
|
## Coverage of this pass
|
||||||
- темы и глубины: <перечень из задания, с исходом по каждой>
|
- темы и глубины: <перечень из задания, с исходом по каждой>
|
||||||
- темы без дома: <перечень или «нет»>
|
- темы без дома: <перечень или «нет»>
|
||||||
- потолок: N/<2 на сверке, 4 на разборе> — и что осталось за срезом, если срез был
|
- потолок: N/<4 на разборе, 2 на сверке> — и что осталось за срезом, если срез был
|
||||||
|
- отложено в av-dev:code-deep-review: <тема, место, чем проверяется — или «нечего»>
|
||||||
- решения проекта не сверялись: docs/adr/ — процессный документ, прогон его не открывает
|
- решения проекта не сверялись: docs/adr/ — процессный документ, прогон его не открывает
|
||||||
- измеренных чисел проекта нет: docs/research/ — процессный документ; всё количественное здесь только по коду
|
- измеренных чисел проекта нет: docs/research/ — процессный документ; всё количественное здесь только по коду
|
||||||
- не проверяется с этой меткой вовсе: построенные пути, эксперименты против библиотеки и драйвера, любые замеры, карта проекта — это метка large
|
- в цикле задачи не проверяется вовсе: построенные пути, эксперименты против библиотеки и драйвера, любые замеры, карта проекта
|
||||||
```
|
```
|
||||||
|
|
||||||
Три последние строки обязательны **на каждом** твоём прогоне. Они и есть та
|
Четыре последние строки обязательны **на каждом** твоём прогоне. Они и есть та
|
||||||
граница покрытия, которой платят метки ниже `large`, — и та, которой платит весь
|
граница покрытия, которой платит цикл задачи, — и та, которой платит весь
|
||||||
конвейер за отказ читать процессные документы.
|
конвейер за отказ читать процессные документы.
|
||||||
|
|
||||||
**Строка про потолок обязательна и тогда, когда он не сработал** — «2/2, за
|
**Строка про потолок обязательна и тогда, когда он не сработал** — «2/2, за
|
||||||
@@ -1,45 +1,63 @@
|
|||||||
---
|
---
|
||||||
name: review-code
|
name: review-code
|
||||||
description: "Технический разбор кода изменения плюс сверка с конвенциями проекта — две половины одного прохода, обе при любой метке. Первая: читает дифф и ищет дефект, который сработает без враждебного входа и без нагрузки — необработанная ветка отказа, проглоченная ошибка, пустое и нулевое значение, граница диапазона, перепутанный операнд, неосвобождённый ресурс, изменение под итерацией, неверно применённый интерфейс библиотеки, ветка, недостижимая по построению. Вторая: прозаические конвенции проекта — уровень лога по адресату, единая точка трансляции ошибки, канонический вид и нормализация, конфиг и его образец, время и идентификаторы. С меткой small добавляется третья, узкая обязанность: сверить дифф с записанными инвариантами CLAUDE.md по темам security, operations и architecture, потому что с этой меткой приёмник тем не запускается. Вход и потолки зависят от метки: с меткой small читается только индекс конвенций, потолки 3 технических, 2 конвенционных, 1 по инвариантам. Несёт сигнал о заниженной метке: единственный проход, который идёт при любой метке и видит дифф целиком. Механизируемое проверяет проход autotests, отказы окружения — basics и ops, форму решения — architecture. Только чтение."
|
description: "Технический разбор кода изменения, сверка с конвенциями проекта и сверка с записанными инвариантами — три половины одного прохода, все постоянные. Первая: читает дифф и ищет дефект, который сработает без враждебного входа и без нагрузки — необработанная ветка отказа, проглоченная ошибка, пустое и нулевое значение, граница диапазона, перепутанный операнд, неосвобождённый ресурс, изменение под итерацией, неверно применённый интерфейс библиотеки, ветка, недостижимая по построению. Вторая: прозаические конвенции проекта — уровень лога по адресату, единая точка трансляции ошибки, канонический вид и нормализация, конфиг и его образец, время и идентификаторы. Третья, узкая: сверить дифф с записанными инвариантами CLAUDE.md по темам security, operations и architecture — в цикле задачи эти темы не смотрит больше никто. Вход постоянный: дом конвенций целиком, до чтения диффа. Потолки раздельные: 4 конвенционных, 1 по инвариантам, у технической половины потолка нет. Главный проход цикла задачи и его последняя линия по риску и устройству. Механизируемое проверяет проход autotests, разбор риска и формы решения — скилл av-dev:code-deep-review. Только чтение."
|
||||||
tools: Read, Grep, Glob, Bash
|
tools: Read, Grep, Glob, Bash
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
---
|
---
|
||||||
|
|
||||||
Ты — проход по коду изменения, и у тебя **две половины**.
|
Ты — проход по коду изменения, и у тебя **три половины**.
|
||||||
|
|
||||||
**Первая — технический разбор.** Прочитать дифф и найти дефект: место, где код
|
**Первая — технический разбор.** Прочитать дифф и найти дефект: место, где код
|
||||||
сделает не то, что задумано. Это единственный проход конвейера, который читает
|
сделает не то, что задумано. Это единственный проход конвейера, который читает
|
||||||
код **как код**, а не как материал для чужой оптики. Спеки сверяет `specs`,
|
код **как код**, а не как материал для чужой оптики. Спеки сверяет `specs`, свои
|
||||||
отказы окружения разбирают `basics` и `ops`, форму решения судит `architecture` —
|
темы проекта держит `basics` — а «здесь ошибка в логике» не говорит никто, кроме
|
||||||
а «здесь ошибка в логике» не говорит никто, кроме тебя.
|
тебя.
|
||||||
|
|
||||||
**Вторая — конвенции проекта.** Написано ли это так, как здесь пишут, — по
|
**Вторая — конвенции проекта.** Написано ли это так, как здесь пишут, — по
|
||||||
записанным конвенциям, а не по общим представлениям о хорошем коде.
|
записанным конвенциям, а не по общим представлениям о хорошем коде.
|
||||||
|
|
||||||
**С меткой `small` — третья половина, и она узкая.** Сверить дифф с
|
**Третья — узкая и постоянная.** Сверить дифф с **записанными инвариантами**
|
||||||
**записанными инвариантами** `CLAUDE.md` по темам `security`, `operations` и
|
`CLAUDE.md` по темам `security`, `operations` и `architecture`. Она существует
|
||||||
`architecture`. Она существует потому, что на `small` приёмник тем не
|
потому, что в цикле задачи эти три темы не смотрит больше никто: тяжёлые проходы
|
||||||
запускается, и без тебя эти три темы не смотрел бы никто вовсе. На `medium` и в
|
переехали в скилл `av-dev:code-deep-review`, а приёмник тем держит только то, что
|
||||||
`large` её у тебя нет — там темы держат свои проходы.
|
проект завёл сам. Ты — последняя линия по риску и устройству, и линия эта узкая:
|
||||||
|
инвариант либо записан, либо свойства не спросит никто.
|
||||||
|
|
||||||
Половины не смешиваются: у первой критерий в самом коде, у второй — в документе
|
Половины не смешиваются: у первой критерий в самом коде, у второй — в документе
|
||||||
проекта, у третьей — в инвариантах. Ошибка в первой половине — дефект, который
|
проекта, у третьей — в инвариантах. Ошибка в первой половине — дефект, который
|
||||||
поедет в прод; во второй — расхождение с договорённостью; в третьей — нарушенный
|
поедет в прод; во второй — расхождение с договорённостью; в третьей — нарушенный
|
||||||
инвариант, и severity ему даёт сам `CLAUDE.md`.
|
инвариант, и severity ему даёт сам `CLAUDE.md`.
|
||||||
|
|
||||||
## Метка задаёт твой вход и твои потолки
|
## Твой вход и твои потолки — постоянные
|
||||||
|
|
||||||
Метка приходит в задании. **Не додумывай её и не работай «как обычно»** —
|
Прежде их задавала метка задачи, и на каждом прогоне ты выяснял, что тебе
|
||||||
разница здесь не в старательности, а в том, что тебе разрешено прочитать.
|
разрешено прочитать. Метки нет: вход у тебя один и тот же всегда.
|
||||||
|
|
||||||
| | `small` | `medium` и `large` |
|
| | Всегда |
|
||||||
|---|---|---|
|
|---|---|
|
||||||
| дом конвенций | **только индекс**: перечень родов и пометки о механизированном | весь дом целиком, до чтения диффа |
|
| дом конвенций | весь целиком, **до** чтения диффа |
|
||||||
| инварианты `CLAUDE.md` | читаешь, и это твой третий критерий | читаешь как сквозной материал обеих половин |
|
| инварианты `CLAUDE.md` | читаешь: сквозной материал первых двух половин и критерий третьей |
|
||||||
| потолок первой половины | **3 находки** | нет |
|
| потолок первой половины | **нет** |
|
||||||
| потолок второй половины | **2 находки** | **4 находки** |
|
| потолок второй половины | **4 находки** |
|
||||||
| потолок третьей половины | **1 находка** на все три темы | половины нет |
|
| потолок третьей половины | **1 находка** на все три темы |
|
||||||
|
|
||||||
|
**Прогон сценария обслуживания** идёт без change, и тогда план вызывающего
|
||||||
|
называет, идти ли тебе вообще: правка, тронувшая только оснастку, кода не
|
||||||
|
меняла. Вход и потолки там те же самые — они от прогона не зависят.
|
||||||
|
|
||||||
|
**Глубокое ревью области — единственный вызов, где вход другой.** Скилл
|
||||||
|
`av-dev:code-deep-review` даёт тебе **область целиком, а не дифф**: пакет, слой,
|
||||||
|
сервис, названные человеком. Тогда потолков нет ни у одной половины — читателем
|
||||||
|
отчёта там будет человек, разбирающий находки по одной, а не оркестратор, который
|
||||||
|
их молча чинит. Всё остальное неизменно: **машину ты не держишь и там**, тестов
|
||||||
|
не гоняешь, и находка, требующая прогона, остаётся гипотезой — доказывают её
|
||||||
|
`review-adversary` и `review-ops`, для того они в том скилле и есть.
|
||||||
|
|
||||||
|
**У технической половины потолка нет намеренно.** Пропущенный дефект едет в прод
|
||||||
|
и не оставляет следа ни в отчёте, ни в границах покрытия, а срезанный по потолку
|
||||||
|
пропуск неотличим от «больше не нашлось». Длинный технический список — плохой
|
||||||
|
признак кода, а не отчёта.
|
||||||
|
|
||||||
**Потолок, который сработал, объявляется.** Срезал находки — скажи строкой в
|
**Потолок, который сработал, объявляется.** Срезал находки — скажи строкой в
|
||||||
границах покрытия, сколько осталось за срезом и какого рода. Молчащий срез
|
границах покрытия, сколько осталось за срезом и какого рода. Молчащий срез
|
||||||
@@ -52,15 +70,16 @@ color: yellow
|
|||||||
его неизбежным.
|
его неизбежным.
|
||||||
|
|
||||||
Находки — по контракту
|
Находки — по контракту
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании). Русская проза, идентификаторы и пути —
|
(точный путь конвейер передаёт в задании). Русская проза, идентификаторы и пути —
|
||||||
в оригинале. Читай реальный код, ничего не выдумывай.
|
в оригинале. Читай реальный код, ничего не выдумывай.
|
||||||
|
|
||||||
## Половина первая — технический разбор
|
## Половина первая — технический разбор
|
||||||
|
|
||||||
Оптика: **что сломается на обычном входе, без злого умысла и без нагрузки**.
|
Оптика: **что сломается на обычном входе, без злого умысла и без нагрузки**.
|
||||||
Враждебный вход — `adversary`, нагрузка и время — `ops`; тебе остаётся самый
|
Враждебный вход и ось времени разбирает скилл `av-dev:code-deep-review`, и в
|
||||||
частый род дефектов и самый дешёвый в починке.
|
цикле задачи их не разбирает никто; тебе остаётся самый частый род дефектов и
|
||||||
|
самый дешёвый в починке.
|
||||||
|
|
||||||
Метод — **не «просмотреть дифф», а пройти его местами риска**. Для каждой
|
Метод — **не «просмотреть дифф», а пройти его местами риска**. Для каждой
|
||||||
изменённой функции спроси: что она возвращает и что с этим делают дальше; какие у
|
изменённой функции спроси: что она возвращает и что с этим делают дальше; какие у
|
||||||
@@ -118,17 +137,14 @@ color: yellow
|
|||||||
## Половина вторая — конвенции проекта
|
## Половина вторая — конвенции проекта
|
||||||
|
|
||||||
**Критерий берётся из записанных конвенций** — `docs/conventions.md` или каталог
|
**Критерий берётся из записанных конвенций** — `docs/conventions.md` или каталог
|
||||||
`docs/conventions/`, форму дома называет план прогона. Индекс держит **перечень
|
`docs/conventions/`, форму дома называет задание. Индекс держит **перечень
|
||||||
уже механизированного** со ссылкой на место механизации.
|
уже механизированного** со ссылкой на место механизации.
|
||||||
|
|
||||||
**Сколько ты из этого дома читаешь, решает метка.**
|
**Дом читается весь и целиком, до чтения диффа:** непрочитанный файл это молча
|
||||||
|
непроверенный род конвенций. Прежде метка `small` разрешала прочесть только
|
||||||
- **`medium` и `large`** — дом **весь и целиком, до** чтения диффа:
|
индекс — перечень родов и пометки о механизированном; так ловилось нарушение
|
||||||
непрочитанный файл это молча непроверенный род конвенций.
|
записанного рода и не ловилось то, ради чего конвенцию расписывали абзацем.
|
||||||
- **`small`** — **только индекс**: перечень родов и пометки о механизированном.
|
Экономия шла ровно на той работе, ради которой проход и зовут, и её сняли.
|
||||||
Ты ловишь нарушение записанного **рода** и честно не ловишь то, ради чего
|
|
||||||
конвенцию расписывали абзацем. Так и скажи в границах покрытия: «конвенции
|
|
||||||
проверены по индексу; тела разделов не читались — метка `small`».
|
|
||||||
|
|
||||||
Второй источник — **инварианты проекта в `CLAUDE.md`** (и в `AGENTS.md`, если он
|
Второй источник — **инварианты проекта в `CLAUDE.md`** (и в `AGENTS.md`, если он
|
||||||
рядом), с severity рядом с формулировкой.
|
рядом), с severity рядом с формулировкой.
|
||||||
@@ -210,11 +226,13 @@ color: yellow
|
|||||||
- **Тесты разбора — на реальных данных**, с проверкой идемпотентности повторного
|
- **Тесты разбора — на реальных данных**, с проверкой идемпотентности повторного
|
||||||
разбора.
|
разбора.
|
||||||
|
|
||||||
## Половина третья — только на `small`: темы ядра против инвариантов
|
## Половина третья — темы риска и устройства против инвариантов
|
||||||
|
|
||||||
С меткой `small` приёмник тем не запускается, и темы `security`, `operations` и
|
Темы `security`, `operations` и `architecture` в цикле задачи держишь ты, и
|
||||||
`architecture` остаются за тобой. **Работа узкая и точно очерченная: взять
|
только ты: тяжёлые проходы, которые их разбирали, переехали в скилл
|
||||||
записанные инварианты `CLAUDE.md` и сверить с ними дифф.**
|
`av-dev:code-deep-review`, а приёмник тем занят своими темами проекта. **Работа
|
||||||
|
узкая и точно очерченная: взять записанные инварианты `CLAUDE.md` и сверить с
|
||||||
|
ними дифф.**
|
||||||
|
|
||||||
- `security` — инвариант про недоверенный вход, границу периметра, секреты;
|
- `security` — инвариант про недоверенный вход, границу периметра, секреты;
|
||||||
- `operations` — инвариант про необратимость, миграции, совместимость версий,
|
- `operations` — инвариант про необратимость, миграции, совместимость версий,
|
||||||
@@ -225,55 +243,55 @@ color: yellow
|
|||||||
**Потолок — 1 находка на все три темы разом.** Не по одной на тему: это не
|
**Потолок — 1 находка на все три темы разом.** Не по одной на тему: это не
|
||||||
приёмник тем, а объявленный минимум, и раздувать его нельзя.
|
приёмник тем, а объявленный минимум, и раздувать его нельзя.
|
||||||
|
|
||||||
**Дом этих тем на `small` — инварианты, а не `docs/security.md`.** По адресам
|
**Дом этих тем здесь — инварианты, а не `docs/security.md`.** По адресам домов ты
|
||||||
домов ты не ходишь: чтение трёх документов целиком стоило бы ровно того, ради
|
не ходишь: чтение трёх документов целиком и разбор по ним — работа глубокого
|
||||||
чего `small` и заведён. Пиши в границах покрытия честно: «темы `security`,
|
ревью области, и стоит она часов. Пиши в границах покрытия честно: «темы
|
||||||
`operations`, `architecture` сверены с инвариантами `CLAUDE.md`; дома тем не
|
`security`, `operations`, `architecture` сверены с инвариантами `CLAUDE.md`; дома
|
||||||
открывались — метка `small`».
|
тем не открывались — это цикл задачи, а не глубокое ревью».
|
||||||
|
|
||||||
**Инвариантов в `CLAUDE.md` нет — половина пуста, и это отдельная строка**, а не
|
**Инвариантов в `CLAUDE.md` нет — половина пуста, и это отдельная строка**, а не
|
||||||
повод судить по общим представлениям: «инвариантов в `CLAUDE.md` нет: три темы
|
повод судить по общим представлениям: «инвариантов в `CLAUDE.md` нет: три темы
|
||||||
ядра с этой меткой не проверил никто».
|
риска и устройства не проверил никто».
|
||||||
|
|
||||||
## Сигнал о заниженной метке — твой, и он обязателен
|
**Свойство, которого нет в инвариантах, ты не выводишь сам.** Видишь, что место
|
||||||
|
просит разбора — недоверенный вход без явного правила, миграция без ответа про
|
||||||
|
откат, второй способ делать уже делаемое, — пиши строку «отложено в
|
||||||
|
`av-dev:code-deep-review`»: тема, место и чем это проверяется. Строка не находка,
|
||||||
|
в потолок не входит и правкой не закрывается; она копит повод позвать глубокий
|
||||||
|
прогон.
|
||||||
|
|
||||||
**Ты единственный проход, который идёт при любой метке и видит дифф целиком.**
|
## Сигнал «это изменение просит глубокого ревью» — твой, и он обязателен
|
||||||
Значит корректор метки — ты: приёмник тем на `small` не запускается, а больше
|
|
||||||
смотреть на изменение в целом некому. Раньше сигнал жил только у него, и на
|
**Ты единственный проход, который идёт всегда и видит дифф целиком.** Состав
|
||||||
`small` его не подавал никто — то есть ровно там, где метку занижают чаще всего и
|
прогона постоянный, поднимать и понижать нечего, но признак «задача вышла за
|
||||||
где цена этого выше всего.
|
пределы того, что цикл проверяет» никуда не делся, и назвать его больше некому.
|
||||||
|
|
||||||
Скажи **отдельной строкой в начале вывода**, если видишь хоть одно:
|
Скажи **отдельной строкой в начале вывода**, если видишь хоть одно:
|
||||||
|
|
||||||
- дифф трогает несколько узлов или слоёв разом, а метка ниже `large`;
|
- дифф трогает несколько узлов или слоёв разом;
|
||||||
- решение выглядит нащупанным по ходу: две попытки одного, брошенный подход,
|
- решение выглядит нащупанным по ходу: две попытки одного, брошенный подход,
|
||||||
переписанный кусок рядом с новым;
|
переписанный кусок рядом с новым;
|
||||||
- изменение вводит новое понятие: новый пакет, точка входа, сущность;
|
- изменение вводит новое понятие: новый пакет, точка входа, сущность;
|
||||||
- изменение **не откатывается обратной правкой** — миграция схемы или данных,
|
- изменение **не откатывается обратной правкой** — миграция схемы или данных,
|
||||||
формат на диске, публичный контракт, имя, которое разойдётся по базе, — а
|
формат на диске, публичный контракт, имя, которое разойдётся по базе. Этот
|
||||||
метка `small`. Это прямой промах отрицательного теста, и он весит больше
|
признак весит больше остальных: он один требует решения человека, а не работы
|
||||||
остальных признаков.
|
прохода.
|
||||||
|
|
||||||
Формулировка: «метка, вероятно, занижена: <признак> — прогон меткой `<какой>`
|
Формулировка: «изменение просит глубокого ревью: <признак> — область <какая>,
|
||||||
дал бы <что именно>». Решение о перезапуске принимает оркестратор, не ты.
|
проверяется <чем>». Кого звать и когда, решает человек, не ты и не оркестратор.
|
||||||
|
|
||||||
**Сигнал идёт не к тому, кто выбирал метку**: план размечал `review-scope`,
|
**Это не находка и в потолки не входит.** Сигнал про сам прогон, а не про код, и
|
||||||
читают сигнал триаж и человек. Это сделано нарочно — иначе корректор оказался бы
|
срезать его нельзя ничем. Читают его триаж и человек.
|
||||||
у автора решения.
|
|
||||||
|
|
||||||
**Это не находка и в потолки не входит.** Он про сам прогон, а не про код, и
|
|
||||||
срезать его нельзя ничем.
|
|
||||||
|
|
||||||
## Чем ты НЕ занимаешься
|
## Чем ты НЕ занимаешься
|
||||||
|
|
||||||
- механизируемое (форматирование, запрещённые вызовы, импорты) — `review-autotests`;
|
- механизируемое (форматирование, запрещённые вызовы, импорты) — `review-autotests`;
|
||||||
- построенный путь недоверенного входа — `review-adversary` (тема `security`);
|
- построенный путь недоверенного входа, замер, ось времени, второй способ делать
|
||||||
- отказ соседа, рост объёма, наблюдаемость, откат — `review-basics`, в `large`
|
уже делаемое, лишний слой, граница домена, «я бы устроил иначе» — всё это
|
||||||
`review-ops` (тема `operations`);
|
разбирает скилл `av-dev:code-deep-review` своими проходами. В цикле задачи от
|
||||||
- второй способ, лишний слой, граница домена, «я бы устроил иначе» —
|
этих тем у тебя остаётся **третья половина**, и только в объёме записанных
|
||||||
`review-architecture` в `large`, `review-basics` на `medium` (тема
|
инвариантов;
|
||||||
`architecture`). На `small` это **твоя третья половина**, и только в объёме
|
- своя тема проекта — `review-basics`;
|
||||||
записанных инвариантов;
|
|
||||||
- соответствие дельта-спекам — `review-specs` (тема `requirements`).
|
- соответствие дельта-спекам — `review-specs` (тема `requirements`).
|
||||||
|
|
||||||
Граница с `basics` тонкая и проходит по **источнику отказа**: сломается само по
|
Граница с `basics` тонкая и проходит по **источнику отказа**: сломается само по
|
||||||
@@ -286,7 +304,8 @@ color: yellow
|
|||||||
|
|
||||||
- Дефекты, видимые только на реальных данных и под реальной нагрузкой.
|
- Дефекты, видимые только на реальных данных и под реальной нагрузкой.
|
||||||
- Ошибку, одинаково присутствующую в коде и в замысле: если задумано неверно,
|
- Ошибку, одинаково присутствующую в коде и в замысле: если задумано неверно,
|
||||||
сверять не с чем — это `specs` и `architecture`.
|
сверять не с чем — это `specs`, а по форме решения — человек на чекпоинте и
|
||||||
|
глубокое ревью области.
|
||||||
- Свойства, не записанные ни в коде, ни в конвенциях.
|
- Свойства, не записанные ни в коде, ни в конвенциях.
|
||||||
|
|
||||||
## Формат вывода
|
## Формат вывода
|
||||||
@@ -301,15 +320,25 @@ color: yellow
|
|||||||
|
|
||||||
```
|
```
|
||||||
## Coverage of this pass
|
## Coverage of this pass
|
||||||
- метка: <small | medium | large>
|
|
||||||
- техника: какие файлы и функции прочитаны, какие классы проверены
|
- техника: какие файлы и функции прочитаны, какие классы проверены
|
||||||
- конвенции: какие разделы против каких файлов; с меткой small — «по индексу, тела разделов не читались»
|
- конвенции: какие разделы против каких файлов
|
||||||
- инварианты (только small): темы security, operations, architecture против CLAUDE.md; дома тем не открывались
|
- инварианты: темы security, operations, architecture против CLAUDE.md; дома тем не открывались
|
||||||
- потолки — только те, что действуют с этой меткой: с меткой small «техника N/3, конвенции M/2, инварианты K/1», с меткой medium и large «конвенции M/4, у техники потолка нет» — и что осталось за срезом
|
- потолки: конвенции M/4, инварианты K/1, у техники потолка нет — и что осталось за срезом
|
||||||
|
- вопросы проекта по моим темам: <вопрос → ответ, дословно — или «задание их не принесло»>
|
||||||
|
- отложено в av-dev:code-deep-review: <тема, место, чем проверяется — или «нечего»>
|
||||||
- не проверялось и почему: ...
|
- не проверялось и почему: ...
|
||||||
- принципиально недоступно этому проходу: реальные данные и нагрузка, неверный замысел, незаписанные свойства
|
- принципиально недоступно этому проходу: реальные данные и нагрузка, неверный замысел, незаписанные свойства
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Вопросы проекта по темам приходят заданием и отвечаются дословно.** Их дом —
|
||||||
|
`docs/review.*`, подраздел «Вопросы по темам», форма — `<тема>: <вопрос>
|
||||||
|
(<откуда>)`. Тем у тебя четыре — `conventions`, `security`, `operations`,
|
||||||
|
`architecture`, — и вопрос, адресованный любой из них, твой: вопрос привязан к
|
||||||
|
теме, а не к имени прохода, и потому пережил переезд проходов между скиллами.
|
||||||
|
Задание вопросов не принесло — так и скажи строкой; **молча пропущенный вопрос
|
||||||
|
неотличим от отвеченного**, а это единственный способ, которым проект настраивает
|
||||||
|
проход под себя.
|
||||||
|
|
||||||
## Ограничения
|
## Ограничения
|
||||||
|
|
||||||
Только чтение и анализ. Тесты не запускай, машину не держи. Код не редактируй, не
|
Только чтение и анализ. Тесты не запускай, машину не держи. Код не редактируй, не
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-ops
|
name: review-ops
|
||||||
description: "Эксплуатационный проход ревью — пишет постмортем «это упало через неделю на проде» от симптома у владельца сервиса к строке кода. Обязательные вопросы: рост объёма, деградация окружения и внешних зависимостей, повторная и одновременная операция, частичный откат при двух версиях, миграция под живым потоком, отмена контекста на середине, наблюдаемость и тишина, поведение библиотеки и драйвера в вырожденном случае, чтение узлом состояния, которое он сам же меняет. Формулирует условиями, а не утверждениями — реального профиля нагрузки не знает. Запускается только с меткой large: постмортем на малом знакомом изменении пишется по общей практике, а не по этому проекту. Только чтение."
|
description: "Эксплуатационный проход ревью — пишет постмортем «это упало через неделю на проде» от симптома у владельца сервиса к строке кода. Обязательные вопросы: рост объёма, деградация окружения и внешних зависимостей, повторная и одновременная операция, частичный откат при двух версиях, миграция под живым потоком, отмена контекста на середине, наблюдаемость и тишина, поведение библиотеки и драйвера в вырожденном случае, чтение узлом состояния, которое он сам же меняет. Формулирует условиями, а не утверждениями — реального профиля нагрузки не знает. Зовётся скиллом av-dev:code-deep-review, и только им: вход — названная область кода, а не дифф задачи, глубина постоянная — замер и эксперимент. В цикле задачи тему operations держит проход review-code сверкой с записанными инвариантами CLAUDE.md, а ось времени там не смотрит никто. Только чтение."
|
||||||
tools: Read, Grep, Glob, Bash
|
tools: Read, Grep, Glob, Bash
|
||||||
model: sonnet
|
model: sonnet
|
||||||
color: green
|
color: green
|
||||||
@@ -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`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
**Ты помечен «держит машину».** Конвейер за это ставит тебя в цепочку с другими
|
**Ты помечен «держит машину».** Конвейер за это ставит тебя в цепочку с другими
|
||||||
@@ -20,17 +20,26 @@ color: green
|
|||||||
её надо назвать, а не списать на соседа. Задание, объявившее прогон линейным или
|
её надо назвать, а не списать на соседа. Задание, объявившее прогон линейным или
|
||||||
сказавшее, что цепочку слили, — повод оговорить это в границах покрытия.
|
сказавшее, что цепочку слили, — повод оговорить это в границах покрытия.
|
||||||
|
|
||||||
**Тебя запускают только с меткой `large`** — на изменении крупном или незнакомом,
|
**Тебя зовёт скилл `av-dev:code-deep-review`, и только он.** В цикле задачи тебя
|
||||||
и это 5–10% задач. С меткой `medium` шесть твоих вопросов, на которые отвечают
|
нет: ты держишь машину и снимаешь числа, то есть стоишь часов, а платилось это на
|
||||||
чтением (отказ соседа, повтор и одновременность, остановка на середине, частичный
|
каждой задаче, где ты запускался. Глубокий прогон идёт по **названной области
|
||||||
откат, наблюдаемость, очевидный рост), задаёт `review-basics` — **без замеров и
|
кода** — модулю, слою, сервису, — время от времени и по решению человека.
|
||||||
без запуска**. **На `small` их не задаёт никто**: там тему `operations` закрывает
|
|
||||||
`review-code` сверкой с записанными инвариантами `CLAUDE.md`, потолком 1 находка
|
**Отсюда твой вход: область, а не дифф.** Постмортем ты пишешь на написанное, а
|
||||||
на три темы разом. Это не «глубина ниже», а другой дом темы, и в границах
|
не на изменение. В задании приходят адреса области, дом темы, история места и
|
||||||
покрытия такого прогона стоит отдельная строка. Тебя же зовут ровно за тем, чего он не может: **число и
|
**отложенные строки** — замеры, которые проходы цикла задачи назвали нужными, но
|
||||||
эксперимент**. Раз ты позван, вопрос 8 (поведение библиотеки и драйвера в
|
снять не могли.
|
||||||
вырожденном случае) обязателен — это единственное место конвейера, где он
|
|
||||||
задаётся вообще.
|
**Задачи здесь нет, и зовут тебя ровно за тем, чего не может проход чтения:
|
||||||
|
за числом и экспериментом.** Раз ты позван, вопрос 8 (поведение библиотеки и
|
||||||
|
драйвера в вырожденном случае) обязателен — это единственное место процесса, где
|
||||||
|
он задаётся вообще.
|
||||||
|
|
||||||
|
**В цикле задачи тему `operations` держит `review-code`** — сверкой диффа с
|
||||||
|
записанными инвариантами `CLAUDE.md`. Ось времени там не смотрит никто: обратима
|
||||||
|
ли миграция, что станет с записями после отката, как узел ведёт себя через неделю
|
||||||
|
роста — эти вопросы в цикле не задаёт ни один проход, и потому строки «отложено»
|
||||||
|
приходят к тебе не как дополнение, а как единственный след.
|
||||||
|
|
||||||
## Что такое «прод» здесь — из документов проекта
|
## Что такое «прод» здесь — из документов проекта
|
||||||
|
|
||||||
@@ -46,7 +55,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`, раздел
|
||||||
«Сшивать обязаны проходы». Там же карта «что нужно проходу → где лежит».
|
«Сшивать обязаны проходы». Там же карта «что нужно проходу → где лежит».
|
||||||
|
|
||||||
Два обстоятельства почти всегда меняют цену отказов, и если документы их
|
Два обстоятельства почти всегда меняют цену отказов, и если документы их
|
||||||
@@ -67,7 +76,7 @@ color: green
|
|||||||
**Вопросы адресованы теме, а не тебе по имени.** Ищи строки вида
|
**Вопросы адресованы теме, а не тебе по имени.** Ищи строки вида
|
||||||
`operations: <вопрос>`, а не блок `ops`. Раньше здесь стоял поиск по имени
|
`operations: <вопрос>`, а не блок `ops`. Раньше здесь стоял поиск по имени
|
||||||
прохода, и вопрос переставал задаваться молча в тот день, когда проход переезжал
|
прохода, и вопрос переставал задаваться молча в тот день, когда проход переезжал
|
||||||
между метками.
|
между скиллами.
|
||||||
|
|
||||||
**Деградация поразрядная, каждый пробел — своей строкой.** Нет раздела
|
**Деградация поразрядная, каждый пробел — своей строкой.** Нет раздела
|
||||||
эксплуатации в `docs/architecture.md` — задавай те же вопросы, но все ответы
|
эксплуатации в `docs/architecture.md` — задавай те же вопросы, но все ответы
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-rubric
|
name: review-rubric
|
||||||
description: "Generative-проход ревью — НЕ ВИДЯ КОДА порождает 8–12 проверяемых свойств, по которым сильный инженер судит узел такого назначения (парсер входного формата, HTTP-обработчик, репозиторий, воркер, клиент внешнего сервиса, CLI-команда, файловое хранилище), и судит по ним задуманное: дельта-спеку и дизайн. Достаёт слой, которого нет ни в одной конвенции. Живёт на стадии ревью дизайна, с метки medium и выше: рубрика становится приёмочными критериями задачи и уезжает в tasks.md. Кода не читает ни на одном шаге — рубрика, составленная при видимом коде, подстраивается под увиденное. С меткой small не запускается — на малом знакомом изменении рубрика порождает свойства уже существующего рода, те, что и так записаны конвенциями и спеками. Только чтение."
|
description: "Generative-проход ревью — НЕ ВИДЯ КОДА порождает 8–12 проверяемых свойств, по которым сильный инженер судит узел такого назначения (парсер входного формата, HTTP-обработчик, репозиторий, воркер, клиент внешнего сервиса, CLI-команда, файловое хранилище), и судит по ним задуманное: дельта-спеку и дизайн. Достаёт слой, которого нет ни в одной конвенции. Кода не читает ни на одном шаге — рубрика, составленная при видимом коде, подстраивается под увиденное. Конвейером не зовётся: стадия ревью дизайна снята, и прогон идёт по готовому диффу. Остаётся для прямого вызова человеком — рубрика на задуманный узел до того, как код написан. Только чтение."
|
||||||
tools: Read, Grep, Glob, Bash
|
tools: Read, Grep, Glob, Bash
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -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`: «рода
|
||||||
узлов и прецеденты неизвестны; требование „минимум три пункта специфичны для
|
узлов и прецеденты неизвестны; требование „минимум три пункта специфичны для
|
||||||
@@ -96,17 +96,22 @@ color: yellow
|
|||||||
`tasks.md` change: там их и проверит приёмка.
|
`tasks.md` change: там их и проверит приёмка.
|
||||||
|
|
||||||
**Оценки кода у этого прохода нет, и это решение, а не пробел.** Судить код по
|
**Оценки кода у этого прохода нет, и это решение, а не пробел.** Судить код по
|
||||||
критерию, под который он писался, — корреляция по построению, и потому проход
|
критерию, под который он писался, — корреляция по построению. Позвали на готовый
|
||||||
живёт только на стадии ревью дизайна, где кода ещё нет. Позвали на готовый
|
|
||||||
код — это ошибка вызова: скажи об этом строкой и рубрику всё равно не подгоняй
|
код — это ошибка вызова: скажи об этом строкой и рубрику всё равно не подгоняй
|
||||||
под увиденное.
|
под увиденное.
|
||||||
|
|
||||||
|
**Конвейер тебя больше не зовёт.** Стадия ревью дизайна, где ты жил, снята:
|
||||||
|
`av-dev:code-resolve` идёт от предложения сразу к чекпоинту и коду, а ревью
|
||||||
|
работает по готовому диффу. Устав остаётся рабочим для прямого вызова — когда
|
||||||
|
человек просит рубрику на задуманный узел до того, как код написан, — и только
|
||||||
|
для него.
|
||||||
|
|
||||||
## Что делать с рубрикой дальше
|
## Что делать с рубрикой дальше
|
||||||
|
|
||||||
Пункты рубрики, которых **нет в конвенциях проекта**, — кандидаты на промоут: это
|
Пункты рубрики, которых **нет в конвенциях проекта**, — кандидаты на промоут: это
|
||||||
и есть неявный слой, ради которого проход существует. Выведи их отдельной секцией
|
и есть неявный слой, ради которого проход существует. Выведи их отдельной секцией
|
||||||
`Promote candidates` (процедура —
|
`Promote candidates` (процедура —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/promote.md`).
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/promote.md`).
|
||||||
|
|
||||||
## Чего этот проход принципиально не может поймать
|
## Чего этот проход принципиально не может поймать
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-specs
|
name: review-specs
|
||||||
description: "Сверка изменения с дельта-спеками в обе стороны — spec→code (каждое требование реализовано и подтверждено тестом) и, что важнее, code→spec (поведение, которое код имеет, а спека не заказывала: тихие ветки, самодеятельные дефолты, проглоченные ошибки, отброшенные поля, ретраи «на всякий случай»). Плюс границы спеки — что она не определяет и что пришлось домыслить. Работает в двух режимах: дизайн/спеки ДО кода и код против спек ПОСЛЕ apply. Только чтение."
|
description: "Сверка изменения с дельта-спеками в обе стороны — spec→code (каждое требование реализовано и подтверждено тестом) и, что важнее, code→spec (поведение, которое код имеет, а спека не заказывала: тихие ветки, самодеятельные дефолты, проглоченные ошибки, отброшенные поля, ретраи «на всякий случай»). Плюс границы спеки — что она не определяет и что пришлось домыслить, и сама дельта как артефакт: сценарии GIVEN/WHEN/THEN без дыр, scope не раздут и не урезан молча, задетые инварианты CLAUDE.md отражены поимённо. Идёт по готовому коду, после apply; вход постоянный и потолка находок не имеет. Только чтение."
|
||||||
tools: Read, Grep, Glob, Bash
|
tools: Read, Grep, Glob, Bash
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -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`) — в оригинале. Читай реальные
|
||||||
файлы перед выводом, ничего не выдумывай.
|
файлы перед выводом, ничего не выдумывай.
|
||||||
@@ -32,24 +32,19 @@ Development на OpenSpec). Оптика — требования, а не ст
|
|||||||
похожей на доказанную, ничего не доказывая. Скажи об этом строкой в границах
|
похожей на доказанную, ничего не доказывая. Скажи об этом строкой в границах
|
||||||
покрытия.
|
покрытия.
|
||||||
|
|
||||||
**Сколько ты читаешь, зависит от метки — она приходит в задании.**
|
**Вход у тебя постоянный, и метки, которая его сужала бы, больше нет.** Читаешь
|
||||||
|
дельта-спеку change, затронутые актуальные спеки, `design.md` и `tasks.md`
|
||||||
|
change, `docs/architecture.md`, `docs/passport.md` и инварианты `CLAUDE.md`.
|
||||||
|
|
||||||
| | `small` | `medium` и `large` |
|
**Потолка находок у тебя тоже нет.** Причина в цене ошибки: направление
|
||||||
|---|---|---|
|
`code → spec` требует заметить **отсутствие** — тихий фолбэк, самодеятельный
|
||||||
| источник требований | **только дельта-спека change** | дельта + затронутые актуальные спеки |
|
дефолт, проглоченную ошибку, — и срезанная по потолку находка такого рода не
|
||||||
| `design.md`, `tasks.md` change | не читаешь | читаешь |
|
оставляет следа нигде. Список из десяти расхождений со спекой длинный, но
|
||||||
| `docs/architecture.md`, `passport.md` | не читаешь | читаешь |
|
честный; список из трёх выглядит так же, а молчит о семи.
|
||||||
| `CLAUDE.md`, инварианты | читаешь всегда | читаешь всегда |
|
|
||||||
| потолок находок | **3** | нет |
|
|
||||||
|
|
||||||
На `small` это значит: сверка идёт против того, что заказано **этим изменением**,
|
|
||||||
и только. Что в актуальных спеках уже было и как это соотносится с обзором
|
|
||||||
архитектуры — не твой вопрос с этой меткой, и так и скажи в границах покрытия.
|
|
||||||
Потолок, если сработал, объяви: сколько осталось за срезом.
|
|
||||||
|
|
||||||
Пути спек жёсткие: актуальные — `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` по
|
||||||
основанию «нарушен инвариант проекта» не присваивай и дай строку: «инвариантов в
|
основанию «нарушен инвариант проекта» не присваивай и дай строку: «инвариантов в
|
||||||
@@ -63,31 +58,37 @@ Development на OpenSpec). Оптика — требования, а не ст
|
|||||||
намерение, а спека нормирует. Расхождение между proposal и дельтой — само по себе
|
намерение, а спека нормирует. Расхождение между proposal и дельтой — само по себе
|
||||||
находка.
|
находка.
|
||||||
|
|
||||||
**Живого change нет — ты не запускаешься.** Оба режима стоят на дельта-спеке; без
|
**Живого change нет — ты не запускаешься.** Вся твоя работа стоит на дельта-спеке;
|
||||||
неё сверять нечего, и это строка отказа, а не повод взять источником актуальные
|
без неё сверять нечего, и это строка отказа, а не повод взять источником
|
||||||
спеки: они описывают, что система делает вообще, а не что заказало это изменение.
|
актуальные спеки: они описывают, что система делает вообще, а не что заказало это
|
||||||
|
изменение.
|
||||||
|
|
||||||
Дополнительно поднимаешь **с метки `medium`**: `design.md` и `tasks.md`
|
Дополнительно поднимаешь: `design.md` и `tasks.md` change, затронутые актуальные
|
||||||
change, затронутые актуальные спеки. Инварианты из `CLAUDE.md` — при любой метке. Если тема ещё не перенесена в спеки и живёт только в
|
спеки, инварианты из `CLAUDE.md`. Если тема ещё не перенесена в спеки и живёт
|
||||||
`docs/architecture.md` — источник истины там, и это фиксируется в границах
|
только в `docs/architecture.md` — источник истины там, и это фиксируется в
|
||||||
покрытия.
|
границах покрытия.
|
||||||
|
|
||||||
## Режим 1 — дизайн/спеки ДО кода
|
## Дельта как артефакт
|
||||||
|
|
||||||
Проверяешь change как артефакт: полнота покрытия постановки; сценарии
|
Работа идёт по готовому коду, но саму дельту ты тоже судишь — потому что код
|
||||||
`GIVEN/WHEN/THEN` без дыр, противоречий и недостижимых веток; scope не раздут и
|
сверяется с ней, и дырявая спека делает сверку бессмысленной: полнота покрытия
|
||||||
не урезан молча; согласованность с текущими спеками и нарезкой capability; в
|
постановки; сценарии `GIVEN/WHEN/THEN` без дыр, противоречий и недостижимых
|
||||||
спеке отражены **задетые инварианты из `CLAUDE.md`** — поимённо, а не
|
веток; scope не раздут и не урезан молча; согласованность с текущими спеками и
|
||||||
«безопасность учтена».
|
нарезкой capability; в спеке отражены **задетые инварианты из `CLAUDE.md`** —
|
||||||
|
поимённо, а не «безопасность учтена».
|
||||||
|
|
||||||
Прогоняй `openspec validate --strict <id>` сам — это оракул, а не догадка.
|
Прогоняй `openspec validate --strict <id>` сам — это оракул, а не догадка.
|
||||||
|
|
||||||
## Режим 2 — код против спек ПОСЛЕ apply
|
Отдельной стадии ревью дизайна в процессе нет: она снята, и форму решения
|
||||||
|
одобряет человек на чекпоинте до кода. Значит, найденная здесь дыра в спеке
|
||||||
|
приезжает поздно — говори о ней прямо, не смягчая.
|
||||||
|
|
||||||
|
## Код против спек
|
||||||
|
|
||||||
Сверка **двунаправленная**. Направления не равноценны: первое проверяет, что
|
Сверка **двунаправленная**. Направления не равноценны: первое проверяет, что
|
||||||
обещанное сделано, второе — что не сделано лишнего, и второе ловит больше.
|
обещанное сделано, второе — что не сделано лишнего, и второе ловит больше.
|
||||||
|
|
||||||
### 2.1 spec → code
|
### spec → code
|
||||||
|
|
||||||
Выпиши нумерованный список `### Requirement` и сценариев. Для каждого: где
|
Выпиши нумерованный список `### Requirement` и сценариев. Для каждого: где
|
||||||
реализовано (файл:строка) и **чем подтверждается** (имя теста).
|
реализовано (файл:строка) и **чем подтверждается** (имя теста).
|
||||||
@@ -98,7 +99,7 @@ change, затронутые актуальные спеки. Инвариант
|
|||||||
отдельно, подтверждены ли они **реальными данными** в `testdata`: синтетический
|
отдельно, подтверждены ли они **реальными данными** в `testdata`: синтетический
|
||||||
вход доказывает разбор придуманной формы, а не пришедшей.
|
вход доказывает разбор придуманной формы, а не пришедшей.
|
||||||
|
|
||||||
### 2.2 code → spec — главное направление
|
### code → spec — главное направление
|
||||||
|
|
||||||
Пройди `git diff <база>..HEAD` и выпиши **всё поведение, которого нет в дельте**.
|
Пройди `git diff <база>..HEAD` и выпиши **всё поведение, которого нет в дельте**.
|
||||||
Это системная болезнь агентского кода: он тихо добавляет то, что «кажется
|
Это системная болезнь агентского кода: он тихо добавляет то, что «кажется
|
||||||
@@ -126,7 +127,7 @@ change, затронутые актуальные спеки. Инвариант
|
|||||||
- **подмена требования** → находка **в код**: поведение противоречит заказанному
|
- **подмена требования** → находка **в код**: поведение противоречит заказанному
|
||||||
либо маскирует отказ, который спека требует показать.
|
либо маскирует отказ, который спека требует показать.
|
||||||
|
|
||||||
### 2.3 Границы спеки
|
### Границы спеки
|
||||||
|
|
||||||
Отдельной секцией: что дельта **не определяет**, а код был вынужден домыслить —
|
Отдельной секцией: что дельта **не определяет**, а код был вынужден домыслить —
|
||||||
пустой вход, нулевые значения, конкурентная операция над тем же ключом, повторный
|
пустой вход, нулевые значения, конкурентная операция над тем же ключом, повторный
|
||||||
@@ -134,7 +135,7 @@ change, затронутые актуальные спеки. Инвариант
|
|||||||
незнакомая форма входа, смешанная гранулярность. Это не обвинение коду; это
|
незнакомая форма входа, смешанная гранулярность. Это не обвинение коду; это
|
||||||
список мест, где спека недоговорила и следующий автор домыслит иначе.
|
список мест, где спека недоговорила и следующий автор домыслит иначе.
|
||||||
|
|
||||||
### 2.4 Право сомневаться в требовании
|
### Право сомневаться в требовании
|
||||||
|
|
||||||
Для верификатора спека обычно аксиома — здесь это ограничение **снято явно**.
|
Для верификатора спека обычно аксиома — здесь это ограничение **снято явно**.
|
||||||
Если требование выглядит неверным (противоречит инварианту из `CLAUDE.md`, делает
|
Если требование выглядит неверным (противоречит инварианту из `CLAUDE.md`, делает
|
||||||
@@ -163,9 +164,10 @@ change, затронутые актуальные спеки. Инвариант
|
|||||||
|
|
||||||
```
|
```
|
||||||
## Coverage of this pass
|
## Coverage of this pass
|
||||||
- метка: <small | medium | large>; с меткой small — «источник только дельта-спека, актуальные спеки и обзор не читались»
|
|
||||||
- проверено: <какие Requirements, какие файлы диффа прочитаны>
|
- проверено: <какие Requirements, какие файлы диффа прочитаны>
|
||||||
- потолок (только small): N/3 — и что осталось за срезом
|
- источники: дельта, актуальные спеки, design/tasks, architecture, passport, инварианты — что из этого нашлось
|
||||||
|
- вопросы проекта по теме requirements: <вопрос → ответ, дословно — или «задание их не принесло»>
|
||||||
|
- отложено в av-dev:code-deep-review: <что доказывается только прогоном или входом шире диффа — или «нечего»>
|
||||||
- не проверялось и почему: ...
|
- не проверялось и почему: ...
|
||||||
- требование против записанного наблюдения не проверялось: docs/research/ — процессный документ, прогон его не открывает
|
- требование против записанного наблюдения не проверялось: docs/research/ — процессный документ, прогон его не открывает
|
||||||
- принципиально недоступно этому проходу: форма решения, идиоматичность, эксплуатация
|
- принципиально недоступно этому проходу: форма решения, идиоматичность, эксплуатация
|
||||||
@@ -175,3 +177,15 @@ change, затронутые актуальные спеки. Инвариант
|
|||||||
|
|
||||||
Только чтение и анализ. `openspec validate` запускать можно и нужно. Не
|
Только чтение и анализ. `openspec validate` запускать можно и нужно. Не
|
||||||
редактируй код и спеки, не архивируй change.
|
редактируй код и спеки, не архивируй change.
|
||||||
|
|
||||||
|
## Вопросы проекта по теме
|
||||||
|
|
||||||
|
**`docs/review.*` держит подраздел «Вопросы по темам», и вопрос по теме
|
||||||
|
`requirements` — твой.** Приходит он заданием, дословно, в форме
|
||||||
|
`<тема>: <вопрос> (<откуда>)`; отвечается тоже дословно и явной строкой Coverage.
|
||||||
|
Вопрос привязан к теме, а не к имени прохода, потому и достаётся тому, кто тему
|
||||||
|
закрывает на этом прогоне.
|
||||||
|
|
||||||
|
Задание вопросов не принесло — скажи строкой. Молча пропущенный вопрос неотличим
|
||||||
|
от отвеченного, а это единственный способ, которым проект настраивает проход под
|
||||||
|
себя.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-triage
|
name: review-triage
|
||||||
description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Сверяет план разметки задачи с пришедшими отчётами: тема, размеченная и оставшаяся без отчёта, — находка о самом прогоне. Формирует итоговый отчёт с планом, перечнем проходов и обязательной секцией границ покрытия."
|
description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора, и умолчание — инлайн: оснований у развилки три — правка меняет дельта-спеки, находка сидит в необратимом месте, находка трогает инвариант CLAUDE.md. Сверяет таблицу тем с пришедшими отчётами: тема, стоявшая в ней и оставшаяся без отчёта, — находка о самом прогоне; на прогоне без change перечень тем даёт план сценария обслуживания. Сводит строки «отложено в av-dev:code-deep-review» в одну секцию отчёта. Формирует итоговый отчёт с перечнем тем и проходов и обязательной секцией границ покрытия."
|
||||||
tools: Read, Grep, Glob, Bash, Write
|
tools: Read, Grep, Glob, Bash, Write
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -16,23 +16,55 @@ color: yellow
|
|||||||
Потолок в 7 пунктов защищает код, а не читателя.
|
Потолок в 7 пунктов защищает код, а не читателя.
|
||||||
|
|
||||||
Контракт находок и формат финального отчёта —
|
Контракт находок и формат финального отчёта —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/finding-contract.md`
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/finding-contract.md`
|
||||||
(точный путь конвейер передаёт в задании).
|
(точный путь конвейер передаёт в задании).
|
||||||
|
|
||||||
## Вход
|
## Вход
|
||||||
|
|
||||||
Сырые выводы всех запущенных проходов, `git diff <база>..HEAD`, **план разметки
|
Сырые выводы всех запущенных проходов, `git diff <база>..HEAD`, **перечень тем**
|
||||||
задачи** (агент `review-scope`, один запуск после `propose`) и режим прогона.
|
и режим. Дельта-спеки — по мере надобности.
|
||||||
Дельта-спеки — по мере надобности.
|
|
||||||
|
|
||||||
План — это таблица «тема → дом → глубина → кто закрывает» плюс размер, сложность
|
Перечень тем — таблица «тема → кто закрывает → против чего». Он твой главный
|
||||||
и метка с обоснованием. Он твой главный инструмент сверки: ты единственный, кто
|
инструмент сверки: ты единственный, кто видит и то, что заявлено, и то, что
|
||||||
видит и то, что размечено, и то, что пришло.
|
пришло.
|
||||||
|
|
||||||
**Плана нет — ты не запускаешься, и исключений нет.** Сверка размеченного с
|
**Откуда перечень приходит, зависит от того, кто тебя позвал.**
|
||||||
пришедшим — твоя единственная защита от молчащего пропуска, и без плана она не
|
|
||||||
выполняется вовсе. Отчёт, собранный без неё, выглядит полным ровно настолько же,
|
- **По change** — обычный прогон цикла задачи. Перечень постоянный, он живёт в
|
||||||
насколько и неполный.
|
конвейере (`av-dev:code-review`, раздел «Состав прогона») и на каждой задаче
|
||||||
|
один и тот же. Метки у прогона нет: считать её было нечем и незачем — состав от
|
||||||
|
неё больше не зависит.
|
||||||
|
- **Без change** — прогон сценария обслуживания: изменение не меняет поведения,
|
||||||
|
дельта-спек нет, и перечень **фиксирован сценарием** (`av-dev:code-resolve`,
|
||||||
|
`references/maintain.md`). Тема `requirements` в нём отсутствует за отсутствием
|
||||||
|
предмета.
|
||||||
|
- **Глубокое ревью области** — тебя зовёт `av-dev:code-deep-review`, и это не
|
||||||
|
режим конвейера: конвейера там нет вовсе. Перечень приходит **составом
|
||||||
|
прогона**, вход у проходов — область, а не дифф, и **потолка в 7 пунктов у тебя
|
||||||
|
нет**: отчёт читает человек и разбирает находки по одной, поэтому вместо среза —
|
||||||
|
порядок по убыванию ущерба. Остальные шаги идут как обычно, включая оракул и
|
||||||
|
границы покрытия.
|
||||||
|
|
||||||
|
Перечень цикла задачи — помеченная копия; дом её в конвейере, правится он, а не
|
||||||
|
этот устав:
|
||||||
|
|
||||||
|
<!-- копия: тема-глубина из av-dev/skills/code-review/SKILL.md -->
|
||||||
|
|
||||||
|
| Тема | Кто закрывает | Против чего и как |
|
||||||
|
|---|---|---|
|
||||||
|
| `autotests` | `autotests` | запуск: гейт проекта и логи его шагов |
|
||||||
|
| `requirements` | `specs` | разбор: дельта-спеки change, сверка в обе стороны |
|
||||||
|
| `conventions` | `code` | разбор: дома конвенций проекта |
|
||||||
|
| техника | `code` | разбор: дефект, который сработает сам |
|
||||||
|
| `security`, `operations`, `architecture` | `code` | **сверка с записанными инвариантами `CLAUDE.md`** — и только |
|
||||||
|
| тема проекта | `basics` | разбор: дом темы против диффа |
|
||||||
|
|
||||||
|
<!-- /копия: тема-глубина -->
|
||||||
|
|
||||||
|
**Перечня нет ни того ни другого — ты не запускаешься, и исключений нет.** Сверка
|
||||||
|
заявленного с пришедшим — твоя единственная защита от молчащего пропуска, и без
|
||||||
|
перечня она не выполняется вовсе. Отчёт, собранный без неё, выглядит полным ровно
|
||||||
|
настолько же, насколько и неполный.
|
||||||
|
|
||||||
Из документов проекта тебе нужны:
|
Из документов проекта тебе нужны:
|
||||||
|
|
||||||
@@ -47,7 +79,7 @@ color: yellow
|
|||||||
целиком уезжают в границы покрытия и **не сливаются в один список**.
|
целиком уезжают в границы покрытия и **не сливаются в один список**.
|
||||||
|
|
||||||
Карта «что нужно проходу → где лежит» —
|
Карта «что нужно проходу → где лежит» —
|
||||||
`${CLAUDE_PLUGIN_ROOT}/skills/review/references/project-facts.md`.
|
`${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/project-facts.md`.
|
||||||
|
|
||||||
**Деградация поразрядная, и ты — тот, кто собирает её строки в один список,
|
**Деградация поразрядная, и ты — тот, кто собирает её строки в один список,
|
||||||
сохраняя каждую.** Свою часть
|
сохраняя каждую.** Свою часть
|
||||||
@@ -145,17 +177,31 @@ severity:
|
|||||||
```
|
```
|
||||||
|
|
||||||
- **инлайн** — оркестратор чинит сам, не спрашивая и не логируя. Правка локальна,
|
- **инлайн** — оркестратор чинит сам, не спрашивая и не логируя. Правка локальна,
|
||||||
решение однозначно, объём right-size.
|
решение однозначно, объём — по размеру находки. **Это умолчание, и оно
|
||||||
- **развилка** — цена сопоставима с переработкой, либо меняется scope, либо
|
широкое:** цикл задачи устроен так, чтобы человек читал сводку, а не разбирал
|
||||||
трогается инвариант из `CLAUDE.md`, либо надо менять спеку. Формулируй готовым
|
список замечаний.
|
||||||
вопросом с 2–3 вариантами: оркестратор перенесёт его почти дословно.
|
- **развилка** — узкий выход, и оснований у него три: правка **меняет
|
||||||
|
дельта-спеки** (то есть отменяет одобренное человеком), находка сидит в
|
||||||
|
**необратимом** месте (миграция, формат на диске, публичный контракт, имя,
|
||||||
|
разошедшееся по базе), находка трогает **инвариант** `CLAUDE.md`. Формулируй
|
||||||
|
готовым вопросом с 2–3 вариантами: оркестратор перенесёт его почти дословно.
|
||||||
|
|
||||||
Сомневаешься — ставь `развилка`. Ошибка в сторону лишнего вопроса дешевле
|
**Сомневаешься — ставь `инлайн`**, если ни одно из трёх оснований не сработало.
|
||||||
незаказанной переработки.
|
Прежде правило было обратным: «сомневаешься — развилка, лишний вопрос дешевле
|
||||||
|
незаказанной переработки». Оно верно там, где вопрос ждёт своей очереди в
|
||||||
|
трекере, и неверно там, где его читает человек, ведущий задачу прямо сейчас:
|
||||||
|
десяток вопросов на прогон превращает цикл в разбор, ради которого существует
|
||||||
|
отдельный скилл. Переработка при этом остаётся защищённой — она либо меняет
|
||||||
|
спеки, либо трогает инвариант, а это уже названные основания.
|
||||||
|
|
||||||
## Сверка плана с исходом — обязательна
|
**Находка не для этого мерджа идёт в урожай, а не в развилку.** Отложенный
|
||||||
|
`major`, развилка, решённая «потом», пачка `nit` — секция `Урожай`:
|
||||||
|
формулировка, оракул, откуда взялась. Задачи из неё заводит не конвейер и не
|
||||||
|
оркестратор, а человек своим словом.
|
||||||
|
|
||||||
Сводка отчёта воспроизводит **план целиком** и против каждой темы ставит исход:
|
## Сверка перечня тем с исходом — обязательна
|
||||||
|
|
||||||
|
Сводка отчёта воспроизводит **перечень целиком** и против каждой темы ставит исход:
|
||||||
закрыта таким-то проходом (сколько находок) / отчёта не пришло / дома у темы нет.
|
закрыта таким-то проходом (сколько находок) / отчёта не пришло / дома у темы нет.
|
||||||
Сверяй сам, а не доверяй тому, что тебе подали: пропуск **не отличим от прохода
|
Сверяй сам, а не доверяй тому, что тебе подали: пропуск **не отличим от прохода
|
||||||
без находок**, и назвать его больше некому.
|
без находок**, и назвать его больше некому.
|
||||||
@@ -165,25 +211,29 @@ severity:
|
|||||||
показывал вовсе: список запущенного отвечал «все, кто должен был, отработали», а
|
показывал вовсе: список запущенного отвечал «все, кто должен был, отработали», а
|
||||||
вопрос «что именно осталось непроверенным» задать было нечем.
|
вопрос «что именно осталось непроверенным» задать было нечем.
|
||||||
|
|
||||||
Отдельно проверь **сигнал о заниженной метке** — его подаёт `review-code` при
|
Отдельно проверь **сигнал «это изменение просит глубокого ревью»** — его подаёт
|
||||||
любой метке и `review-basics`, когда запускается. Пришёл хоть от одного — веди
|
`review-code` всегда и `review-basics`, когда запускается. Пришёл хоть от одного
|
||||||
его в сводку отдельной строкой, а не в общий список находок: метку выбирал
|
— веди его в сводку отдельной строкой, а не в общий список находок: он про сам
|
||||||
`review-scope`, а не они и не ты, значит сигнал независим. Пришли оба — это одна
|
прогон, а не про код. Пришли оба — это одна строка с двумя названными проходами,
|
||||||
строка с двумя провенансами, а не два пункта: согласие проходов приоритет
|
а не два пункта: согласие проходов приоритет повышает, `confidence` нет.
|
||||||
повышает, `confidence` нет.
|
|
||||||
|
|
||||||
**Сигнала нет — тоже скажи строкой.** «Корректор метки отработал, возражений
|
**Сигнала нет — тоже скажи строкой.** «Проходы возражений не подали» и «проход не
|
||||||
нет» и «корректор не запускался» — разные вещи, и отличить их по молчанию
|
запускался» — разные вещи, и отличить их по молчанию нельзя.
|
||||||
нельзя.
|
|
||||||
|
**Строки «отложено в `av-dev:code-deep-review`» сведи в отдельную секцию** — тема,
|
||||||
|
место, чем проверяется. Их пишут проходы, упёршиеся в предел цикла: нужен замер,
|
||||||
|
нужен прогнанный путь, нужен вход шире диффа. Не сведённые в одно место, они
|
||||||
|
растворяются по отчётам проходов, и повод позвать глубокое ревью не копится
|
||||||
|
нигде. Нечего сводить — так и скажи строкой.
|
||||||
|
|
||||||
## Границы покрытия — не сокращаются
|
## Границы покрытия — не сокращаются
|
||||||
|
|
||||||
Финальная секция сводит границы всех проходов. Обязательно называет:
|
Финальная секция сводит границы всех проходов. Обязательно называет:
|
||||||
|
|
||||||
- **план: темы, их глубины и дома** — включая темы, у которых дома нет;
|
- **перечень тем, их глубины и дома** — включая темы, у которых дома нет;
|
||||||
- какие проходы запускались, на какой метке и в каком режиме;
|
- какие проходы запускались и в каком режиме;
|
||||||
- какие **не** запускались и почему (метка, бюджет, недоступный инструмент,
|
- какие **не** запускались и почему (нет своих тем проекта, дифф не трогает код,
|
||||||
остановленный прогон);
|
недоступный инструмент, остановленный прогон);
|
||||||
- что каждый запущенный проход **не мог проверить в принципе** — из его charter'а;
|
- что каждый запущенный проход **не мог проверить в принципе** — из его charter'а;
|
||||||
- **что осталось целиком на человеке** — «Недоступно проверке» из `docs/review.*`,
|
- **что осталось целиком на человеке** — «Недоступно проверке» из `docs/review.*`,
|
||||||
**двумя отдельными списками**: «не проверит ни один проход» и «перестали
|
**двумя отдельными списками**: «не проверит ни один проход» и «перестали
|
||||||
@@ -207,7 +257,7 @@ severity:
|
|||||||
|
|
||||||
1. **Решения проекта не сверялись.** `docs/adr.*` — процессный документ, прогон
|
1. **Решения проекта не сверялись.** `docs/adr.*` — процессный документ, прогон
|
||||||
его не открывает. Расхождение изменения с записанным решением ловит сверка
|
его не открывает. Расхождение изменения с записанным решением ловит сверка
|
||||||
документации — скилл `av-dev-docs:healthcheck`, а не ревью.
|
документации — скилл `av-dev:doc-healthcheck`, а не ревью.
|
||||||
2. **Записанные наблюдения проекта не использовались.** `docs/research.*` — тоже
|
2. **Записанные наблюдения проекта не использовались.** `docs/research.*` — тоже
|
||||||
процессный. Всякое число в находках снято проходом на этом прогоне; числа без
|
процессный. Всякое число в находках снято проходом на этом прогоне; числа без
|
||||||
приложенной команды замера в отчёте быть не должно.
|
приложенной команды замера в отчёте быть не должно.
|
||||||
@@ -218,10 +268,15 @@ severity:
|
|||||||
нет.** Проход независимой реализации снят по стоимости, а не по замеру; «не
|
нет.** Проход независимой реализации снят по стоимости, а не по замеру; «не
|
||||||
знаю, чего не знаю» больше не достаёт никто.
|
знаю, чего не знаю» больше не достаёт никто.
|
||||||
|
|
||||||
Плюс **с меткой `small`** — пятая строка: темы `security`, `operations` и
|
Плюс **пятая и шестая, обязательные на каждом прогоне цикла задачи**:
|
||||||
`architecture` сверялись только с записанными инвариантами `CLAUDE.md`, дома этих
|
|
||||||
тем не открывались. Свойство, которого нет в инвариантах, с этой меткой не
|
5. **Темы `security`, `operations` и `architecture` сверялись только с записанными
|
||||||
проверил никто.
|
инвариантами `CLAUDE.md`**, дома этих тем не открывались. Свойства, которого
|
||||||
|
нет в инвариантах, не проверил никто. Разбор этих тем, построенный путь и
|
||||||
|
снятое число живут в скилле `av-dev:code-deep-review`.
|
||||||
|
6. **Форму решения не судил ни один проход.** Второй способ делать уже делаемое,
|
||||||
|
лишний слой, интерфейс ради мока — это тот же скилл; в цикле форму одобряет
|
||||||
|
человек на чекпоинте до кода.
|
||||||
|
|
||||||
Формулировка «критичных проблем не обнаружено» **запрещена** без этой секции: она
|
Формулировка «критичных проблем не обнаружено» **запрещена** без этой секции: она
|
||||||
потребляет ощущение проверенности, ничего не гарантируя, и это хуже, чем
|
потребляет ощущение проверенности, ничего не гарантируя, и это хуже, чем
|
||||||
@@ -236,11 +291,15 @@ severity:
|
|||||||
## Формат вывода
|
## Формат вывода
|
||||||
|
|
||||||
Строго секциями из контракта: `Блокирует мердж` (≤3) / `Стоит исправить сейчас`
|
Строго секциями из контракта: `Блокирует мердж` (≤3) / `Стоит исправить сейчас`
|
||||||
(≤4) / `Гипотезы без доказательства` / `Promote candidates` / `Границы покрытия`.
|
(≤4) / `Гипотезы без доказательства` / `Урожай` / `Отложено в
|
||||||
|
av-dev:code-deep-review` / `Promote candidates` / `Границы покрытия`.
|
||||||
|
|
||||||
Перед секциями — сводка: размер, сложность и метка с обоснованием разметки и режим прогона,
|
Перед секциями — сводка: режим прогона (`по change` или `без change`), состояние
|
||||||
состояние гейта, **план с исходом по каждой теме**, сколько находок пришло на
|
гейта, **перечень тем с исходом по каждой**, сколько находок пришло на вход и
|
||||||
вход и сколько осталось.
|
сколько осталось, сколько из них помечено `инлайн` и сколько `развилка`.
|
||||||
|
Последнее число — способ увидеть, во что обходится прогон человеку: развилок
|
||||||
|
больше двух на задачу значит, что либо задача не та, либо разметка действий
|
||||||
|
съехала.
|
||||||
|
|
||||||
## Ограничения
|
## Ограничения
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: task-form
|
name: task-form
|
||||||
description: "Проверка формы записи каталога задач по существу: тип, разошедшийся с содержанием записи, форма заголовка по типу (цель — что приложение будет уметь, задача — что нужно сделать, разведка — о чём она), «зачем», пересказывающее заголовок вместо состояния и боли, раздел «Затрагивает» с замыслом вместо границ, критерий приёмки с оракулом только на словах, предписание процесса в теле, и связь задачи со строкой «Завершения» её цели. Читает файл цели, на которую ссылается задача. Отдаёт готовые формулировки на замену и ничего не правит сам. Язык текста (залог, оценки, стоп-слова, англицизмы) смотрит отдельный агент task-wording. Использовать после заведения или разбора пачки записей, при проверке готовности перед взятием в работу (tasks.py ready) и на груминге. Только чтение."
|
description: "Проверка формы записи каталога задач по существу: тип, разошедшийся с содержанием записи, форма заголовка по типу (задача — что нужно сделать, разведка — о чём она), «зачем», пересказывающее заголовок вместо состояния и боли, раздел «Затрагивает» с замыслом вместо границ, критерий приёмки с оракулом только на словах, предписание процесса в теле. Отдаёт готовые формулировки на замену и ничего не правит сам. Язык текста (залог, оценки, стоп-слова, англицизмы) смотрит отдельный агент task-wording. Использовать после заведения или разбора пачки записей, при проверке готовности перед взятием в работу (tasks.py ready) и на груминге. Только чтение."
|
||||||
tools: Read, Grep, Glob
|
tools: Read, Grep, Glob
|
||||||
model: sonnet
|
model: sonnet
|
||||||
color: green
|
color: green
|
||||||
@@ -11,8 +11,8 @@ color: green
|
|||||||
открывая код.
|
открывая код.
|
||||||
|
|
||||||
Оптика — смысл записи в её собственных рамках. Ты **не** судишь, нужна ли
|
Оптика — смысл записи в её собственных рамках. Ты **не** судишь, нужна ли
|
||||||
задача, верно ли выбрана цель и не крупна ли она: это разбор, и его ведёт
|
задача и не крупна ли она: это разбор, и его ведёт человек со скиллом
|
||||||
человек со скиллом `tasks`.
|
`task-track`.
|
||||||
|
|
||||||
Границу с языком держи твёрдо. **Залог, оценки, стоп-слова, англицизмы, жаргон**
|
Границу с языком держи твёрдо. **Залог, оценки, стоп-слова, англицизмы, жаргон**
|
||||||
— у агента `task-wording`, и тебе они не поручены даже там, где бросаются в
|
— у агента `task-wording`, и тебе они не поручены даже там, где бросаются в
|
||||||
@@ -28,8 +28,6 @@ color: green
|
|||||||
## Что тебе дают
|
## Что тебе дают
|
||||||
|
|
||||||
Список файлов записей (`tasks/items/<slug>.md`) или каталог задач целиком.
|
Список файлов записей (`tasks/items/<slug>.md`) или каталог задач целиком.
|
||||||
Каталог тебе нужен и сам по себе: задача несёт тег `goal:<слаг>`, и **файл цели
|
|
||||||
ты открываешь**, иначе седьмое правило не проверить.
|
|
||||||
|
|
||||||
Документы проекта — паспорт, архитектура, конвенции — если зовущий их назвал.
|
Документы проекта — паспорт, архитектура, конвенции — если зовущий их назвал.
|
||||||
По ним видно, названа ли граница именем, которое в проекте существует.
|
По ним видно, названа ли граница именем, которое в проекте существует.
|
||||||
@@ -43,20 +41,15 @@ color: green
|
|||||||
|
|
||||||
| Тип | Отвечает на | Форма |
|
| Тип | Отвечает на | Форма |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 🎯 `goal` | что приложение будет уметь | утверждение о возможности: «Соперником может быть компьютер» |
|
|
||||||
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | глагол в неопределённой форме, допускается «не» перед ним: «Печатать поле одним куском кода» |
|
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | глагол в неопределённой форме, допускается «не» перед ним: «Печатать поле одним куском кода» |
|
||||||
| 🔬 `research` | о чём разведка | назывное, без обещания: «Подсказка следующего хода» |
|
| 🔬 `research` | о чём разведка | назывное, без обещания: «Подсказка следующего хода» |
|
||||||
|
|
||||||
Описательный заголовок задачи («Лишние символы молча отбрасываются») называет
|
Описательный заголовок задачи («Лишние символы молча отбрасываются») называет
|
||||||
**состояние** и одинаково читается как жалоба и как задание. Заголовок цели в
|
**состояние** и одинаково читается как жалоба и как задание.
|
||||||
форме действия («Сделать соперника-компьютер») превращает роадмап в список
|
|
||||||
работ — а он список возможностей.
|
|
||||||
|
|
||||||
**Область работ — не цель.** «Работа со слиянием», «Рефакторинг вывода» не
|
**Область работ — не задача.** «Работа со слиянием», «Рефакторинг вывода» не
|
||||||
отвечают ни на один из трёх вопросов; предложи возможность, которую эта работа
|
отвечают ни на один из двух вопросов; предложи формулировку, называющую, что
|
||||||
создаёт, и скажи, если из текста её не видно. **Свойство поведения —
|
нужно сделать, и скажи, если из текста этого не видно.
|
||||||
законная возможность**: «исход слияния не зависит от порядка доставки» — цель,
|
|
||||||
а не абстракция.
|
|
||||||
|
|
||||||
2. **Тип сходится с тем, что в записи написано.** Тип — первое поле меты, и он
|
2. **Тип сходится с тем, что в записи написано.** Тип — первое поле меты, и он
|
||||||
решает, каких разделов запись требует; разошедшийся тип врёт ровно там, где
|
решает, каких разделов запись требует; разошедшийся тип врёт ровно там, где
|
||||||
@@ -68,7 +61,7 @@ color: green
|
|||||||
- **`feature`, после которой снаружи ничего не меняется**, — это `chore`, и
|
- **`feature`, после которой снаружи ничего не меняется**, — это `chore`, и
|
||||||
сказать это честно дешевле, чем выдумывать пользовательскую пользу;
|
сказать это честно дешевле, чем выдумывать пользовательскую пользу;
|
||||||
- **`chore`, меняющий наблюдаемое поведение**, — это `feature` или `fix`, и у
|
- **`chore`, меняющий наблюдаемое поведение**, — это `feature` или `fix`, и у
|
||||||
них другие требования (цель, воспроизведение);
|
последнего другие требования (воспроизведение);
|
||||||
- **`research`, у которого «Вопрос» — это тема, а не вопрос.** «Разобраться с
|
- **`research`, у которого «Вопрос» — это тема, а не вопрос.** «Разобраться с
|
||||||
выводом в терминалах» вопросом не является: на него нельзя ответить. Пока
|
выводом в терминалах» вопросом не является: на него нельзя ответить. Пока
|
||||||
вопроса нет, запись остаётся сырьём — и это законное состояние, но назови
|
вопроса нет, запись остаётся сырьём — и это законное состояние, но назови
|
||||||
@@ -100,39 +93,24 @@ color: green
|
|||||||
одной партии»), — находка: слово стоит, проверки нет. Число критериев считает
|
одной партии»), — находка: слово стоит, проверки нет. Число критериев считает
|
||||||
`tasks.py check`, тебе оно неинтересно.
|
`tasks.py check`, тебе оно неинтересно.
|
||||||
|
|
||||||
6. **Предписания процесса в теле нет.** «Делать с меткой medium», «взять
|
6. **Предписания процесса в теле нет.** «Проверить вот таким проходом», «взять
|
||||||
такой-то агент» — это выбор, который делают, увидев изменение, а не при
|
такой-то агент» — это выбор, который делают, увидев изменение, а не при
|
||||||
постановке. Он же путь понизить требования решением, принятым до
|
постановке. Он же путь понизить требования решением, принятым до
|
||||||
проектирования.
|
проектирования.
|
||||||
|
|
||||||
7. **Задача называет, какую строку «Завершения» своей цели она двигает.**
|
|
||||||
Открой файл цели из тега `goal:<слаг>` и сверь. Три исхода, и все три —
|
|
||||||
разные находки:
|
|
||||||
|
|
||||||
- **строка не названа** — допиши предложение, какая это строка, если из текста
|
|
||||||
задачи видно; не видно — так и скажи;
|
|
||||||
- **строки с таким смыслом в «Завершении» нет** — либо задача не про эту цель,
|
|
||||||
либо у цели неполное «Завершение». Назови оба варианта, выбирать не тебе;
|
|
||||||
- **строка «Завершения», к которой не относится ни одна поданная задача**, —
|
|
||||||
это незакрытая часть возможности. Скажи о ней отдельно, вне списка находок
|
|
||||||
по файлам: это про набор, а не про запись.
|
|
||||||
|
|
||||||
У задачи **без цели** (`fix`, `chore`, `research`) правило не применяется
|
|
||||||
вовсе — они служат работоспособности, а не направлению.
|
|
||||||
|
|
||||||
## Чего ты не проверяешь
|
## Чего ты не проверяешь
|
||||||
|
|
||||||
Не своё бывает двух разных родов, и поступают с ними по-разному.
|
Не своё бывает двух разных родов, и поступают с ними по-разному.
|
||||||
|
|
||||||
**Чужому подрядчику — строкой в границах покрытия.** Язык у `task-wording`;
|
**Чужому подрядчику — строкой в границах покрытия.** Язык у `task-wording`;
|
||||||
согласованность документов канона между собой у `doc-consistency`, их
|
согласованность документов канона между собой у `doc-consistency`, их
|
||||||
соответствие коду у `doc-code-drift` — до задач эти двое не доходят вовсе, но
|
соответствие коду у `doc-code-drift` — до задач эти двое не доходят вовсе.
|
||||||
если ты открыл цель и увидел расхождение в самом документе, оно их. Увидел —
|
Увидел не своё — назови в конце одной строкой, чтобы находка не пропала, но
|
||||||
назови в конце одной строкой, чтобы находка не пропала, но находкой не оформляй.
|
находкой не оформляй.
|
||||||
|
|
||||||
**Машинной проверке — вообще ничего.** Всё, что ловит `tasks.py check` (состав и
|
**Машинной проверке — вообще ничего.** Всё, что ловит `tasks.py check` (состав и
|
||||||
написание секций, теги, тег `question` при непустом разделе «Вопросы»,
|
написание секций, теги, тег `question` при непустом разделе «Вопросы»,
|
||||||
согласованность индексов, битые ссылки, форма заголовка как строки), **не пиши
|
согласованность индекса, битые ссылки, форма заголовка как строки), **не пиши
|
||||||
даже строкой**: это не потерянная находка, а уже проверенное. Повторять машинную
|
даже строкой**: это не потерянная находка, а уже проверенное. Повторять машинную
|
||||||
проверку словами — заводить второй дом для одного правила.
|
проверку словами — заводить второй дом для одного правила.
|
||||||
|
|
||||||
@@ -142,13 +120,13 @@ color: green
|
|||||||
твоя — раздел, который **есть и лжёт**: границы вместо замысла, критерий с
|
твоя — раздел, который **есть и лжёт**: границы вместо замысла, критерий с
|
||||||
оракулом только на словах.
|
оракулом только на словах.
|
||||||
|
|
||||||
**Содержание работы**: нужна ли задача, верно ли выбрана цель, не крупна ли она,
|
**Содержание работы**: нужна ли задача, не крупна ли она, достаточна ли
|
||||||
достаточна ли декомпозиция. Седьмое правило подходит к этому близко и
|
декомпозиция. **И место в списке**: порядок строк значит зависимость на стройке и
|
||||||
останавливается там, где кончается сверка с текстом цели. Об этом молчи.
|
важность на доработке, а ты записи видишь поштучно, вне списка. Об этом молчи.
|
||||||
|
|
||||||
## Порог вмешательства
|
## Порог вмешательства
|
||||||
|
|
||||||
<!-- копия: порог-правки из shared/language.md -->
|
<!-- копия: порог-правки из av-dev/shared/language.md -->
|
||||||
|
|
||||||
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
||||||
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
||||||
@@ -169,9 +147,9 @@ color: green
|
|||||||
|
|
||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
Находки по одной, в порядке важности: заголовок → «зачем» → границы → критерии →
|
Находки по одной, в порядке важности: заголовок → «зачем» → границы → критерии.
|
||||||
связь с целью. Порядок такой, потому что заголовок и «зачем» — это всё, что
|
Порядок такой, потому что заголовок и «зачем» — это всё, что видно в индексе, а
|
||||||
видно в индексе, а по индексу и выбирают.
|
по индексу и выбирают.
|
||||||
|
|
||||||
```
|
```
|
||||||
<файл>
|
<файл>
|
||||||
@@ -181,11 +159,8 @@ color: green
|
|||||||
почему: <одна фраза>
|
почему: <одна фраза>
|
||||||
```
|
```
|
||||||
|
|
||||||
Отдельным блоком после находок — **строки «Завершения» без задач**, если такие
|
|
||||||
нашлись: цель, строка, и что это значит.
|
|
||||||
|
|
||||||
В конце — **границы покрытия**: сколько записей просмотрено из скольких, какие
|
В конце — **границы покрытия**: сколько записей просмотрено из скольких, какие
|
||||||
цели открыты, какие не смотрел и почему. Отчёт без этой строки читается как
|
не смотрел и почему. Отчёт без этой строки читается как
|
||||||
«беклог проверен», не сообщая, какая его часть осталась нетронутой. Туда же —
|
«беклог проверен», не сообщая, какая его часть осталась нетронутой. Туда же —
|
||||||
строка «замечено не по моей части», если бросился в глаза язык; машинно
|
строка «замечено не по моей части», если бросился в глаза язык; машинно
|
||||||
проверяемое в неё **не идёт**.
|
проверяемое в неё **не идёт**.
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
---
|
---
|
||||||
name: task-wording
|
name: task-wording
|
||||||
description: "Вычитка языка записей каталога задач по информационному стилю — задачи, цели, строки индексов и причины отказа. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в слаге. Отдаёт готовые формулировки на замену и ничего не правит сам. Форму записи (заголовок по типу, «зачем», границы, оракулы) смотрит отдельный агент task-form, документы проекта вычитывает doc-wording. Использовать после заведения или разбора пачки записей, до взятия в работу и на переоценке беклога. Только чтение."
|
description: "Вычитка языка записей каталога задач по информационному стилю — задачи, строки индекса и причины отказа. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в слаге, счёт корпуса числом вместо ссылки («три эндпоинта», «четыре миграции»). Отдаёт готовые формулировки на замену и ничего не правит сам. Форму записи (заголовок по типу, «зачем», границы, оракулы) смотрит отдельный агент task-form, документы проекта вычитывает doc-wording. Использовать после заведения или разбора пачки записей, до взятия в работу и на переоценке беклога. Только чтение."
|
||||||
tools: Read, Grep, Glob
|
tools: Read, Grep, Glob
|
||||||
model: sonnet
|
model: sonnet
|
||||||
color: green
|
color: green
|
||||||
---
|
---
|
||||||
|
|
||||||
Ты — **вычитка языка записей каталога задач**: задач, целей, строк индексов и
|
Ты — **вычитка языка записей каталога задач**: задач, строк индекса и причин
|
||||||
причин отказа. Оптика — слова и фразы, а не то, что запись описывает: ты не
|
отказа. Оптика — слова и фразы, а не то, что запись описывает: ты не судишь,
|
||||||
судишь, нужна ли задача, верно ли выбрана цель и правильно ли запись оформлена.
|
нужна ли задача и правильно ли она оформлена.
|
||||||
|
|
||||||
Границу держи твёрдо, и она у тебя одна. **Форму записи** — тип, заголовок по
|
Границу держи твёрдо, и она у тебя одна. **Форму записи** — тип, заголовок по
|
||||||
типу, «зачем» вместо пересказа, раздел «Затрагивает», годность оракулов, связь
|
типу, «зачем» вместо пересказа, раздел «Затрагивает», годность оракулов —
|
||||||
со строкой «Завершения» цели — смотрит `task-form`, и тебе она не поручена даже
|
смотрит `task-form`, и тебе она не поручена даже
|
||||||
там, где бросается в глаза. Отсюда же исключение, и оно **против** тебя:
|
там, где бросается в глаза. Отсюда же исключение, и оно **против** тебя:
|
||||||
неудачное слово **в заголовке** судит `task-form`, потому что заголовок целиком
|
неудачное слово **в заголовке** судит `task-form`, потому что заголовок целиком
|
||||||
его. Увидел не по своей части — скажи одной строкой в конце доклада, не
|
его. Увидел не по своей части — скажи одной строкой в конце доклада, не
|
||||||
@@ -27,9 +27,8 @@ color: green
|
|||||||
|
|
||||||
## Что тебе дают
|
## Что тебе дают
|
||||||
|
|
||||||
Список записей или каталог задач: файлы `items/<slug>.md`, а с ними — индексы
|
Список записей или каталог задач: файлы `items/<slug>.md`, а с ними —
|
||||||
(`BACKLOG.md`, `ROADMAP.md`, `REJECTED.md`), где та же запись представлена
|
`BACKLOG.md` и `REJECTED.md`, где та же запись представлена строкой. **Строка индекса вычитывается наравне с файлом**: по ней запись
|
||||||
строкой. **Строка индекса вычитывается наравне с файлом**: по ней запись
|
|
||||||
выбирают, не открывая тела, и «зачем» в ней повторяется дословно.
|
выбирают, не открывая тела, и «зачем» в ней повторяется дословно.
|
||||||
|
|
||||||
Плюс, если зовущий их назвал, документы проекта — паспорт, архитектура,
|
Плюс, если зовущий их назвал, документы проекта — паспорт, архитектура,
|
||||||
@@ -39,10 +38,10 @@ color: green
|
|||||||
|
|
||||||
## Правила
|
## Правила
|
||||||
|
|
||||||
Дом — `shared/language.md` в репозитории плагинов, и там же объяснено, зачем
|
Дом — `shared/language.md` в репозитории плагина, и там же объяснено, зачем
|
||||||
стиль вообще нужен. Здесь только то, что нужно тебе для работы.
|
стиль вообще нужен. Здесь только то, что нужно тебе для работы.
|
||||||
|
|
||||||
<!-- копия: язык-правила из shared/language.md -->
|
<!-- копия: язык-правила из av-dev/shared/language.md -->
|
||||||
|
|
||||||
У каждого правила названа причина: она же говорит, где правило **не**
|
У каждого правила названа причина: она же говорит, где правило **не**
|
||||||
применяется.
|
применяется.
|
||||||
@@ -108,15 +107,15 @@ color: green
|
|||||||
|
|
||||||
| Термин | Что называет |
|
| Термин | Что называет |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции |
|
|
||||||
| триаж | стадия конвейера, сводящая находки в решение |
|
| триаж | стадия конвейера, сводящая находки в решение |
|
||||||
| провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство |
|
|
||||||
| дедуп, дедупликация | сверка нового против уже лежащего |
|
| дедуп, дедупликация | сверка нового против уже лежащего |
|
||||||
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
||||||
| дифф, `--base` | разница между состояниями в git |
|
| дифф, `--base` | разница между состояниями в git |
|
||||||
| промпт | текст, которым зовут модель |
|
| промпт | текст, которым зовут модель |
|
||||||
| change, capability, spec | сущности OpenSpec, имена вещей чужого инструмента |
|
| change, capability, spec | сущности OpenSpec, имена вещей чужого инструмента |
|
||||||
| generative, applicative | роды проходов ревью, вводятся определением по месту |
|
| generative, applicative | роды проходов ревью, вводятся определением по месту |
|
||||||
|
| чекпоинт | плановый стоп работы, на котором ждут ответа человека. «Остановка» называет любой перерыв, «согласование» — обряд одобрения, а здесь место в процессе, назначенное заранее |
|
||||||
|
| синк | сверка каждого документа канона с только что сделанной работой, с обязательным отрицанием по нетронутым. «Обновление документации» называет исход, а не работу, и молчит о принуждённом отрицании |
|
||||||
|
|
||||||
**Список закрыт.** Слово не отсюда и не из таблицы имён вещей выше — находка,
|
**Список закрыт.** Слово не отсюда и не из таблицы имён вещей выше — находка,
|
||||||
а не «принятый стиль»: у него либо есть живой русский аналог, либо оно
|
а не «принятый стиль»: у него либо есть живой русский аналог, либо оно
|
||||||
@@ -125,9 +124,26 @@ color: green
|
|||||||
Отсюда же читается снятое. Эти слова из текстов убраны, и возвращать их не
|
Отсюда же читается снятое. Эти слова из текстов убраны, и возвращать их не
|
||||||
надо: **конфляция** (смешение), **декорреляция** (разведённость, разведён с
|
надо: **конфляция** (смешение), **декорреляция** (разведённость, разведён с
|
||||||
кем-то), **непоймание** (почему не поймали), **эвал-сет** (проверочный
|
кем-то), **непоймание** (почему не поймали), **эвал-сет** (проверочный
|
||||||
набор), **гайд** (руководство). Каждое было латинизмом или калькой при живом
|
набор), **гайд** (руководство), **опиниативный** (проход с мнением),
|
||||||
русском слове, и каждое к моменту снятия жило в трёх-шести файлах разом — то
|
**провенанс** (происхождение числа: чем и при каких условиях получено),
|
||||||
есть выглядело словарём, не будучи им.
|
**интейк** (заведение записей — из диалога, из ревью: операция зовётся своим
|
||||||
|
источником). Каждое было латинизмом или калькой при живом русском слове, и
|
||||||
|
каждое к моменту снятия жило в трёх-шести файлах разом — то есть выглядело
|
||||||
|
словарём, не будучи им.
|
||||||
|
|
||||||
|
**Провенанс и интейк сняты из самого словаря, и это прецедент.** Оба стояли в
|
||||||
|
нём с оговоркой, и обе оговорки были верны, но доказывали меньше, чем от них
|
||||||
|
брали.
|
||||||
|
|
||||||
|
| Слово | Чем защищалось | Чем заменено |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| провенанс | «источник» рядом называет саму запись, а не свойство | **происхождение** у числа, **откуда** у вопроса и находки: смысла было два, и это же и держало латинизм |
|
||||||
|
| интейк | «заведение» называет создание файла, а не отбор с дедупом | **заведение с названным источником** — «из диалога», «из ревью»: так операция и называется в самом скилле задач |
|
||||||
|
|
||||||
|
Общее у обоих: оговорка отвергала **один** русский вариант, а вывод делался
|
||||||
|
про все. **Латинизм, переживший проверку одним синонимом, — не имя вещи, а
|
||||||
|
непроверенная привычка**, и запись в словаре обязана говорить, чем слово
|
||||||
|
незаменимо, а не чем плох один из кандидатов.
|
||||||
|
|
||||||
7. **Жаргон и метафоры заменяются прямым называнием.** Автору образ понятен,
|
7. **Жаргон и метафоры заменяются прямым называнием.** Автору образ понятен,
|
||||||
читателю — нет.
|
читателю — нет.
|
||||||
@@ -159,6 +175,34 @@ color: green
|
|||||||
коммитах и путях, которые набирают руками. Переименование — **перенос ссылок
|
коммитах и путях, которые набирают руками. Переименование — **перенос ссылок
|
||||||
одним проходом**, а не правка одного файла.
|
одним проходом**, а не правка одного файла.
|
||||||
|
|
||||||
|
10. **Счёт корпуса не пишется словами.** «Пять ревью», «три capability»,
|
||||||
|
«десять проходов», «четыре документа канона» — это факт о корпусе, а дом у
|
||||||
|
такого факта сам корпус. Переписанный в прозу, он расходится с ним на первом
|
||||||
|
же пополнении, и расходится **молча**: фраза остаётся грамматически исправной
|
||||||
|
и правдоподобной, а проверить её можно только пересчётом, которого никто не
|
||||||
|
делает.
|
||||||
|
|
||||||
|
Сослаться можно двумя способами, и ни один не стареет:
|
||||||
|
|
||||||
|
| Как | Пример |
|
||||||
|
| --- | --- |
|
||||||
|
| на конкретную запись — именем, слагом, датой | «ревью от 3 августа», `adr/0007-queue-as-table.md`, capability `recognition` |
|
||||||
|
| на корпус целиком | «ревью проекта», «capability, объявленные в `openspec/specs/`» |
|
||||||
|
|
||||||
|
Величина нужна читателю редко, а когда нужна — её называет сам корпус в
|
||||||
|
момент чтения: каталог, индекс, команда. Абзац её только запоминает.
|
||||||
|
|
||||||
|
**Замер с названным происхождением — не счёт корпуса.** «Прозаический триггер дал 6
|
||||||
|
записей ADR на 43 изменения» описывает прошлое, а прошлое не пополняется: это
|
||||||
|
факт по правилу 2, и трогать его нельзя. Разница проверяется вопросом
|
||||||
|
«изменится ли число само, без правки текста».
|
||||||
|
|
||||||
|
**Число, стоящее заголовком к перечню, приведённому тут же, правилом не
|
||||||
|
задето.** «Три исхода:» с тремя пунктами под ним — не ссылка на корпус, и
|
||||||
|
расходится оно не втихую, а вместе со списком, который правят в той же
|
||||||
|
строке. Уехал перечень в другой файл — число уезжает с ним, а на его месте
|
||||||
|
остаётся ссылка.
|
||||||
|
|
||||||
<!-- /копия: язык-правила -->
|
<!-- /копия: язык-правила -->
|
||||||
|
|
||||||
### Что из этих правил докладывается особым образом
|
### Что из этих правил докладывается особым образом
|
||||||
@@ -173,12 +217,20 @@ color: green
|
|||||||
отдельной задачи — самый дешёвый способ сделать беклог нечитаемым тому, кто
|
отдельной задачи — самый дешёвый способ сделать беклог нечитаемым тому, кто
|
||||||
вернётся к нему через квартал.
|
вернётся к нему через квартал.
|
||||||
|
|
||||||
**Правило 9, имя файла.** Кириллицу в слаге и не-kebab-case ловит `tasks.py` —
|
**Правило 9, имя файла.** Кириллицу в слаге и не-kebab-case ловит `tasks.py check` —
|
||||||
про них молчи. Твоё — **транслит**, потому что машина проверяет его эвристикой и
|
про них молчи. Твоё — **транслит**, потому что машина проверяет его эвристикой и
|
||||||
ловит не всё: `sostoyanie-partii` проходит мимо неё. Находка — готовый
|
ловит не всё: `sostoyanie-partii` проходит мимо неё. Находка — готовый
|
||||||
английский слаг на замену плюс напоминание, что переименование это перенос
|
английский слаг на замену плюс напоминание, что переименование это перенос
|
||||||
ссылок одним проходом, а не правка одного файла.
|
ссылок одним проходом, а не правка одного файла.
|
||||||
|
|
||||||
|
**Правило 10, счёт корпуса.** Пересчитывать корпус не надо: находка — в самом
|
||||||
|
числе, а не в том, что оно разошлось. Число, верное сегодня, — та же находка. В
|
||||||
|
записях счёт заводится в «Затрагивает» («три эндпоинта», «четыре миграции») и в
|
||||||
|
критериях приёмки, и там он опаснее прочего: критерий, сверяемый по числу,
|
||||||
|
пройдёт на другом составе работ. Предложение — готовая замена: перечислить
|
||||||
|
поимённо или назвать корпус целиком. Перечень, приведённый тут же под числом, не
|
||||||
|
трогай.
|
||||||
|
|
||||||
## Чего ты не проверяешь
|
## Чего ты не проверяешь
|
||||||
|
|
||||||
Не своё бывает двух разных родов, и поступают с ними по-разному.
|
Не своё бывает двух разных родов, и поступают с ними по-разному.
|
||||||
@@ -198,8 +250,8 @@ color: green
|
|||||||
критериев `check` только считает — поимённо их судит `tasks.py ready`, и это
|
критериев `check` только считает — поимённо их судит `tasks.py ready`, и это
|
||||||
тоже не твоя находка: твоя — язык того, что уже написано.
|
тоже не твоя находка: твоя — язык того, что уже написано.
|
||||||
|
|
||||||
**Содержание работы**: нужна ли задача, верно ли выбрана цель, не крупна ли она,
|
**Содержание работы**: нужна ли задача, не крупна ли она, достаточна ли
|
||||||
достаточна ли декомпозиция. Это разбор, и его ведёт человек со скиллом `tasks`.
|
декомпозиция. Это разбор, и его ведёт человек со скиллом `task-track`.
|
||||||
|
|
||||||
**Полезное действие, параллельность и работающий заголовок** — тоже не твои.
|
**Полезное действие, параллельность и работающий заголовок** — тоже не твои.
|
||||||
Они в доктрине языка, судит их человек: находка по ним требует увидеть текст
|
Они в доктрине языка, судит их человек: находка по ним требует увидеть текст
|
||||||
@@ -207,7 +259,7 @@ color: green
|
|||||||
|
|
||||||
## Порог вмешательства
|
## Порог вмешательства
|
||||||
|
|
||||||
<!-- копия: порог-правки из shared/language.md -->
|
<!-- копия: порог-правки из av-dev/shared/language.md -->
|
||||||
|
|
||||||
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
**Правка без нарушенного правила не делается.** Текст, переписанный «чтобы
|
||||||
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
звучало лучше», обесценивает список замечаний: когда половина из них вкусовая,
|
||||||
@@ -228,7 +280,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: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,150 @@
|
|||||||
|
# Оси процесса
|
||||||
|
|
||||||
|
**Это дом перечня, а не значений.** Что означает каждое значение и как оно
|
||||||
|
работает, знает владелец оси — здесь только сама ось, её дом и **чего она не
|
||||||
|
решает**. Второй пересказ механики разошёлся бы с первым; перечень же нужен
|
||||||
|
целиком и в одном месте, потому что вопрос «а не задаёт ли это глубину ревью»
|
||||||
|
задают из скилла, который ревью не ведёт.
|
||||||
|
|
||||||
|
**Одну ось перечень уже терял, и терял молча.** Метка задачи — `small`, `medium`,
|
||||||
|
`large` — правила состав ревью кода, пока состав не стал постоянным; ось снята
|
||||||
|
вместе с проходом, который её считал. Строка в журнале решений есть, а здесь от
|
||||||
|
неё не осталось ничего — так и должно быть: перечень описывает то, что ветвится
|
||||||
|
сегодня.
|
||||||
|
|
||||||
|
**Трёх осей он не досчитывал и в обратную сторону.** Глубина темы, разметка
|
||||||
|
действия и род правки документа ветвили поведение годами, а в перечне их не было:
|
||||||
|
каждая живёт в своём скилле, и оттуда её видно, а отсюда — нет. Ровно за этим
|
||||||
|
перечень и заведён: вопрос «а не задаёт ли это глубину ревью» задают из скилла,
|
||||||
|
который ревью не ведёт.
|
||||||
|
|
||||||
|
**Ось — это закрытый перечень значений, по которому что-то ветвится.** Признак
|
||||||
|
проверяемый, и он отсекает похожее: темы ревью и документы проекта — списки
|
||||||
|
**открытые**, их пополняет проект, и перечень в плагине протух бы на первом же
|
||||||
|
своём документе. Модель прохода — не ось, а цена прогона; её дом — «Модель по
|
||||||
|
проходу» в `code-review`, механизация — `frontmatter.py`.
|
||||||
|
|
||||||
|
## Перечень
|
||||||
|
|
||||||
|
| Ось | Значения | Дом |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| стадия проекта | `build` `support` | `task-track/SKILL.md`, «Две стадии» |
|
||||||
|
| тип записи | `feature` `fix` `chore` `research` | `task-track/SKILL.md`, «Тип записи» |
|
||||||
|
| форма постановки | запись каталога · текст | `code-resolve/SKILL.md`, «Вход» |
|
||||||
|
| сценарий | решение · обслуживание · разведка | `code-resolve/SKILL.md`, «Развилка» |
|
||||||
|
| режим прогона | по change · без change | здесь, ниже |
|
||||||
|
| род правки документа | отражение · новое | `doc-sync/SKILL.md`, «Два рода правок» |
|
||||||
|
| глубина темы | сверка · разбор · доказательство | `code-review/SKILL.md`, таблица тем |
|
||||||
|
| разметка действия | инлайн · развилка | `code-review/SKILL.md`, «Что происходит с находками» |
|
||||||
|
| категория документа | тема · источник темы · процессный | `canon/references/canon.md` |
|
||||||
|
| severity находки | `critical` `major` `minor` `nit` | `code-review/references/finding-contract.md` |
|
||||||
|
| коды выхода | 0 1 2 3 4 | здесь, ниже |
|
||||||
|
|
||||||
|
Две оси стоят домом **здесь**, и обе по одной причине: владельца у них нет.
|
||||||
|
Коды выхода делят все скрипты плагина и зовущие их скиллы, режим прогона —
|
||||||
|
конвейер, сценарий обслуживания и уставы вычитки.
|
||||||
|
|
||||||
|
## Что на что влияет
|
||||||
|
|
||||||
|
Клетка называет **место**, где связка описана; сама связка живёт там.
|
||||||
|
|
||||||
|
| Влияет | На что | Где описано |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| стадия проекта | что значит порядок строк беклога: зависимость или важность | `task-track/SKILL.md`, «Две стадии» |
|
||||||
|
| стадия проекта | сколько у беклога секций, как его пополняют, применим ли груминг | там же и `task-groom/SKILL.md`, «Груминг — операция доработки» |
|
||||||
|
| стадия проекта | глубину ревью — **не влияет, и это записано явно** | `task-track/SKILL.md`, «Тип записи» |
|
||||||
|
| стадия проекта | тип записи — **не влияет, и это записано явно** | `task-track/SKILL.md`, «Две стадии» |
|
||||||
|
| стадия проекта | как серьёзность находки ложится в список | `task-track/references/from-review.md` |
|
||||||
|
| стадия проекта | где сценарии кладут свой исход и чем закрывают переходное состояние | `code-resolve/references/research.md`, `task-track/references/adopt.md` |
|
||||||
|
| форма постановки | проверку готовности, кто называет тип, есть ли шаг закрытия | `code-resolve/SKILL.md`, «Постановка текстом» |
|
||||||
|
| форма постановки | сценарий и глубину ревью — **не влияет, и это записано явно** | там же: развилка у обеих форм общая |
|
||||||
|
| тип записи | сценарий — **предлагает**, подтверждает предмет работы | `task-track/SKILL.md`, «Тип записи» |
|
||||||
|
| тип записи | глубину ревью — **не влияет, и это записано явно** | там же |
|
||||||
|
| сценарий | режим прогона: обслуживание идёт без change | `code-resolve/references/maintain.md` |
|
||||||
|
| режим прогона | состав проходов и саму возможность запуска прохода | `code-review/SKILL.md`, «Прогон без change» |
|
||||||
|
| категория документа | заводит ли документ направление проверки | `canon.md`, «Три категории» |
|
||||||
|
| severity | что с находкой делают дальше | `code-review/SKILL.md`, «Что происходит с находками» |
|
||||||
|
| род правки | спрашивают ли человека перед письмом в документ | `doc-sync/SKILL.md`, «Два рода правок» |
|
||||||
|
| глубина темы | что проход делает с домом темы и какой потолок у находок | `code-review/SKILL.md`, таблица тем |
|
||||||
|
| разметка действия | чинится находка молча или уходит человеку вопросом | `code-review/SKILL.md`, «Что происходит с находками» |
|
||||||
|
| разметка действия | возвращается ли прогон на чекпоинт — **не задаёт**: возврат старше развилки и решается признаком «меняются ли дельта-спеки» | `code-resolve/references/solve.md`, шаг 5 |
|
||||||
|
| сценарий | какова доля отражения в синке: обслуживание двигает факты и потому спрашивает редко | `code-resolve/references/maintain.md`, шаг 5 |
|
||||||
|
|
||||||
|
**Четыре клетки пусты, и это сказано намеренно, а не забыто.**
|
||||||
|
|
||||||
|
**Категория документа × режим прогона.** На прогоне **по change** своя тема
|
||||||
|
проекта закрыта: `review-basics` — её приёмник, и запускается он тогда и только
|
||||||
|
тогда, когда такие темы у проекта есть. На прогоне **без change** план фиксирован
|
||||||
|
сценарием — `autotests`, `operations`, `conventions`, — и своих тем проекта в нём
|
||||||
|
нет. Значит, документ, заведённый проектом как тема, на обслуживании не смотрит
|
||||||
|
никто, и строкой это нигде не называется.
|
||||||
|
|
||||||
|
**Род правки × severity и × режим прогона.** Не влияет ни туда, ни обратно: род
|
||||||
|
правки — свойство того, что пишется в документ, и с находкой ревью он не
|
||||||
|
встречается. Находка, доехавшая до конвенции, меняет род не сама по себе, а тем,
|
||||||
|
что становится новой нормой, — и спрашивается тогда как всякое новое.
|
||||||
|
|
||||||
|
**Стадия проекта × режим прогона.** Не влияет: режим выбирает сценарий. Прогон
|
||||||
|
обслуживания на стройке — обычное дело (первые шаги плана заводят гейт и сборку),
|
||||||
|
и идёт он там так же, как на доработке.
|
||||||
|
|
||||||
|
**Стадия проекта × категория документа, × коды выхода и × форма постановки.** Не
|
||||||
|
влияет: категория — свойство документа, коды — общий словарь скриптов, а форму
|
||||||
|
постановки выбирает тот, кто зовёт скилл, и на стройке она такая же, как на
|
||||||
|
доработке. Названо потому, что перечень объявлен полным, и клетка без ответа
|
||||||
|
читается как забытая.
|
||||||
|
|
||||||
|
**Режим прогона × severity.** Триаж обязателен всегда, в том числе без change. Но
|
||||||
|
часть оснований `critical` — построенный путь к отказу, замер — добывается только
|
||||||
|
скиллом `av-dev:code-deep-review`, а в цикле задачи не добывается ни на одном
|
||||||
|
прогоне. Значит ли это, что `critical` там не бывает вовсе, или что его основания
|
||||||
|
другие, не сказано.
|
||||||
|
|
||||||
|
## Режим прогона
|
||||||
|
|
||||||
|
<!-- дом: режим-прогона -->
|
||||||
|
|
||||||
|
**Прогон ревью идёт в одном из двух режимов, и режим — не глубина.**
|
||||||
|
|
||||||
|
- **По change** — обычный прогон цикла задачи: есть дифф и дельта-спеки, состав
|
||||||
|
постоянный и живёт в конвейере.
|
||||||
|
- **Без change** — дельта-спек нет по построению, и вместе с ними нет темы
|
||||||
|
`requirements`. План фиксирован и назван вызывающим; так идёт сценарий
|
||||||
|
обслуживания.
|
||||||
|
|
||||||
|
**Режим правит состав, а не глубину.** Глубина темы стоит в таблице тем
|
||||||
|
конвейера, одна на все прогоны по change; на прогоне без change её называет план
|
||||||
|
сценария — иначе проход, чьей темы в плане нет, взял бы глубину наугад.
|
||||||
|
|
||||||
|
**Третьего режима у конвейера нет.** Скилл `av-dev:code-deep-review` конвейер не
|
||||||
|
зовёт вовсе: состав, глубина и вход у него свои, а общее с конвейером — уставы
|
||||||
|
проходов и контракт находок.
|
||||||
|
|
||||||
|
<!-- /дом: режим-прогона -->
|
||||||
|
|
||||||
|
## Коды выхода
|
||||||
|
|
||||||
|
<!-- дом: коды-выхода -->
|
||||||
|
|
||||||
|
**Коды выхода — общий словарь всех скриптов `av-dev`. Ветвись на коде, а не на
|
||||||
|
тексте вывода.**
|
||||||
|
|
||||||
|
| Код | Что случилось |
|
||||||
|
| --- | --- |
|
||||||
|
| 0 | сошлось |
|
||||||
|
| 1 | дрейф: рабочая ситуация, чинится |
|
||||||
|
| 2 | ошибка употребления: аргументы или нарушенное правило |
|
||||||
|
| 3 | окружение: не тот каталог, битый конфиг, нет инструмента |
|
||||||
|
| 4 | внутренний сбой — дефект скрипта, доложить |
|
||||||
|
|
||||||
|
**Различать 1 и 3 обязательно.** «Дрейф» — рабочая ситуация, и чинится она
|
||||||
|
правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет.
|
||||||
|
Одинаковая реакция на них неверна в обоих случаях.
|
||||||
|
|
||||||
|
<!-- /дом: коды-выхода -->
|
||||||
|
|
||||||
|
Словарь был объявлен «общим» в одиннадцати местах, и каждое объявление
|
||||||
|
перечисляло **свой** набор соседей: «тот же, что у `tasks.py`», «тот же, что у
|
||||||
|
`tasks.py`, `docs.py` и `copies.py`», «общий словарь скриптов av-dev». Ни одно из
|
||||||
|
них не было домом, все — списки по памяти. Отсюда дом здесь: у словаря восемь
|
||||||
|
скриптов-потребителей и ни одного владельца.
|
||||||
@@ -0,0 +1,355 @@
|
|||||||
|
#!/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" # каталог задач от корня репозитория
|
||||||
|
stage = "build" # стадия проекта: build | support
|
||||||
|
items = "items" # имена частей каталога — необязательны
|
||||||
|
backlog = "BACKLOG.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
|
||||||
|
# скилла `canon`, повышает его операция `upgrade`.
|
||||||
|
VERSION = 5
|
||||||
|
|
||||||
|
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 set_section_key(root: Path, name: str, key: str, value: str) -> None:
|
||||||
|
"""Заменить значение ключа секции, не тронув остального.
|
||||||
|
|
||||||
|
Отличается от `merge_section` ровно тем, ради чего и заведена: та **не
|
||||||
|
трогает** ключ, который уже есть, потому что дописывает умолчания в чужой
|
||||||
|
файл. Здесь же значение меняет команда, которую позвал человек, и не
|
||||||
|
переписать его значило бы промолчать о выполненном действии. Ключа нет —
|
||||||
|
он дописывается, секции нет — заводится: и то и другое законное состояние
|
||||||
|
файла, который правят руками.
|
||||||
|
"""
|
||||||
|
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:
|
||||||
|
merge_section(root, name, {key: value})
|
||||||
|
return
|
||||||
|
end = next((i for i in range(start + 1, len(lines)) if _is_header(lines[i])),
|
||||||
|
len(lines))
|
||||||
|
pattern = re.compile(rf"^(\s*{re.escape(key)}\s*=\s*)([^#]*?)(\s*(?:#.*)?)$")
|
||||||
|
for i in range(start + 1, end):
|
||||||
|
if (match := pattern.match(lines[i])):
|
||||||
|
lines[i] = f"{match.group(1)}{quote(value)}{match.group(3)}"
|
||||||
|
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
return
|
||||||
|
merge_section(root, name, {key: value})
|
||||||
|
|
||||||
|
|
||||||
|
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'))}"]
|
||||||
|
if tasks.get("stage"):
|
||||||
|
out += ["# стадия проекта: build — беклог это план стройки, порядок строк"
|
||||||
|
" значит зависимость;",
|
||||||
|
"# support — беклог это очередь правок, порядок значит важность",
|
||||||
|
f"stage = {quote(tasks['stage'])}"]
|
||||||
|
for key in ("items", "backlog", "rejected"):
|
||||||
|
if tasks.get(key):
|
||||||
|
out.append(f"{key} = {quote(tasks[key])}")
|
||||||
|
return "\n".join(out) + "\n"
|
||||||
@@ -1,29 +1,38 @@
|
|||||||
# Язык проектных текстов
|
# Язык проектных текстов
|
||||||
|
|
||||||
**Это дом.** Файл не входит ни в один плагин: язык общий для документов канона и
|
**Это дом.** Файл не принадлежит ни одному скиллу: язык общий для документов
|
||||||
для задач, и хранить его внутри одного из них значило бы отдать общее правило во
|
канона, для задач и для решений ADR, и хранить его внутри одного из них значило
|
||||||
владение половине. Плагины везут **копии**, помеченные разметкой `copies.py`, и
|
бы отдать общее правило во владение части. Скиллы читают **этот файл** по
|
||||||
расхождение ловит гейт коммита, а не внимание.
|
ссылке; дословные **копии**, помеченные разметкой `copies.py`, уезжают только в
|
||||||
|
уставы вычитки — там текст обязан лежать внутри самого промпта, потому что
|
||||||
|
именно он и есть критерий суждения. Расхождение ловит гейт коммита, а не
|
||||||
|
внимание.
|
||||||
|
|
||||||
Правится **здесь**. Копия, поправленная у себя, — расхождение, а не правка.
|
Правится **здесь**. Копия, поправленная у себя, — расхождение, а не правка.
|
||||||
|
|
||||||
Три блока, и делятся они по потребителю, а не по теме:
|
Два блока копируются, и делятся они по потребителю, а не по теме:
|
||||||
|
|
||||||
| Блок | Что в нём | Кто копирует |
|
| Блок | Что в нём | Кто копирует |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `язык-доктрина` | зачем стиль нужен, что взято сверх устава, что отброшено | справочник языка в плагине |
|
| `язык-правила` | правила, по которым судят текст | уставы вычитки |
|
||||||
| `язык-правила` | девять правил, по которым судят текст | справочник и уставы вычитки |
|
| `порог-правки` | когда находка не заводится | уставы вычитки, `task-form` |
|
||||||
| `порог-правки` | когда находка не заводится | справочник, уставы вычитки, `task-form` |
|
|
||||||
|
|
||||||
`порог-правки` вынесен из правил намеренно: он нужен и тому, кто правил языка не
|
`порог-правки` вынесен из правил намеренно: он нужен и тому, кто правил языка не
|
||||||
проверяет вовсе, — а вложенных блоков разметка не знает, и внутри `язык-правила`
|
проверяет вовсе, — а вложенных блоков разметка не знает, и внутри `язык-правила`
|
||||||
его было бы не забрать отдельно.
|
его было бы не забрать отдельно.
|
||||||
|
|
||||||
<!-- дом: язык-доктрина -->
|
Правила — для всего, что пишется словами **в этом плагине**: задачи, документы
|
||||||
|
канона, решения ADR, записки разведки. Не для кода и не для сообщений программы
|
||||||
|
пользователю — там свои конвенции проекта.
|
||||||
|
|
||||||
Правила — для всего, что пишется словами: задачи и цели, документы канона,
|
**Сообщения коммитов сюда не входят, и это названо намеренно.** Их форму держит
|
||||||
решения ADR, записки разведки, сообщения коммитов. Не для кода и не для
|
отдельный плагин `av-dev-git`, скилл `commit`, и она с этими правилами
|
||||||
сообщений программы пользователю — там свои конвенции проекта.
|
расходится по существу: там предписан результат страдательным залогом
|
||||||
|
(«добавлены», «обновлено»), здесь — действие активным. Расхождение осознанное:
|
||||||
|
строка коммита отвечает на «что стало», а не на «что я сделал», и читают её в
|
||||||
|
`git log` подряд сотнями. Объявлять юрисдикцию над чужим плагином, до которого
|
||||||
|
отсюда нет и ссылки, значило бы завести правило, нарушение которого никто не
|
||||||
|
увидит.
|
||||||
|
|
||||||
Основа — **информационный стиль** Максима Ильяхова ([учебник
|
Основа — **информационный стиль** Максима Ильяхова ([учебник
|
||||||
бюро](https://bureau.ru/projects/book-text/), книга «Пиши, сокращай»). Он
|
бюро](https://bureau.ru/projects/book-text/), книга «Пиши, сокращай»). Он
|
||||||
@@ -38,6 +47,35 @@
|
|||||||
эстетическая: текст, из которого нельзя достать факт, заставляет открывать код,
|
эстетическая: текст, из которого нельзя достать факт, заставляет открывать код,
|
||||||
а это и есть цена, которой мы избегаем.
|
а это и есть цена, которой мы избегаем.
|
||||||
|
|
||||||
|
## Образец: научно-популярная книга
|
||||||
|
|
||||||
|
**Так, как пишут хорошую научно-популярную книгу.** Не спецификация, не статья в
|
||||||
|
блоге, не конспект для себя: текст, который объясняет устройство **точными
|
||||||
|
простыми словами** и понятен с первого прохода тому, кто эту систему не писал.
|
||||||
|
|
||||||
|
Из образца следуют три умолчания, и все три — про плотность, а не про красоту:
|
||||||
|
|
||||||
|
- **воды нет.** Каждая фраза несёт сведение: что устроено так, почему так и что
|
||||||
|
из этого следует. Абзац, из которого ничего нельзя достать, вычёркивается
|
||||||
|
целиком, а не переписывается;
|
||||||
|
- **сложных конструкций нет.** Причастный оборот внутри придаточного, три
|
||||||
|
отрицания подряд, предложение на пять строк — читатель разбирает такую фразу
|
||||||
|
дважды, и второй раз он её уже не разбирает. Причинную связь при этом не
|
||||||
|
режут: «поэтому», «иначе», «раз так» — сведения;
|
||||||
|
- **англицизм — исключение, требующее причины.** Умолчание обратное принятому в
|
||||||
|
разработке: пишем по-русски, а иностранное слово остаётся, только когда оно
|
||||||
|
**имя вещи** или когда русский аналог искажает смысл. Какая причина годится,
|
||||||
|
разбирает правило 5; закрытый список принятых слов — правило 6.
|
||||||
|
|
||||||
|
Термин здесь не запрещён — запрещена **перегрузка**: термин, который вводится
|
||||||
|
одной строкой, дешевле описания в три предложения, а термин, который
|
||||||
|
предполагается известным, дороже обоих (правило 8).
|
||||||
|
|
||||||
|
**Образец находок не порождает.** Он для того, кто пишет; вычитка судит по
|
||||||
|
правилам, и правка без нарушенного правила не делается (раздел «Порог правки»).
|
||||||
|
Иначе «мне кажется, звучит сложно» стало бы находкой, и список замечаний
|
||||||
|
перестали бы читать целиком.
|
||||||
|
|
||||||
## Что взято сверх правил вычитки
|
## Что взято сверх правил вычитки
|
||||||
|
|
||||||
Эти три требования судит человек, а не проход вычитки: находка по ним требует
|
Эти три требования судит человек, а не проход вычитки: находка по ним требует
|
||||||
@@ -81,8 +119,6 @@
|
|||||||
значит называть состояние и остаток, а не пересказывать, как было интересно
|
значит называть состояние и остаток, а не пересказывать, как было интересно
|
||||||
разбираться.
|
разбираться.
|
||||||
|
|
||||||
<!-- /дом: язык-доктрина -->
|
|
||||||
|
|
||||||
## Правила
|
## Правила
|
||||||
|
|
||||||
<!-- дом: язык-правила -->
|
<!-- дом: язык-правила -->
|
||||||
@@ -151,15 +187,15 @@
|
|||||||
|
|
||||||
| Термин | Что называет |
|
| Термин | Что называет |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| интейк | заведение записи с фильтром и дедупом: «заведение» называет создание файла, слить их — смешать две операции |
|
|
||||||
| триаж | стадия конвейера, сводящая находки в решение |
|
| триаж | стадия конвейера, сводящая находки в решение |
|
||||||
| провенанс | обязательное свойство числа: чем и при каких условиях получено. «Источник» рядом называет саму запись, а не свойство |
|
|
||||||
| дедуп, дедупликация | сверка нового против уже лежащего |
|
| дедуп, дедупликация | сверка нового против уже лежащего |
|
||||||
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
| чек-лист | перечень, по которому идут сверху вниз, называя исход каждой строки |
|
||||||
| дифф, `--base` | разница между состояниями в git |
|
| дифф, `--base` | разница между состояниями в git |
|
||||||
| промпт | текст, которым зовут модель |
|
| промпт | текст, которым зовут модель |
|
||||||
| change, capability, spec | сущности OpenSpec, имена вещей чужого инструмента |
|
| change, capability, spec | сущности OpenSpec, имена вещей чужого инструмента |
|
||||||
| generative, applicative | роды проходов ревью, вводятся определением по месту |
|
| generative, applicative | роды проходов ревью, вводятся определением по месту |
|
||||||
|
| чекпоинт | плановый стоп работы, на котором ждут ответа человека. «Остановка» называет любой перерыв, «согласование» — обряд одобрения, а здесь место в процессе, назначенное заранее |
|
||||||
|
| синк | сверка каждого документа канона с только что сделанной работой, с обязательным отрицанием по нетронутым. «Обновление документации» называет исход, а не работу, и молчит о принуждённом отрицании |
|
||||||
|
|
||||||
**Список закрыт.** Слово не отсюда и не из таблицы имён вещей выше — находка,
|
**Список закрыт.** Слово не отсюда и не из таблицы имён вещей выше — находка,
|
||||||
а не «принятый стиль»: у него либо есть живой русский аналог, либо оно
|
а не «принятый стиль»: у него либо есть живой русский аналог, либо оно
|
||||||
@@ -168,9 +204,26 @@
|
|||||||
Отсюда же читается снятое. Эти слова из текстов убраны, и возвращать их не
|
Отсюда же читается снятое. Эти слова из текстов убраны, и возвращать их не
|
||||||
надо: **конфляция** (смешение), **декорреляция** (разведённость, разведён с
|
надо: **конфляция** (смешение), **декорреляция** (разведённость, разведён с
|
||||||
кем-то), **непоймание** (почему не поймали), **эвал-сет** (проверочный
|
кем-то), **непоймание** (почему не поймали), **эвал-сет** (проверочный
|
||||||
набор), **гайд** (руководство). Каждое было латинизмом или калькой при живом
|
набор), **гайд** (руководство), **опиниативный** (проход с мнением),
|
||||||
русском слове, и каждое к моменту снятия жило в трёх-шести файлах разом — то
|
**провенанс** (происхождение числа: чем и при каких условиях получено),
|
||||||
есть выглядело словарём, не будучи им.
|
**интейк** (заведение записей — из диалога, из ревью: операция зовётся своим
|
||||||
|
источником). Каждое было латинизмом или калькой при живом русском слове, и
|
||||||
|
каждое к моменту снятия жило в трёх-шести файлах разом — то есть выглядело
|
||||||
|
словарём, не будучи им.
|
||||||
|
|
||||||
|
**Провенанс и интейк сняты из самого словаря, и это прецедент.** Оба стояли в
|
||||||
|
нём с оговоркой, и обе оговорки были верны, но доказывали меньше, чем от них
|
||||||
|
брали.
|
||||||
|
|
||||||
|
| Слово | Чем защищалось | Чем заменено |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| провенанс | «источник» рядом называет саму запись, а не свойство | **происхождение** у числа, **откуда** у вопроса и находки: смысла было два, и это же и держало латинизм |
|
||||||
|
| интейк | «заведение» называет создание файла, а не отбор с дедупом | **заведение с названным источником** — «из диалога», «из ревью»: так операция и называется в самом скилле задач |
|
||||||
|
|
||||||
|
Общее у обоих: оговорка отвергала **один** русский вариант, а вывод делался
|
||||||
|
про все. **Латинизм, переживший проверку одним синонимом, — не имя вещи, а
|
||||||
|
непроверенная привычка**, и запись в словаре обязана говорить, чем слово
|
||||||
|
незаменимо, а не чем плох один из кандидатов.
|
||||||
|
|
||||||
7. **Жаргон и метафоры заменяются прямым называнием.** Автору образ понятен,
|
7. **Жаргон и метафоры заменяются прямым называнием.** Автору образ понятен,
|
||||||
читателю — нет.
|
читателю — нет.
|
||||||
@@ -202,6 +255,34 @@
|
|||||||
коммитах и путях, которые набирают руками. Переименование — **перенос ссылок
|
коммитах и путях, которые набирают руками. Переименование — **перенос ссылок
|
||||||
одним проходом**, а не правка одного файла.
|
одним проходом**, а не правка одного файла.
|
||||||
|
|
||||||
|
10. **Счёт корпуса не пишется словами.** «Пять ревью», «три capability»,
|
||||||
|
«десять проходов», «четыре документа канона» — это факт о корпусе, а дом у
|
||||||
|
такого факта сам корпус. Переписанный в прозу, он расходится с ним на первом
|
||||||
|
же пополнении, и расходится **молча**: фраза остаётся грамматически исправной
|
||||||
|
и правдоподобной, а проверить её можно только пересчётом, которого никто не
|
||||||
|
делает.
|
||||||
|
|
||||||
|
Сослаться можно двумя способами, и ни один не стареет:
|
||||||
|
|
||||||
|
| Как | Пример |
|
||||||
|
| --- | --- |
|
||||||
|
| на конкретную запись — именем, слагом, датой | «ревью от 3 августа», `adr/0007-queue-as-table.md`, capability `recognition` |
|
||||||
|
| на корпус целиком | «ревью проекта», «capability, объявленные в `openspec/specs/`» |
|
||||||
|
|
||||||
|
Величина нужна читателю редко, а когда нужна — её называет сам корпус в
|
||||||
|
момент чтения: каталог, индекс, команда. Абзац её только запоминает.
|
||||||
|
|
||||||
|
**Замер с названным происхождением — не счёт корпуса.** «Прозаический триггер дал 6
|
||||||
|
записей ADR на 43 изменения» описывает прошлое, а прошлое не пополняется: это
|
||||||
|
факт по правилу 2, и трогать его нельзя. Разница проверяется вопросом
|
||||||
|
«изменится ли число само, без правки текста».
|
||||||
|
|
||||||
|
**Число, стоящее заголовком к перечню, приведённому тут же, правилом не
|
||||||
|
задето.** «Три исхода:» с тремя пунктами под ним — не ссылка на корпус, и
|
||||||
|
расходится оно не втихую, а вместе со списком, который правят в той же
|
||||||
|
строке. Уехал перечень в другой файл — число уезжает с ним, а на его месте
|
||||||
|
остаётся ссылка.
|
||||||
|
|
||||||
<!-- /дом: язык-правила -->
|
<!-- /дом: язык-правила -->
|
||||||
|
|
||||||
## Порог правки
|
## Порог правки
|
||||||
@@ -228,8 +309,8 @@
|
|||||||
## Доклад вычитки
|
## Доклад вычитки
|
||||||
|
|
||||||
Не правило языка, а **контракт прохода**: форма, в которой находка приходит к
|
Не правило языка, а **контракт прохода**: форма, в которой находка приходит к
|
||||||
человеку. Живёт здесь потому, что проходов вычитки несколько — по одному на
|
человеку. Живёт здесь потому, что проходов вычитки два — `doc-wording` по документам и
|
||||||
плагин, — и разойтись формой они не должны.
|
`task-wording` по записям задач, — и разойтись формой они не должны.
|
||||||
|
|
||||||
<!-- дом: вычитка-доклад -->
|
<!-- дом: вычитка-доклад -->
|
||||||
|
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
# Сопровождение и эксплуатация
|
# Сопровождение и эксплуатация
|
||||||
|
|
||||||
**Это дом.** Словарь «чем держат проект» назван в трёх местах трёх разных
|
**Это дом.** Словарь «чем держат проект» назван в трёх местах трёх разных
|
||||||
плагинов: секция `Сопровождение` в роадмапе (`av-dev-tasks`), раздел
|
скиллов: задачи типа `chore` (`task-track`), раздел «Эксплуатация»
|
||||||
«Эксплуатация» в `architecture.md` (`av-dev-docs`) и тема ревью `operations`
|
в `architecture.md` (`canon`) и тема ревью `operations` (`code-review`). Ни
|
||||||
(`av-dev-code`). Ни один из трёх им не владеет, поэтому дом стоит снаружи, а
|
один из трёх им не владеет, поэтому дом стоит в `shared/`.
|
||||||
плагины везут копии.
|
|
||||||
|
|
||||||
Три перечня «чем держат проект» уже разъезжались — на «метриках и логах» против
|
Три перечня «чем держат проект» уже разъезжались — на «метриках и логах» против
|
||||||
«мониторинга», — и разъехались молча. Отсюда дословная копия вместо ссылки.
|
«мониторинга», — и разъехались молча. Пока скиллы жили тремя плагинами, отсюда
|
||||||
|
уезжали дословные копии: путь в чужое дерево не разрешался. Теперь дерево одно —
|
||||||
<!-- дом: сопровождение-словарь -->
|
кому словарь нужен, тот открывает **этот файл**, и сверять машиной больше нечего.
|
||||||
|
|
||||||
Одна тема живёт в трёх местах, и путать их слова нельзя.
|
Одна тема живёт в трёх местах, и путать их слова нельзя.
|
||||||
|
|
||||||
@@ -19,17 +18,18 @@
|
|||||||
|
|
||||||
| Место | Уровень | Что там |
|
| Место | Уровень | Что там |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `ROADMAP.md`, секция `Сопровождение` | план | **работы**, которые собираемся делать: цели и их задачи |
|
| `BACKLOG.md`, задачи `chore` | план | **работы**, которые собираемся делать |
|
||||||
| `architecture.md`, раздел «Эксплуатация» | состояние | **как устроено сейчас**: где работает, что рядом, кто перезапускает |
|
| `architecture.md`, раздел «Эксплуатация» | состояние | **как устроено сейчас**: где работает, что рядом, кто перезапускает |
|
||||||
| тема ревью `operations` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
| тема ревью `operations` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
||||||
|
|
||||||
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
||||||
пользователю, а это другая работа.
|
пользователю, а это другая работа. По той же причине им не названа и **стадия
|
||||||
|
проекта**: у неё имя «доработка» (`support`), словарь — `task-track`, «Две
|
||||||
|
стадии».
|
||||||
|
|
||||||
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
||||||
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
||||||
целей: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
задач: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
||||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в задачи
|
||||||
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
разных типов, и это верно — типы отвечают на разные вопросы.
|
||||||
|
|
||||||
<!-- /дом: сопровождение-словарь -->
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: canon
|
name: 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) в канон с переносом файлов и вызовом владельцев каталога задач и openspec/; upgrade — повышение проекта с версии N до текущей по журналу версий, и повышается им вся раскладка, включая каталог задач. Использовать, когда просят проверить документацию проекта, перевести проект на канон, обновить его под новую версию раскладки или когда пришли в старый проект и надо понять, что в нём не так. Имя без префикса намеренно — скилл держит форму всех артефактов проекта, а не один их вид. Содержимое документов ведёт av-dev:doc-sync, форму записей задач — av-dev:task-track, заведение проекта с нуля — av-dev:doc-init.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Приведение проекта к канону
|
# Форма раскладки проекта
|
||||||
|
|
||||||
Три операции, одна машина сравнения с разными исходами:
|
Три операции, одна машина сравнения с разными исходами:
|
||||||
|
|
||||||
@@ -13,6 +13,14 @@ description: Привести проект к канону документов
|
|||||||
| `adopt` | проект в чужой раскладке | перенос в канон |
|
| `adopt` | проект в чужой раскладке | перенос в канон |
|
||||||
| `upgrade` | канон вырос, проект отстал | по журналу версий |
|
| `upgrade` | канон вырос, проект отстал | по журналу версий |
|
||||||
|
|
||||||
|
**Имя без префикса, и это не случайность.** Остальные скиллы названы по
|
||||||
|
материалу, с которым работают, — `doc-`, `task-`, `code-`; этот работает не с
|
||||||
|
материалом, а с **формой**, и она у всех частей проекта одна. `check` сверяет
|
||||||
|
раскладку документов, `adopt` заводит все части сразу и зовёт владельцев каталога
|
||||||
|
задач и `openspec/`, `upgrade` повышает **всю** раскладку одним журналом версий —
|
||||||
|
и документы, и каталог задач. Содержимое при этом не его: документы ведёт
|
||||||
|
`av-dev:doc-sync`, записи задач — `av-dev:task-track`.
|
||||||
|
|
||||||
**Определение канона — [references/canon.md](references/canon.md).** Здесь оно не
|
**Определение канона — [references/canon.md](references/canon.md).** Здесь оно не
|
||||||
пересказывается: два описания одной раскладки разъедутся, и работать будет то,
|
пересказывается: два описания одной раскладки разъедутся, и работать будет то,
|
||||||
которое прочитали последним. Прочитай его **до** первой правки.
|
которое прочитали последним. Прочитай его **до** первой правки.
|
||||||
@@ -20,14 +28,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) — журнал версий раскладки;
|
||||||
|
закрытые журналы до слияния плагинов лежат рядом.
|
||||||
|
|
||||||
## Три правила, из которых всё следует
|
## Три правила, из которых всё следует
|
||||||
|
|
||||||
@@ -48,20 +56,40 @@ description: Привести проект к канону документов
|
|||||||
ds="$CLAUDE_PLUGIN_ROOT/skills/canon/scripts/docs.py"
|
ds="$CLAUDE_PLUGIN_ROOT/skills/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/` нет — форму
|
||||||
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
|
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
|
||||||
верна.
|
верна.
|
||||||
|
|
||||||
**Коды выхода — тот же словарь, что у `tasks.py`:** 0 сошлось, 1 дрейф, 2 ошибка
|
**Копия.** Дом словаря — `shared/axes.md` в репозитории плагина: словарь общий
|
||||||
употребления, 3 окружение, 4 внутренний сбой. Ветвись на коде, а не на тексте.
|
для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл.
|
||||||
|
|
||||||
Различать 1 и 3 обязательно: «дрейф раскладки» — рабочая ситуация, «это не
|
<!-- копия: коды-выхода из av-dev/shared/axes.md -->
|
||||||
корень проекта» — нерабочая.
|
|
||||||
|
**Коды выхода — общий словарь всех скриптов `av-dev`. Ветвись на коде, а не на
|
||||||
|
тексте вывода.**
|
||||||
|
|
||||||
|
| Код | Что случилось |
|
||||||
|
| --- | --- |
|
||||||
|
| 0 | сошлось |
|
||||||
|
| 1 | дрейф: рабочая ситуация, чинится |
|
||||||
|
| 2 | ошибка употребления: аргументы или нарушенное правило |
|
||||||
|
| 3 | окружение: не тот каталог, битый конфиг, нет инструмента |
|
||||||
|
| 4 | внутренний сбой — дефект скрипта, доложить |
|
||||||
|
|
||||||
|
**Различать 1 и 3 обязательно.** «Дрейф» — рабочая ситуация, и чинится она
|
||||||
|
правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет.
|
||||||
|
Одинаковая реакция на них неверна в обоих случаях.
|
||||||
|
|
||||||
|
<!-- /копия: коды-выхода -->
|
||||||
|
|
||||||
|
Здесь это значит: «дрейф раскладки» — рабочая ситуация, «это не корень проекта» —
|
||||||
|
нерабочая.
|
||||||
|
|
||||||
### Граница механизируемого — объявляется вслух
|
### Граница механизируемого — объявляется вслух
|
||||||
|
|
||||||
@@ -80,48 +108,54 @@ capability: незаполненный канон это переходное с
|
|||||||
|
|
||||||
| Агент | Что смотрит | Читает |
|
| Агент | Что смотрит | Читает |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `doc-consistency` | смысловой дубль, прямое противоречие между документами, поведение в `architecture.md` вместо спек, ADR без ссылки и парного статуса, число без провенанса, заглушка вместо честной строки | `docs/`, `openspec/` |
|
| `doc-consistency` | смысловой дубль, прямое противоречие между документами, поведение в `architecture.md` вместо спек, ADR без ссылки и парного статуса, число без происхождения, заглушка вместо честной строки | `docs/`, `openspec/` |
|
||||||
| `doc-code-drift` | протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки проекта, capability | весь репозиторий |
|
| `doc-code-drift` | протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки проекта, 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: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 +164,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 +212,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 +239,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 +253,13 @@ capability), `openspec/config.yaml`.
|
|||||||
отказ. Пересчитай эти пункты в докладе переходного состояния — не выдавай
|
отказ. Пересчитай эти пункты в докладе переходного состояния — не выдавай
|
||||||
их за поломку и не молчи о них.
|
их за поломку и не молчи о них.
|
||||||
|
|
||||||
**Задачи `docs.py` не проверяет** — их ведёт другой плагин, и согласованность
|
**Задачи `docs.py` не проверяет** — их ведёт другой скилл, и согласованность
|
||||||
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
|
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
|
||||||
его отчёт идёт в доклад отдельной строкой, и пункт «задачи без цели» в нём
|
его отчёт идёт в доклад отдельной строкой, и зелёным он сразу не станет:
|
||||||
зелёным не станет: цели не сочиняются адаптацией (запрет записан у того, кто
|
у перенесённых записей нет критериев приёмки, а `check` без объявленной
|
||||||
ведёт задачи), их проставляет человек порциями переоценки на первом груминге —
|
**стадии** отказывает вовсе. Стадию называет человек (`tasks.py stage
|
||||||
скилл `av-dev-tasks:groom`.
|
build|support`) — машина её не выводит: список пунктов одинаково выглядит и
|
||||||
|
планом стройки, и очередью правок.
|
||||||
|
|
||||||
### 5. Объяви переходное состояние
|
### 5. Объяви переходное состояние
|
||||||
|
|
||||||
@@ -241,7 +278,7 @@ capability), `openspec/config.yaml`.
|
|||||||
проекте обычно самый урожайный — правило единственного дома до адаптации никто не
|
проекте обычно самый урожайный — правило единственного дома до адаптации никто не
|
||||||
проверял.
|
проверял.
|
||||||
|
|
||||||
Вызови Skill **`av-dev-docs:healthcheck`** — он зовёт обоих судей на весь канон
|
Вызови Skill **`av-dev:doc-healthcheck`** — он зовёт обоих судей на весь канон
|
||||||
разом и держит разбор урожая порциями.
|
разом и держит разбор урожая порциями.
|
||||||
|
|
||||||
**Передай им объявленное переходное состояние из шага 5** — иначе честная строка
|
**Передай им объявленное переходное состояние из шага 5** — иначе честная строка
|
||||||
@@ -267,9 +304,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 +317,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 +341,8 @@ capability), `openspec/config.yaml`.
|
|||||||
хуже отсутствующего: по нему будут строиться находки.
|
хуже отсутствующего: по нему будут строиться находки.
|
||||||
- **Не удаляет то, чьё содержимое не нашло дом.** Оригинал живёт, пока не
|
- **Не удаляет то, чьё содержимое не нашло дом.** Оригинал живёт, пока не
|
||||||
названо поимённо, куда переехал каждый его кусок.
|
названо поимённо, куда переехал каждый его кусок.
|
||||||
- **Не ведёт содержимое канона** — это скилл `docs`. Здесь только раскладка.
|
- **Не ведёт содержимое канона** — это скилл `doc-sync`. Здесь только раскладка.
|
||||||
- **Не заводит проект с нуля** — это скилл `init`.
|
- **Не заводит проект с нуля** — это скилл `doc-init`.
|
||||||
- **Не правит историю.** В старых коммитах старые пути остаются, и это нормально.
|
- **Не правит историю.** В старых коммитах старые пути остаются, и это нормально.
|
||||||
|
|
||||||
## Доклад
|
## Доклад
|
||||||
+153
-134
@@ -6,7 +6,7 @@
|
|||||||
[журнала](changelog.md). Литерал в шапке был третьим и отстал на первом же
|
[журнала](changelog.md). Литерал в шапке был третьим и отстал на первом же
|
||||||
повышении — версию 13 он пережил, объявляя канон двенадцатым.
|
повышении — версию 13 он пережил, объявляя канон двенадцатым.
|
||||||
|
|
||||||
Это **единственный дом определения канона**. Скиллы `init`, `canon` и `docs`
|
Это **единственный дом определения канона**. Скиллы `doc-init`, `canon` и `doc-sync`
|
||||||
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
||||||
работать будет то, которое прочитали последним. Меняется канон — меняется этот
|
работать будет то, которое прочитали последним. Меняется канон — меняется этот
|
||||||
файл и появляется запись в [changelog.md](changelog.md).
|
файл и появляется запись в [changelog.md](changelog.md).
|
||||||
@@ -23,39 +23,16 @@
|
|||||||
|
|
||||||
Раскладка отвечает, **где** текст лежит и на какой вопрос отвечает. Каким он
|
Раскладка отвечает, **где** текст лежит и на какой вопрос отвечает. Каким он
|
||||||
должен быть **словами** — общий для всех документов канона файл
|
должен быть **словами** — общий для всех документов канона файл
|
||||||
[language.md](language.md): информационный стиль, англицизмы, жаргон. Он
|
[shared/language.md](../../../shared/language.md): информационный стиль, англицизмы, жаргон. Он
|
||||||
относится и к задачам, и к решениям ADR, и к запискам разведки.
|
относится и к задачам, и к решениям ADR, и к запискам разведки.
|
||||||
|
|
||||||
## Сопровождение и эксплуатация — целое и часть
|
## Сопровождение и эксплуатация — целое и часть
|
||||||
|
|
||||||
**Копия.** Дом — `shared/operations.md` в репозитории плагинов: словарь
|
Словарь этой темы — [shared/operations.md](../../../shared/operations.md):
|
||||||
делят роадмап, архитектура и тема ревью `operations`, то есть три плагина,
|
целое и часть, три места одной темы (задачи `chore`, раздел архитектуры, тема
|
||||||
и ни один из трёх им не владеет. Правится дом, а не этот файл.
|
ревью `operations`) и граница с возможностями проекта. Здесь он не
|
||||||
|
пересказывается: копия жила рядом с домом в одном дереве и была ровно тем
|
||||||
<!-- копия: сопровождение-словарь из shared/operations.md -->
|
вторым домом, против которого правило и написано.
|
||||||
|
|
||||||
Одна тема живёт в трёх местах, и путать их слова нельзя.
|
|
||||||
|
|
||||||
**Сопровождение** — всё, чем держат проект: инструмент и сборка, процесс,
|
|
||||||
выкладка, метрики и логи, инфраструктура, дежурство. **Эксплуатация** — его
|
|
||||||
часть: работа системы на проде. Целое и часть, и никогда наоборот.
|
|
||||||
|
|
||||||
| Место | Уровень | Что там |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `ROADMAP.md`, секция `Сопровождение` | план | **работы**, которые собираемся делать: цели и их задачи |
|
|
||||||
| `architecture.md`, раздел «Эксплуатация» | состояние | **как устроено сейчас**: где работает, что рядом, кто перезапускает |
|
|
||||||
| тема ревью `operations` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
|
||||||
|
|
||||||
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
|
||||||
пользователю, а это другая работа.
|
|
||||||
|
|
||||||
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
|
||||||
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
|
||||||
целей: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
|
||||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
|
||||||
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
|
||||||
|
|
||||||
<!-- /копия: сопровождение-словарь -->
|
|
||||||
|
|
||||||
## Раскладка
|
## Раскладка
|
||||||
|
|
||||||
@@ -68,18 +45,19 @@
|
|||||||
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/ схема хранилища; представление данных и настройки
|
||||||
security.md | security/ периметр; недоверенный вход; что вне модели
|
security.md | security/ периметр; недоверенный вход; что вне модели
|
||||||
conventions.md | conventions/ как пишем код; что механизировано
|
conventions.md | conventions/ как пишем код; что механизировано
|
||||||
research.md | research/ наблюдения и числа с провенансом
|
research.md | research/ наблюдения и числа с происхождением
|
||||||
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 только нужды генерации артефактов + ссылки
|
||||||
@@ -93,10 +71,13 @@ openspec/
|
|||||||
|
|
||||||
## Три категории документов
|
## Три категории документов
|
||||||
|
|
||||||
|
Категория документа — ось процесса; перечень осей и того, чего каждая **не**
|
||||||
|
решает, — [shared/axes.md](../../../shared/axes.md).
|
||||||
|
|
||||||
Раньше здесь стояло плоское правило «каждый документ `docs/` — тема ревью». Оно
|
Раньше здесь стояло плоское правило «каждый документ `docs/` — тема ревью». Оно
|
||||||
неверно ровно наполовину: паспорт и схема хранилища ревью нужны, но темами не
|
неверно ровно наполовину: паспорт и схема хранилища ревью нужны, но темами не
|
||||||
являются, а журнал решений и журнал наблюдений ревью изменения не нужны вовсе.
|
являются, а журнал решений и журнал наблюдений ревью изменения не нужны вовсе.
|
||||||
Плоское правило заставляло разметчика либо плодить фантомные темы, либо терять
|
Плоское правило заставляло прогон либо плодить фантомные темы, либо терять
|
||||||
документы молча — а молчащая потеря и есть то, против чего канон написан.
|
документы молча — а молчащая потеря и есть то, против чего канон написан.
|
||||||
|
|
||||||
**Разрез один и проверяемый: можно ли по документу сказать «в этом изменении
|
**Разрез один и проверяемый: можно ли по документу сказать «в этом изменении
|
||||||
@@ -119,11 +100,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` | процессный | — (служебный файл, не документ) |
|
||||||
|
|
||||||
**Список тем открытый, и это не послабление, а механизм.** Категории
|
**Список тем открытый, и это не послабление, а механизм.** Категории
|
||||||
`источник` и `процессный` **закрыты** — они перечислены здесь поимённо и
|
`источник` и `процессный` **закрыты** — они перечислены здесь поимённо и
|
||||||
@@ -146,7 +127,7 @@ openspec/
|
|||||||
**Процессный документ — не документ второго сорта.** `adr/` и `research/`
|
**Процессный документ — не документ второго сорта.** `adr/` и `research/`
|
||||||
проверяются наравне с остальными, но **сверкой документации**, а не прогоном
|
проверяются наравне с остальными, но **сверкой документации**, а не прогоном
|
||||||
ревью: ADR без ссылки на источник, замена без парного статуса, число
|
ревью: ADR без ссылки на источник, замена без парного статуса, число
|
||||||
без провенанса — это работа агентов `doc-consistency` и `doc-code-drift`, и она
|
без происхождения — это работа агентов `doc-consistency` и `doc-code-drift`, и она
|
||||||
осталась там же, где была. Изменилось одно: прогон ревью не открывает их как
|
осталась там же, где была. Изменилось одно: прогон ревью не открывает их как
|
||||||
критерий и не судит по ним изменение.
|
критерий и не судит по ним изменение.
|
||||||
|
|
||||||
@@ -186,19 +167,17 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
|
|
||||||
Одна строка на каждый — на какой вопрос он отвечает, в какой он категории и
|
Одна строка на каждый — на какой вопрос он отвечает, в какой он категории и
|
||||||
какую тему питает. **Кто именно закрывает тему, здесь не указано намеренно**: это
|
какую тему питает. **Кто именно закрывает тему, здесь не указано намеренно**: это
|
||||||
зависит от метки прогона и меняется вместе с конвейером, а документ живёт
|
меняется вместе с конвейером, а документ живёт дольше. Раскладку «тема → кто
|
||||||
дольше. Раскладку «тема → проход → глубина» держит скилл
|
закрывает → против чего» держит скилл `av-dev:code-review`.
|
||||||
`av-dev-code:review`.
|
|
||||||
|
|
||||||
**Общего словаря у канона с конвейером ровно три вида имён: имена категорий,
|
**Общего словаря у канона с конвейером два вида имён: имена категорий и имена
|
||||||
имена тем и имена меток.** Категорий три — `тема`, `источник`, `процессный`;
|
тем.** Категорий три — `тема`, `источник`, `процессный`; список тем открытый, и
|
||||||
**меток тоже три, и они закрыты: `small`, `medium`, `large`.** Метка это итог
|
пополняет его сам проект своим документом. Настраивает проект ревью двумя вещами:
|
||||||
классификации задачи, и по ней конвейер выбирает исполнителей на обеих стадиях
|
вопросами по темам и признаками, по которым зовут глубокое ревью. **Имён проходов
|
||||||
ревью; проект её не выдумывает, а только уточняет триггеры. Ими проект и
|
канон не называет нигде**, включая вывод `docs.py`: проход переименовывается и
|
||||||
настраивает ревью — вопросами по темам и триггерами метки. **Имён проходов канон не называет нигде**, включая вывод `docs.py`:
|
переезжает в другой скилл, и канон, назвавший его, в этот день соврёт молча.
|
||||||
проход переименовывается и переезжает между метками, и канон, назвавший его, в
|
Обратное направление законно — конвейер называет документы канона поимённо,
|
||||||
этот день соврёт молча. Обратное направление законно — конвейер называет
|
потому что он их читатель.
|
||||||
документы канона поимённо, потому что он их читатель.
|
|
||||||
|
|
||||||
| Документ | Вопрос | Категория и тема |
|
| Документ | Вопрос | Категория и тема |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
@@ -286,7 +265,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
|
|
||||||
Наблюдения за внешним миром: что реально шлёт источник, чем документация формата
|
Наблюдения за внешним миром: что реально шлёт источник, чем документация формата
|
||||||
расходится с практикой, какие числа сняты с живого потока. **Числа — с
|
расходится с практикой, какие числа сняты с живого потока. **Числа — с
|
||||||
провенансом**, то есть с командой или условиями, которыми получены.
|
происхождением**, то есть с командой или условиями, которыми получены.
|
||||||
`README.md` — как снималось и индекс тем.
|
`README.md` — как снималось и индекс тем.
|
||||||
|
|
||||||
Число без источника проход обязан читать как условие, а не как замер. Число, чей
|
Число без источника проход обязан читать как условие, а не как замер. Число, чей
|
||||||
@@ -295,8 +274,8 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
|
|
||||||
### `adr/`
|
### `adr/`
|
||||||
|
|
||||||
**ADR — промоут поверх уже написанного, а не второе сочинение.** Запись цитирует
|
**ADR продвигает уже написанное решение, а не сочиняет его заново.** Запись
|
||||||
решение и ссылается на источник. Источников два, и оба законны:
|
цитирует решение и ссылается на источник. Источников два, и оба законны:
|
||||||
|
|
||||||
- **архивный `design.md`** — решение принято по ходу изменения:
|
- **архивный `design.md`** — решение принято по ходу изменения:
|
||||||
`openspec/changes/archive/<id>/design.md`. Обычный случай;
|
`openspec/changes/archive/<id>/design.md`. Обычный случай;
|
||||||
@@ -316,6 +295,11 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
«заменено на».
|
«заменено на».
|
||||||
<!-- /дом: adr-когда-заводить -->
|
<!-- /дом: adr-когда-заводить -->
|
||||||
|
|
||||||
|
**Сработавший триггер даёт предложение, а не запись.** Заводит ADR человек своим
|
||||||
|
словом — правило и его причина в скилле `av-dev:doc-sync`, раздел «Два рода
|
||||||
|
правок». Канон здесь отвечает за другое: за то, при каких условиях предлагать
|
||||||
|
вообще есть что.
|
||||||
|
|
||||||
Не заводится для рутины и для того, что видно из кода и `git log`.
|
Не заводится для рутины и для того, что видно из кода и `git log`.
|
||||||
|
|
||||||
Записи неизменяемы: передумали — заводится новая, старая получает статус.
|
Записи неизменяемы: передумали — заводится новая, старая получает статус.
|
||||||
@@ -337,76 +321,76 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
- **Типовые узлы** — рода узлов проекта и 3–5 проверяемых свойств к каждому;
|
- **Типовые узлы** — рода узлов проекта и 3–5 проверяемых свойств к каждому;
|
||||||
- **Типовые ложноположительные** — находки, которые здесь выглядят убедительно и
|
- **Типовые ложноположительные** — находки, которые здесь выглядят убедительно и
|
||||||
всегда неверны, каждая со строкой «почему здесь это не дефект»;
|
всегда неверны, каждая со строкой «почему здесь это не дефект»;
|
||||||
- **Вопросы по темам** — в форме `<тема>: <вопрос> (<провенанс>)`. **Не по именам
|
- **Вопросы по темам** — в форме `<тема>: <вопрос> (<откуда>)`. **Не по именам
|
||||||
проходов**: проход уезжает между метками, а тема остаётся, и вопрос,
|
проходов**: проход уезжает в другой скилл, а тема остаётся, и вопрос,
|
||||||
адресованный проходу, перестал бы задаваться молча в тот день, когда тот уехал
|
адресованный проходу, перестал бы задаваться молча в тот день, когда тот уехал.
|
||||||
в старшую метку. Задаёт вопрос тот, кто закрывает тему на этом прогоне.
|
Задаёт вопрос тот, кто закрывает тему на этом прогоне.
|
||||||
Адресовать можно только теме: `passport`, `database`, `adr`, `research` и
|
Адресовать можно только теме: `passport`, `database`, `adr`, `research` и
|
||||||
`review` — не темы, и вопрос, адресованный им, не задаст никто;
|
`review` — не темы, и вопрос, адресованный им, не задаст никто;
|
||||||
- **Триггеры метки** — проектная конкретизация правила выбора метки ревью,
|
- **Когда звать глубокое ревью** — проектная конкретизация признаков, по которым
|
||||||
**тремя списками**. Два поднимают, по одному на ось: что в этом проекте считается
|
зовут `av-dev:code-deep-review`, **двумя списками**: области, которые смотрят
|
||||||
**крупным** (объём: сколько узлов и слоёв трогает) и что считается
|
целиком (узлы с частым возвратом, места с историей инцидентов, код под дорогое
|
||||||
**незнакомым** (форма решения: известна до начала или нащупывается по ходу).
|
решение), и **необратимое здесь** — что в этом проекте после мерджа не
|
||||||
Любая из двух осей поднимает прогон до `large`, старшей метки, — а она
|
откатывается обратной правкой. Второй список работает и в цикле задачи: находка
|
||||||
рассчитана на 5–10% задач. Третий список — что считается **мелким** (опускает
|
в таком месте уходит человеку развилкой, а не чинится молча. Перечнем мест,
|
||||||
до `small`); он один, потому что вниз метку опускает только совпадение обеих
|
узлами или capability, а не вторым определением класса;
|
||||||
осей сразу. Перечнем мест, узлами или capability, а не вторым определением
|
|
||||||
класса. Уточняет умолчания, а не отменяет их. Рабочее умолчание — `medium`:
|
|
||||||
миграция схемы и публичный контракт метку **не** поднимают, их проверяют
|
|
||||||
проходы, которые в `medium` и так есть;
|
|
||||||
- **Недоступно проверке** — два подраздела, оба **по темам**: «не проверит ни
|
- **Недоступно проверке** — два подраздела, оба **по темам**: «не проверит ни
|
||||||
один проход» (принципиальная граница, по факту промаха не пересматривается) и
|
один проход» (принципиальная граница, по факту промаха не пересматривается) и
|
||||||
«перестали проверять сознательно» (пересматривается первым). Тема, у которой в
|
«перестали проверять сознательно» (пересматривается первым). Тема, у которой в
|
||||||
проекте нет дома, сюда не пишется: её и так называет план каждого прогона.
|
проекте нет дома, сюда не пишется: её и так называет план каждого прогона.
|
||||||
|
|
||||||
**Журнал дефектов:** запись на каждый воспроизведённый дефект с пометкой
|
**Журнал дефектов:** запись на каждый воспроизведённый дефект с пометкой
|
||||||
**проскочил / пойман ревью**. Проскочившие — проверочный набор для калибровки конвейера,
|
**проскочил / пойман ревью**. Запись — новое, и заводится она по слову человека
|
||||||
|
(`av-dev:doc-sync`, «Два рода правок»): «на каждый» задаёт **обязанность
|
||||||
|
предложить**, а не право записать молча. Человек отказал — записи нет, и
|
||||||
|
калибровка конвейера по этому дефекту не состоится; это его решение и его цена.
|
||||||
|
|
||||||
|
Проскочившие — проверочный набор для калибровки конвейера,
|
||||||
выборка по пометке. Пойманные с оракулом — лучшая опора для прохода: проектные,
|
выборка по пометке. Пойманные с оракулом — лучшая опора для прохода: проектные,
|
||||||
воспроизводимые, однажды оказавшиеся правдой.
|
воспроизводимые, однажды оказавшиеся правдой.
|
||||||
|
|
||||||
### `tasks/`
|
### `tasks/`
|
||||||
|
|
||||||
**Каталог задач канону не принадлежит.** Его ведёт отдельный плагин
|
**Каталог задач канону не принадлежит.** Его ведёт скилл `task-track` — своим
|
||||||
`av-dev-tasks` — своим скриптом, своим конфигом `<каталог>/.tasks.json`, своей
|
скриптом и своими проверками; где каталог лежит и как названы его части, говорит
|
||||||
версией формата в нём же и своим журналом версий. Канон о том числе не
|
секция `[tasks]` в `.av-dev.toml`. Версия там одна на всю раскладку: канон и
|
||||||
высказывается и его не двигает: повышает каталог задач тот, кто его ведёт.
|
каталог задач двигаются вместе, потому что ведёт их один плагин.
|
||||||
Канон **резервирует место** в `docs/` и внутрь не смотрит:
|
Каталог лежит **в корне репозитория, вне `docs/`** — места в `docs/` канон ему
|
||||||
`docs.py` каталог не открывает, его отсутствия не считает дрейфом и согласованность
|
больше не резервирует (переезд — запись 11 закрытого журнала), а внутрь не
|
||||||
задач не проверяет. Проект, поставивший только канон документов, задач не ведёт
|
смотрит и подавно: `docs.py` каталог не открывает, его отсутствия не считает
|
||||||
|
дрейфом и согласованность задач не проверяет. Проект, не заведший каталог задач, их не ведёт
|
||||||
вовсе, и отказом это быть не может.
|
вовсе, и отказом это быть не может.
|
||||||
|
|
||||||
Раскладку, форму записи и команды держит скилл `av-dev-tasks:tasks`. Ниже — то,
|
Раскладку, форму записи и команды держит скилл `av-dev:task-track`. Ниже — то,
|
||||||
от чего зависит, читается ли проект как продукт: канон высказывается об этом
|
от чего зависит, читается ли проект как продукт.
|
||||||
потому, что роадмап отвечает на вопрос о **системе**, а не о работах.
|
|
||||||
|
|
||||||
**`ROADMAP.md` отвечает на «что приложение уже умеет и чего ещё не умеет».** Это
|
**Индекс работ один — `BACKLOG.md`, и «что приложение уже умеет» он не
|
||||||
не очередь работ: цель — **возможность приложения**, задача — шаг к ней.
|
отвечает.** На этот вопрос отвечают `openspec/specs/` (нормативное поведение) и
|
||||||
Достигнутая цель из роадмапа **не исчезает** — строка с датой переезжает в
|
`git log` индекса (когда и в каком порядке оно появилось). Роадмапа в каноне
|
||||||
секцию достигнутого, потому что «что умеет» и есть половина вопроса, ради
|
нет: половину своего вопроса он дублировал беклогом, а вторую — спеками.
|
||||||
которого документ открывают. Вторым домом поведения роадмап при этом не
|
|
||||||
становится: нормативное поведение живёт в `openspec/specs/`, роадмап отвечает,
|
|
||||||
**когда и в каком порядке** оно появилось.
|
|
||||||
|
|
||||||
**У каждой записи есть тип, и тип решает, что с ней можно делать.** Дом типа —
|
**У проекта есть стадия, и она решает, что значит порядок строк беклога:**
|
||||||
|
`build` — зависимость, `support` — важность. Канон её называет, потому что от
|
||||||
|
неё зависит, читается ли список работ как план стройки или как очередь правок;
|
||||||
|
механика — `task-track`, «Две стадии».
|
||||||
|
|
||||||
|
**У каждой задачи есть тип, и тип решает, что с ней можно делать.** Дом типа —
|
||||||
поле меты `Тип` первой строкой; эмодзи в заголовке от него производна. Словарь
|
поле меты `Тип` первой строкой; эмодзи в заголовке от него производна. Словарь
|
||||||
закрыт:
|
закрыт:
|
||||||
|
|
||||||
| Тип | Что это |
|
| Тип | Что это |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 🎯 `goal` | возможность приложения |
|
|
||||||
| ✨ `feature` | снаружи появляется то, чего не было |
|
| ✨ `feature` | снаружи появляется то, чего не было |
|
||||||
| 🐞 `fix` | поведение расходится с заявленным |
|
| 🐞 `fix` | поведение расходится с заявленным |
|
||||||
| 🧹 `chore` | обслуживание, поведение не меняется |
|
| 🧹 `chore` | обслуживание, поведение не меняется |
|
||||||
| 🔬 `research` | исход — знание, а не изменение |
|
| 🔬 `research` | исход — знание, а не изменение |
|
||||||
|
|
||||||
**Схемы записи здесь нет намеренно.** Какие разделы тип требует, нужна ли ему
|
**Схемы записи здесь нет намеренно.** Какие разделы тип требует — скилл
|
||||||
цель и берётся ли он в работу — скилл `av-dev-tasks:tasks`, раздел «Тип
|
`av-dev:task-track`, раздел «Тип записи», подробно — по файлу на тип в его
|
||||||
записи», подробно — по файлу на тип в его `references/task-<тип>.md`. Ссылки в
|
`references/task-<тип>.md`. Канон фиксирует **словарь**, потому что от него
|
||||||
дерево того плагина здесь нет намеренно: он ставится отдельно, и путь наружу
|
зависит, читается ли проект как продукт; схема — механика ведения задач, и второй
|
||||||
разрешился бы не всегда. Канон фиксирует **словарь**, потому что
|
её экземпляр разошёлся бы с первым (он и разошёлся: канон успел объявить цель у
|
||||||
от него зависит, читается ли проект как продукт; схема — механика ведения задач,
|
`fix` запрещённой, хотя она была необязательна, — и целей теперь нет вовсе).
|
||||||
и второй её экземпляр разошёлся бы с первым (он и разошёлся: канон успел
|
|
||||||
объявить цель у `fix` запрещённой, хотя она там необязательна).
|
|
||||||
|
|
||||||
Схема требуется **к взятию в работу**, а не к заведению: беклог пополняется чаще,
|
Схема требуется **к взятию в работу**, а не к заведению: беклог пополняется чаще,
|
||||||
чем разбирается, и требование на входе выгоняло бы в заметки то, что должно
|
чем разбирается, и требование на входе выгоняло бы в заметки то, что должно
|
||||||
@@ -418,7 +402,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
работу не берётся и лежит в конце своей категории.
|
работу не берётся и лежит в конце своей категории.
|
||||||
|
|
||||||
Раскладку, форму записи и алгоритм работы над каждым типом держит скилл
|
Раскладку, форму записи и алгоритм работы над каждым типом держит скилл
|
||||||
`av-dev-tasks:tasks`.
|
`av-dev:task-track`.
|
||||||
|
|
||||||
### `CLAUDE.md`
|
### `CLAUDE.md`
|
||||||
|
|
||||||
@@ -440,15 +424,15 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
шкала ранжирования триажа и право проходов на `critical`;
|
шкала ранжирования триажа и право проходов на `critical`;
|
||||||
- **что считается сломанным** — красная проверка, обгоняющая развитие;
|
- **что считается сломанным** — красная проверка, обгоняющая развитие;
|
||||||
**ориентир по размеру порции**, если он замерялся. Оба слота читает скилл
|
**ориентир по размеру порции**, если он замерялся. Оба слота читает скилл
|
||||||
`av-dev-tasks:groom`, и имена их — его; названы они здесь потому, что дом
|
`av-dev:task-groom`, и имена их — его; названы они здесь потому, что дом
|
||||||
содержимого `CLAUDE.md` один и он тут.
|
содержимого `CLAUDE.md` один и он тут.
|
||||||
|
|
||||||
### `openspec/config.yaml`
|
### `openspec/config.yaml`
|
||||||
|
|
||||||
**Файл канону не принадлежит, и проверяет его тоже не канон.** Каталог
|
**Файл канону не принадлежит, и проверяет его тоже не канон.** Каталог
|
||||||
`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/` — **дом темы
|
||||||
@@ -474,7 +458,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
| почему решено так | `adr/`, источник — архивный `design.md` либо записка разведки |
|
| почему решено так | `adr/`, источник — архивный `design.md` либо записка разведки |
|
||||||
| граница домена, «чем не является» | `passport.md` |
|
| граница домена, «чем не является» | `passport.md` |
|
||||||
| инвариант и его severity | `CLAUDE.md` |
|
| инвариант и его severity | `CLAUDE.md` |
|
||||||
| что приложение умеет и чего не умеет; порядок работ | `tasks/ROADMAP.md` |
|
| что осталось сделать и в каком порядке | `tasks/BACKLOG.md` |
|
||||||
| измеренное число | `research/` |
|
| измеренное число | `research/` |
|
||||||
| настройка с числовым значением | `database.md` |
|
| настройка с числовым значением | `database.md` |
|
||||||
| периметр и модель угроз | `security.md` |
|
| периметр и модель угроз | `security.md` |
|
||||||
@@ -484,6 +468,12 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
| что уже механизировано правилом | `conventions.*`, раздел «Механизировано» |
|
| что уже механизировано правилом | `conventions.*`, раздел «Механизировано» |
|
||||||
<!-- /дом: карта-домов -->
|
<!-- /дом: карта-домов -->
|
||||||
|
|
||||||
|
**Сколько чего в корпусе — тоже факт, и дом у него сам корпус.** «Пять ревью»,
|
||||||
|
«три capability», «четыре документа» в прозе — второй дом, расходящийся с первым
|
||||||
|
на ближайшем пополнении и молча. Правило и оба законных способа сослаться —
|
||||||
|
`av-dev/shared/language.md`, правило 10; здесь оно названо потому, что счёт
|
||||||
|
корпуса выглядит не копией, а собственным наблюдением документа.
|
||||||
|
|
||||||
## Пустое называется пустым
|
## Пустое называется пустым
|
||||||
|
|
||||||
Скелет канона заводится **целиком** с первого дня. Незаполненный документ держит
|
Скелет канона заводится **целиком** с первого дня. Незаполненный документ держит
|
||||||
@@ -508,8 +498,8 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `docs/review-brief.md` | документы канона и есть бриф; остаток — в `review.md` |
|
| `docs/review-brief.md` | документы канона и есть бриф; остаток — в `review.md` |
|
||||||
| `docs/specs/` | `openspec/specs/` (поведение) и `architecture.md` (обзор) |
|
| `docs/specs/` | `openspec/specs/` (поведение) и `architecture.md` (обзор) |
|
||||||
| `docs/drafts/` | идея → запись `research`; отказ → ADR; порядок → `ROADMAP.md`; размышление → `opsx:explore` |
|
| `docs/drafts/` | идея → запись `research`; отказ → ADR; порядок → `BACKLOG.md`; размышление → `opsx:explore` |
|
||||||
| `docs/plan.md` | `tasks/ROADMAP.md` |
|
| `docs/plan.md` | `tasks/BACKLOG.md` |
|
||||||
| `BRIEF.md` | `passport.md` |
|
| `BRIEF.md` | `passport.md` |
|
||||||
| `docs/backlog/` | `tasks/` в корне репозитория |
|
| `docs/backlog/` | `tasks/` в корне репозитория |
|
||||||
| `docs/review-journal.md`, `docs/review/journal.md` | `docs/review.md` |
|
| `docs/review-journal.md`, `docs/review/journal.md` | `docs/review.md` |
|
||||||
@@ -537,7 +527,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,7 +536,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
читающие команды. Слитый агент делал бы одну половину поверхностной; тот же
|
читающие команды. Слитый агент делал бы одну половину поверхностной; тот же
|
||||||
разрез, что между `task-form` и `task-wording`.
|
разрез, что между `task-form` и `task-wording`.
|
||||||
|
|
||||||
**Зовутся оба одинаково и одним скиллом — `av-dev-docs:healthcheck`, на весь
|
**Зовутся оба одинаково и одним скиллом — `av-dev:doc-healthcheck`, на весь
|
||||||
канон разом; шагом `adopt` и шагом `upgrade` его зовёт `canon`.** Не на синке
|
канон разом; шагом `adopt` и шагом `upgrade` его зовёт `canon`.** Не на синке
|
||||||
документации: `doc-consistency` на
|
документации: `doc-consistency` на
|
||||||
`opus` по каждой сделанной задаче не окупается, а расхождение между двумя документами по
|
`opus` по каждой сделанной задаче не окупается, а расхождение между двумя документами по
|
||||||
@@ -561,40 +551,69 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
архитектуру с кодом» задача без дна, и агент, которому её поставили, выдаёт
|
архитектуру с кодом» задача без дна, и агент, которому её поставили, выдаёт
|
||||||
правдоподобную труху вместо находок.
|
правдоподобную труху вместо находок.
|
||||||
|
|
||||||
## `docs/.docs.json`
|
## `.av-dev.toml`
|
||||||
|
|
||||||
```json
|
```toml
|
||||||
{
|
# Раскладка av-dev в этом проекте: версия и настройки проверок.
|
||||||
"canon": <текущая версия>,
|
|
||||||
"migrations": "internal/store/migrations"
|
version = 1 # версия раскладки
|
||||||
}
|
|
||||||
|
[docs]
|
||||||
|
migrations = "internal/store/migrations" # если БД есть
|
||||||
|
healthcheck_last = "a1b2c3d" # сверка документов: коммит прошлого прогона
|
||||||
|
|
||||||
|
[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`.
|
||||||
|
|
||||||
**Имя файла — имя плагина, который его завёл.** Канон документов ведёт
|
`[docs] healthcheck_last` — коммит, на котором в последний раз проходила сверка
|
||||||
`av-dev-docs`, поэтому `.docs.json`; у каталога задач по тому же правилу
|
документов; ставит его сам `av-dev:doc-healthcheck` последним шагом прогона, а
|
||||||
`.tasks.json`, у конвейера — `openspec/config.yaml`. До версии 13 файл звался
|
читает `av-dev:doc-sync`, чтобы сосчитать задачи, сделанные с тех пор. **Ключ
|
||||||
`.pm.json` — по плагину `av-dev-pm`, который распался на четыре и которого
|
необязательный и в скелете его нет намеренно**: у нового проекта сверок не было,
|
||||||
больше нет; имя пережило владельца и указывало в пустоту. Прежнее имя `docs.py`
|
и пустое значение врало бы про это меньше, чем отсутствие ключа, только на вид.
|
||||||
не читает: два дома для одной версии канона расходятся молча, а переименование
|
Отсутствие читается однозначно — «не сверялись ни разу».
|
||||||
стоит одну команду (версия 13 журнала, и `check` называет её сам, когда видит
|
|
||||||
старый файл).
|
|
||||||
|
|
||||||
**Ключа `tasks` здесь больше нет.** Настройки каталога задач вернулись в свой
|
Секции он достался по смыслу: `[docs]` — настройки проверок документов, а сверка
|
||||||
файл `<каталог задач>/.tasks.json`, потому что ведёт их другой плагин: конфиг,
|
документов и есть такая проверка. Своя секция верхнего уровня стоила бы правки
|
||||||
лежащий в `docs/`, был бы домом, которого нет у проекта, поставившего учёт работ
|
общего читателя `shared/config.py` и сделала бы файл, объявленный «версией и
|
||||||
без канона документов. Состав ключей описывает тот плагин, а не канон. Там же —
|
настройками», хранилищем состояния.
|
||||||
**версия формата задач**, и она своя: у проекта без `docs/` версии канона нет
|
|
||||||
вовсе, сверять её было бы не с чем. Прежний ключ читается, пока живы
|
|
||||||
непереехавшие проекты, и `tasks.py` говорит о нём замечанием на каждом
|
|
||||||
прогоне — версия 8 журнала просит его убрать.
|
|
||||||
|
|
||||||
Ключей будет больше по мере роста проверок; неизвестный ключ `docs.py`
|
**Формат TOML взят ради комментариев.** Файл лежит в репозитории проекта, и
|
||||||
игнорирует, отсутствующий — считает «проверка неприменима» и говорит об этом
|
человек, открывший его через полгода, обязан прочитать в нём, что означает
|
||||||
строкой, а не молчит.
|
число. JSON комментариев не знает, и объяснение приходилось держать в другом
|
||||||
|
файле. Отсюда же правило записи: скрипты правят **строку**, а не переписывают
|
||||||
|
файл — перезапись стёрла бы то, ради чего формат и выбран.
|
||||||
|
|
||||||
|
**Файл один, и лежит он в корне.** До слияния плагинов их было два —
|
||||||
|
`docs/.docs.json` с версией канона и `<каталог задач>/.tasks.json` с версией
|
||||||
|
формата задач, — и версии двигались порознь, потому что плагины ставились
|
||||||
|
порознь. Плагин теперь один, версия одна, а корень выбран потому, что он есть и
|
||||||
|
у проекта без `docs/`, и у проекта без каталога задач. Прежние имена не
|
||||||
|
читаются: два дома для одной версии расходятся молча. Увидев их, `docs.py` и
|
||||||
|
`tasks.py` говорят «прежняя раскладка» и зовут `upgrade` — версия 1 журнала.
|
||||||
|
|
||||||
|
Ключей будет больше по мере роста проверок, но **заводятся они только вместе с
|
||||||
|
правкой скрипта**: неизвестный ключ — не безмолвный пропуск, а **отказ кодом
|
||||||
|
3**. Верхний уровень стережёт `shared/config.py` (`TOP_KEYS`), секцию `[docs]` —
|
||||||
|
`docs.py` (`DOCS_KEYS`), секцию `[tasks]` — `tasks.py`. Довод у отказа
|
||||||
|
проверяемый: ключ, положенный не в ту секцию, при молчаливом пропуске не значит
|
||||||
|
ничего — проверка объявляет себя неприменимой, отчёт выходит зелёным, и на месте
|
||||||
|
настройки оказывается тишина.
|
||||||
|
|
||||||
|
**Здесь это правило однажды соврало, и цена была немедленной.** Абзац обещал, что
|
||||||
|
неизвестный ключ игнорируется; по этому обещанию скилл сверки завёл себе секцию
|
||||||
|
`[healthcheck]` верхнего уровня — и первый же её прогон сделал бы нерабочими
|
||||||
|
`docs.py`, `tasks.py` и гейт проекта, который их зовёт. Отсюда и порядок: **новый
|
||||||
|
ключ заводится правкой константы в скрипте-владельце, и только потом появляется
|
||||||
|
здесь**.
|
||||||
|
|
||||||
|
**Отсутствующий** ключ — другое дело: он значит «проверка неприменима», и скрипт
|
||||||
|
говорит об этом строкой, а не молчит.
|
||||||
+14
-20
@@ -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`.
|
|
||||||
|
|
||||||
Версия — целое число. Обратной совместимости у канона нет: есть «приведён» и «не
|
|
||||||
приведён».
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -223,7 +217,7 @@ OpenSpec уехал в конвейер. Каталог `openspec/` версие
|
|||||||
канона: `init` его заводил, `adopt` тоже, образец `config.yaml` лежал в скелетах,
|
канона: `init` его заводил, `adopt` тоже, образец `config.yaml` лежал в скелетах,
|
||||||
а отсутствие каталога `docs.py` считал отказом. Разрез был проведён не там. По
|
а отсутствие каталога `docs.py` считал отказом. Разрез был проведён не там. По
|
||||||
OpenSpec работает конвейер — без каталога не запускаются ни `opsx:propose`, ни
|
OpenSpec работает конвейер — без каталога не запускаются ни `opsx:propose`, ни
|
||||||
ревью дизайна, ни сверка требований, — а канон документов о нём только
|
сверка требований, — а канон документов о нём только
|
||||||
высказывался. Проект, которому конвейер не нужен, получал отказ за отсутствие
|
высказывался. Проект, которому конвейер не нужен, получал отказ за отсутствие
|
||||||
того, чем не пользуется.
|
того, чем не пользуется.
|
||||||
|
|
||||||
@@ -296,7 +290,7 @@ OpenSpec работает конвейер — без каталога не за
|
|||||||
`config.yaml` описан абзацем — а заводил всё это человек руками, и проверялось
|
`config.yaml` описан абзацем — а заводил всё это человек руками, и проверялось
|
||||||
из перечисленного ничего. Заведение нового проекта проходило мимо: `init`
|
из перечисленного ничего. Заведение нового проекта проходило мимо: `init`
|
||||||
собирал документы канона и оставлял проект без каталога, без которого не работают
|
собирал документы канона и оставлял проект без каталога, без которого не работают
|
||||||
ни `opsx:propose`, ни ревью дизайна, ни сверка требований.
|
ни `opsx:propose`, ни сверка требований.
|
||||||
|
|
||||||
Хуже отсутствия оказался файл из коробки. `openspec init` кладёт `config.yaml`,
|
Хуже отсутствия оказался файл из коробки. `openspec init` кладёт `config.yaml`,
|
||||||
где `context` и `rules` — закомментированный пример на английском. Такой файл
|
где `context` и `rules` — закомментированный пример на английском. Такой файл
|
||||||
@@ -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,266 @@
|
|||||||
|
# Журнал версий раскладки
|
||||||
|
|
||||||
|
Одна запись на версию. Проект знает свою версию из ключа `version` в
|
||||||
|
`.av-dev.toml`; операция `upgrade` скилла `av-dev: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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Версия 5 — 2026-08-23
|
||||||
|
|
||||||
|
**Метка задачи снята из процесса целиком**, и вместе с ней — подраздел «Триггеры
|
||||||
|
метки» в `docs/review.md`. Состав прогона ревью стал постоянным: он один и тот же
|
||||||
|
на всякой задаче, выбирать нечего, и признаки, по которым метка поднималась,
|
||||||
|
перестали что-либо решать. На месте подраздела — **«Когда звать глубокое ревью»**:
|
||||||
|
те же наблюдения проекта, но адресованные другому решению — звать ли
|
||||||
|
`av-dev:code-deep-review` по области кода.
|
||||||
|
|
||||||
|
**Что переехало в проекте.** Скелет `docs/review.md`, раздел настройки конвейера:
|
||||||
|
подраздел «Триггеры метки» заменён подразделом «Когда звать глубокое ревью» —
|
||||||
|
**двумя списками**: области, которые смотрят целиком (узлы с частым возвратом,
|
||||||
|
места с историей инцидентов, код под дорогое решение), и **необратимое здесь** —
|
||||||
|
что в этом проекте после мерджа не откатывается обратной правкой. Второй список
|
||||||
|
работает и в цикле задачи: находка в таком месте уходит человеку развилкой, а не
|
||||||
|
чинится молча. Само правило — в [canon.md](canon.md), раздел `review.md`.
|
||||||
|
|
||||||
|
**Что сделать проекту.**
|
||||||
|
|
||||||
|
1. **Переписать подраздел в `docs/review.md`.** Заголовок «Триггеры метки»
|
||||||
|
становится «Когда звать глубокое ревью», содержимое — два списка выше.
|
||||||
|
Признаки, годные только для выбора метки («больше N файлов», «затронуто больше
|
||||||
|
одного слоя»), выбрасываются: состава прогона они не меняют. Что из прежнего
|
||||||
|
списка называло **необратимое место** — переносится во второй список дословно.
|
||||||
|
2. **Пройти по документам** — `grep -rniE "small|medium|large|метк" docs/`.
|
||||||
|
Найденное в `review.md`, `conventions/` и `adr/` правится по смыслу: описание
|
||||||
|
прошлого решения остаётся как свидетельство, действующая инструкция —
|
||||||
|
переписывается или снимается.
|
||||||
|
3. **Поднять версию** — `docs.py bump`, последним шагом.
|
||||||
|
4. `docs.py check` — до отсутствия дрейфа.
|
||||||
|
|
||||||
|
**Чего делать не надо.** Заводить ключ `[docs] healthcheck_last` руками: он
|
||||||
|
необязательный и появится сам первым прогоном `av-dev:doc-healthcheck`. Править
|
||||||
|
прошлые записи журналов и архивные change — тоже: метка, стоявшая в них, верна
|
||||||
|
как свидетельство о том дне.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Версия 4 — 2026-08-13
|
||||||
|
|
||||||
|
Слово **провенанс** снято из словаря языка проектных текстов и заменено русским.
|
||||||
|
Оно стояло в закрытом списке своих терминов с оговоркой «„источник“ рядом
|
||||||
|
называет саму запись, а не свойство» — верной, но доказывающей лишь то, что не
|
||||||
|
годится одно русское слово. Годятся два, и по смыслу они разные: **происхождение**
|
||||||
|
у числа (чем и при каких условиях получено) и **откуда** у вопроса или находки
|
||||||
|
(кто нашёл, каким проходом, из какой записи журнала).
|
||||||
|
|
||||||
|
**Что переехало в проекте.** Скелет `docs/review.md`, подраздел «Вопросы по
|
||||||
|
темам»: форма вопроса записана как `<тема>: <вопрос> (<откуда>)` вместо
|
||||||
|
`(<провенанс>)`. Само правило — в [canon.md](canon.md), раздел `review.*`;
|
||||||
|
требование к числам `research/` не изменилось по существу, изменилось слово.
|
||||||
|
|
||||||
|
**Что сделать проекту.**
|
||||||
|
|
||||||
|
1. **Поправить форму в `docs/review.md`** — строка «Форма: `<тема>: <вопрос>
|
||||||
|
(<провенанс>)`» становится «Форма: `<тема>: <вопрос> (<откуда>)`». Уже
|
||||||
|
записанные вопросы переписывать не надо: слово стояло в шаблоне, а не в них.
|
||||||
|
2. **Пройти по документам** — `grep -rn "провенанс" docs/`. Найденное в
|
||||||
|
`research/` и в `adr/` заменяется на **происхождение** (речь о числе) или на
|
||||||
|
**откуда** (речь о том, из чего вопрос или находка выросли). Ничего не
|
||||||
|
нашлось — шаг закрыт строкой, это обычный исход.
|
||||||
|
3. **Поднять версию** — `docs.py bump`, последним шагом.
|
||||||
|
4. `docs.py check` — до отсутствия дрейфа.
|
||||||
|
|
||||||
|
**Чего делать не надо.** Править прошлые записи журналов и архивные change:
|
||||||
|
слово, верное на день записи, остаётся верным как свидетельство.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Версия 3 — 2026-08-13
|
||||||
|
|
||||||
|
Тип записи `goal` и индекс `ROADMAP.md` упразднены; у проекта появилась
|
||||||
|
**стадия** — `build` (беклог это план стройки, порядок строк значит зависимость)
|
||||||
|
или `support` (очередь правок, порядок значит важность).
|
||||||
|
|
||||||
|
Цель была зонтиком над параллельными направлениями — она нужна там, где список
|
||||||
|
работ нельзя выстроить в один порядок. У проекта, который ведёт один человек,
|
||||||
|
такого не бывает, и роадмап при этом наполовину дублировал беклог («чего ещё не
|
||||||
|
умеет» = «что осталось в списке»), а вторую половину («что уже умеет») отвечают
|
||||||
|
`openspec/specs/` и `git log` индекса.
|
||||||
|
|
||||||
|
**Что переехало.** Индекс остался один — `BACKLOG.md`. Поле меты `Секция` стало
|
||||||
|
`Категория`; теги `goal:<слаг>`, `decomposed` и раздел `Завершение` упразднены;
|
||||||
|
команды `list --goal`, `edit --goal`, `edit --section` и ключи `[tasks] roadmap`,
|
||||||
|
`[tasks] completion_heading` — тоже. Появились ключ `[tasks] stage`, команда
|
||||||
|
`tasks.py stage` и флаги `init --stage`, `adopt scan --stage`.
|
||||||
|
|
||||||
|
**Что сделать проекту. Порядок шагов обязателен**, и первый шаг — не команда:
|
||||||
|
пока в `[tasks]` лежит упразднённый ключ, **любая** подкоманда `tasks.py`
|
||||||
|
отвечает кодом 3 и работать нечем.
|
||||||
|
|
||||||
|
1. **Вычистить конфиг руками.** Из секции `[tasks]` в `.av-dev.toml` удалить
|
||||||
|
ключи `roadmap` (или `plan`) и `completion_heading`. Каждый из них — код 3 на
|
||||||
|
любой команде, и названы они здесь оба: второй легко пропустить, потому что
|
||||||
|
его упразднение не видно по имени файла.
|
||||||
|
2. **Удалить `tasks/ROADMAP.md`.** Секция `Готово` уходит вместе с ним и **не
|
||||||
|
переносится**: «что приложение умеет» отвечают спеки, «когда это появилось» —
|
||||||
|
`git log` беклога. Проект без `openspec/specs/` теряет здесь единственный
|
||||||
|
связный перечень достигнутого — если он нужен, сохрани его сам до удаления
|
||||||
|
(документом проекта, не задачами).
|
||||||
|
3. **Прогнать `tasks.py check --fix`.** Он снимет теги `goal:<слаг>` и
|
||||||
|
`decomposed`, переименует поле `Секция` → `Категория` и перепишет старую
|
||||||
|
форму меты — **в том числе у самих записей типа `goal`**. Записи `goal` при
|
||||||
|
этом останутся: во что превращается цель, машина не решает и говорит
|
||||||
|
`НЕОДНОЗНАЧНО`.
|
||||||
|
4. **Разобрать цели поштучно.** У каждой два исхода, и выбирает человек: она
|
||||||
|
становится задачей (`edit <слаг> --type feature|fix|chore|research`) либо
|
||||||
|
уходит (`close <слаг> --reason …`). Строки в беклоге у неё нет — её жильём
|
||||||
|
был роадмап, — и `edit --type` заведёт её сам, в первую секцию и в конец,
|
||||||
|
сказав об этом; место назначь потом. Раздел `Завершение` в теле переехавшей
|
||||||
|
записи **удали руками**: схеме нового типа он не принадлежит, и `check`
|
||||||
|
оставит о нём замечание. Задачи, носившие тег цели, живут дальше сами по
|
||||||
|
себе — разбирать их не нужно.
|
||||||
|
5. **Объявить стадию** — `tasks.py stage build` или `tasks.py stage support`.
|
||||||
|
Приложение ещё строится и список работ линеен по зависимости — `build`;
|
||||||
|
работает и правится точечно — `support`. Без ключа `check` отказывает: порядок
|
||||||
|
строк нечем прочитать.
|
||||||
|
|
||||||
|
**Объявление беклог не трогает** — ни секций, ни файлов: оно называет то, что
|
||||||
|
уже верно. Поэтому проекту с несколькими полками, объявляющему `build`,
|
||||||
|
команда откажет и назовёт выход: слить полки самому (`move <слаг> --section
|
||||||
|
<куда> --reason …`), потому что порядок строк в слитом списке знает только
|
||||||
|
человек. Флаг `--sections` при объявлении не принимается — он для **смены**
|
||||||
|
стадии, где сливать просят явно.
|
||||||
|
6. **Поправить шапку `BACKLOG.md`.** Абзац про стадию теперь размечен парой
|
||||||
|
`<!-- стадия -->` … `<!-- /стадия -->`, и по нему `check` сверяет шапку с
|
||||||
|
конфигом. В беклоге, заведённом до этой версии, разметки нет — `stage` об
|
||||||
|
этом скажет. Возьми готовый абзац из свежего каталога (`tasks.py init` во
|
||||||
|
временном месте) или напиши сам: он объясняет, что значит порядок строк, и
|
||||||
|
читают вместо документации именно его.
|
||||||
|
7. **Поднять версию** — `docs.py bump`. Последним шагом. Он двигает **одну**
|
||||||
|
запись за раз: отставшему на две записи проекту зовётся дважды, следом за
|
||||||
|
шагами каждой.
|
||||||
|
8. `docs.py check` и `tasks.py check --dir <каталог задач>` — до отсутствия
|
||||||
|
дрейфа.
|
||||||
|
|
||||||
|
**Проект, не прошедший записи 1 и 2, начинает с этой.** Их собственные шаги
|
||||||
|
велят гонять `tasks.py check` до зелёного, а он на упразднённом ключе отвечает
|
||||||
|
кодом 3 — то есть пройти их сегодня нельзя, не сделав шаг 1 отсюда. Записи от
|
||||||
|
этого не переписываются: порядок между ними прежний, добавлено одно условие
|
||||||
|
входа.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Версия 2 — 2026-08-13
|
||||||
|
|
||||||
|
Скилл `doc-canon` стал `canon`: префикс называл материал (`doc-`), а скилл
|
||||||
|
занят не материалом, а **формой** — раскладкой всех частей проекта и общим
|
||||||
|
повышением версии. Ни один файл проекта от этого не переехал; сменились **путь к
|
||||||
|
скрипту** и **имя вызова**, а оба живут в проекте: первый — строкой гейта, второй
|
||||||
|
— в `CLAUDE.md` и в записях задач.
|
||||||
|
|
||||||
|
**Что переехало в вызовах.** `av-dev:doc-canon` → `av-dev:canon`. Прочие имена не
|
||||||
|
тронуты.
|
||||||
|
|
||||||
|
**Что сделать проекту.**
|
||||||
|
|
||||||
|
1. **Поправить шаг гейта.** Путь к `docs.py` сменился вместе с именем каталога
|
||||||
|
скилла: `skills/doc-canon/scripts/docs.py` →
|
||||||
|
`skills/canon/scripts/docs.py`. Шаг, который не нашёл скрипт, обязан
|
||||||
|
краснеть, а не пропускаться, — проверь, что он краснеет.
|
||||||
|
2. **Поправить свои вызовы скилла** — `grep -rn "doc-canon" --exclude-dir=.git .`
|
||||||
|
по проекту целиком: имя встречается в `CLAUDE.md`, в `Taskfile`, в записях
|
||||||
|
задач и в документах канона. Прежнее полное имя не разрешится вовсе.
|
||||||
|
3. **Поднять версию** — `docs.py bump`. Последним шагом.
|
||||||
|
4. `docs.py check` и `tasks.py check --dir <каталог задач>` — до отсутствия
|
||||||
|
дрейфа.
|
||||||
|
|
||||||
|
**Проект, не прошедший запись 1, переименовывает дважды подряд** — `skills/canon/`
|
||||||
|
→ `skills/doc-canon/` записью 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/.docs.json`, чтобы не заводить факту
|
||||||
|
второй дом. Такое чтение переезжает на `.av-dev.toml` и на `tomllib` вместо
|
||||||
|
`json`: `python3 -c 'import tomllib; print(tomllib.load(open(".av-dev.toml","rb"))["docs"]["migrations"])'`.
|
||||||
|
Ищется командой `grep -rn "\.docs\.json\|\.tasks\.json" --exclude-dir=.git .`
|
||||||
|
— по проекту целиком, а не по документам: на первом же живом переезде это
|
||||||
|
нашлось в `Taskfile.yml`, и нашёл это гейт, а не человек.
|
||||||
|
8. **Поднять версию** — `docs.py bump`. Последним шагом: число объявляет
|
||||||
|
пройденными шаги журнала, и раньше времени поднятое врёт.
|
||||||
|
9. `docs.py check` и `tasks.py check --dir <каталог задач>` — до отсутствия
|
||||||
|
дрейфа.
|
||||||
|
|
||||||
|
**Чего делать не надо.** Переписывать прошлые записи журналов под новые имена.
|
||||||
|
Они описывают состояния, которые были, и адрес, верный на день записи, остаётся
|
||||||
|
верным как свидетельство.
|
||||||
+50
-50
@@ -32,7 +32,7 @@
|
|||||||
# Паспорт проекта
|
# Паспорт проекта
|
||||||
|
|
||||||
Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
|
Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
|
||||||
устроено», [tasks/ROADMAP.md](../tasks/ROADMAP.md) — «в каком порядке», паспорт —
|
устроено», [tasks/BACKLOG.md](../tasks/BACKLOG.md) — «что осталось», паспорт —
|
||||||
«зачем и для кого».
|
«зачем и для кого».
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
со строкой «запись лежит сжатой и распаковывается целиком».
|
со строкой «запись лежит сжатой и распаковывается целиком».
|
||||||
```
|
```
|
||||||
|
|
||||||
Нет БД — файла нет, и в `docs/.docs.json` нет ключа `migrations`.
|
Нет БД — файла нет, и в `.av-dev.toml` нет ключа `[docs] migrations`.
|
||||||
|
|
||||||
## `docs/security.md`
|
## `docs/security.md`
|
||||||
|
|
||||||
@@ -200,8 +200,8 @@
|
|||||||
```markdown
|
```markdown
|
||||||
# Журнал решений
|
# Журнал решений
|
||||||
|
|
||||||
Одна запись — одно решение. **ADR это промоут поверх уже написанного**, а не
|
Одна запись — одно решение. **ADR продвигает уже написанное решение, а не
|
||||||
второе сочинение: запись цитирует решение и ссылается на источник —
|
сочиняет его заново**: запись цитирует решение и ссылается на источник —
|
||||||
`openspec/changes/archive/<id>/design.md`, а у решения, принятого разведкой без
|
`openspec/changes/archive/<id>/design.md`, а у решения, принятого разведкой без
|
||||||
изменения, на её записку.
|
изменения, на её записку.
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
|
|
||||||
Верно одно из трёх:
|
Верно одно из трёх:
|
||||||
|
|
||||||
<!-- копия: adr-когда-заводить из av-dev-docs/skills/canon/references/canon.md -->
|
<!-- копия: adr-когда-заводить из av-dev/skills/canon/references/canon.md -->
|
||||||
- **дорогой откат** — переделка стоит дороже переписывания одного файла;
|
- **дорогой откат** — переделка стоит дороже переписывания одного файла;
|
||||||
- **намеренный отказ** от очевидного подхода;
|
- **намеренный отказ** от очевидного подхода;
|
||||||
- **пересмотр прежнего решения** — тогда у старой записи обязателен статус
|
- **пересмотр прежнего решения** — тогда у старой записи обязателен статус
|
||||||
@@ -261,7 +261,7 @@
|
|||||||
## Последствия
|
## Последствия
|
||||||
|
|
||||||
- `+` что стало лучше.
|
- `+` что стало лучше.
|
||||||
- `−` чем платим: ограничения, риски, нагрузка на поддержку.
|
- `−` чем платим: ограничения, риски, нагрузка на сопровождение.
|
||||||
```
|
```
|
||||||
|
|
||||||
## `docs/review.md`
|
## `docs/review.md`
|
||||||
@@ -283,14 +283,13 @@
|
|||||||
|
|
||||||
### Вопросы по темам
|
### Вопросы по темам
|
||||||
|
|
||||||
Форма: `<тема>: <вопрос> (<провенанс>)`. Главный источник — журнал ниже. Вопрос
|
Форма: `<тема>: <вопрос> (<откуда>)`. Главный источник — журнал ниже. Вопрос
|
||||||
задаёт тот проход, который закрывает эту тему на текущем прогоне, дополнительно
|
задаёт тот проход, который закрывает эту тему на текущем прогоне, дополнительно
|
||||||
к обязательным.
|
к обязательным.
|
||||||
|
|
||||||
**Адресуй теме, а не имени прохода.** Проходы переезжают между метками и
|
**Адресуй теме, а не имени прохода.** Проходы переезжают между скиллами и
|
||||||
упраздняются; вопрос, адресованный проходу, перестанет задаваться в тот день,
|
упраздняются; вопрос, адресованный проходу, перестанет задаваться в тот день,
|
||||||
когда тот уедет в старшую метку, — и заметить это будет нечем. Тема переезд
|
когда тот уедет, — и заметить это будет нечем. Тема переезд переживает.
|
||||||
переживает.
|
|
||||||
|
|
||||||
Темы ядра: `requirements`, `autotests`, `conventions`, `architecture`,
|
Темы ядра: `requirements`, `autotests`, `conventions`, `architecture`,
|
||||||
`security`, `operations`. Плюс любая своя — та, под которую проект завёл в
|
`security`, `operations`. Плюс любая своя — та, под которую проект завёл в
|
||||||
@@ -299,30 +298,22 @@
|
|||||||
`review` нельзя — таких тем нет. Вопрос про границу домена адресуй
|
`review` нельзя — таких тем нет. Вопрос про границу домена адресуй
|
||||||
`architecture`, вопрос про хранилище и числа — `operations`.
|
`architecture`, вопрос про хранилище и числа — `operations`.
|
||||||
|
|
||||||
### Триггеры метки
|
### Когда звать глубокое ревью
|
||||||
|
|
||||||
Проектная конкретизация правила выбора метки. **Списка три: по одному на
|
Проектная конкретизация признаков, по которым зовут `av-dev:code-deep-review`.
|
||||||
каждую ось вверх и один вниз** — поимённо, узлами или capability.
|
**Списка два, оба поимённо — узлами, слоями или capability.**
|
||||||
|
|
||||||
**Крупное здесь** — про объём: что трогает несколько узлов или слоёв, переносит
|
**Области, которые смотрят целиком:** узлы, куда задачи возвращаются чаще
|
||||||
ответственность между ними, перекладывает существующий код в новую форму.
|
прочих, места с историей инцидентов, код, на который обопрётся дорогое решение.
|
||||||
|
|
||||||
**Незнакомое здесь** — про форму решения: то, чего в проекте ещё не было и чью
|
**Необратимое здесь:** что в этом проекте после мерджа не откатывается обратной
|
||||||
форму предстоит нащупать по ходу. Признак простой: перед работой нельзя назвать,
|
правкой — миграции, формат на диске, публичный контракт, имена, расходящиеся по
|
||||||
какие узлы будут тронуты.
|
базе. Находка в таком месте уходит человеку развилкой, а не чинится молча, и
|
||||||
|
список нужен затем, чтобы «необратимое» не решалось на глаз.
|
||||||
|
|
||||||
Любая из двух осей поднимает прогон до `large`, старшей метки: там `security`,
|
Цикл задачи проверяет корректность и механику одним и тем же составом; глубину
|
||||||
`operations` и `architecture` проверяют запуском, и там же единственные замеры.
|
даёт только отдельный прогон по области, и **зовёт его человек**. Списки уточняют
|
||||||
Метка рассчитана на **5–10% задач**; если сюда попадает каждая третья, списки
|
признаки, а не заводят расписание.
|
||||||
написаны слишком широко.
|
|
||||||
|
|
||||||
**Мелкое здесь** — опускает до `small`. Ориентир по доле — до трети задач, и в
|
|
||||||
любом случае меньше, чем `medium`: перевес `small` значит, что рабочее умолчание
|
|
||||||
сместилось само. Помни отрицательный тест конвейера: что
|
|
||||||
после мерджа не откатывается обратной правкой (миграция, формат на диске,
|
|
||||||
публичный контракт, имя), — не `small`, каким бы маленьким ни был дифф.
|
|
||||||
|
|
||||||
Уточняет умолчания конвейера, не отменяет их; рабочее умолчание — `medium`.
|
|
||||||
|
|
||||||
### Недоступно проверке
|
### Недоступно проверке
|
||||||
|
|
||||||
@@ -345,7 +336,7 @@
|
|||||||
|
|
||||||
Форма:
|
Форма:
|
||||||
|
|
||||||
<!-- копия: журнал-дефектов-форма из av-dev-code/skills/review/references/review-journal.md -->
|
<!-- копия: журнал-дефектов-форма из av-dev/skills/code-review/references/review-journal.md -->
|
||||||
## ГГГГ-ММ-ДД — <краткое последствие> [проскочил|пойман]
|
## ГГГГ-ММ-ДД — <краткое последствие> [проскочил|пойман]
|
||||||
|
|
||||||
- **Где:** путь:строка либо «конвейер, а не код»
|
- **Где:** путь:строка либо «конвейер, а не код»
|
||||||
@@ -428,36 +419,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`.
|
||||||
@@ -6,37 +6,57 @@
|
|||||||
маркеры долга и две сверки с кодом. Смысловые дубли и оставшееся в архитектуре
|
маркеры долга и две сверки с кодом. Смысловые дубли и оставшееся в архитектуре
|
||||||
поведение судит агент — скрипт об этом говорит вслух в конце отчёта.
|
поведение судит агент — скрипт об этом говорит вслух в конце отчёта.
|
||||||
|
|
||||||
Коды выхода — тот же словарь, что у tasks.py:
|
Коды выхода — общий словарь скриптов av-dev; дом словаря и разбор «дрейф
|
||||||
0 сошлось
|
против окружения» — av-dev/shared/axes.md. Значения — в константах ниже.
|
||||||
1 дрейф раскладки (рабочая ситуация, чинится)
|
|
||||||
2 ошибка употребления
|
|
||||||
3 окружение: не тот каталог, битый конфиг
|
|
||||||
4 внутренний сбой
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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 +97,7 @@ CONDITIONAL_DOCS = {
|
|||||||
# Обязательные файлы вне раскладки docs/.
|
# Обязательные файлы вне раскладки docs/.
|
||||||
REQUIRED = {
|
REQUIRED = {
|
||||||
"CLAUDE.md": "памятка агенту: инварианты с severity, команды, семантика гейта",
|
"CLAUDE.md": "памятка агенту: инварианты с severity, команды, семантика гейта",
|
||||||
CONFIG: "версия канона и пути, нужные проверкам",
|
CONFIG: "версия раскладки av-dev и пути, нужные проверкам",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Файлы, которые документ-каталог обязан держать сверх README.md.
|
# Файлы, которые документ-каталог обязан держать сверх README.md.
|
||||||
@@ -86,9 +106,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 +127,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/BACKLOG.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, порядок → BACKLOG.md",
|
||||||
"backlog": "→ tasks/ в корне репозитория (плагин av-dev-tasks)",
|
"backlog": "→ tasks/ в корне репозитория (ведёт скилл task-track)",
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Слаги в именах файлов --------------------------------------------------
|
# --- Слаги в именах файлов --------------------------------------------------
|
||||||
@@ -264,16 +285,30 @@ 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]`. Секцию знает этот скрипт, а не общий читатель: ключ
|
||||||
|
# заводится вместе с проверкой, которая его читает.
|
||||||
|
#
|
||||||
|
# `healthcheck_last` — коммит прошлой сверки документов; пишет его скилл
|
||||||
|
# `av-dev:doc-healthcheck`, читает `av-dev:doc-sync`, чтобы сосчитать задачи с
|
||||||
|
# тех пор. Здесь он стоит **только чтобы файл не отвергли**: неизвестный ключ —
|
||||||
|
# отказ кодом 3, то есть ключ, заведённый скиллом мимо этой константы, сделал бы
|
||||||
|
# нерабочими и `docs.py`, и `tasks.py`, и гейт проекта, который их зовёт.
|
||||||
|
# Проверки, читающей его, у скрипта нет и не предполагается: значение — след
|
||||||
|
# работы человека, а не настройка.
|
||||||
|
DOCS_KEYS = ("migrations", "healthcheck_last")
|
||||||
|
|
||||||
|
|
||||||
|
def docs_cfg(cfg: dict) -> dict:
|
||||||
|
return conf.section(cfg, "docs")
|
||||||
|
|
||||||
|
|
||||||
# --- Проверки ---------------------------------------------------------------
|
# --- Проверки ---------------------------------------------------------------
|
||||||
@@ -282,22 +317,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: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 +359,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: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 +410,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 +603,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 +648,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 +670,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 +683,49 @@ 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" устарел плагин, обнови маркетплейс")
|
||||||
|
# Двигается **одна** запись за раз, а не сразу до текущей: число объявляет
|
||||||
|
# пройденными шаги журнала, и прыжок через запись объявил бы пройденным то,
|
||||||
|
# чего никто не делал. Отставшему на три записи проекту `bump` зовётся три
|
||||||
|
# раза — по разу на запись, следом за её шагами.
|
||||||
|
#
|
||||||
|
# Версии нет вовсе — случай другой: проект не жил ни одной записью журнала,
|
||||||
|
# его раскладку только что вывели сегодняшним форматом (`adopt`), и
|
||||||
|
# объявлять ему нечего, кроме текущего числа.
|
||||||
|
target = LAYOUT_VERSION if was is None else was + 1
|
||||||
|
conf.set_version(root, target)
|
||||||
|
print(f"версия раскладки: {was if was is not None else 'не была объявлена'}"
|
||||||
|
f" → {target} в {CONFIG}")
|
||||||
|
if target < LAYOUT_VERSION:
|
||||||
|
print(f" до текущей ({LAYOUT_VERSION}) осталось записей журнала:"
|
||||||
|
f" {LAYOUT_VERSION - target}. Пройди шаги следующей и позови bump"
|
||||||
|
f" снова — по разу на запись")
|
||||||
return OK
|
return OK
|
||||||
|
|
||||||
|
|
||||||
@@ -648,10 +741,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)
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
---
|
||||||
|
name: code-deep-review
|
||||||
|
description: "Глубокое ревью области кода — не задачи, а куска проекта: модуля, слоя, сервиса целиком. Зовёт проходы, которых нет в цикле задачи: review-adversary (строит путь и прогоняет падающий тест), review-ops (снимает числа замером), review-architecture на входе шире диффа и по форме решения, review-code по коду целиком, а сводит их review-triage. Здесь единственное место процесса, где форму решения судят после кода и где находка доказывается прогоном и замером. Проходы, помеченные «держит машину», идут цепочкой. Исход — не правки, а разговор: находки предлагаются человеку, обсуждаются по одной, и согласованное уезжает задачами через av-dev:task-track, сценарий «задачи из ревью и аудита». Использовать время от времени и по признаку: накопился десяток задач в одной области, перед тем как опереться на узел в дорогом решении, после инцидента, по строке «отложено в code-deep-review» из отчётов ревью. Дорого — не на задаче и не по расписанию. Ревью одного изменения — скилл av-dev:code-review."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Глубокое ревью области
|
||||||
|
|
||||||
|
Смотрит **не задачу, а место в проекте**: модуль, слой, сервис целиком. Отсюда и
|
||||||
|
всё остальное устройство — вход, состав проходов, исход.
|
||||||
|
|
||||||
|
Разрез с конвейером задачи проверяемый: **`av-dev:code-review` судит изменение,
|
||||||
|
этот скилл судит написанное**. Там вход — дифф и дельта-спеки, здесь — область
|
||||||
|
кода и её история. Там исход — правки в том же прогоне, здесь — разговор и
|
||||||
|
задачи.
|
||||||
|
|
||||||
|
## Зачем он появился
|
||||||
|
|
||||||
|
Тяжёлые проходы стояли в цикле задачи: `review-adversary` строил путь и прогонял
|
||||||
|
падающий тест, `review-ops` снимал числа замером, `review-architecture` судил
|
||||||
|
форму решения на входе шире диффа. Первые двое держали машину и шли цепочкой,
|
||||||
|
третий требовал карты проекта; все трое стоили часов **на каждой задаче**, где
|
||||||
|
запускались, — при том что их ценность оплачивается на каждой, а получается на
|
||||||
|
немногих.
|
||||||
|
|
||||||
|
Их вынесли сюда целиком, и цикл задачи после этого проверяет **корректность и
|
||||||
|
механику**: заказанное против сделанного, дефект, который сработает сам,
|
||||||
|
конвенции проекта и сверку с записанными инвариантами `CLAUDE.md`. Темы
|
||||||
|
`security`, `operations` и `architecture` остались там ровно в объёме
|
||||||
|
инвариантов — свойства, которого в них нет, цикл не спросит.
|
||||||
|
|
||||||
|
**Вход этому скиллу копят проходы цикла.** Строка «отложено в
|
||||||
|
`av-dev:code-deep-review`» в границах покрытия называет тему, место и запуск,
|
||||||
|
которым это проверяется; триаж сводит такие строки в отдельную секцию отчёта.
|
||||||
|
Второй источник — сигнал «это изменение просит глубокого ревью»: его подаёт
|
||||||
|
`review-code` всегда и `review-basics`, когда запускается.
|
||||||
|
|
||||||
|
## Когда звать
|
||||||
|
|
||||||
|
**Зовёт человек**, и признак наблюдаемый, а не календарный:
|
||||||
|
|
||||||
|
- **накопился десяток задач в одной области** — по отдельности каждая прошла
|
||||||
|
обычный цикл, а вместе они переписали узел;
|
||||||
|
- **строки «отложено» скопились**: в отчётах ревью по одному месту повторяется
|
||||||
|
один и тот же неснятый замер;
|
||||||
|
- **перед дорогим решением**, которое обопрётся на этот узел;
|
||||||
|
- **после инцидента** — когда уже известно, где болит, и надо понять, что рядом;
|
||||||
|
- **узел, в который возвращаются третий раз**: цикл задачи проверяет его каждый
|
||||||
|
раз заново и одним и тем же составом, а здесь это повод посмотреть узел целиком.
|
||||||
|
|
||||||
|
**Не на задаче и не по расписанию.** Цена реальная: два прохода держат машину и
|
||||||
|
идут цепочкой, вход шире диффа собирается командой проекта, а разбор находок
|
||||||
|
требует человека. Прогон по каждой задаче был бы ровно той церемонией, ради
|
||||||
|
снятия которой проходы отсюда и переехали.
|
||||||
|
|
||||||
|
## Чего может не быть
|
||||||
|
|
||||||
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
|
поведении.
|
||||||
|
|
||||||
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
|
|
||||||
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
|
<!-- /копия: отсутствие -->
|
||||||
|
|
||||||
|
**Каталога задач нет** — находки остаются списком в докладе, и это говорится
|
||||||
|
строкой: заводить их некуда, а держать в голове до следующего прогона нечем.
|
||||||
|
|
||||||
|
## Вход — область, а не дифф
|
||||||
|
|
||||||
|
**Область называет человек, и называет до запуска.** Пакет, слой, сервис,
|
||||||
|
capability — одним адресом или несколькими. Скилл область не выбирает сам: выбор
|
||||||
|
области и есть решение о том, во что вложить часы, и оно человеческое.
|
||||||
|
|
||||||
|
Область не названа — **спроси, а не бери репозиторий целиком**. Прогон по всему
|
||||||
|
проекту даёт находки, рассыпанные по местам, между которыми нет связи, а разбор
|
||||||
|
такого урожая не доводится до конца никогда.
|
||||||
|
|
||||||
|
К области собирается **корпус**:
|
||||||
|
|
||||||
|
| Что | Откуда | Зачем |
|
||||||
|
|---|---|---|
|
||||||
|
| код области целиком | адреса, названные человеком | вход всех проходов |
|
||||||
|
| история области | `git log` по этим путям | что переписывалось и сколько раз |
|
||||||
|
| отложенное | строки «отложено в `av-dev:code-deep-review`» из отчётов ревью | неснятые замеры и недостроенные пути |
|
||||||
|
| журнал дефектов | `docs/review.md` | что уже проскакивало мимо конвейера |
|
||||||
|
| дома тем | `docs/security.*`, `docs/architecture.*`, `docs/conventions.*` | против чего судить |
|
||||||
|
|
||||||
|
Отложенного нет вовсе — скажи это строкой. Пустой список значит либо что цикл
|
||||||
|
ничего не откладывал, либо что проходы не писали свою строку; вторая причина —
|
||||||
|
находка о процессе, и она идёт в доклад.
|
||||||
|
|
||||||
|
## Состав прогона
|
||||||
|
|
||||||
|
Состав **постоянный**, но глубина у проходов **разная, и это не небрежность**.
|
||||||
|
Доказательство дают те двое, что держат машину: `review-adversary` прогоняет
|
||||||
|
падающий тест, `review-ops` снимает числа замером. `review-architecture` и
|
||||||
|
`review-code` машину не держат — они дают **разбор на входе шире диффа**, и
|
||||||
|
выдать доказательство им нечем. Постоянен и состав цикла задачи, но он другой и
|
||||||
|
мельче: разница между скиллами не в старательности, а в том, что здесь запускают,
|
||||||
|
меряют и строят путь.
|
||||||
|
|
||||||
|
| Проход | Тема | Что делает |
|
||||||
|
|---|---|---|
|
||||||
|
| `review-adversary` | `security` | строит путь и **прогоняет** падающий тест |
|
||||||
|
| `review-ops` | `operations` | снимает числа замером: удержание, рост, деградация |
|
||||||
|
| `review-architecture` | `architecture` | концептуальная целостность на входе шире диффа |
|
||||||
|
| `review-code` | `conventions`, техника, инварианты | читает код **как код**, целиком, а не диффом; потолков здесь нет |
|
||||||
|
| `review-triage` | — | единственный сток: дедуп, оракулы, потолок |
|
||||||
|
|
||||||
|
**Гейта здесь нет, и это не пропуск.** Гейт судит изменение — красный он или
|
||||||
|
зелёный, к написанному месяц назад коду это не относится. Если гейт проекта
|
||||||
|
красный, скажи это строкой: находки о коде, который не собирается, стоят меньше.
|
||||||
|
|
||||||
|
**Цепочка за машину остаётся.** `review-adversary` и `review-ops` помечены
|
||||||
|
«держит машину» и идут друг за другом, а не разом: два прохода на одной машине
|
||||||
|
выдают числа, которые не воспроизведутся. Правило и его причина — дом в
|
||||||
|
`av-dev:code-review`, раздел «Кто держит машину». Здесь эта цена приемлема:
|
||||||
|
скилл идёт не на задаче, и часы у него есть.
|
||||||
|
|
||||||
|
`review-architecture` и `review-code` машину не держат — уходят первой волной,
|
||||||
|
разом.
|
||||||
|
|
||||||
|
**Задание каждому проходу собирается адресами**: область, дома его тем, контракт
|
||||||
|
находок, отложенные строки по его теме и признак «вход — область, а не дифф».
|
||||||
|
Проход, получивший привычное «суди дифф», сузит себя сам.
|
||||||
|
|
||||||
|
## Триаж — тот же, вход другой
|
||||||
|
|
||||||
|
`review-triage` сводит выводы всех проходов: дедуп по причине, оракул на всё
|
||||||
|
`critical` и `major`, понижение неподтверждённого до гипотезы, отсев вкусовщины,
|
||||||
|
ранжирование по ущербу × вероятности.
|
||||||
|
|
||||||
|
**Потолка в 7 пунктов здесь нет.** Он существует потому, что отчёт по задаче
|
||||||
|
читает тот, кто **молча реализует** прочитанное, и длинный список превращается в
|
||||||
|
разросшийся код. Здесь читатель — человек, и каждый пункт он разбирает вслух.
|
||||||
|
Вместо потолка — **порядок**: находки идут по убыванию ущерба, и разговор
|
||||||
|
начинается сверху.
|
||||||
|
|
||||||
|
План прогона триажу передаётся составом: перечень проходов и тем. Тема, не
|
||||||
|
вернувшая отчёта, называется в границах покрытия — правило то же, что в конвейере
|
||||||
|
задачи.
|
||||||
|
|
||||||
|
## Разбор с человеком — главный шаг
|
||||||
|
|
||||||
|
**Исход этого скилла — не правки, а согласованный список работ.** Ни одной
|
||||||
|
находки скилл не чинит сам, даже мелкой: правка по ходу разбора превращает
|
||||||
|
разговор в работу и съедает то время, ради которого прогон и затевался.
|
||||||
|
|
||||||
|
Находки разбираются **по одной, сверху вниз**, и по каждой человек говорит одно
|
||||||
|
из трёх:
|
||||||
|
|
||||||
|
- **берём** — находка становится задачей;
|
||||||
|
- **не берём** — с причиной; причина уезжает в журнал дефектов `docs/review.md`,
|
||||||
|
потому что отказ от находки это тоже решение о качестве;
|
||||||
|
- **не находка** — проход ошибся; это тоже строка журнала, и по ней потом видно,
|
||||||
|
какой проход даёт ложные срабатывания.
|
||||||
|
|
||||||
|
**Показывай находку целиком**, а не заголовком: оракул и последствие — это и есть
|
||||||
|
то, по чему человек решает. Заголовок без оракула читается как мнение.
|
||||||
|
|
||||||
|
**Длинный список разбирается порциями.** Десяток пунктов за раз — потолок
|
||||||
|
внимания, а не формальность; остальное ждёт следующей порции в том же прогоне.
|
||||||
|
|
||||||
|
## Задачи заводит `av-dev:task-track`
|
||||||
|
|
||||||
|
**Вызови Skill `av-dev:task-track`** и попроси завести задачи по согласованному
|
||||||
|
списку — у него на этот вход отдельный сценарий «задачи из ревью и аудита»: своя
|
||||||
|
нарезка, свой формат, свои правила дублей. Формулировку, оракул и происхождение
|
||||||
|
находки передавай **дословно**: пересказ теряет как раз оракул, а без него задача
|
||||||
|
превращается в пожелание.
|
||||||
|
|
||||||
|
Заводить записи руками, править индексы или придумывать свой формат нельзя —
|
||||||
|
мост между скиллами это вызов, а не путь к файлу.
|
||||||
|
|
||||||
|
## Запись в журнал ревью
|
||||||
|
|
||||||
|
**Прогон оставляет след в `docs/review.md`** — вызовом `av-dev:doc-sync`, который
|
||||||
|
владеет этим документом. В следе: область, состав проходов, что взято задачами,
|
||||||
|
что отвергнуто и почему, что проверить было невозможно.
|
||||||
|
|
||||||
|
**Второго вопроса здесь не задают, хотя `review.md` — документ рода «новое»**
|
||||||
|
(`av-dev:doc-sync`, «Два рода правок»): слово по каждой находке человек уже сказал
|
||||||
|
в разборе, и след цитирует ровно его решения. Правило то же, что у сужения
|
||||||
|
проверок: спрашивается новое, которое заметил ты, а не то, что человек только что
|
||||||
|
решил вслух.
|
||||||
|
|
||||||
|
Без этого следа второй прогон по той же области начнётся с нуля и предложит те же
|
||||||
|
находки, от которых человек уже отказался, — а отказ, не оставивший записи,
|
||||||
|
неотличим от непойманного.
|
||||||
|
|
||||||
|
## Доклад
|
||||||
|
|
||||||
|
- **область** — что смотрели, адресами;
|
||||||
|
- **состав прогона** — какие проходы шли, какие темы закрыты, какие нет;
|
||||||
|
- **находки** — сколько выжило после триажа, сколько взято задачами, сколько
|
||||||
|
отвергнуто с причиной;
|
||||||
|
- **заведённые задачи** — слагами, либо строка «каталога задач нет, список
|
||||||
|
остаётся в докладе»;
|
||||||
|
- **границы покрытия** — что проверить было невозможно: недоступный инструмент,
|
||||||
|
неподнимаемая зависимость, область, до которой не дошли;
|
||||||
|
- **отложенное, которое сняли** — какие строки «отложено» из отчётов ревью
|
||||||
|
закрыты этим прогоном.
|
||||||
|
|
||||||
|
## Тонкости
|
||||||
|
|
||||||
|
- **Прогон не правит код** — ни строки. Единственный его артефакт, кроме
|
||||||
|
разговора, это задачи и запись в журнале ревью.
|
||||||
|
- **Область меньше — прогон лучше.** Модуль разбирается до конца, сервис целиком
|
||||||
|
даёт список, который бросают на середине.
|
||||||
|
- **Находка о процессе — тоже находка.** Пустой список отложенного, дефект,
|
||||||
|
трижды проскочивший в одном узле, тема без дома — всё это идёт в доклад наравне
|
||||||
|
с находками о коде.
|
||||||
|
- **Задачи здесь нет, и границей служит только область.** Дифф, дельта-спеки,
|
||||||
|
критерии приёмки — всё это про задачу; сюда они не приходят, и подставлять их
|
||||||
|
«по аналогии» нельзя.
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
---
|
---
|
||||||
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, ни сверка требований."
|
||||||
---
|
---
|
||||||
|
|
||||||
# OpenSpec в проекте
|
# OpenSpec в проекте
|
||||||
|
|
||||||
Каталог `openspec/` — **предпосылка конвейера**, а не канона документов. Без него
|
Каталог `openspec/` — **предпосылка конвейера**, а не канона документов. Без него
|
||||||
не работают ни `opsx:propose`, ни ревью дизайна, ни `review-specs`: у требований
|
не работают ни `opsx:propose`, ни `review-specs`: у требований
|
||||||
не остаётся дома. Поэтому заводит и настраивает его этот плагин — тот, кто по
|
не остаётся дома. Поэтому заводит и настраивает его этот скилл — тот, кто по
|
||||||
OpenSpec и работает.
|
OpenSpec и работает.
|
||||||
|
|
||||||
Канон документов о файле не высказывается вовсе: `docs.py` его не открывает и об
|
Канон документов о файле не высказывается вовсе: `docs.py` его не открывает и об
|
||||||
его отсутствии молчит. Проект без конвейера живёт без OpenSpec законно, и
|
его отсутствии молчит. Проект, не ведущий задачи циклом SDD, живёт без OpenSpec
|
||||||
|
законно, и
|
||||||
проверять там нечего. За каноном остаётся одно — **единственный дом**: не
|
проверять там нечего. За каноном остаётся одно — **единственный дом**: не
|
||||||
пересказан ли в `context` документ, у которого есть свой файл. Это суждение, а не
|
пересказан ли в `context` документ, у которого есть свой файл. Это суждение, а не
|
||||||
форма, и смотрит его агент.
|
форма, и смотрит его агент.
|
||||||
@@ -44,20 +45,20 @@ openspec init --tools claude
|
|||||||
проекта.
|
проекта.
|
||||||
|
|
||||||
Сюда же — **требования к форме `proposal` и `design`**, на которых стоит чекпоинт
|
Сюда же — **требования к форме `proposal` и `design`**, на которых стоит чекпоинт
|
||||||
скилла `av-dev-code:resolve`: объяснение человеку собирается из этих двух
|
скилла `av-dev:code-resolve`: объяснение человеку собирается из этих двух
|
||||||
артефактов, и требование к ним обязано применяться в момент, когда их пишут, а не
|
артефактов, и требование к ним обязано применяться в момент, когда их пишут, а не
|
||||||
вспоминаться шагом позже. Образец их содержит.
|
вспоминаться шагом позже. Образец их содержит.
|
||||||
|
|
||||||
Пересказ паспорта, инвариантов, конвенций и правил ревью сюда **не переносится**.
|
Пересказ паспорта, инвариантов, конвенций и правил ревью сюда **не переносится**.
|
||||||
Место для второго дома здесь самое частое: `context` читается при порождении
|
Место для второго дома здесь самое частое: `context` читается при порождении
|
||||||
каждого артефакта, туда удобно дописать «чтобы агент знал», и так заводятся копии
|
каждого артефакта, туда удобно дописать «чтобы агент знал», и так заводятся копии
|
||||||
инвариантов, состава гейта и правил выбора метки. Расходятся они молча, а
|
инвариантов, состава гейта и правил ревью. Расходятся они молча, а
|
||||||
замечают это в уже написанном предложении.
|
замечают это в уже написанном предложении.
|
||||||
|
|
||||||
Разрез, по которому отличают одно от другого: **утверждение, которое можно
|
Разрез, по которому отличают одно от другого: **утверждение, которое можно
|
||||||
опровергнуть, открыв другой файл проекта, — пересказ; строка, которая говорит,
|
опровергнуть, открыв другой файл проекта, — пересказ; строка, которая говорит,
|
||||||
какой файл открыть, — ссылка.** Машина этот разрез не проверяет; его смотрит
|
какой файл открыть, — ссылка.** Машина этот разрез не проверяет; его смотрит
|
||||||
агент `doc-consistency` из плагина канона, когда тот подключён.
|
агент `doc-consistency`, когда документы канона в проекте есть.
|
||||||
|
|
||||||
Два адреса обязательны — `docs/passport.md` и `CLAUDE.md`: предложение пишется до
|
Два адреса обязательны — `docs/passport.md` и `CLAUDE.md`: предложение пишется до
|
||||||
того, как кто-либо откроет `docs/`, и без них его пишут, не зная ни границы
|
того, как кто-либо откроет `docs/`, и без них его пишут, не зная ни границы
|
||||||
@@ -67,16 +68,36 @@ 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
|
||||||
```
|
```
|
||||||
|
|
||||||
**Коды выхода — общий словарь скриптов av-dev:** 0 сошлось, 1 дрейф, 2 ошибка
|
**Копия.** Дом словаря — `shared/axes.md` в репозитории плагина: словарь общий
|
||||||
употребления, 3 окружение, 4 внутренний сбой. Ветвись на коде, а не на тексте.
|
для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл.
|
||||||
Различать 1 и 3 обязательно: «форма разошлась» — рабочая ситуация, «openspec не
|
|
||||||
отвечает» — нерабочая.
|
<!-- копия: коды-выхода из av-dev/shared/axes.md -->
|
||||||
|
|
||||||
|
**Коды выхода — общий словарь всех скриптов `av-dev`. Ветвись на коде, а не на
|
||||||
|
тексте вывода.**
|
||||||
|
|
||||||
|
| Код | Что случилось |
|
||||||
|
| --- | --- |
|
||||||
|
| 0 | сошлось |
|
||||||
|
| 1 | дрейф: рабочая ситуация, чинится |
|
||||||
|
| 2 | ошибка употребления: аргументы или нарушенное правило |
|
||||||
|
| 3 | окружение: не тот каталог, битый конфиг, нет инструмента |
|
||||||
|
| 4 | внутренний сбой — дефект скрипта, доложить |
|
||||||
|
|
||||||
|
**Различать 1 и 3 обязательно.** «Дрейф» — рабочая ситуация, и чинится она
|
||||||
|
правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет.
|
||||||
|
Одинаковая реакция на них неверна в обоих случаях.
|
||||||
|
|
||||||
|
<!-- /копия: коды-выхода -->
|
||||||
|
|
||||||
|
Здесь это значит: «форма разошлась» — рабочая ситуация, «openspec не отвечает» —
|
||||||
|
нерабочая.
|
||||||
|
|
||||||
`check` проверяет форму, и каждая проверка — про молчащий пробел, а не про вкус:
|
`check` проверяет форму, и каждая проверка — про молчащий пробел, а не про вкус:
|
||||||
каталог есть; имя именно `config.yaml` (`config.yml` OpenSpec не читает и об этом
|
каталог есть; имя именно `config.yaml` (`config.yml` OpenSpec не читает и об этом
|
||||||
@@ -87,9 +108,9 @@ python3 $os form # слепок формы против жив
|
|||||||
имена артефактов схемы, а не свободные слова. Числа проверок здесь нет намеренно:
|
имена артефактов схемы, а не свободные слова. Числа проверок здесь нет намеренно:
|
||||||
оно протухает от каждой добавленной.
|
оно протухает от каждой добавленной.
|
||||||
|
|
||||||
**Адреса требуются только к тем документам, которые в проекте есть.** Канон
|
**Адреса требуются только к тем документам, которые в проекте есть.** Документы
|
||||||
документов ставится отдельным плагином и может быть не подключён; требовать
|
канона могут быть не заведены; требовать ссылку на несуществующий файл значит
|
||||||
ссылку на несуществующий файл значит требовать битую ссылку. Нет
|
требовать битую ссылку. Нет
|
||||||
`docs/passport.md` — проверка по нему идёт строкой «не проверялось», и там же
|
`docs/passport.md` — проверка по нему идёт строкой «не проверялось», и там же
|
||||||
сказано, что без канона конвейер работает вслепую.
|
сказано, что без канона конвейер работает вслепую.
|
||||||
|
|
||||||
@@ -116,54 +137,61 @@ python3 $os form # слепок формы против жив
|
|||||||
|
|
||||||
## Кто зовёт этот скилл
|
## Кто зовёт этот скилл
|
||||||
|
|
||||||
- `av-dev-docs:init` — шагом заведения нового проекта, до первого документа;
|
- `av-dev:doc-init` — шагом заведения нового проекта, до первого документа;
|
||||||
- `av-dev-docs:canon` в режиме `adopt` — если на переводимом проекте каталога нет
|
- `av-dev: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: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:canon`, и адреса в
|
||||||
`context` только на них ссылаются.
|
`context` только на них ссылаются.
|
||||||
- **Не чинит расхождение формы с версией OpenSpec в проекте.** Оно чинится в
|
- **Не чинит расхождение формы с версией OpenSpec в проекте.** Оно чинится в
|
||||||
плагине: константы скрипта, образец здесь, запись в журнал версий канона.
|
плагине: константы скрипта, образец здесь, запись в журнал версий канона.
|
||||||
- **Не судит, ссылается `context` на документы или пересказывает их.** Машине
|
- **Не судит, ссылается `context` на документы или пересказывает их.** Машине
|
||||||
этот разрез не виден; его смотрит агент `doc-consistency` из плагина канона.
|
этот разрез не виден; его смотрит агент `doc-consistency`. Документов канона в
|
||||||
Плагина нет — эту проверку не делает никто, и так и скажи.
|
проекте нет — сверять пересказ не с чем, и так и скажи.
|
||||||
+5
-7
@@ -43,8 +43,8 @@ context: |
|
|||||||
Пересказа этих документов здесь нет намеренно: второй дом факта расходится с
|
Пересказа этих документов здесь нет намеренно: второй дом факта расходится с
|
||||||
первым молча, и заметно это становится в предложении, которое уже написано.
|
первым молча, и заметно это становится в предложении, которое уже написано.
|
||||||
|
|
||||||
Ревью: правило выбора метки и состав проходов здесь не пересказываем — их дом
|
Ревью: состав проходов и глубину тем здесь не пересказываем — их дом скилл
|
||||||
скилл av-dev-code:review, проектная настройка — docs/review.md.
|
av-dev:code-review, проектная настройка — docs/review.md.
|
||||||
|
|
||||||
Конвенции кода: механизированное проверяет гейт, прозой остаётся
|
Конвенции кода: механизированное проверяет гейт, прозой остаётся
|
||||||
docs/conventions/. Ни состав шагов гейта, ни перечень конвенций здесь не
|
docs/conventions/. Ни состав шагов гейта, ни перечень конвенций здесь не
|
||||||
@@ -69,7 +69,6 @@ rules:
|
|||||||
- "Заголовки и WHEN/THEN/GIVEN — на английском, остальной текст на русском"
|
- "Заголовки и WHEN/THEN/GIVEN — на английском, остальной текст на русском"
|
||||||
tasks:
|
tasks:
|
||||||
- "Критерии приёмки задачи — отдельным блоком и дословно: файл задачи закрытие удалит, критерии обязаны его пережить"
|
- "Критерии приёмки задачи — отдельным блоком и дословно: файл задачи закрытие удалит, критерии обязаны его пережить"
|
||||||
- "Рубрика ревью дизайна, если оно её дало, идёт в тот же блок: приёмка судится по одному списку, а не по двум"
|
|
||||||
- "Шаг плана формулируется проверяемо — по нему видно «сделано / не сделано» без суждения"
|
- "Шаг плана формулируется проверяемо — по нему видно «сделано / не сделано» без суждения"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -78,7 +77,7 @@ rules:
|
|||||||
узнаёт их падением `openspec validate --strict`.
|
узнаёт их падением `openspec validate --strict`.
|
||||||
|
|
||||||
**Правила для `proposal` и `design` держат чекпоинт скилла
|
**Правила для `proposal` и `design` держат чекпоинт скилла
|
||||||
`av-dev-code:resolve`.** Там работа останавливается и человеку объясняют, в
|
`av-dev:code-resolve`.** Там работа останавливается и человеку объясняют, в
|
||||||
чём проблема и как её решают, — а объяснение **собирается из этих двух
|
чём проблема и как её решают, — а объяснение **собирается из этих двух
|
||||||
артефактов**, а не сочиняется заново: третий пересказ одного и того же разошёлся
|
артефактов**, а не сочиняется заново: третий пересказ одного и того же разошёлся
|
||||||
бы с обоими. Требование поэтому стоит здесь, в момент порождения артефакта, а не
|
бы с обоими. Требование поэтому стоит здесь, в момент порождения артефакта, а не
|
||||||
@@ -88,9 +87,8 @@ ADR** — отвергнутый вариант с названной причи
|
|||||||
**Правила для `tasks` держит тот же скилл, и по той же причине — момент
|
**Правила для `tasks` держит тот же скилл, и по той же причине — момент
|
||||||
порождения.** `tasks.md` — единственное, что переживает задачу: файл задачи
|
порождения.** `tasks.md` — единственное, что переживает задачу: файл задачи
|
||||||
закрытие удаляет, а приёмка потом судится по критериям, которые в него
|
закрытие удаляет, а приёмка потом судится по критериям, которые в него
|
||||||
скопированы. Туда же ложится рубрика ревью дизайна, если оно её дало. Записанное
|
скопированы. Записанное в момент порождения не приходится вспоминать шагом позже,
|
||||||
в момент порождения не приходится вспоминать шагом позже, когда артефакт уже
|
когда артефакт уже написан. Блок `context` проект
|
||||||
написан. Блок `context` проект
|
|
||||||
дополняет своим (стек, разведка, особенности домена), но **адреса паспорта и
|
дополняет своим (стек, разведка, особенности домена), но **адреса паспорта и
|
||||||
`CLAUDE.md` обязательны** — отсутствие адреса к существующему документу
|
`CLAUDE.md` обязательны** — отсутствие адреса к существующему документу
|
||||||
`openspec.py check` называет отказом.
|
`openspec.py check` называет отказом.
|
||||||
+9
-13
@@ -2,9 +2,9 @@
|
|||||||
"""Форма `openspec/config.yaml`: проверка проекта и сверка слепка с инструментом.
|
"""Форма `openspec/config.yaml`: проверка проекта и сверка слепка с инструментом.
|
||||||
|
|
||||||
Каталог `openspec/` — предпосылка **конвейера**, а не канона документов: без него
|
Каталог `openspec/` — предпосылка **конвейера**, а не канона документов: без него
|
||||||
не работают ни `opsx:propose`, ни ревью дизайна, ни сверка требований. Поэтому и
|
не работают ни `opsx:propose`, ни сверка требований конвейером. Поэтому и
|
||||||
проверка формы живёт здесь, рядом со скиллом, который каталог заводит. Раньше она
|
проверка формы живёт здесь, рядом со скиллом, который каталог заводит. Раньше она
|
||||||
жила в `docs.py` плагина канона, и у файла было два владельца: один заводит,
|
жила в `docs.py`, у скилла канона, и у файла было два владельца: один заводит,
|
||||||
другой проверяет.
|
другой проверяет.
|
||||||
|
|
||||||
Проверяется то, что **молчит при поломке**. Файл из коробки хуже отсутствующего:
|
Проверяется то, что **молчит при поломке**. Файл из коробки хуже отсутствующего:
|
||||||
@@ -16,12 +16,8 @@
|
|||||||
PyYAML в стандартной библиотеке нет. Всё проверяемое различимо построчно,
|
PyYAML в стандартной библиотеке нет. Всё проверяемое различимо построчно,
|
||||||
комментарии отброшены, ключи верхнего уровня стоят в первой колонке.
|
комментарии отброшены, ключи верхнего уровня стоят в первой колонке.
|
||||||
|
|
||||||
Коды выхода — общий словарь скриптов av-dev:
|
Коды выхода — общий словарь скриптов av-dev; дом словаря и разбор «дрейф
|
||||||
0 сошлось
|
против окружения» — av-dev/shared/axes.md. Значения — в константах ниже.
|
||||||
1 дрейф: форма разошлась с ожидаемой
|
|
||||||
2 ошибка употребления: аргументы
|
|
||||||
3 окружение: не тот каталог, инструмент не отвечает
|
|
||||||
4 внутренний сбой
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -114,7 +110,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 +215,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:canon"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
if pointer not in live:
|
if pointer not in live:
|
||||||
@@ -289,8 +285,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)} пунктов.")
|
||||||
@@ -0,0 +1,552 @@
|
|||||||
|
---
|
||||||
|
name: code-resolve
|
||||||
|
description: "Взять одну задачу и довести её до закрытия. Одна точка входа, три сценария, и выбирает сценарий сам скилл, прочитав постановку. Способ известен и меняется поведение — сценарий решения: цикл Spec Driven Development (opsx propose → чекпоинт с объяснением человеческим языком, где форму решения одобряет человек → opsx apply → ревью кода постоянным составом → archive и отражение в документах молча → одна реплика о новом, где человек решает, что заводится: ADR, конвенция, задачи из урожая ревью → письмо одобренного → коммит → закрытие). Плановых стопов у сценария два, и оба про решения человека: чекпоинт до кода решает форму решения, реплика после кода — что из найденного переживёт задачу. Способ известен, а спека не меняется (тип chore: тулчейн, зависимости, сборка, гит-хуки, перенос, чистка) — сценарий обслуживания: правка → гейт со сверкой состава проверок → ревью фиксированным планом без change (autotests, operations, плюс conventions и техника, если тронут код) → синк документации, где почти всё письмо — отражение фактов и идёт молча → коммит → закрытие; планового стопа нет, change не заводится. Нашлась дельта-спека — задача оказалась шире своего типа: стоп с объяснением простым языком и двумя решениями человека, переформулировать запись в fix или feature и решать её процессом того типа следующим прогоном либо прекратить работу. Способа нет, постановка мутная, тип research — сценарий разведки: вопрос и рамки → чтение документов, кода и внешних источников (можно opsx:explore) → чекпоинт вариантов: 2–4 способа решить, цена каждого, что становится невозможным, рекомендация → ответ уезжает в документы канона, исход — в задачи → вычитка написанного → коммит → закрытие. Разведка кода не пишет и change не заводит, а выбранный способ реализуется следующим прогоном. На входе путь к файлу задачи, её слаг или просто текст постановки: размеченная запись не обязательна — текст берётся так же, как его берёт opsx:propose, и текстом идут все три сценария. Использовать, когда просят взять, сделать или решить задачу — хоть записью из каталога, хоть описанием прямо в разговоре, — обновить зависимости или сборку, разобраться, изучить, сравнить подходы, проработать сырую идею, ответить на вопрос из беклога."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Работа над одной задачей
|
||||||
|
|
||||||
|
Проводит **одну** задачу от постановки до закрытия. Вокруг планового стопа — без
|
||||||
|
согласований: механику не обсуждаем, делаем.
|
||||||
|
|
||||||
|
**Сценария три, а точка входа одна.** Какой из них идёт, решает **скилл**,
|
||||||
|
прочитав постановку, а не человек до вызова: «есть ли у задачи очевидный способ
|
||||||
|
решения» и «меняется ли то, что записано в спеке» видно после чтения записи, и
|
||||||
|
требовать этих суждений от вызывающего значит требовать их раньше, чем они
|
||||||
|
возможны.
|
||||||
|
|
||||||
|
| Сценарий | Когда | Чем кончается |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **решение** | способ известен, меняется поведение | код, ревью, архив, коммит, закрытие |
|
||||||
|
| **обслуживание** | способ известен, спека не меняется: тип `chore` | правка, ревью, синк, коммит, закрытие |
|
||||||
|
| **разведка** | способа нет: тип `research`, сырая идея, мутная постановка | ответ в документах, задачи, коммит, закрытие |
|
||||||
|
|
||||||
|
Ход каждого сценария живёт своим справочником: **решение** —
|
||||||
|
[references/solve.md](references/solve.md), **обслуживание** —
|
||||||
|
[references/maintain.md](references/maintain.md), **разведка** —
|
||||||
|
[references/research.md](references/research.md). Здесь только общее: вход,
|
||||||
|
развилка, правила, которые не зависят от сценария. Сценарии лежат порознь и
|
||||||
|
одинаково, потому что привилегированного среди них нет: тот, что жил бы прямо
|
||||||
|
здесь, читался бы как основной, а прочие — как оговорка.
|
||||||
|
|
||||||
|
## Предпосылки
|
||||||
|
|
||||||
|
- **OpenSpec и скиллы `opsx:*` — жёсткая предпосылка сценария решения**, а не
|
||||||
|
опция. На них стоят его шаги 2, 4 и 6 и проход `review-specs`
|
||||||
|
(они завязаны на `openspec/changes/<id>/specs/*/spec.md` и на
|
||||||
|
`openspec validate --strict`). **Проект без OpenSpec этим скиллом не ведётся** —
|
||||||
|
подключай OpenSpec, а не вырождай цикл сценария; почему ветка деградации здесь
|
||||||
|
не
|
||||||
|
пишется, сказано в `av-dev:code-review`, раздел «Предпосылки», и дом у этого
|
||||||
|
довода там. Заводить руками не надо: каталог и настройку в `config.yaml`
|
||||||
|
делает скилл `av-dev:code-openspec`. **Сценариям разведки и обслуживания
|
||||||
|
OpenSpec не нужен** — они не заводят change; `opsx:explore` берётся разведкой,
|
||||||
|
если плагин есть.
|
||||||
|
- **Проектные копии этих скиллов и агентов удаляются при установке плагина.**
|
||||||
|
|
||||||
|
<!-- копия: проектные-копии из README.md -->
|
||||||
|
|
||||||
|
Голые имена в `.claude/skills/`: `resolve`, `review`, а у проектов прошлого
|
||||||
|
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`. С префиксом
|
||||||
|
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`. Агенты:
|
||||||
|
`.claude/agents/<проект>-review-*.md`.
|
||||||
|
|
||||||
|
Две копии одного скилла расходятся, и побеждает та, что **короче названа**:
|
||||||
|
короткое имя разрешится в устаревшую проектную копию молча и без признаков
|
||||||
|
подмены.
|
||||||
|
|
||||||
|
<!-- /копия: проектные-копии -->
|
||||||
|
|
||||||
|
### Чего может не быть
|
||||||
|
|
||||||
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина: правило
|
||||||
|
общее для всех, кто приходит в чужой проект, и ни один скилл им не владеет.
|
||||||
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
|
поведении.
|
||||||
|
|
||||||
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
|
|
||||||
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
|
<!-- /копия: отсутствие -->
|
||||||
|
|
||||||
|
Скилл зовёт `av-dev:code-review`, `av-dev:doc-sync` и `av-dev:task-track` —
|
||||||
|
все трое в этом же плагине и разрешаются всегда. Чем оборачивается отсутствие
|
||||||
|
части раскладки, под которую они работают, сказано на самих шагах сценариев.
|
||||||
|
|
||||||
|
Перед стартом прочитай `CLAUDE.md` проекта и то, на что он ссылается, если ещё
|
||||||
|
не в контексте. Проектные факты, нужные ревью — инварианты, семантика гейта,
|
||||||
|
объёмы, модель угроз, прецеденты, — живут в **документах канона**;
|
||||||
|
карта «что где» — `references/project-facts.md` конвейера ревью.
|
||||||
|
|
||||||
|
**Документов канона нет — проект к нему не приведён.** Скажи это строкой и
|
||||||
|
предложи скилл `av-dev:canon`: одна операция на проект против поразрядной
|
||||||
|
деградации на каждой задаче. Работу при этом не останавливай.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
Задача задаётся **путём к файлу, именем файла, слагом или просто текстом**.
|
||||||
|
Ничего из этого не задано — попроси у вызывающего и остановись; сам в беклог не
|
||||||
|
лезь и приоритеты не интерпретируй: что делать дальше, решает не этот скилл.
|
||||||
|
|
||||||
|
**Форм постановки две, и обе полноправны:** запись каталога задач и текст,
|
||||||
|
переданный вызовом. Форма — не сценарий: развилка ниже у них общая, и текст
|
||||||
|
принимают все три сценария.
|
||||||
|
|
||||||
|
### Запись из каталога
|
||||||
|
|
||||||
|
**Запись сперва проверяется на готовность, и проверяет её машина.**
|
||||||
|
Вызови Skill `av-dev:task-track` и попроси прогнать `ready <слаг>`: он смотрит
|
||||||
|
тип, пустой ли раздел вопросов и собраны ли разделы схемы типа. Судить это глазами нельзя — ровно тот случай, где машина дешевле и точнее,
|
||||||
|
а цена ошибки отложенная: недостающие критерии приёмки обнаружатся на приёмке,
|
||||||
|
когда сверять уже не с чем.
|
||||||
|
|
||||||
|
**`ready` отказал — это исход, а не препятствие для тебя.** Скажи, чего не
|
||||||
|
хватает, и остановись: дописывать чужую запись за автора не твоя работа. Исход —
|
||||||
|
«не доведена», с названной причиной.
|
||||||
|
|
||||||
|
**Отказ `ready` сценарий не выбирает.** Запись `research` без раздела «Вопрос»
|
||||||
|
(сырьё) — отказ и здесь: у неё нет вопроса, и разведывать нечего.
|
||||||
|
|
||||||
|
Каталога задач в проекте нет — прогонять нечего, и постановка приходит текстом
|
||||||
|
по построению: дальше по разделу ниже.
|
||||||
|
|
||||||
|
### Постановка текстом
|
||||||
|
|
||||||
|
**Текст — вход, а не урезанный режим.** Ровно так берёт постановку
|
||||||
|
`opsx:propose`: предложение делается из фразы человека, а не из заранее
|
||||||
|
размеченной записи. Требовать записи там, где работа уместилась в разговор,
|
||||||
|
значит заводить учёт ради учёта — след у прогона остаётся и без неё: коммит, а у
|
||||||
|
решения ещё и заархивированный change.
|
||||||
|
|
||||||
|
**Первой репликой покажи, как ты понял постановку** — рядом с названным
|
||||||
|
сценарием, одной-двумя фразами: что считаешь предметом работы и где проводишь
|
||||||
|
границу. Запись толкуется по разделам, текст — молча, и расходится он с замыслом
|
||||||
|
ровно там, где его никто не показал. Человек, написавший текст, сидит в этом же
|
||||||
|
разговоре и поправляет одной фразой; автора записи, написанной месяц назад,
|
||||||
|
рядом нет, и потому текстовая постановка проверяется дешевле, а не хуже.
|
||||||
|
|
||||||
|
Что несёт запись и чем это заменяется, когда её нет:
|
||||||
|
|
||||||
|
| Что несёт запись | Чем заменяется у текста |
|
||||||
|
| --- | --- |
|
||||||
|
| готовность, проверенную машиной | читаешь постановку сам и говоришь строкой, что `ready` не гонялся |
|
||||||
|
| тип, объявленный автором | тип называешь ты — вслух, первой репликой, вместе со сценарием |
|
||||||
|
| критерии приёмки с оракулами | те, что есть в тексте; недостающие [решение](references/solve.md) добирает на чекпоинте, [обслуживание](references/maintain.md) объявляет строкой отсутствующими |
|
||||||
|
| адрес, куда ляжет ответ разведки | назначаешь сам и по канону, а не по удобству — [research.md](references/research.md), шаг 1 |
|
||||||
|
| закрытие как след работы | закрывать нечего, и шаг закрытия отпадает вместе с записью |
|
||||||
|
|
||||||
|
**Записи в каталог этот скилл не заводит — ни перед работой, ни задним числом
|
||||||
|
ради закрытия.** Граница «беклогом не владеет» действует и здесь. Работа не
|
||||||
|
уместилась в прогон, её надо ставить в очередь или из неё выросла пачка — скажи
|
||||||
|
это строкой и предложи `av-dev:task-track`: заводит он и по своим правилам.
|
||||||
|
|
||||||
|
**Похожую запись в беклоге не ищешь.** Человек назвал работу текстом — значит,
|
||||||
|
предмет прогона этот текст, а не строка индекса, которая на него похожа.
|
||||||
|
Наткнулся на такую строку по ходу — скажи о ней строкой доклада и не закрывай:
|
||||||
|
закрытие записи это приёмка, и поручали её не тебе.
|
||||||
|
|
||||||
|
## Развилка: какой сценарий
|
||||||
|
|
||||||
|
Сценарий — ось процесса; перечень осей и их границ —
|
||||||
|
[shared/axes.md](../../shared/axes.md).
|
||||||
|
|
||||||
|
Она в два вопроса, и оба стоят до всякой работы.
|
||||||
|
|
||||||
|
**Первый: есть ли у задачи один очевидный способ решения?**
|
||||||
|
|
||||||
|
- **нет** — тип `research`, сырая идея, новое и незнакомое, мутная постановка,
|
||||||
|
два подхода с разной ценой. **Сценарий разведки** —
|
||||||
|
[references/research.md](references/research.md);
|
||||||
|
- **есть** — что делать, понятно; спорно только как. Тогда второй вопрос.
|
||||||
|
|
||||||
|
**Второй: меняется ли то, что записано в `openspec/specs/`?**
|
||||||
|
|
||||||
|
- **меняется** — появляется или правится поведение. **Сценарий решения** —
|
||||||
|
[references/solve.md](references/solve.md);
|
||||||
|
- **не меняется** — тулчейн и сборка, зависимости, гит-хуки и шаги гейта,
|
||||||
|
перенос, чистка. **Сценарий обслуживания** —
|
||||||
|
[references/maintain.md](references/maintain.md).
|
||||||
|
|
||||||
|
**Второй вопрос решается связкой из двух признаков, и оба обязательны:** тип
|
||||||
|
записи предлагает (`chore`, реже `fix`, возвращающий поведение к уже
|
||||||
|
записанному), а отсутствие дельт подтверждает. Тип объявляет автор и может
|
||||||
|
ошибиться; отсутствие дельт — твоё суждение и принимается только вместе с типом.
|
||||||
|
Признаки разошлись — это стоп, а не выбор: скажи, что тип и предмет работы не
|
||||||
|
сходятся, и остановись. Подробно — [maintain.md](references/maintain.md), раздел
|
||||||
|
«Признак — связка, а не одно условие».
|
||||||
|
|
||||||
|
**Признак не в объёме работы, и это относится к обоим вопросам.** Крупная задача
|
||||||
|
с очевидным способом идёт в решение; маленькая, но незнакомая — в разведку;
|
||||||
|
однострочная правка, меняющая поведение, идёт полным циклом решения, а не
|
||||||
|
обслуживанием. Путь, выбираемый по самооценке размера, — самый дешёвый способ
|
||||||
|
«ускориться» и самый дорогой по последствиям. Тип `research` в разведку идёт
|
||||||
|
всегда: её исход знание, а не изменение системы.
|
||||||
|
|
||||||
|
**Назови выбранный сценарий вслух первой репликой** — одной строкой, с причиной.
|
||||||
|
Молча выбранный сценарий человек обнаруживает по тому, что работа пошла не туда,
|
||||||
|
и обнаруживает поздно.
|
||||||
|
|
||||||
|
### Сценарий выбирается один раз
|
||||||
|
|
||||||
|
**Смена сценария по ходу — событие, а не тихий поворот**, и каждая смена
|
||||||
|
устроена по-своему:
|
||||||
|
|
||||||
|
- **решение → разведка**: обнаружилось, что очевидного способа нет. Это **стоп**
|
||||||
|
с исходом «нужна разведка»: назови, что именно неясно, и не продолжай.
|
||||||
|
Кода к этому моменту не написано, и писать его «пока разбираемся» нельзя;
|
||||||
|
- **обслуживание → решение**: нашлась дельта-спека, то есть поведение всё-таки
|
||||||
|
меняется. Задача не сломалась — она **оказалась шире своего типа**, и стоп
|
||||||
|
здесь несёт человеку выбор: назови тип, которым она оказалась (`fix` —
|
||||||
|
расходится с заявленным, `feature` — снаружи появляется то, чего не было),
|
||||||
|
объясни простым языком, что нашлось, и дай два решения — **переформулировать
|
||||||
|
запись и решать процессом того типа следующим прогоном** либо **прекратить
|
||||||
|
работу**. Третьего — «доделать как обслуживание» — нет. Исход в обоих случаях
|
||||||
|
«меняется спека»; сделанное остаётся в рабочем дереве незакоммиченным, тип
|
||||||
|
меняет `av-dev:task-track` и только после ответа. Подробно —
|
||||||
|
[maintain.md](references/maintain.md), раздел «Дельта нашлась по ходу»;
|
||||||
|
- **обслуживание → разведка**: форма правки неизвестна (мажорное обновление,
|
||||||
|
смена сборщика). **Стоп** с исходом «нужна разведка», по тому же основанию,
|
||||||
|
что и у решения;
|
||||||
|
- **разведка → решение или обслуживание**: способ выбран на чекпоинте вариантов.
|
||||||
|
Разведка **всё равно доводится до конца** — ответ записан, задачи уточнены,
|
||||||
|
коммит сделан, — и работа идёт **следующим прогоном**, который запускает
|
||||||
|
человек.
|
||||||
|
|
||||||
|
Обратной смены «решение → обслуживание» нет: задача, заведшая change, доводится
|
||||||
|
циклом решения. Дельта-спеки, оказавшиеся пустыми, — повод назвать это на
|
||||||
|
чекпоинте, а не свернуть на короткий путь из середины длинного.
|
||||||
|
|
||||||
|
**Соблазн «разведаю по ходу» живёт именно здесь**, и он дорог тем, что выглядит
|
||||||
|
экономией одного прогона. Разведка внутри решения не имеет своего чекпоинта:
|
||||||
|
выбор делается тем, кто уже начал писать, и человек видит его только в
|
||||||
|
объяснении, где обсуждать выбор поздно. Ровно за это сценарии и разведены — не за
|
||||||
|
то, что это разные работы, а за то, что у них разные моменты для человека.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
in["вход: файл, слаг или текст"]
|
||||||
|
form{"форма постановки"}
|
||||||
|
ready["ready: готовность записи<br/>av-dev:task-track"]
|
||||||
|
plain["понимание, тип и границы —<br/>первой репликой; ready не гонится,<br/>закрывать потом нечего"]
|
||||||
|
fork{"есть очевидный<br/>способ решения?"}
|
||||||
|
fork2{"меняется ли<br/>спека?"}
|
||||||
|
solve["сценарий решения<br/>references/solve.md<br/>код, ревью, архив, коммит"]
|
||||||
|
main["сценарий обслуживания<br/>references/maintain.md<br/>правка, ревью, синк, коммит"]
|
||||||
|
res["сценарий разведки<br/>references/research.md<br/>ответ в документы и задачи"]
|
||||||
|
|
||||||
|
in --> form
|
||||||
|
form -->|"запись каталога"| ready --> fork
|
||||||
|
form -->|"текст"| plain --> fork
|
||||||
|
fork -->|"да"| fork2
|
||||||
|
fork -->|"нет"| res
|
||||||
|
fork2 -->|"да"| solve
|
||||||
|
fork2 -->|"нет: тип chore"| main
|
||||||
|
solve -.->|"способа всё же нет:<br/>стоп, кода не написано"| res
|
||||||
|
main -.->|"нашлась дельта: стоп,<br/>тип на fix или feature,<br/>следующим прогоном"| solve
|
||||||
|
main -.->|"форма неизвестна:<br/>стоп"| res
|
||||||
|
res -.->|"способ выбран:<br/>следующим прогоном,<br/>зовёт человек"| solve
|
||||||
|
```
|
||||||
|
|
||||||
|
Схема — **сводка**: содержание сценариев в их справочниках, и при расхождении
|
||||||
|
прав справочник.
|
||||||
|
|
||||||
|
## Кто пишет: письмо уходит агентам
|
||||||
|
|
||||||
|
**Своими руками этот скилл не пишет ничего** — ни спек, ни кода, ни правок по
|
||||||
|
находкам ревью. Каждую такую работу выполняет **отдельный агент**: оркестратор
|
||||||
|
ставит задание и читает возврат. Дальше эта работа зовётся **письмом** — всё, что
|
||||||
|
скилл написал бы сам, если бы писал.
|
||||||
|
|
||||||
|
Причина про контекст, и она одна. Оркестратор ведёт задачу целиком: выбрал
|
||||||
|
сценарий, собирает чекпоинт, сверяет перечень тем с исходом, пишет доклад, — а
|
||||||
|
для всего этого надо помнить постановку, критерии приёмки и то, что человек
|
||||||
|
одобрил. Письмо тянет в контекст ровно то, чего в докладе не будет ни строкой:
|
||||||
|
содержимое тронутых файлов, вывод линтеров и гейта, перебранные варианты правки.
|
||||||
|
Забитый этим контекст теряет одобренное и постановку — и теряет **молча**: доклад
|
||||||
|
остаётся связным, а сверять его уже не с чем.
|
||||||
|
|
||||||
|
**Разведённость тут ни при чём**, и путать два довода нельзя. Агент пишет по
|
||||||
|
заданию оркестратора и отвечает перед ним; разведён с автором тот, кто работу
|
||||||
|
**судит**, — проходы ревью.
|
||||||
|
|
||||||
|
| Работа | Где шаг |
|
||||||
|
| --- | --- |
|
||||||
|
| предложение и дельта-спеки — `opsx:propose` | [solve](references/solve.md), шаг 2 |
|
||||||
|
| правки спек и дизайна по сказанному на чекпоинте | [solve](references/solve.md), шаг 3 |
|
||||||
|
| код — `opsx:apply`, вместе с гейтом до зелёного и поведенческой верификацией | [solve](references/solve.md), шаг 4 |
|
||||||
|
| правки по находкам триажа, помеченным `инлайн` | [solve](references/solve.md), шаг 5; [maintain](references/maintain.md), шаг 4 |
|
||||||
|
| правка оснастки в сценарии обслуживания | [maintain](references/maintain.md), шаг 2 |
|
||||||
|
| архивация change и отражение в документах — `opsx:archive` и `av-dev:doc-sync` | [solve](references/solve.md), шаг 6, такт 1; [maintain](references/maintain.md), шаг 5 |
|
||||||
|
| письмо одобренного нового в документы канона | [solve](references/solve.md), шаг 6, такт 3; [maintain](references/maintain.md), шаг 5 |
|
||||||
|
|
||||||
|
**Остальное остаётся оркестратору, и перечень закрыт:** выбор сценария и стопы,
|
||||||
|
чекпоинт, вызовы `av-dev:code-review`, `av-dev-git:commit` и `av-dev:task-track`,
|
||||||
|
сверка плана с исходом, урожай и доклад. **Коммит и закрытие задачи агенту не
|
||||||
|
отдаются ни в одном сценарии** — они необратимы для учёта: закрытие удаляет запись
|
||||||
|
и правит индексы, а коммит уезжает в историю. Оркестратор делает их сам, уже
|
||||||
|
сверив перечень тем с исходом. Ни одна из этих
|
||||||
|
работ не пишет файлов проекта — они и есть та работа, ради которой контекст
|
||||||
|
берегут.
|
||||||
|
|
||||||
|
**Разведка сюда не попадает вовсе.** Её письмо — записка в документы канона и
|
||||||
|
записи задач, то есть тот самый текст, из которого собираются чекпоинт вариантов
|
||||||
|
и доклад. Отдать его агенту значило бы получить обратно пересказом то, что
|
||||||
|
и так надо держать целиком.
|
||||||
|
|
||||||
|
### Устава у этих агентов нет
|
||||||
|
|
||||||
|
Проходы ревью ходят уставами (`av-dev/agents/`), потому что уставом задаётся
|
||||||
|
**суждение**: что искать и что считать находкой. Здесь суждения нет — работа
|
||||||
|
нормирована скиллами `opsx:*`, конвенциями проекта и находками триажа, а устав
|
||||||
|
стал бы вторым домом того же и разошёлся бы с ним молча. Зовётся агент общего
|
||||||
|
назначения, и всё, чем один его прогон отличается от другого, приходит заданием.
|
||||||
|
|
||||||
|
### Задание собирается адресами
|
||||||
|
|
||||||
|
**Агент не видел разговора.** Он не знает ни постановки, ни выбранного сценария,
|
||||||
|
ни того, что уже одобрено на чекпоинте. Поэтому задание самодостаточно, а вещи в
|
||||||
|
нём называются **адресами, а не пересказом** — по тому же правилу, по которому
|
||||||
|
проход ревью получает дом темы путём и разделом. В задании:
|
||||||
|
|
||||||
|
- корень проекта, текущая ветка и база диффа. Ветку агент не создаёт и не
|
||||||
|
переключает, не пушит — правило то же, что у скилла;
|
||||||
|
- **что делать**: файл задачи либо её текст дословно, критерии приёмки,
|
||||||
|
идентификатор change;
|
||||||
|
- **что читать**: `CLAUDE.md`, конвенции проекта, дельта-спеки change;
|
||||||
|
- **находки — дословно**, как их вернул триаж, вместе с
|
||||||
|
оракулом;
|
||||||
|
- **границы**: правится названное, соседнее не улучшается заодно; развилок агент
|
||||||
|
не решает, задач не заводит, ничего не коммитит и наружу не ходит — правило
|
||||||
|
необратимого действует и на него (раздел «Когда спрашивать вне чекпоинта»);
|
||||||
|
- **чем кончает**: гейт зелёный, а если задача меняет наблюдаемое поведение —
|
||||||
|
прогнана поведенческая верификация.
|
||||||
|
|
||||||
|
**Пересказ находки — самая дорогая экономия из возможных.** Находка триажа несёт
|
||||||
|
оракул, и пересказ теряет как раз его: агент чинит то, что понял, гейт зеленеет,
|
||||||
|
а в отчёт уезжает «исправлено».
|
||||||
|
|
||||||
|
### Возврат — не длиннее экрана
|
||||||
|
|
||||||
|
Агент возвращает: что сделано, **адресами** тронутого; исход гейта, чем он
|
||||||
|
прогнан, где логи шагов и **отпечаток дерева сразу после прогона**; что не
|
||||||
|
удалось и почему; вопросы, если по заданию их не разрешить. Отпечаток нужен
|
||||||
|
ревью: по нему ступень автотестов засчитывает этот прогон вместо своего
|
||||||
|
(`av-dev:code-review`, ступень 1) — без него гейт гоняется дважды на том же
|
||||||
|
дереве.
|
||||||
|
Диффа, пересказа кода и логов в возврате нет — иначе экономия, ради которой шаг
|
||||||
|
и вынесен, отменяется в момент возврата.
|
||||||
|
|
||||||
|
**Чек-лист синка — единственное исключение из «не длиннее экрана».** Он приходит
|
||||||
|
из хвостового агента целиком и целиком уезжает в доклад: тронутые документы
|
||||||
|
поимённо, предложенное — строкой с основанием, нетронутые — одной строкой с общей
|
||||||
|
причиной. Сжать его своими словами значит потерять принуждённое отрицание, ради
|
||||||
|
которого шаг и существует.
|
||||||
|
|
||||||
|
**Предложения из этого чек-листа оркестратор не исполняет сам.** Они уезжают в
|
||||||
|
реплику человеку вместе с урожаем ревью, и написанным становится только то, что
|
||||||
|
он назвал (`solve.md`, шаг 6, такт второй). Агент, вернувший предложение, свою
|
||||||
|
работу сделал — заведение нового не его решение и не твоё.
|
||||||
|
|
||||||
|
**Возврату на слово не верят, и перечитывать за агентом дифф для этого не надо.**
|
||||||
|
Верят независимым артефактам: зелёному гейту, отчёту триажа, ревью следующего
|
||||||
|
шага. Своей прозе здесь верить нельзя ровно по той причине, по которой ей не
|
||||||
|
верит конвейер ревью, — её написал тот, кто мог и пропустить.
|
||||||
|
|
||||||
|
**Артефакты, написанные для человека, оркестратор читает сам**: `proposal.md` и
|
||||||
|
`design.md` нужны ему на чекпоинте. Это не переполнение контекста, а его работа.
|
||||||
|
|
||||||
|
### Один агент на шаг, а не на файл
|
||||||
|
|
||||||
|
Нарезка по файлам разводит одну правку по разным контекстам, и сходиться она
|
||||||
|
будет в гейте, то есть после. Повторный проход того же шага — **новое задание**,
|
||||||
|
а не продолжение прежнего: агент прежнего не помнит, и рассчитывать на его память
|
||||||
|
нельзя.
|
||||||
|
|
||||||
|
**Правило про контекст, а не про полномочия.** Агент упал, вернул не то или не
|
||||||
|
понял задания — повтори задание, дописав то, чего в нём не хватило. Не вышло и во
|
||||||
|
второй раз — делай сам и **скажи это строкой доклада**: прогон стоил дороже, чем
|
||||||
|
должен, и это факт для человека, а не стоп.
|
||||||
|
|
||||||
|
## Автономность и плановые стопы
|
||||||
|
|
||||||
|
**Стопов у сценария не больше двух, и каждый — про решение человека, а не про
|
||||||
|
ход работ.**
|
||||||
|
|
||||||
|
| Сценарий | Стоп до письма | Стоп после письма |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| решение | чекпоинт: объяснение после предложения и до кода | реплика шага 6: что из найденного заводится |
|
||||||
|
| разведка | чекпоинт вариантов до первого написанного требования | — исход и так уезжает в документы по выбранному варианту |
|
||||||
|
| обслуживание | — планового нет | реплика шага 5, и только если появилось новое |
|
||||||
|
|
||||||
|
**Второй стоп короче первого и часто не случается вовсе.** Первый решает форму
|
||||||
|
решения, и без ответа работа не идёт дальше; второй решает, что из найденного
|
||||||
|
переживёт задачу, и при пустом списке нового его просто нет. Правило вокруг обоих
|
||||||
|
общее.
|
||||||
|
|
||||||
|
**У обслуживания планового стопа нет вовсе, и это следствие, а не поблажка.**
|
||||||
|
Один стоп с ожиданием ответа у него всё же есть — по найденной дельта-спеке, — но
|
||||||
|
плановым он не является: через него проходят только те прогоны, где задача
|
||||||
|
оказалась не тем, чем объявлена.
|
||||||
|
Чекпоинт объясняет человеку **выбор**, а у обслуживания выбора нет по
|
||||||
|
построению: что делать, сказано в записи, и объяснение свелось бы к пересказу
|
||||||
|
задачи её же автору. Стоп, на котором нечего решать, вырождается в обряд
|
||||||
|
одобрения и обесценивает те стопы, где решать есть что. Правило необратимого
|
||||||
|
(ниже) действует там полностью и срабатывает чаще, чем в двух других сценариях:
|
||||||
|
выкладка, токены, хуки и чужие данные — обычное содержимое задач обслуживания.
|
||||||
|
|
||||||
|
**Вокруг чекпоинта умолчание прежнее — делать, а не спрашивать.** Чекпоинт не
|
||||||
|
отменяет автономность, он даёт развилкам плановое место, куда копиться.
|
||||||
|
|
||||||
|
Разрез простой:
|
||||||
|
|
||||||
|
- развилка найдена **до** чекпоинта — она его и ждёт. Не спрашивай отдельно:
|
||||||
|
чекпоинт рядом и стоит дёшево, а три вопроса подряд стоят дороже одного
|
||||||
|
разговора;
|
||||||
|
- развилка найдена **после** чекпоинта — старое правило: **запиши вопрос и доведи
|
||||||
|
остаток**, не останавливаясь.
|
||||||
|
|
||||||
|
Запись вопроса устроена так:
|
||||||
|
|
||||||
|
1. **Запиши там, где проект держит вопросы** (секция беклога, файл задачи,
|
||||||
|
трекер — это знает проект). Проект не сказал, куда, — отдельной секцией
|
||||||
|
`Вопросы` в своём докладе, и это тоже исход. Тело отвечает на три вещи: **что
|
||||||
|
именно решить**, **какие есть варианты и цена каждого**, **что стоит, пока
|
||||||
|
решения нет**. Плюс твоя рекомендация — человек чаще соглашается, чем выбирает
|
||||||
|
заново, и готовое суждение экономит ему весь контекст.
|
||||||
|
2. **Переформулируй задачу на остаток** — то, что делается без этого решения.
|
||||||
|
Назови границу: докуда доводим сейчас.
|
||||||
|
3. Доведи остаток до конца и закоммить. Задача не «висит на вопросе», она сделана
|
||||||
|
в объявленных границах. В разведке остаток — это ответ в объявленных рамках:
|
||||||
|
что успели узнать, где остановились и почему.
|
||||||
|
|
||||||
|
**Что остатком не является — правило живёт не здесь.** Канонический текст с обеими
|
||||||
|
оговорками — в скилле `av-dev:task-groom`, раздел
|
||||||
|
`## Вопрос, блокер, необратимое`, подраздел «Отличать вопрос от застревания».
|
||||||
|
Правило принадлежит управлению задачами, потому что решает **сделана задача или
|
||||||
|
вышла**, — это исход планирования, а не исполнения. **Ссылайся, не
|
||||||
|
пересказывай:** копия, заведённая здесь, уже однажды разошлась с оригиналом и
|
||||||
|
потеряла из перечня самое необратимое — запись **наружу**.
|
||||||
|
|
||||||
|
Коротко, чтобы знать, когда идти читать: остаток проверяется двумя порогами —
|
||||||
|
**материализация нерешённого** (запись состояния, зависящего от неотвеченного
|
||||||
|
вопроса) и **пол по пользе** (из остатка пропала польза, названная в постановке).
|
||||||
|
Оба порога — стоп: первый поднимает решение до начала записи, второй даёт исход
|
||||||
|
«не доведена».
|
||||||
|
|
||||||
|
Каталога задач в проекте нет — правило не отменяется, а становится
|
||||||
|
осторожнее: прежде чем записать зависящее от нерешённого куда бы то ни было —
|
||||||
|
в хранилище, в журнал, в витрину или наружу, — спрашивай человека.
|
||||||
|
|
||||||
|
Нет полезного остатка — задача заканчивается исходом «не доведена», вопрос
|
||||||
|
записан, ничего не коммитится наполовину.
|
||||||
|
|
||||||
|
### Когда спрашивать вне чекпоинта
|
||||||
|
|
||||||
|
По другому основанию — не «сложное решение», а **необратимое действие**:
|
||||||
|
|
||||||
|
- деплой, выкладка наружу, смена публичного адреса или токенов;
|
||||||
|
- удаление или перезапись рабочих данных, включая подрезку архивов;
|
||||||
|
- всё, что уходит за пределы машины.
|
||||||
|
|
||||||
|
Здесь ошибка не откатывается коммитом, поэтому спрашиваем даже когда решение
|
||||||
|
кажется очевидным.
|
||||||
|
|
||||||
|
## Границы: чем этот скилл не владеет
|
||||||
|
|
||||||
|
- **Беклогом и порядком работ.** Задача приходит извне. Скилл её не выбирает,
|
||||||
|
не переставляет, не заводит и не переоценивает.
|
||||||
|
- **Форматом задач и документов.** Индексы и документы канона руками не правятся,
|
||||||
|
путь к чужому скрипту не выдумывается: этим владеют `av-dev:task-track` и
|
||||||
|
`av-dev:doc-sync`. Закрытие — работа этого скилла, и это осознанное решение с
|
||||||
|
названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек
|
||||||
|
возвращает задачу `reopen` с причиной (на доработке это делают грумингом,
|
||||||
|
`av-dev:task-groom`, на стройке — сразу, как заметили), а доклад
|
||||||
|
по критериям приёмки становится единственным, по чему приёмка вообще возможна.
|
||||||
|
- **Определением ценности.** «Нужна ли эта функциональность» — не вопрос этого
|
||||||
|
скилла ни на одном шаге и ни в одном сценарии. Чекпоинт решения спрашивает «так
|
||||||
|
ли решаем», чекпоинт разведки — «каким из способов», но не «надо ли».
|
||||||
|
|
||||||
|
Что не принадлежит **отдельному сценарию**, названо у него же: урожай ревью и
|
||||||
|
выбор способа — в [solve.md](references/solve.md), изменение поведения и нарезка
|
||||||
|
пачки — в [maintain.md](references/maintain.md), код и приоритет — в
|
||||||
|
[research.md](references/research.md).
|
||||||
|
|
||||||
|
## Наблюдаемые исходы
|
||||||
|
|
||||||
|
**У каждого сценария их четыре**, и живут они у сценария:
|
||||||
|
[решение](references/solve.md) — сделана, не доведена, оказалась крупнее задачи,
|
||||||
|
нужна разведка; [обслуживание](references/maintain.md) — сделана, не доведена,
|
||||||
|
меняется спека, нужна разведка; [разведка](references/research.md) — способ
|
||||||
|
выбран, знание записано, отказ, не доведена.
|
||||||
|
|
||||||
|
Общего исхода нет намеренно. «Сделана» у решения и «знание записано» у разведки —
|
||||||
|
разные вещи с разной приёмкой, и слово, накрывающее оба, скрывало бы именно то,
|
||||||
|
чем прогон кончился. «Сделана» у решения и у обслуживания совпадает словом, но не
|
||||||
|
определением: у первого в него входит пройденный чекпоинт и заархивированный
|
||||||
|
change, у второго — сверенный состав гейта и синк.
|
||||||
|
|
||||||
|
## Доклад
|
||||||
|
|
||||||
|
Ядро общее, и в нём обязательно:
|
||||||
|
|
||||||
|
- **какой сценарий шёл** — решение, обслуживание или разведка, — и почему выбран
|
||||||
|
он;
|
||||||
|
- **исход** одним из четырёх слов своего сценария и, если он не благополучный,
|
||||||
|
чем ограничен результат;
|
||||||
|
- **постановка пришла текстом** — сказать это прямо: как она понята, что `ready`
|
||||||
|
не гонялся и что закрывать было нечего;
|
||||||
|
- что сделано, какие вопросы записаны и куда;
|
||||||
|
- **шаг письма, сделанный не агентом, а тобой** — с причиной: раздел «Кто пишет»
|
||||||
|
требует называть это строкой, а не молча;
|
||||||
|
- чего проверить или узнать **не удалось**.
|
||||||
|
|
||||||
|
Сверх ядра каждый сценарий добавляет своё: [solve.md](references/solve.md) —
|
||||||
|
чекпоинт, change, критерии приёмки, урожай и границы покрытия;
|
||||||
|
[maintain.md](references/maintain.md) — чем подтверждён признак, состав гейта до
|
||||||
|
и после, критерии приёмки, урожай и границы покрытия;
|
||||||
|
[research.md](references/research.md) — вопрос и ответ, адреса записи, заведённые
|
||||||
|
задачи, рамки.
|
||||||
|
|
||||||
|
## Тонкости
|
||||||
|
|
||||||
|
- **Не завязывайся на основную ветку и корень репозитория.** Скилл работает в
|
||||||
|
текущем worktree и на текущей ветке: не делай `git checkout`/`switch`, не
|
||||||
|
создавай веток, не пушь.
|
||||||
|
- Прогон проходит **не больше одного** чекпоинта, и это норма, а не упрощение.
|
||||||
|
Два чекпоинта за одну задачу — цена незнания способа, и платится она двумя
|
||||||
|
прогонами, а не одним длинным. У обслуживания чекпоинта нет ни одного, и это
|
||||||
|
тоже норма: там нечего решать. Реплика о новом чекпоинтом не является и этого
|
||||||
|
счёта не касается — она решает не форму решения, а судьбу находок.
|
||||||
|
- Держи вызывающего в цикле короткими репликами на переходах фаз, но не проси
|
||||||
|
подтверждать механику. Мест, где **ждут ответа**, ровно два, и оба названы в
|
||||||
|
«Автономности»: чекпоинт до кода и реплика о новом после него. Третьего нет ни
|
||||||
|
в одном сценарии, и заводить его нельзя.
|
||||||
|
- **Сценарий назван вслух — значит, его можно оспорить.** Человек, увидевший в
|
||||||
|
первой реплике «иду разведкой, потому что способа не видно», поправит выбор
|
||||||
|
одной фразой; молча выбранный сценарий он поправит через полчаса работы.
|
||||||
@@ -0,0 +1,520 @@
|
|||||||
|
# Сценарий «обслуживание»
|
||||||
|
|
||||||
|
Способ решения известен, а **того, что нормирует спека, задача не трогает**:
|
||||||
|
тулчейн и сборка, зависимости, гит-хуки и шаги гейта, перенос и чистка. Сценарий
|
||||||
|
**пишет код**, но не заводит change и не пишет требований. Исход — работающая
|
||||||
|
оснастка и синхронная ей документация.
|
||||||
|
|
||||||
|
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
||||||
|
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
||||||
|
ход; общее для всех трёх сценариев — вход, чего может не быть, правило
|
||||||
|
записанного вопроса, правило необратимого — живёт в SKILL.md и тут не
|
||||||
|
пересказывается.
|
||||||
|
|
||||||
|
## Почему цикл SDD здесь не урезан, а остался без входа
|
||||||
|
|
||||||
|
Это не поблажка по цене, и называть сценарий «коротким путём для мелких задач»
|
||||||
|
нельзя: путь, выбираемый по самооценке размера, и есть тот самый дешёвый способ
|
||||||
|
«ускориться», против которого написана вся защита сценария решения.
|
||||||
|
|
||||||
|
**У обслуживания нет дельта-спек по построению.** Тип `chore` определён через
|
||||||
|
«наблюдаемое поведение не меняется», а на дельтах стоит весь цикл: `propose`
|
||||||
|
их порождает, `review-specs` сверяет **с ними**, объяснение чекпоинта собирается
|
||||||
|
из `proposal.md` и `design.md`, `archive` вливает их в актуальные спеки. Change
|
||||||
|
без дельт — пустой артефакт, который потом надо архивировать.
|
||||||
|
|
||||||
|
Шаги цикла здесь не пропущены — **им нечего обрабатывать**. Отсюда и состав
|
||||||
|
сценария: выпали ровно те шаги, у которых нет предмета, и не выпал ни один из
|
||||||
|
тех, у которых он есть.
|
||||||
|
|
||||||
|
## Признак — связка, а не одно условие
|
||||||
|
|
||||||
|
Сценарий выбирается двумя проверками сразу, и обе обязательны:
|
||||||
|
|
||||||
|
1. **тип записи предлагает** — `chore`, реже `fix`, чьё исправление возвращает
|
||||||
|
поведение к уже записанному в спеке;
|
||||||
|
2. **отсутствие дельт подтверждает** — прочитав постановку, ты не находишь
|
||||||
|
требования, которое пришлось бы добавить, изменить или снять.
|
||||||
|
|
||||||
|
Один признак без второго не выбирает сценарий. Тип объявляет автор записи, и он
|
||||||
|
может ошибиться в обе стороны; отсутствие дельт — суждение исполнителя, и оно
|
||||||
|
принимается только тогда, когда согласуется с объявленным типом. Расхождение
|
||||||
|
двух признаков — это не развилка, а стоп: скажи, что тип и предмет работы
|
||||||
|
разошлись, и остановись.
|
||||||
|
|
||||||
|
**Имя сценария не равно имени типа, и это намеренно.** `fix` без дельта-спеки
|
||||||
|
идёт сюда законно — поведение разошлось с **заявленным**, значит заявленное уже
|
||||||
|
записано, и менять спеку не нужно. Сценарий, названный именем типа, такую задачу
|
||||||
|
либо отправил бы в полный цикл ради пустого change, либо принял бы как
|
||||||
|
исключение, а исключения не исполняются.
|
||||||
|
|
||||||
|
### Постановка текстом — тип называешь ты, и называешь вслух
|
||||||
|
|
||||||
|
Первый признак приходит от автора записи; **текст типа не несёт** (SKILL.md,
|
||||||
|
«Постановка текстом»). Оба признака тогда твои, и связка выродилась бы в одно
|
||||||
|
суждение — то самое, ради разведения которого она и заведена.
|
||||||
|
|
||||||
|
Разведённость здесь восстанавливается местом, а не вторым автором: **тип и
|
||||||
|
предмет работы называются до начала работы, первой репликой** — «иду
|
||||||
|
обслуживанием: считаю это `chore`, потому что …; спека не меняется, потому что
|
||||||
|
…». Человек, написавший текст, читает это раньше первой правки и поправляет
|
||||||
|
одной фразой. Названный **после** работы тип не признак, а объяснение уже
|
||||||
|
сделанного: к этому моменту у тебя есть готовый дифф, и он всегда подтверждает
|
||||||
|
тот тип, под который писался.
|
||||||
|
|
||||||
|
Не назвал — признака нет вовсе, и сценарий выбрал сам себя. Это ровно тот
|
||||||
|
случай, где «самый частый способ соврать этим сценарием» (раздел «Тонкости»)
|
||||||
|
ничего не стоит: автора, чей тип можно было бы опровергнуть, здесь нет.
|
||||||
|
|
||||||
|
## Дельта нашлась по ходу — стоп, и у него свой порядок
|
||||||
|
|
||||||
|
Признак тот же, что у отработки ревью в решении (шаг 5): **меняется ли то, что записано в
|
||||||
|
`openspec/specs/`**. Обнаружилось, что меняется, — работа перестала быть
|
||||||
|
обслуживанием в ту же секунду, и продолжать её нельзя: коммит обслуживания
|
||||||
|
заявляет «поведение не менялось», а оно меняется.
|
||||||
|
|
||||||
|
**Задача при этом не сломалась — она оказалась шире своего типа.** Поэтому стоп
|
||||||
|
здесь не «бросить и доложить», а три шага по порядку.
|
||||||
|
|
||||||
|
**1. Назови тип, которым задача оказалась.** Разрез тот же, по которому типы и
|
||||||
|
разведены:
|
||||||
|
|
||||||
|
- **`fix`** — поведение расходится с **заявленным**: спека уже описывает верное,
|
||||||
|
и правка возвращает систему к записанному;
|
||||||
|
- **`feature`** — снаружи появляется то, чего не было: спеке нужно новое
|
||||||
|
требование.
|
||||||
|
|
||||||
|
Тип называется прямо и с причиной. «Нужно менять спеки» без имени типа
|
||||||
|
перекладывает классификацию на человека в тот момент, когда весь материал для неё
|
||||||
|
у тебя.
|
||||||
|
|
||||||
|
**2. Объясни человеку простым языком.** Экран текста, не больше:
|
||||||
|
|
||||||
|
- **что просили сделать** — одной фразой из записи;
|
||||||
|
- **что нашлось** — какое поведение меняется, словами домена, а не именами
|
||||||
|
файлов и функций;
|
||||||
|
- **почему это перестало быть обслуживанием** — одной фразой: у обслуживания
|
||||||
|
поведение не меняется по определению;
|
||||||
|
- **чем задача становится** — `fix` или `feature`, с причиной из разреза выше;
|
||||||
|
- **что уже сделано** и что из этого лежит в рабочем дереве.
|
||||||
|
|
||||||
|
Проверка на простой язык — общая у трёх сценариев:
|
||||||
|
|
||||||
|
<!-- копия: чекпоинт-простой-язык из av-dev/skills/code-resolve/references/solve.md -->
|
||||||
|
|
||||||
|
**в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||||
|
по-русски, и нет слов, которых нет в паспорте проекта.**
|
||||||
|
|
||||||
|
<!-- /копия: чекпоинт-простой-язык -->
|
||||||
|
|
||||||
|
**3. Дай два решения и жди ответа.** Их ровно два, и оба законны:
|
||||||
|
|
||||||
|
- **переформулировать запись** — тип меняется на названный, и дальше задача идёт
|
||||||
|
**процессом своего типа**: сценарием решения, следующим прогоном. Формат записи
|
||||||
|
правит `av-dev:task-track`, а не ты: у нового типа своя схема разделов, и
|
||||||
|
готовность её проверит `ready` — той же машиной, что и на входе. Прогон
|
||||||
|
обслуживания на этом кончается, исход — «меняется спека».
|
||||||
|
**Постановка пришла текстом — переформулировать нечего:** человек либо
|
||||||
|
запускает следующий прогон тем же текстом, и он пойдёт решением, либо заводит
|
||||||
|
запись через `av-dev:task-track`, если работа должна пережить разговор. Выбор
|
||||||
|
между этими двумя — его, не твой: заводить запись сам этот скилл не вправе;
|
||||||
|
- **прекратить работу** — человек не готов расширять задачу сейчас. Исход тот
|
||||||
|
же, запись остаётся как была, вопрос записывается там, где проект держит
|
||||||
|
вопросы.
|
||||||
|
|
||||||
|
**Третьего решения — «доделать как обслуживание» — нет.** Оно и есть то самое
|
||||||
|
молчаливое изменение поведения, против которого стоит весь разрез: под коммитом,
|
||||||
|
заявляющим «поменяли оснастку», уехала бы правка, не прошедшая ни чекпоинта, ни
|
||||||
|
ревью цикла задачи, и не оставившая следа в спеках.
|
||||||
|
|
||||||
|
**Сделанное не выбрасывается ни при каком из двух решений.** Оно остаётся в
|
||||||
|
рабочем дереве незакоммиченным: при переформулировке уезжает в change следующим
|
||||||
|
прогоном, при отказе — человек решает сам, откатить или оставить. Коммитить его
|
||||||
|
сообщением про обслуживание нельзя.
|
||||||
|
|
||||||
|
**Прогон, дошедший до этого стопа, стоит дороже обычного** — и это довод за
|
||||||
|
проверку признака на шаге 1, а не после написанного кода.
|
||||||
|
|
||||||
|
## OpenSpec здесь не предпосылка
|
||||||
|
|
||||||
|
Как и разведке, обслуживанию OpenSpec не нужен: оно не заводит change, не пишет
|
||||||
|
дельта-спек и не архивирует. Ни один из проходов его плана ревью на дельта-спеки
|
||||||
|
не завязан — это сказано и в `av-dev:code-review`, раздел «Прогон без change».
|
||||||
|
Каталога в проекте нет — обслуживание идёт целиком, и деградацией это не
|
||||||
|
является.
|
||||||
|
|
||||||
|
## Планового стопа у этого сценария нет
|
||||||
|
|
||||||
|
**И это следствие, а не упрощение.** Чекпоинт решения объясняет человеку
|
||||||
|
**выбор**: в чём проблема, как решаем, чем рискуем. У обслуживания выбора нет по
|
||||||
|
построению — что делать, сказано в записи, а критерии приёмки у него самые
|
||||||
|
дешёвые из всех типов: команда, которая раньше падала или требовала трёх шагов.
|
||||||
|
Объяснение свелось бы к пересказу задачи её же автору. Стоп, на котором нечего
|
||||||
|
решать, вырождается в обряд одобрения и обесценивает те стопы, где решать есть
|
||||||
|
что.
|
||||||
|
|
||||||
|
**Мест, где ответа всё же ждут, два, и через оба проходят не все прогоны.**
|
||||||
|
Первое — стоп по найденной дельте (раздел «Дельта нашлась по ходу»), и плановым
|
||||||
|
он не является: через него идут те прогоны, где задача оказалась не тем, чем
|
||||||
|
объявлена. Второе — **реплика о новом на шаге 5**, и она случается, только если
|
||||||
|
обслуживание завело в документах что-то, чего не было: запрет или инвариант.
|
||||||
|
Обычный прогон обслуживания не проходит ни через одно из двух.
|
||||||
|
|
||||||
|
**Правило необратимого при этом действует полностью** (SKILL.md, «Когда
|
||||||
|
спрашивать вне чекпоинта»), и здесь оно опаснее, чем кажется. Единственный
|
||||||
|
сценарий без планового стопа — ровно тот, чья работа чаще прочих лезет в
|
||||||
|
выкладку, в токены, в хуки и в чужие данные. Правка оснастки выглядит безобидной
|
||||||
|
до момента, когда её уже не откатить.
|
||||||
|
|
||||||
|
## Ход работы
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
in["сценарий выбран: обслуживание"]
|
||||||
|
s1["1. прочитать задачу<br/>критерии приёмки и границы"]
|
||||||
|
s2["2. правка агентом<br/>гейт тронут — состав снять до правки"]
|
||||||
|
s3["3. гейт проекта до зелёного"]
|
||||||
|
s4["4. ревью фиксированным планом<br/>av-dev:code-review, без change"]
|
||||||
|
s5["5. синк документации — av-dev:doc-sync"]
|
||||||
|
s6["6. коммит работы — av-dev-git:commit"]
|
||||||
|
s7["7. закрыть задачу — av-dev:task-track,<br/>вторым коммитом учёта"]
|
||||||
|
out["исход назван"]
|
||||||
|
|
||||||
|
in --> s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> out
|
||||||
|
s1 -.->|"форма правки неизвестна"| stop1["стоп: нужна разведка"]
|
||||||
|
s2 -.->|"нашлась дельта-спека"| stop2["стоп: назвать тип,<br/>объяснить, дать два решения"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Схема — **сводка**: содержание каждого шага в его разделе ниже, и при расхождении
|
||||||
|
прав текст.
|
||||||
|
|
||||||
|
## Наблюдаемые исходы сценария
|
||||||
|
|
||||||
|
Четыре, и каждый обязан быть назван в докладе прямо:
|
||||||
|
|
||||||
|
- **сделана** — определение сделанного выполнено целиком;
|
||||||
|
- **не доведена** — с причиной и с записанным вопросом; названо, что именно
|
||||||
|
сделано и до какой границы;
|
||||||
|
- **меняется спека** — работа оказалась шире своего типа. Стоп с объяснением и
|
||||||
|
двумя решениями человека: переформулировать запись в `fix` или `feature` и
|
||||||
|
решать её процессом того типа следующим прогоном — либо прекратить. Сделанное
|
||||||
|
остаётся в рабочем дереве незакоммиченным. Доклад называет **оба**: какой тип
|
||||||
|
предложен и что человек выбрал;
|
||||||
|
- **нужна разведка** — форма правки неизвестна (мажорное обновление, смена
|
||||||
|
сборщика, переезд гейта на другой инструмент) **или сработал триггер ADR**.
|
||||||
|
Перечень триггеров не пересказывается: он живёт в
|
||||||
|
[canon.md](../../canon/references/canon.md#adr), и здесь он работает
|
||||||
|
стоп-признаком — то есть от его точности зависит выбор сценария, а пересказ
|
||||||
|
расходится с домом молча. Стоп с названной причиной, разведка идёт следующим
|
||||||
|
прогоном.
|
||||||
|
|
||||||
|
**Последний исход — не редкость, и его стоит ждать.** Незнакомое обслуживание —
|
||||||
|
это выбор подхода с ценой и с тем, что становится невозможным, — предмет чекпоинта
|
||||||
|
вариантов, а не работы без стопа. И там же решение получает законный источник для
|
||||||
|
ADR: список источников канон закрыл двумя — архивный `design.md` и записка
|
||||||
|
разведки, — а обслуживание не производит ни того ни другого.
|
||||||
|
|
||||||
|
## Определение сделанного
|
||||||
|
|
||||||
|
Задача сделана, когда верно всё:
|
||||||
|
|
||||||
|
1. гейт проекта зелёный, и **если правка трогала сам гейт — сверен его состав**,
|
||||||
|
а не только цвет;
|
||||||
|
2. ревью проведено фиксированным планом сценария, исход назван по каждой теме
|
||||||
|
плана, а темы, которых в плане нет, названы в границах покрытия;
|
||||||
|
3. **документация синхронизирована с принуждённым отрицанием** — каждый документ
|
||||||
|
канона получил строку;
|
||||||
|
4. коммит сделан в текущую ветку;
|
||||||
|
5. **критерии приёмки, если проект их дал, выписаны поимённо, и по каждому назван
|
||||||
|
оракул и наблюдаемый исход.** Это доклад приёмщику, а не отметка «принято»:
|
||||||
|
исполнитель и приёмщик здесь совпали, и правило то же, что в решении.
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### 1. Прочитать задачу
|
||||||
|
|
||||||
|
Прочитай запись. У `chore` обязательны два раздела, и оба нужны тебе прямо
|
||||||
|
сейчас: **«Затрагивает»** — границы, которые у обслуживания часто не в коде
|
||||||
|
(конфиг и его образцы, версия зависимости, команда сборки, файл CI), и
|
||||||
|
**«Критерии приёмки»** — с оракулами.
|
||||||
|
|
||||||
|
**Постановка пришла текстом — этих двух разделов нет, и оба нужны тебе тем же
|
||||||
|
составом.** Границы назови сам и покажи в первой реплике, вместе с типом:
|
||||||
|
обслуживание чаще прочих сценариев расползается, а границы у него лежат не в
|
||||||
|
коде, и невидимая граница расползание не удержит. Критериев приёмки в тексте
|
||||||
|
может не быть вовсе — тогда скажи строкой, что их нет и приёмка идёт по докладу.
|
||||||
|
Сочинить их себе здесь нельзя даже так, как это делает решение: чекпоинта, на
|
||||||
|
котором человек их утвердит, у обслуживания нет.
|
||||||
|
|
||||||
|
Здесь же обе проверки признака: тип предлагает, отсутствие дельт подтверждает
|
||||||
|
(раздел «Признак — связка»); постановка текстом типа не объявляла — тогда
|
||||||
|
называешь его ты, и вслух (раздел «Постановка текстом»). И здесь же — проверка
|
||||||
|
на незнакомое: если форма правки не известна до начала, а нащупывается по ходу,
|
||||||
|
объявляй исход **нужна разведка** и не начинай.
|
||||||
|
|
||||||
|
**Проверка на «заодно».** Обслуживание любит склеиваться в пачку — обновить
|
||||||
|
зависимости, переписать сборку и убрать мёртвый код одной задачей. Не мерджится
|
||||||
|
порознь — это несколько задач: объявляй исход **не доведена** с причиной «задача
|
||||||
|
не одна» и останавливайся. Нарезкой владеет `av-dev:task-track`, а не ты, и
|
||||||
|
делать её по ходу нельзя — получится один коммит, в котором обновление
|
||||||
|
зависимости не отделить от чистки.
|
||||||
|
|
||||||
|
### 2. Сделать правку
|
||||||
|
|
||||||
|
**Правку делает агент** (SKILL.md, «Кто пишет: письмо уходит агентам»): задание
|
||||||
|
несёт постановку, конвенции проекта, границы правки и требование довести гейт до
|
||||||
|
зелёного; возврат — адреса тронутого и исход гейта. Код и конфиги — по конвенциям
|
||||||
|
проекта. Правка по размеру задачи: чинится названное в записи, соседнее не
|
||||||
|
улучшается заодно.
|
||||||
|
|
||||||
|
**Гейта ещё нет — сказать это, а не изображать сверку.** Первые шаги плана
|
||||||
|
стройки заводят гейт, сборку и хуки: у них нет ни «до», ни «прежнего», и
|
||||||
|
определение сделанного через зелёный гейт на них не выполнимо буквально. Такая
|
||||||
|
задача сделана, когда **заведённое работает на чистом клоне** и это показано в
|
||||||
|
докладе; пункты 1 и 3 определения ниже закрываются строкой «заводится впервые,
|
||||||
|
сверять не с чем». Изображать сверку с несуществующим прежним состоянием нельзя —
|
||||||
|
это ровно то враньё, против которого весь абзац ниже и написан.
|
||||||
|
|
||||||
|
**Гейт правится — сверь состав, а не цвет.** Красный, ставший зелёным, виден
|
||||||
|
сразу; убыли проверок гейт не покажет — он зелёный и до, и после. Это самая
|
||||||
|
дорогая из возможных правок оснастки: молча выключено то, чем проверяется всё
|
||||||
|
остальное. Состав проверок и способ его снять — **дело проекта**: он объявляет
|
||||||
|
их семантикой гейта в `CLAUDE.md`. Снимай исходное состояние **до** правки, по
|
||||||
|
тому, как проект это описал.
|
||||||
|
|
||||||
|
**Исходный состав снимаешь ты, а не агент, и это не мелочь.** Сверка «до и
|
||||||
|
после» уезжает в твой доклад, а снятое тем же, кто правил, сверкой не является:
|
||||||
|
агент вернёт состав, который получился, и назовёт его исходным. Снимок делается
|
||||||
|
до того, как задание ушло.
|
||||||
|
|
||||||
|
**Проект состав не описал — скажи строкой доклада, что сверен только цвет.**
|
||||||
|
Обходного пути не выдумывай: угаданный состав хуже отсутствующего, потому что
|
||||||
|
читается как сверенный. Это же строка и повод — предложить проекту дописать слот
|
||||||
|
в `CLAUDE.md`.
|
||||||
|
|
||||||
|
### 3. Гейт до зелёного
|
||||||
|
|
||||||
|
Прогони гейт и добейся зелёного — он же условие следующего шага: пока гейт
|
||||||
|
красный, проходы с мнением не запускаются.
|
||||||
|
|
||||||
|
**Сразу после зелёного сними отпечаток дерева** (`av-dev:code-review`, ступень 1)
|
||||||
|
и сохрани его вместе со сводкой и путём к логам шагов. Шаг 4 передаёт их ревью, и
|
||||||
|
тогда ступень автотестов не гоняет тот же гейт второй раз.
|
||||||
|
|
||||||
|
**Поведенческая верификация здесь другая, чем в решении.** Проверяется не новое
|
||||||
|
поведение, а то, что прежнее не поехало: команда из `CLAUDE.md` поднимается, шаг
|
||||||
|
сборки отрабатывает, хук ставится на чистом клоне. **У оснастки, заводимой
|
||||||
|
впервые, прежнего нет** — тогда проверяется, что заведённое работает на чистом
|
||||||
|
клоне, и в докладе это называется своим именем, а не «регрессий не найдено». Правка, которую нельзя
|
||||||
|
проверить ничем, кроме «у меня локально работает», называется в докладе строкой.
|
||||||
|
|
||||||
|
### 4. Ревью — план фиксирован сценарием
|
||||||
|
|
||||||
|
Вызови Skill **`av-dev:code-review`**, дав базу диффа, режим, **план сценария** и
|
||||||
|
**исход гейта с шага 3** — сводку, путь к логам шагов и отпечаток дерева. Change
|
||||||
|
ты не передаёшь — его нет.
|
||||||
|
|
||||||
|
**План у сценария свой, и он не совпадает с перечнем тем цикла задачи.** Тема
|
||||||
|
`requirements` там есть, а здесь её предмета нет вовсе; `operations` в цикле
|
||||||
|
закрыта сверкой с инвариантами внутри `review-code`, а здесь её берёт `basics` —
|
||||||
|
правка оснастки задевает выкладку, откат и соседей чаще, чем что-либо ещё, и
|
||||||
|
инвариантов на этот счёт у проекта обычно нет.
|
||||||
|
|
||||||
|
<!-- дом: план-обслуживания -->
|
||||||
|
|
||||||
|
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда |
|
||||||
|
| `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда |
|
||||||
|
| `conventions` + технический разбор | `conventions.*` | `review-code` | как в цикле: дом конвенций целиком, потолок 4 конвенционных, у техники потолка нет; сверка с инвариантами `CLAUDE.md` по темам `security`, `operations`, `architecture`, потолок 1 | дифф трогает код, а не только оснастку |
|
||||||
|
|
||||||
|
<!-- /дом: план-обслуживания -->
|
||||||
|
|
||||||
|
**Глубина названа в плане потому, что иначе её неоткуда взять.** У `review-basics`
|
||||||
|
и тема, и глубина приходят заданием — в цикле он держит только свои темы проекта,
|
||||||
|
а здесь ему дают чужую; без строки плана он взял бы её наугад, то есть по-разному
|
||||||
|
от прогона к прогону и молча.
|
||||||
|
|
||||||
|
**`review-code` идёт тем же составом, что в цикле, и это не совпадение.** Обе его
|
||||||
|
половины и сверка с инвариантами постоянны — от прогона они не зависят, потому и
|
||||||
|
переносятся сюда без оговорок. Единственное, что план решает за него, — идти ли
|
||||||
|
вообще: правка, тронувшая только оснастку, кода не меняла.
|
||||||
|
|
||||||
|
Триаж обязателен, как и на всяком прогоне: он единственный сток и единственный,
|
||||||
|
кто сверяет план с исходом. На его вход подаётся этот план — вместо перечня тем
|
||||||
|
цикла задачи.
|
||||||
|
|
||||||
|
**Условие третьей строки проверяемое, и смотрится оно по диффу**, а не по
|
||||||
|
намерению: обновление зависимости или правка файла CI кода не трогают, чистка и
|
||||||
|
перенос — трогают. `review-code` — единственный проход, который вообще говорит
|
||||||
|
«здесь ошибка в логике», и чистка, прошедшая без него, проверена только на то,
|
||||||
|
что она собирается.
|
||||||
|
|
||||||
|
**Сигнал «просит глубокого ревью» работает и здесь**, но читается иначе: у
|
||||||
|
обслуживания поднимать нечего — состав фиксирован сценарием. Показалось, что
|
||||||
|
глубины мало, потому что задача крупнее заявленного, — ищи дельту, а не глубину;
|
||||||
|
всё прочее уходит строкой «отложено в `av-dev:code-deep-review`».
|
||||||
|
|
||||||
|
**Границы покрытия называются полностью:**
|
||||||
|
|
||||||
|
- `requirements` — предмета нет, дельта-спек не существует;
|
||||||
|
- `security` — своего прохода нет; сверена против записанных инвариантов внутри
|
||||||
|
`review-code`, а он шёл не всегда. Не шёл — тему не смотрел никто, и это
|
||||||
|
говорится прямо;
|
||||||
|
- `architecture` — то же: только против инвариантов, и только если шёл `code`.
|
||||||
|
|
||||||
|
Отчёт, из которого исчезло «что не смотрел никто», сообщает «проверено», не
|
||||||
|
сообщая, что именно.
|
||||||
|
|
||||||
|
Отработка — как в решении: помеченное `инлайн` чинит **агент** (SKILL.md, «Кто
|
||||||
|
пишет»), находки уходят ему дословно с оракулом, гейт после правок гоняет он же,
|
||||||
|
логировать их не надо; `развилка` — вопросом в запись, и агенту она не отдаётся.
|
||||||
|
Отложенные находки собери в секцию доклада `Урожай`.
|
||||||
|
|
||||||
|
**Задачи из урожая — по слову человека, и спрашивается это репликой шага 5**,
|
||||||
|
вместе с новым в документах: правило общее для всех прогонов конвейера
|
||||||
|
(`av-dev:code-review`, «Что происходит с находками дальше»), и обслуживание не
|
||||||
|
исключение. Сказал «заводим» — зовёшь `av-dev:task-track` сам, сценарий «задачи
|
||||||
|
из ревью и аудита»; не сказал — урожай остаётся строками доклада.
|
||||||
|
|
||||||
|
### 5. Синк документации — главный шаг этого сценария, и делает его агент
|
||||||
|
|
||||||
|
**Синк уходит агенту** (SKILL.md, «Кто пишет»): работа письменная и нормирована
|
||||||
|
чек-листом скилла `av-dev:doc-sync`, а не суждением оркестратора. В задании —
|
||||||
|
корень проекта, база диффа, что было тронуто правкой, требование принуждённого
|
||||||
|
отрицания и требование довести гейт до зелёного после правок. Вычитку языка
|
||||||
|
`av-dev:doc-sync` зовёт сам.
|
||||||
|
|
||||||
|
**Правило то же и такое же жёсткое: принуждённое отрицание** — каждый документ
|
||||||
|
канона либо назван обновлённым, либо получает «не требуется, потому что…».
|
||||||
|
Нетронутые группируются одной строкой. Возврат приходит в этой же форме и уезжает
|
||||||
|
в доклад целиком: тронутое без нетронутого не отличается от невыполненного шага.
|
||||||
|
|
||||||
|
**Второе правило синка тоже действует: отражение пишется молча, новое
|
||||||
|
предлагается** (дом — раздел «Два рода правок» скилла `av-dev:doc-sync`). Здесь
|
||||||
|
оно почти ничего не стоит: обслуживание двигает **факты** — команды, шаги гейта,
|
||||||
|
зависимости поимённо, пути, имя ветки, числа настроек, — а факт в документе,
|
||||||
|
разошедшийся с кодом, это отражение по определению.
|
||||||
|
|
||||||
|
**Повод для реплики у этого сценария один — новый запрет или инвариант в
|
||||||
|
`CLAUDE.md`**: он свяжет все будущие задачи, и заводить его молча нельзя.
|
||||||
|
Сужение проверок в `review.*` поводом не является, хотя тоже новое: проверки
|
||||||
|
сузил человек, и слово по ним уже сказано (`av-dev:doc-sync`, «Два рода правок»).
|
||||||
|
К этому же поводу примыкает урожай ревью с шага 4 — спрашиваются они одной
|
||||||
|
репликой, а не двумя.
|
||||||
|
|
||||||
|
**Нового нет — реплики нет**, и шаг кончается возвратом агента; так идёт
|
||||||
|
большинство прогонов обслуживания.
|
||||||
|
|
||||||
|
**Реплика была — идёт второй заход тем же агентом**, и на нём висит то же, что в
|
||||||
|
решении: письмо одобренного, вычитка `doc-wording` по всей пачке и гейт до
|
||||||
|
зелёного. Первый заход снимает их с себя ровно тогда, когда вернул непустой
|
||||||
|
список предложений, — порядок и его довод описаны в [solve.md](solve.md), шаг 6.
|
||||||
|
Задачи из урожая при этом заводишь **ты сам** вызовом `av-dev:task-track`, а не
|
||||||
|
агент: индексы учёта правит тот, кто коммитит.
|
||||||
|
|
||||||
|
**Здесь этот шаг весит больше, чем в решении, и вот почему.** Обслуживание не
|
||||||
|
меняет поведения — значит, почти всё, что оно меняет, это документация: команды,
|
||||||
|
шаги гейта, зависимости поимённо, пути, имя основной ветки, настройки с числовым
|
||||||
|
значением, место механизации правила. Ровно эти факты `doc-code-drift` и сверяет
|
||||||
|
с кодом (перечень закрыт, живёт в каноне) — то есть сценарий, чаще всех прочих
|
||||||
|
двигающий сверяемые факты, обязан отчитаться по ним раньше всех прочих.
|
||||||
|
|
||||||
|
Отдельно один документ, которого нет в перечне тем, а синку он нужен:
|
||||||
|
**`conventions.*`, раздел «Механизировано»** — если правило переехало в линтер, и
|
||||||
|
тогда его проза из конвенций **удаляется**, а не остаётся вторым домом.
|
||||||
|
|
||||||
|
**`adr/` этот сценарий не пополняет, и это не пропуск.** У ADR закрытый список
|
||||||
|
источников — архивный `design.md` либо записка разведки, — и ни того ни другого
|
||||||
|
обслуживание не производит. Поэтому триггеры ADR здесь работают **стоп-признаком,
|
||||||
|
а не поводом завести запись**: сработал любой из них — сценарий выбран неверно,
|
||||||
|
объявляй исход **нужна разведка** и останавливайся. Перечень триггеров — в
|
||||||
|
[canon.md](../../canon/references/canon.md#adr) и здесь не пересказывается. Решение с ценой обязано
|
||||||
|
пройти чекпоинт вариантов, а не появиться в коммите обслуживания, чей смысл —
|
||||||
|
«ничего не решали, поменяли оснастку».
|
||||||
|
|
||||||
|
Список документов и их триггеров здесь не дублируется — он в чек-листе скилла
|
||||||
|
`av-dev:doc-sync`; копия уже однажды разошлась с оригиналом. Документов канона в
|
||||||
|
проекте нет — синка нет вовсе: скажи это исходом и предложи завести канон
|
||||||
|
скиллом `av-dev:canon`.
|
||||||
|
|
||||||
|
### 6. Коммит
|
||||||
|
|
||||||
|
Коммить **в текущую ветку** (`git rev-parse --abbrev-ref HEAD`), сам ветку не
|
||||||
|
создавай и не переключай, ничего не пушь.
|
||||||
|
|
||||||
|
Сообщение — по-русски, **скиллом `av-dev-git:commit`**. Вызов не разрешился —
|
||||||
|
напиши сам и скажи строкой, что форму коммита не сверял никто. Одна задача — один
|
||||||
|
осмысленный коммит.
|
||||||
|
|
||||||
|
### 7. Закрыть задачу — после коммита, не раньше
|
||||||
|
|
||||||
|
**Вызови Skill `av-dev:task-track`** и попроси закрыть задачу как реализованную.
|
||||||
|
Порядок обязателен: закрытие удаляет файл задачи, и сделанное до коммита оно
|
||||||
|
оставило бы задачу закрытой без следа работы, если шаг 6 упадёт.
|
||||||
|
|
||||||
|
**Закрытие тоже коммитится — вторым коммитом, тут же**, сообщением про учёт:
|
||||||
|
`закрыта задача <slug>`. Каталога задач в проекте нет — ничего не выдумывай:
|
||||||
|
скажи, что учёт остаётся за владельцем, и назови исход.
|
||||||
|
|
||||||
|
**Постановка пришла текстом — шага нет вовсе**: записи не было, закрывать нечего,
|
||||||
|
след работы — коммит шага 6. Заводить запись задним числом ради закрытия нельзя.
|
||||||
|
|
||||||
|
## Границы: чего обслуживание не делает
|
||||||
|
|
||||||
|
- **Не меняет поведения.** Обнаружилось, что меняет, — стоп с исходом «меняется
|
||||||
|
спека»: назвать тип, объяснить, дать два решения. Это единственная граница
|
||||||
|
сценария, у которой есть проверяемый признак, и она же единственная, которую
|
||||||
|
выгодно нарушить молча.
|
||||||
|
- **Не переписывает запись задачи сам.** Тип ты **предлагаешь** с причиной,
|
||||||
|
меняет его `av-dev:task-track` и только после ответа человека: исполнитель,
|
||||||
|
переклеивший тип на ходу, назначает себе другой процесс и другую глубину
|
||||||
|
проверки.
|
||||||
|
- **Не решает, нужна ли работа.** Как и оба соседних сценария: «надо ли» —
|
||||||
|
вопрос человека.
|
||||||
|
- **Не нарезает пачку на задачи.** «Обновить зависимости и переписать сборку» —
|
||||||
|
это `av-dev:task-track` и его правила нарезки.
|
||||||
|
- **Не выбирает форму правки, когда она незнакома, и не принимает решений с
|
||||||
|
ценой.** Мажорное обновление, смена инструмента, намеренный отказ идут
|
||||||
|
разведкой: там есть чекпоинт вариантов и законный источник для ADR, здесь нет
|
||||||
|
ни того ни другого.
|
||||||
|
- **Не заводит задачи из урожая ревью.** Урожай передаётся списком.
|
||||||
|
|
||||||
|
## Доклад обслуживания
|
||||||
|
|
||||||
|
Общее ядро доклада — в SKILL.md; сверх него сценарий обязан назвать:
|
||||||
|
|
||||||
|
- **что подтвердило признак** — тип записи и то, что дельта-спек не нашлось;
|
||||||
|
постановка пришла текстом — **тип назвал ты**, и это говорится прямо, вместе с
|
||||||
|
границами, которые ты объявил себе сам;
|
||||||
|
дельта нашлась — **какой тип предложен, с причиной, и что человек выбрал**:
|
||||||
|
переформулировать или прекратить;
|
||||||
|
- **что стало иначе для разработчика** — одной фразой, адресуясь ему, а не
|
||||||
|
выдуманному пользователю;
|
||||||
|
- **состав гейта до и после**, если правка его трогала; не сверялся — почему;
|
||||||
|
- по каждому критерию приёмки: **оракул и наблюдаемый исход**;
|
||||||
|
- **`Урожай`** — отложенные находки списком и **что человек по нему решил**;
|
||||||
|
- **что заведено нового в документах** и что предложено и отвергнуто — по именам
|
||||||
|
записей; отказ виден только здесь;
|
||||||
|
- **сигнал сверки** — строка синка о том, сколько задач сделано с прошлого
|
||||||
|
прогона `av-dev:doc-healthcheck`, либо что сверки не было ни разу;
|
||||||
|
- **строка границ покрытия**: план сценария фиксирован; темы `requirements` в нём
|
||||||
|
нет — её не смотрел никто, а `security` и `architecture` смотрелись только
|
||||||
|
против записанных инвариантов, и то если шёл проход `code`.
|
||||||
|
|
||||||
|
## Тонкости сценария
|
||||||
|
|
||||||
|
- **Самый частый способ соврать этим сценарием — назвать `chore` то, что меняет
|
||||||
|
поведение.** Тип, оставшийся от первой формулировки, врёт ровно там, где по
|
||||||
|
нему выбирают путь; проверка признака стоит одного чтения записи и делается на
|
||||||
|
шаге 1, а не после написанного кода.
|
||||||
|
- **Отсутствие чекпоинта не делает сценарий автономнее прочих.** Правило
|
||||||
|
необратимого здесь то же, и срабатывает оно чаще: выкладка, токены, хуки,
|
||||||
|
чужие данные — обычное содержимое задач обслуживания.
|
||||||
|
- **Зелёный гейт после правки гейта ничего не доказывает.** Это единственное
|
||||||
|
место конвейера, где инструмент проверяет сам себя, и потому состав сверяется
|
||||||
|
отдельно от цвета.
|
||||||
|
- **Правка оснастки, сделанная «заодно» внутри чужой задачи, этим сценарием не
|
||||||
|
проходит вовсе** — она едет в чужом коммите и не получает ни своего ревью, ни
|
||||||
|
своей строки синка. Заметил нужную правку по ходу решения — вопрос в запись,
|
||||||
|
а не правка мимоходом.
|
||||||
+119
-49
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
||||||
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
||||||
ход; общее для обоих сценариев — вход, обращение к соседним плагинам, правило
|
ход; общее для всех трёх сценариев — вход, чего может не быть, правило
|
||||||
необратимого, доклад — живёт в SKILL.md и тут не пересказывается.
|
необратимого, доклад — живёт в SKILL.md и тут не пересказывается.
|
||||||
|
|
||||||
**Разведка кончается своим исходом, а не переходом к коду.** Выбранный способ
|
**Разведка кончается своим исходом, а не переходом к коду.** Выбранный способ
|
||||||
@@ -20,24 +20,32 @@
|
|||||||
**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).
|
||||||
|
|
||||||
|
**Агентов-исполнителей у разведки нет** (SKILL.md, «Кто пишет: письмо уходит
|
||||||
|
агентам»), и это не пропуск. Её письмо — записка в документы канона и записи
|
||||||
|
задач, то есть тот самый текст, из которого собираются чекпоинт вариантов и
|
||||||
|
доклад: отданный агенту, он вернулся бы пересказом. Вычитку разведка всё же
|
||||||
|
отдаёт — `doc-wording`, `task-form`, `task-wording`: там судят написанное, а не
|
||||||
|
пишут.
|
||||||
|
|
||||||
**Отсутствие канона бьёт по разведке сильнее, чем по решению**, и сказать об этом
|
**Отсутствие канона бьёт по разведке сильнее, чем по решению**, и сказать об этом
|
||||||
строкой мало: без документов у ответа нет дома, и знание осядет в переписке.
|
строкой мало: без документов у ответа нет дома, и знание осядет в переписке.
|
||||||
Назови исход и предложи `av-dev-docs:canon`; работу не останавливай, но адрес
|
Назови исход и предложи `av-dev:canon`; работу не останавливай, но адрес
|
||||||
ответа тогда выбираешь сам и говоришь об этом вслух.
|
ответа тогда выбираешь сам и говоришь об этом вслух.
|
||||||
|
|
||||||
## Что этот сценарий требует от входа
|
## Что этот сценарий требует от входа
|
||||||
|
|
||||||
Вход общий у обоих сценариев (SKILL.md, раздел «Вход»); своего здесь три условия.
|
Вход общий у всех трёх сценариев (SKILL.md, раздел «Вход»); своего здесь три
|
||||||
|
условия.
|
||||||
|
|
||||||
**Паспорт читается раньше кода.** Граница домена и «чем проект **не** является»
|
**Паспорт читается раньше кода.** Граница домена и «чем проект **не** является»
|
||||||
отсекают половину вариантов до того, как их начнут сравнивать, — а сравнение
|
отсекают половину вариантов до того, как их начнут сравнивать, — а сравнение
|
||||||
@@ -47,11 +55,14 @@
|
|||||||
причиной «запись это сырьё»: у неё нет вопроса, а разведка без вопроса
|
причиной «запись это сырьё»: у неё нет вопроса, а разведка без вопроса
|
||||||
превращается в чтение всего подряд с отчётом «интересно, но неприменимо».
|
превращается в чтение всего подряд с отчётом «интересно, но неприменимо».
|
||||||
Дописывать вопрос за автора не твоя работа — этим занят штурм сырья в
|
Дописывать вопрос за автора не твоя работа — этим занят штурм сырья в
|
||||||
`av-dev-tasks:tasks`. Назови, чего не хватает, и остановись.
|
`av-dev:task-track`. Назови, чего не хватает, и остановись.
|
||||||
|
|
||||||
**Разведка пришла текстом — сформулируй вопрос сам одной фразой и покажи
|
**Разведка пришла текстом — сформулируй вопрос сам одной фразой и покажи
|
||||||
формулировку в первой же реплике.** Разведка, чей вопрос не назван вслух,
|
формулировку в первой же реплике.** Разведка, чей вопрос не назван вслух,
|
||||||
признаётся удавшейся любым результатом.
|
признаётся удавшейся любым результатом. Это частный случай общего правила
|
||||||
|
(SKILL.md, «Постановка текстом»): у разведки показать надо не только предмет
|
||||||
|
работы, но и сам вопрос, потому что предмет разведки — он и есть. Вместе с
|
||||||
|
вопросом называются рамки и адрес ответа (шаг 1).
|
||||||
|
|
||||||
## Ход работы
|
## Ход работы
|
||||||
|
|
||||||
@@ -61,16 +72,17 @@ 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/>av-dev-git:commit"]
|
s6["6. вычитка написанного:<br/>документы и записи задач"]
|
||||||
s7["7. закрыть разведку — av-dev-tasks:tasks,<br/>вторым коммитом учёта"]
|
s7["7. гейт проекта, затем коммит<br/>av-dev-git:commit"]
|
||||||
|
s8["8. закрыть разведку — av-dev:task-track,<br/>вторым коммитом учёта"]
|
||||||
out["исход назван: знание, задачи,<br/>отказ или «не доведена»"]
|
out["исход назван: знание, задачи,<br/>отказ или «не доведена»"]
|
||||||
|
|
||||||
in --> s1 --> s2 --> s3
|
in --> s1 --> s2 --> s3
|
||||||
s3 -->|"выбран способ,<br/>отказ или знание"| s4
|
s3 -->|"выбран способ,<br/>отказ или знание"| s4
|
||||||
s3 -.->|"вопрос не тот"| s1
|
s3 -.->|"вопрос не тот"| s1
|
||||||
s4 --> s5 --> s6 --> s7 --> out
|
s4 --> s5 --> s6 --> s7 --> s8 --> out
|
||||||
```
|
```
|
||||||
|
|
||||||
Схема — **сводка**: содержание каждого шага в его разделе ниже, и при расхождении
|
Схема — **сводка**: содержание каждого шага в его разделе ниже, и при расхождении
|
||||||
@@ -100,13 +112,13 @@ 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`. Твоё — содержание ответа, их —
|
||||||
форма и дом.
|
форма и дом.
|
||||||
- **Определением ценности.** «Нужно ли это делать вообще» — вопрос человека.
|
- **Определением ценности.** «Нужно ли это делать вообще» — вопрос человека.
|
||||||
Разведка отвечает «как это можно сделать и чего каждый способ стоит».
|
Разведка отвечает «как это можно сделать и чего каждый способ стоит».
|
||||||
@@ -140,13 +152,15 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
1. **ответ записан по адресу, который назвала задача** — раздел «Куда ляжет
|
1. **ответ записан по адресу, который назвала задача** — раздел «Куда ляжет
|
||||||
ответ». Адреса не было, а вопрос был — ты назначил адрес сам и сказал об этом
|
ответ». Адреса не было, а вопрос был — ты назначил адрес сам и сказал об этом
|
||||||
строкой;
|
строкой;
|
||||||
2. **у каждого числа провенанс** — команда или условия, которыми оно получено.
|
2. **у каждого числа названо происхождение** — команда или условия, которыми оно получено.
|
||||||
Число без источника проход ревью обязан читать как условие, а не как замер, и
|
Число без источника проход ревью обязан читать как условие, а не как замер, и
|
||||||
разведка, оставившая голые числа, вредна: по ним будут решать;
|
разведка, оставившая голые числа, вредна: по ним будут решать;
|
||||||
3. **отвергнутые варианты названы с причиной**. Отвергнутое без причины
|
3. **отвергнутые варианты названы с причиной**. Отвергнутое без причины
|
||||||
возвращается на следующей разведке как новая идея;
|
возвращается на следующей разведке как новая идея;
|
||||||
4. задачи, которые исход породил, заведены — или явно сказано, что не породил;
|
4. задачи, которые исход породил, заведены — или явно сказано, что не породил;
|
||||||
5. написанное закоммичено, разведка закрыта.
|
5. **написанное вычитано** — документы агентом `doc-wording`, записи задач
|
||||||
|
проходами `task-form` и `task-wording`, каждый по своей пачке;
|
||||||
|
6. написанное закоммичено, разведка закрыта.
|
||||||
|
|
||||||
## Шаги
|
## Шаги
|
||||||
|
|
||||||
@@ -169,7 +183,7 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
|
|
||||||
| Что узнали | Дом ответа |
|
| Что узнали | Дом ответа |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| наблюдение о внешнем мире, замер с провенансом | `docs/research/` |
|
| наблюдение о внешнем мире, замер с происхождением | `docs/research/` |
|
||||||
| решение с ценой: намеренный отказ, дорогой откат | `docs/adr/` |
|
| решение с ценой: намеренный отказ, дорогой откат | `docs/adr/` |
|
||||||
| факт об устройстве системы | тема `architecture` (или своя тема проекта) |
|
| факт об устройстве системы | тема `architecture` (или своя тема проекта) |
|
||||||
| граница домена, «чем проект **не** является» | `passport` |
|
| граница домена, «чем проект **не** является» | `passport` |
|
||||||
@@ -192,7 +206,7 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
2. **код и его история** — `git log` по узлу отвечает на «почему так» чаще, чем
|
2. **код и его история** — `git log` по узлу отвечает на «почему так» чаще, чем
|
||||||
кажется;
|
кажется;
|
||||||
3. **внешние источники** — документация формата, чужой опыт, спецификации;
|
3. **внешние источники** — документация формата, чужой опыт, спецификации;
|
||||||
4. **замер** — если вопрос про числа. Числа снимаются с провенансом, иначе они
|
4. **замер** — если вопрос про числа. Числа снимаются с происхождением, иначе они
|
||||||
бесполезны на следующем шаге.
|
бесполезны на следующем шаге.
|
||||||
|
|
||||||
**Скилл `opsx:explore`** — законный инструмент этого шага, если плагин в проекте
|
**Скилл `opsx:explore`** — законный инструмент этого шага, если плагин в проекте
|
||||||
@@ -223,9 +237,14 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
отвергнутое (отвергнутый вариант с названной причиной — половина будущего ADR);
|
отвергнутое (отвергнутый вариант с названной причиной — половина будущего ADR);
|
||||||
приносить один вариант и называть это выбором.
|
приносить один вариант и называть это выбором.
|
||||||
|
|
||||||
Проверка на простой язык та же, что у чекпоинта решения: **в тексте нет `SHALL`,
|
Проверка на простой язык — общая у трёх сценариев:
|
||||||
нет имён файлов и функций там, где вещь называется по-русски, и нет слов, которых
|
|
||||||
нет в паспорте проекта.**
|
<!-- копия: чекпоинт-простой-язык из av-dev/skills/code-resolve/references/solve.md -->
|
||||||
|
|
||||||
|
**в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||||
|
по-русски, и нет слов, которых нет в паспорте проекта.**
|
||||||
|
|
||||||
|
<!-- /копия: чекпоинт-простой-язык -->
|
||||||
|
|
||||||
Исходы чекпоинта:
|
Исходы чекпоинта:
|
||||||
|
|
||||||
@@ -240,17 +259,19 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
|
|
||||||
### 4. Ответ в документы канона
|
### 4. Ответ в документы канона
|
||||||
|
|
||||||
**Вызови Skill `av-dev-docs:docs`**: он владеет содержимым документов канона.
|
**Вызови Skill `av-dev:doc-sync`**: он владеет содержимым документов канона.
|
||||||
Передай ему ответ, адрес из шага 1 и провенанс каждого числа — писать содержание
|
Передай ему ответ, адрес из шага 1 и происхождение каждого числа — писать содержание
|
||||||
за тебя он не будет, но дом, форму и вычитку держит он.
|
за тебя он не будет, но дом и форму держит он. Вычитка языка — тоже его агент, но
|
||||||
|
момент её назван отдельно, шагом 6: пачка собирается из шагов 4 и 5 и до конца
|
||||||
|
пятого не полна.
|
||||||
|
|
||||||
**Что именно уезжает:**
|
**Что именно уезжает:**
|
||||||
|
|
||||||
- **ответ на вопрос** — по адресу из шага 1;
|
- **ответ на вопрос** — по адресу из шага 1;
|
||||||
- **отвергнутые варианты с причинами.** Это половина будущего ADR и единственная
|
- **отвергнутые варианты с причинами.** Это половина будущего ADR и единственная
|
||||||
защита от повторной разведки того же самого;
|
защита от повторной разведки того же самого;
|
||||||
- **решение с ценой — в ADR**, если оно проходит триггер канона (дорогой откат,
|
- **решение с ценой — в ADR**, если оно проходит [триггер
|
||||||
намеренный отказ, пересмотр прежнего решения). У разведки, кончившейся без
|
канона](../../canon/references/canon.md#adr). У разведки, кончившейся без
|
||||||
кода, `design.md` не будет никогда, поэтому здесь ADR цитирует **записку
|
кода, `design.md` не будет никогда, поэтому здесь ADR цитирует **записку
|
||||||
разведки**, а не архивный change; канон это допускает прямо, и в записи
|
разведки**, а не архивный change; канон это допускает прямо, и в записи
|
||||||
источник называется.
|
источник называется.
|
||||||
@@ -260,17 +281,20 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
незачем. Но **названо должно быть каждое место, куда ты писал**, — доклад без
|
незачем. Но **названо должно быть каждое место, куда ты писал**, — доклад без
|
||||||
перечня адресов неотличим от доклада о ненаписанном.
|
перечня адресов неотличим от доклада о ненаписанном.
|
||||||
|
|
||||||
**Плагина `av-dev-docs` нет** — путь в его дерево не разрешится ниоткуда, поэтому
|
**Правило «новое по слову» здесь тоже не задаёт второго вопроса**, хотя ответ
|
||||||
за перечнем документов иди в **свой** reference:
|
разведки — новое от первой до последней строки. Слово уже сказано **чекпоинтом
|
||||||
[references/project-facts.md](../../review/references/project-facts.md) конвейера
|
вариантов**: человек выбрал вариант и тем самым заказал запись. Спросить ещё раз
|
||||||
ревью. `docs/adr/` и `docs/research/` в нём отсутствуют намеренно (проход ревью их
|
значило бы переспросить только что одобренное — и заодно предложить выбросить
|
||||||
не открывает), а разведке нужны как раз они — добавь их руками. Пиши сам, скажи
|
работу, ради которой прогон и шёл. Что записать нового сверх выбранного —
|
||||||
строкой: «ответ записан без скилла документации — форму и вычитку не сверял
|
например ADR по решению с ценой, — предлагается, как везде.
|
||||||
никто».
|
|
||||||
|
**Документов канона в проекте нет** — писать ответ некуда: назови это исходом,
|
||||||
|
предложи завести канон скиллом `av-dev:canon` и оставь ответ в докладе
|
||||||
|
целиком, чтобы работа не пропала. Заводить `docs/` мимо канона нельзя.
|
||||||
|
|
||||||
### 5. Задачи: завести и уточнить
|
### 5. Задачи: завести и уточнить
|
||||||
|
|
||||||
**Вызови Skill `av-dev-tasks:tasks`.** Он владеет форматом, дедупом и индексами;
|
**Вызови Skill `av-dev:task-track`.** Он владеет форматом, дедупом и индексами;
|
||||||
путь к его скрипту не выясняй и индексы руками не правь.
|
путь к его скрипту не выясняй и индексы руками не правь.
|
||||||
|
|
||||||
Что просишь сделать:
|
Что просишь сделать:
|
||||||
@@ -287,10 +311,45 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
способ надуть беклог: она порождает идеи быстрее, чем кто-либо успевает их
|
способ надуть беклог: она порождает идеи быстрее, чем кто-либо успевает их
|
||||||
оценивать, а заведённая задача живёт, пока её кто-то не выкинет руками.
|
оценивать, а заведённая задача живёт, пока её кто-то не выкинет руками.
|
||||||
|
|
||||||
Плагина нет — задачи остаются **списком формулировок в докладе**, и это говорится
|
Каталога задач в проекте нет — задачи остаются **списком формулировок в
|
||||||
строкой: учёт работ остаётся за владельцем.
|
докладе**, и это говорится строкой: учёт работ остаётся за владельцем.
|
||||||
|
|
||||||
### 6. Гейт и коммит
|
### 6. Вычитка написанного — до гейта, не после
|
||||||
|
|
||||||
|
Разведка правит **две вещи сразу**: документы канона (шаг 4) и записи каталога
|
||||||
|
задач (шаг 5). Обе — текст, и портится он в момент письма, а машина этого не
|
||||||
|
видит: `docs.py check` и `tasks.py check` смотрят форму раскладки, а не залог,
|
||||||
|
оценку без факта, жаргон и термин, которого нет в паспорте проекта.
|
||||||
|
|
||||||
|
Пачка **собирается только сейчас**, и поэтому шаг стоит здесь: раньше пятого шага
|
||||||
|
она не полна, а после коммита вычитка уже правит закоммиченное.
|
||||||
|
|
||||||
|
- **Документы** — агент `doc-wording`, владеет им `av-dev:doc-sync` (раздел
|
||||||
|
«Вычитка»). Пачка — адреса, названные на шаге 4, включая `docs/adr/` и
|
||||||
|
`docs/research/`.
|
||||||
|
- **Записи задач** — два прохода, сперва `task-form`, затем `task-wording`;
|
||||||
|
владеет ими `av-dev:task-track` (раздел «Вычитка: два прохода»). Пачка —
|
||||||
|
заведённые и уточнённые на шаге 5 записи. Заголовок и «зачем» правятся **не
|
||||||
|
молча**: покажи предложенное вместе с тем, что было.
|
||||||
|
|
||||||
|
**Что не правилось, то не вычитывается.** Разведка, кончившаяся одним документом
|
||||||
|
и ни одной задачей, зовёт один проход, и это не пропуск — это названная строкой
|
||||||
|
пачка. Скилл-владелец уже прогнал свою пачку по ходу шага — назови это и второй
|
||||||
|
раз тот же файл не гоняй.
|
||||||
|
|
||||||
|
**Судей канона — `doc-consistency` и `doc-code-drift` — здесь не зови.** Они
|
||||||
|
идут на весь канон разом, стоят дорого, и владеет ими `av-dev:doc-healthcheck`,
|
||||||
|
момент вызова которого выбирает человек. Нужно суждение о согласованности — скажи
|
||||||
|
строкой и предложи `healthcheck`, а не зови агентов сам.
|
||||||
|
|
||||||
|
Ни один проход ничего не правит: они возвращают готовые формулировки,
|
||||||
|
подставляешь их ты — и уже с подставленными идёшь на гейт.
|
||||||
|
|
||||||
|
Проходы вычитки — агенты этого же плагина, и разрешаются они всегда. Не
|
||||||
|
разрешились — это поломка установки, а не раскладки проекта: скажи строкой, что
|
||||||
|
написанное не вычитывал никто, и обходного пути не выдумывай.
|
||||||
|
|
||||||
|
### 7. Гейт и коммит
|
||||||
|
|
||||||
**Сперва гейт проекта** — тот же, что гоняет сценарий решения, и по той же
|
**Сперва гейт проекта** — тот же, что гоняет сценарий решения, и по той же
|
||||||
причине: разведка только что правила документы канона и индексы задач, а это
|
причине: разведка только что правила документы канона и индексы задач, а это
|
||||||
@@ -310,14 +369,14 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
Одна разведка — один осмысленный коммит: записанный ответ и заведённые задачи
|
Одна разведка — один осмысленный коммит: записанный ответ и заведённые задачи
|
||||||
уезжают вместе, потому что порознь они полуправда.
|
уезжают вместе, потому что порознь они полуправда.
|
||||||
|
|
||||||
### 7. Закрыть разведку — после коммита, не раньше
|
### 8. Закрыть разведку — после коммита, не раньше
|
||||||
|
|
||||||
**Вызови Skill `av-dev-tasks:tasks`** и попроси закрыть запись: ответ записан —
|
**Вызови Skill `av-dev:task-track`** и попроси закрыть запись: ответ записан —
|
||||||
`close --implemented`, ушла без ответа — `close --reason`, и строка уезжает в
|
`close --implemented`, ушла без ответа — `close --reason`, и строка уезжает в
|
||||||
кладбище.
|
кладбище.
|
||||||
|
|
||||||
**Порядок обязателен.** Закрытие удаляет файл задачи; сделанное до коммита оно
|
**Порядок обязателен.** Закрытие удаляет файл задачи; сделанное до коммита оно
|
||||||
оставило бы разведку закрытой без единого следа работы, если шаг 6 упадёт. У
|
оставило бы разведку закрытой без единого следа работы, если шаг 7 упадёт. У
|
||||||
разведки это опаснее, чем у решения: следом работы там служит код, а здесь —
|
разведки это опаснее, чем у решения: следом работы там служит код, а здесь —
|
||||||
только записанный ответ. Закрытая разведка без него не оставляет следа вообще —
|
только записанный ответ. Закрытая разведка без него не оставляет следа вообще —
|
||||||
файл задачи удалён, ответ был в переписке.
|
файл задачи удалён, ответ был в переписке.
|
||||||
@@ -326,20 +385,31 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
учёт, а не про работу: `закрыта задача <slug>`. Правило «одна разведка — один
|
учёт, а не про работу: `закрыта задача <slug>`. Правило «одна разведка — один
|
||||||
коммит» про работу, а учёт — не работа.
|
коммит» про работу, а учёт — не работа.
|
||||||
|
|
||||||
Плагина в проекте нет — **ничего не выдумывай**: скажи в докладе, что учёт задач
|
**Разведка пришла текстом — шага нет вовсе**: записи не было, закрывать нечего.
|
||||||
остаётся за владельцем, и назови исход.
|
Следом работы здесь служит не код, а **записанный по названному адресу ответ** —
|
||||||
|
он уехал в коммит шагом 7, и потому отсутствие записи разведке ничем не грозит.
|
||||||
|
Ответ записать было некуда и он остался в докладе — вот это как раз тот случай,
|
||||||
|
когда от прогона не осталось ничего: скажи об этом прямо, а не одной строкой
|
||||||
|
среди прочих.
|
||||||
|
|
||||||
|
Каталога задач в проекте нет — **ничего не выдумывай**: скажи в докладе, что
|
||||||
|
учёт задач остаётся за владельцем, и назови исход.
|
||||||
|
|
||||||
## Доклад разведки
|
## Доклад разведки
|
||||||
|
|
||||||
Общая форма доклада — в SKILL.md; у разведки он свой, потому что докладывать
|
Общая форма доклада — в SKILL.md; у разведки он свой, потому что докладывать
|
||||||
нечего из того, о чём спрашивают решение (ни метки ревью, ни критериев приёмки,
|
нечего из того, о чём спрашивают решение: ни критериев приёмки, ни архивного
|
||||||
ни архивного change). Коротко, и в нём обязательно:
|
change, ни исхода ревью — кода она не писала. Коротко, и в нём обязательно:
|
||||||
|
|
||||||
- **исход** одним из четырёх слов;
|
- **исход** одним из четырёх слов;
|
||||||
- **вопрос и ответ** — по фразе на каждое. Ответ, который не сворачивается во
|
- **вопрос и ответ** — по фразе на каждое. Ответ, который не сворачивается во
|
||||||
фразу, — признак того, что разведка отвечала не на один вопрос;
|
фразу, — признак того, что разведка отвечала не на один вопрос;
|
||||||
- **куда записано** — перечнем адресов, а не «документация обновлена»;
|
- **куда записано** — перечнем адресов, а не «документация обновлена»;
|
||||||
- **какие задачи заведены и уточнены** — слагами;
|
- **какие задачи заведены и уточнены** — слагами;
|
||||||
|
- **что вычитано и чем** — пачка документов и пачка записей, каждая со своими
|
||||||
|
проходами; не вычитанное называется прямо, вместе с причиной;
|
||||||
|
- **сигнал сверки** — строка синка о том, сколько задач сделано с прошлого
|
||||||
|
прогона `av-dev:doc-healthcheck`, либо что сверки не было ни разу;
|
||||||
- **что осталось неизвестным** и чего это стоит: разведка без этой строки
|
- **что осталось неизвестным** и чего это стоит: разведка без этой строки
|
||||||
сообщает «выяснено», не сообщая, что именно осталось не выяснено;
|
сообщает «выяснено», не сообщая, что именно осталось не выяснено;
|
||||||
- **рамки**, если они ограничили работу: докуда копали и почему остановились.
|
- **рамки**, если они ограничили работу: докуда копали и почему остановились.
|
||||||
@@ -0,0 +1,529 @@
|
|||||||
|
# Сценарий «решение»
|
||||||
|
|
||||||
|
Способ решения известен, спорно только как. Проводит задачу от постановки до
|
||||||
|
закрытия и **пишет код**: цикл Spec Driven Development с двумя плановыми стопами —
|
||||||
|
объяснением сразу после предложения и репликой о новом после ревью.
|
||||||
|
|
||||||
|
Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел
|
||||||
|
«Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его
|
||||||
|
ход; общее для всех трёх сценариев — вход, чего может не быть, правило
|
||||||
|
записанного вопроса, правило необратимого — живёт в SKILL.md и тут не
|
||||||
|
пересказывается.
|
||||||
|
|
||||||
|
**OpenSpec — жёсткая предпосылка именно этого сценария** (SKILL.md,
|
||||||
|
«Предпосылки»): на нём стоят шаги 2, 4 и 6 и проход `review-specs`.
|
||||||
|
|
||||||
|
Тонкая обёртка над каноническими скиллами `opsx:propose` / `opsx:apply` /
|
||||||
|
`opsx:archive` — их шаги не переизобретаются, а **зовёт их агент**, не ты
|
||||||
|
(SKILL.md, «Кто пишет: письмо уходит агентам»). Ревью — скилл
|
||||||
|
`av-dev:code-review`; состав его прогона постоянный, выбирать и размечать нечего.
|
||||||
|
|
||||||
|
## Ход работы
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
in["сценарий выбран: решение"]
|
||||||
|
s1["1. прочитать задачу<br/>критерии приёмки выписать сразу"]
|
||||||
|
s2["2. opsx:propose — change, дельта-спеки,<br/>tasks.md — агентом"]
|
||||||
|
s3(["3. ЧЕКПОИНТ: объяснение<br/>в чём проблема, как решаем,<br/>чем рискуем"])
|
||||||
|
s4["4. opsx:apply — код, гейт,<br/>поведенческая верификация — агентом"]
|
||||||
|
s5["5. ревью кода — постоянный состав<br/>+ отработка замечаний агентом"]
|
||||||
|
s6["6. opsx:archive + отражение в документах —<br/>одним агентом"]
|
||||||
|
s6q(["РЕПЛИКА: что заводим из нового —<br/>ADR, конвенция, задачи из урожая"])
|
||||||
|
s6b["такт 3: задачи — оркестратором,<br/>документы, вычитка и гейт — агентом"]
|
||||||
|
s7["7. коммит работы — av-dev-git:commit"]
|
||||||
|
s8["8. закрыть задачу — av-dev:task-track,<br/>вторым коммитом учёта"]
|
||||||
|
|
||||||
|
in --> s1
|
||||||
|
s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s6q --> s6b --> s7 --> s8
|
||||||
|
s3 -.->|"скорректировать:<br/>правка спек и дизайна"| s3
|
||||||
|
s5 -.->|"находка отменяет дизайн:<br/>меняются дельта-спеки"| s3
|
||||||
|
s6 -.->|"нового нет:<br/>реплики нет"| s7
|
||||||
|
```
|
||||||
|
|
||||||
|
Схема — **сводка**: содержание каждого шага в его разделе ниже, и при
|
||||||
|
расхождении прав текст.
|
||||||
|
|
||||||
|
## Наблюдаемые исходы сценария
|
||||||
|
|
||||||
|
Четыре, и каждый обязан быть назван в докладе прямо:
|
||||||
|
|
||||||
|
- **сделана** — определение сделанного выполнено целиком;
|
||||||
|
- **не доведена** — с причиной и с записанным вопросом; что именно сделано и до
|
||||||
|
какой границы, названо явно. Сюда же попадает чекпоинт, на котором человек
|
||||||
|
решение не одобрил;
|
||||||
|
- **оказалась крупнее задачи** — распознаётся **до заведения change**, иначе его
|
||||||
|
придётся выбрасывать. Дальше — декомпозиция, и это не работа этого скилла;
|
||||||
|
- **нужна разведка** — очевидного способа решения нет, и это выяснилось уже в
|
||||||
|
работе. Стоп с названной причиной; кода не написано ни строки **намеренно**.
|
||||||
|
Разведка идёт следующим прогоном.
|
||||||
|
|
||||||
|
## Определение сделанного
|
||||||
|
|
||||||
|
Задача сделана, когда верно всё:
|
||||||
|
|
||||||
|
1. гейт проекта зелёный;
|
||||||
|
2. ревью проведено, **перечень тем сверен с исходом по каждой**, темы без отчёта
|
||||||
|
и без дома названы в границах покрытия;
|
||||||
|
3. решение прошло чекпоинт и не разошлось с одобренным — либо разошлось, и
|
||||||
|
чекпоинт был пройден заново;
|
||||||
|
4. change заархивирован, дельты влиты в актуальные спеки, и по **каждому**
|
||||||
|
документу канона назван исход — правка, предложение или отрицание с причиной;
|
||||||
|
5. коммит сделан в текущую ветку;
|
||||||
|
6. **критерии приёмки, если проект их дал, выписаны поимённо, и по каждому назван
|
||||||
|
оракул и наблюдаемый исход** — «прогнал вот это, увидел вот то». Это **доклад,
|
||||||
|
а не сертификация: приёмка — не работа этого скилла.** Исполнитель, ставящий
|
||||||
|
себе галочку «принято», проверяет свою работу своим же взглядом — по границе
|
||||||
|
это может делать только приёмщик, разведённый с исполнителем. Критерии приходят
|
||||||
|
снаружи; скилл их не сочиняет и не занижает. Расхождение «по каждому критерию
|
||||||
|
исход есть, а суть задачи не достигнута» — дефект критериев, и о нём
|
||||||
|
сообщается, а не молча дорабатывается.
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### 1. Прочитать задачу
|
||||||
|
|
||||||
|
Прочитай запись и связанные спеки и черновики.
|
||||||
|
|
||||||
|
Проект даёт задаче **критерии приёмки** — выпиши их сразу: на шаге 2 они уезжают
|
||||||
|
в `tasks.md` change. Файл задачи может быть удалён до коммита, а критерии обязаны
|
||||||
|
его пережить.
|
||||||
|
|
||||||
|
**Постановка пришла текстом** (SKILL.md, «Постановка текстом») — записи нет,
|
||||||
|
читаешь сам текст. Критерии в нём бывают редко: выпиши то, что там есть, а
|
||||||
|
недостающие **предложи на чекпоинте шага 3** и считай их данными только после
|
||||||
|
ответа человека. Сам себе критерии не проставляешь — правило то же, что и с
|
||||||
|
записью: они приходят снаружи, и подсунуть их себе значит назначить себе приёмку.
|
||||||
|
Человек критериев не назвал — скажи строкой, что задача идёт без них и приёмка
|
||||||
|
пойдёт по объяснению чекпоинта.
|
||||||
|
|
||||||
|
Здесь же проверка на «крупнее задачи»: видно, что одним заходом это не
|
||||||
|
мерджится, — объявляй исход **до** заведения change.
|
||||||
|
|
||||||
|
### 2. Завести change — `opsx:propose`
|
||||||
|
|
||||||
|
**Скилл `opsx:propose` зовёт агент** (SKILL.md, «Кто пишет»). В задании:
|
||||||
|
постановка — файл задачи либо её текст дословно, — критерии приёмки, если они
|
||||||
|
были, и требование прогнать `openspec validate --strict <id>`. Возврат:
|
||||||
|
идентификатор change, дельты адресами и исход валидации.
|
||||||
|
|
||||||
|
Шаг оставляет `proposal.md`, дизайн, дельта-спеки
|
||||||
|
(`ADDED`/`MODIFIED`/`REMOVED Requirements`) и `tasks.md`. Форму держит сам
|
||||||
|
`opsx:propose`, и требования к ней идут агенту заданием: каждое
|
||||||
|
`### Requirement` содержит `SHALL`/`MUST`, структурные заголовки английские,
|
||||||
|
сценарии — `GIVEN/WHEN/THEN`.
|
||||||
|
|
||||||
|
**`proposal.md` и `design.md` после возврата читаешь сам** — из них собирается
|
||||||
|
чекпоинт шага 3, и держать их в контексте это твоя работа, а не переполнение.
|
||||||
|
Кода нет, читать нечего сверх них.
|
||||||
|
|
||||||
|
Ещё две вещи задание называет прямо, иначе их не сделает никто. **Критерии
|
||||||
|
приёмки задачи, если они были, копируются в `tasks.md` отдельным блоком.**
|
||||||
|
И **записанное разведкой не переписывается второй раз**: задаче предшествовала
|
||||||
|
разведка — её записка и отвергнутые варианты уже лежат в документах канона
|
||||||
|
(`docs/research/`, `docs/adr/`), и `design.md` на них ссылается. Варианты,
|
||||||
|
разобранные без разведки (способ был очевиден, но у него оказались оттенки), — в
|
||||||
|
`design.md`, с причиной отказа по каждому отвергнутому.
|
||||||
|
|
||||||
|
**`proposal.md` пишется так, чтобы его понял человек, не читавший спек.** Это не
|
||||||
|
стилистическое пожелание: из него собирается чекпоинт шага 3, и переписывать его
|
||||||
|
там заново значит завести второй дом для одного объяснения. Требование стоит в
|
||||||
|
`openspec/config.yaml`, `rules.proposal` — то есть применяется в момент
|
||||||
|
порождения артефакта, а не вспоминается после.
|
||||||
|
|
||||||
|
### 3. Чекпоинт: объяснение
|
||||||
|
|
||||||
|
**Остановись и объясни человеку, что происходит.** Первый из двух плановых стопов
|
||||||
|
сценария, и в отличие от второго он обязателен для всякой задачи: реплика шага 6
|
||||||
|
случается только тогда, когда есть что заводить, а чекпоинт — всегда.
|
||||||
|
|
||||||
|
Он стоит **сразу после предложения и до кода** — намеренно. Раньше между
|
||||||
|
`propose` и чекпоинтом стояла стадия ревью дизайна, и человек читал объяснение,
|
||||||
|
уже просеянное машиной. Стадию сняли ради времени прогона, и просеивать теперь
|
||||||
|
нечем: человек читает предложение как оно есть. Взамен стоп пришёл **раньше** —
|
||||||
|
коррекция здесь стоит правки спеки, а не переписывания готового кода.
|
||||||
|
|
||||||
|
**Это единственное место процесса, где решается форма решения, и решает её
|
||||||
|
человек.** Ревью после кода судит корректность и механику против записанного
|
||||||
|
критерия; «то ли это решение» там не спрашивает ни один проход, а глубокое ревью
|
||||||
|
области придёт позже и не всегда. Значит, чекпоинт — не формальность и не
|
||||||
|
доклад о ходе работ: одобренное здесь уезжает в код без второго суждения о
|
||||||
|
замысле.
|
||||||
|
|
||||||
|
**Объяснение не сочиняется заново — оно собирается из артефактов**, `proposal.md`
|
||||||
|
и `design.md`. Третий пересказ был бы третьим домом одного и того же и разошёлся
|
||||||
|
бы с обоими. Что показываешь:
|
||||||
|
|
||||||
|
- **в чём проблема** — словами домена из паспорта, без имён модулей и функций;
|
||||||
|
- **как решаем** — суть одним абзацем. Не пересказ дельта-спек: спека нормирует,
|
||||||
|
а здесь объясняют;
|
||||||
|
- **что человек увидит иначе**, когда это будет сделано;
|
||||||
|
- **чего мы намеренно не делаем** и почему — граница scope ловится хуже всего;
|
||||||
|
- **чем рискуем и что осталось нерешённым** — сюда съезжаются развилки,
|
||||||
|
накопленные до этого места;
|
||||||
|
- **что дальше**, если возражений нет;
|
||||||
|
- **критерии приёмки, если постановка пришла текстом и не назвала их** —
|
||||||
|
предложенными, а не принятыми: человек их подтверждает или правит здесь же.
|
||||||
|
Это единственное место, где исполнитель вообще может их предложить, и работает
|
||||||
|
оно только потому, что решает всё равно человек.
|
||||||
|
|
||||||
|
Проверка на «простой язык» одна и механическая, и она общая у чекпоинтов всех
|
||||||
|
трёх сценариев — поэтому её дом здесь, а у соседей помеченные копии:
|
||||||
|
|
||||||
|
<!-- дом: чекпоинт-простой-язык -->
|
||||||
|
|
||||||
|
**в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||||
|
по-русски, и нет слов, которых нет в паспорте проекта.**
|
||||||
|
|
||||||
|
<!-- /дом: чекпоинт-простой-язык -->
|
||||||
|
|
||||||
|
Не проходит — переписывай, а не объясняй, почему иначе нельзя.
|
||||||
|
|
||||||
|
Длина — экран. Длинное объяснение не читают, а пролистывают, и чекпоинт
|
||||||
|
превращается в ритуал одобрения.
|
||||||
|
|
||||||
|
Три исхода:
|
||||||
|
|
||||||
|
- **согласен** — идёшь на шаг 4;
|
||||||
|
- **скорректировать** — правку спек и дизайна по сказанному делает **агент**
|
||||||
|
(SKILL.md, «Кто пишет»): сказанное человеком уходит ему дословно, вместе с
|
||||||
|
идентификатором change и требованием перепрогнать
|
||||||
|
`openspec validate --strict <id>`. Затем чекпоинт **заново** — правленое
|
||||||
|
объяснение читает тот же человек;
|
||||||
|
- **не одобрено** — исход «не доведена» с причиной. Change остаётся
|
||||||
|
незаархивированным, задача не закрывается, ничего не коммитится наполовину.
|
||||||
|
|
||||||
|
### 4. Написать код — `opsx:apply`
|
||||||
|
|
||||||
|
**Код пишет агент, и в его же задании лежит весь этот раздел** (SKILL.md, «Кто
|
||||||
|
пишет»): вызов `opsx:apply` для реализации `tasks.md`, гейт до зелёного,
|
||||||
|
поведенческая верификация. Возврат — адреса тронутого, исход гейта и строка
|
||||||
|
верификации; диффа в нём нет. **Исход гейта возвращается сводкой, путём к логам
|
||||||
|
шагов и отпечатком дерева** (SKILL.md, «Возврат — не длиннее экрана»): его
|
||||||
|
передача на шаг 5 избавляет ревью от второго прогона того же гейта.
|
||||||
|
|
||||||
|
Код — по конвенциям проекта
|
||||||
|
(каталог `docs/conventions/`). Меняешь схему — обнови её описание в документации
|
||||||
|
тем же change, если проект этого требует: гейт обычно это проверяет.
|
||||||
|
|
||||||
|
Прогони гейт и добейся зелёного — он же гейт следующего шага.
|
||||||
|
|
||||||
|
**Поведенческая верификация.** Если задача меняет реальное поведение (новый
|
||||||
|
эндпоинт, разбор входа, схема, форма ответа) — зелёных юнит-тестов мало. Подними
|
||||||
|
изменение вживую командой из раздела команд `CLAUDE.md` и прогони сценарий.
|
||||||
|
Пропусти только для чисто внутренних правок без наблюдаемого рантайма.
|
||||||
|
|
||||||
|
**Сервис не оставляем лежать.** Если запуск упал — агент чинит или откатывает до
|
||||||
|
конца шага; возврат с лежащим сервисом — незакрытый шаг, а не исход.
|
||||||
|
|
||||||
|
### 5. Ревью кода — состав постоянный
|
||||||
|
|
||||||
|
Вызови Skill **`av-dev:code-review`**, дав ссылку на change `<id>`, базу диффа,
|
||||||
|
режим запуска и **исход гейта с шага 4** — сводку, путь к логам шагов и отпечаток
|
||||||
|
дерева.
|
||||||
|
|
||||||
|
**Выбирать и размечать нечего.** Состав прогона один и тот же на всякой задаче:
|
||||||
|
гейт, сверка со спекой, разбор кода, триаж; приёмник тем идёт, когда у проекта
|
||||||
|
есть свои темы. Прежде между кодом и ревью стоял отдельный проход разметки — он
|
||||||
|
считал размер по диффу, сложность по постановке и выдавал метку, из которой
|
||||||
|
выводился состав. Метка снята вместе с ним: цикл задачи проверяет корректность и
|
||||||
|
механику, а этой работе нечего добавить и нечего убавить от размера изменения.
|
||||||
|
|
||||||
|
**Режим по умолчанию — `по графу`, и обосновывать его не надо.** Конвейер сам
|
||||||
|
знает свои рёбра: гейт открывает проходы с мнением, триаж — сток. Просить
|
||||||
|
**`линейно`** нужно только по причине, и она называется строкой: так сказал
|
||||||
|
оператор; машина занята чем-то ещё; идёт разбор самого конвейера.
|
||||||
|
|
||||||
|
Скилл сам гоняет гейт, нужные проходы и обязательный триаж. Возвращает отчёт с
|
||||||
|
потолком 7 пунктов, разметкой `Действие: инлайн | развилка`, секцией `Урожай`,
|
||||||
|
секцией отложенного в глубокое ревью и границами покрытия.
|
||||||
|
|
||||||
|
**Сверь перечень тем с исходом, прежде чем коммитить.** Отчёт начинается таблицей
|
||||||
|
«тема → кто закрывает → против чего», и против каждой темы обязан стоять исход.
|
||||||
|
Тема без отчёта и тема без дома — разные вещи, и обе должны быть названы.
|
||||||
|
Реестр постоянный и короткий, сверка стоит одного взгляда.
|
||||||
|
|
||||||
|
#### Отработка — чинится молча, спрашивается редко
|
||||||
|
|
||||||
|
Помеченное `инлайн` чинит **агент** (SKILL.md, «Кто пишет»): находки уходят ему
|
||||||
|
**дословно, вместе с оракулом**, одним заданием на весь урожай инлайна, и гейт
|
||||||
|
после правок гоняет он же. Логировать их не надо. **Это умолчание, и оно
|
||||||
|
широкое** — прогон, вернувший человеку список замечаний вместо готового
|
||||||
|
результата, свою работу не сделал.
|
||||||
|
|
||||||
|
`развилка` — вопросом в запись (он уже сформулирован триажем, его остаётся
|
||||||
|
перенести), и агенту она не отдаётся. Оснований у неё три, и все узкие: правка
|
||||||
|
меняет **дельта-спеки**, находка сидит в **необратимом** месте (миграция, формат
|
||||||
|
на диске, публичный контракт), находка трогает **инвариант** `CLAUDE.md`.
|
||||||
|
Развилок больше двух на задачу — это факт для доклада: либо задача не та, либо
|
||||||
|
разметка действий съехала.
|
||||||
|
|
||||||
|
**Находка, отменяющая одобренный дизайн, отменяет и одобрение.** Признак
|
||||||
|
проверяемый: **меняются ли дельта-спеки**.
|
||||||
|
|
||||||
|
- не меняются — находка внутри дизайна, дожимай сам, это обычная отработка;
|
||||||
|
- меняются — решение стало другим, а одобрено было прежнее. **Вернись на чекпоинт
|
||||||
|
шага 3** с тем, что изменилось и почему; дальше задача идёт своим ходом заново —
|
||||||
|
код, ревью. Такая находка агенту не отдаётся ни при каких условиях: она отменяет
|
||||||
|
одобрение, а это разговор с человеком.
|
||||||
|
|
||||||
|
**Это правило старше правила о развилке.** Находка класса `развилка`, чьё
|
||||||
|
основание — «надо менять спеку», подпадает под оба; побеждает возврат на
|
||||||
|
чекпоинт, а вопрос в запись при нём остаётся, но возврата не заменяет. Иначе
|
||||||
|
одобренный дизайн переделывался бы записанным вопросом, то есть молча.
|
||||||
|
|
||||||
|
Тихо переделать утверждённое нельзя. Чекпоинт, который можно обойти находкой
|
||||||
|
ревью, не значит ничего, а человек при этом уверен, что одобрил именно то, что
|
||||||
|
уехало в коммит.
|
||||||
|
|
||||||
|
#### Урожай — список в докладе, задачи только по слову человека
|
||||||
|
|
||||||
|
Отложенные находки (реальный `major` не для этого мерджа, развилка, решённая
|
||||||
|
«потом», пачка `nit`) собери в секцию доклада `Урожай`: формулировка, оракул,
|
||||||
|
откуда взялась.
|
||||||
|
|
||||||
|
**Задачи из урожая заводятся только тогда, когда человек сказал «заводим».**
|
||||||
|
Спрашивается это **не здесь, а на шаге 6** — там же, где спрашивается новое в
|
||||||
|
документах, и той же одной репликой: два вопроса подряд про одно и то же («что из
|
||||||
|
найденного заводим») стоили бы человеку двух переключений вместо одного. Сюда
|
||||||
|
урожай складывается, а не выносится.
|
||||||
|
|
||||||
|
Сказал «заводим» — зовёшь `av-dev:task-track` **ты сам**, тактом третьим шага 6:
|
||||||
|
у него на этот вход отдельный сценарий «задачи из ревью и аудита» — своя нарезка,
|
||||||
|
свой формат, свои правила дублей, и находка передаётся дословно. Не сказал —
|
||||||
|
урожай остаётся строками доклада, и это исход, а не потеря.
|
||||||
|
|
||||||
|
**Молча беклог не наполняется.** Очередь работ ведёт человек, и задача, заведённая
|
||||||
|
за него по ходу чужого прогона, отнимает у него ровно то решение, ради которого
|
||||||
|
очередь и существует. Прежде вызов `av-dev:task-track` был обязательным шагом —
|
||||||
|
теперь он шаг по ответу.
|
||||||
|
|
||||||
|
**Границы покрытия из отчёта не выбрасывай** — они уезжают в финальный доклад
|
||||||
|
сжатой строкой. Отчёт, из которого исчезло «что проверить было невозможно»,
|
||||||
|
превращается в ложное ощущение проверенности.
|
||||||
|
|
||||||
|
**Строки «отложено в `av-dev:code-deep-review`» перенеси дословно.** Их пишут
|
||||||
|
проходы, упёршиеся в предел цикла: нужен замер, нужен прогнанный путь, нужен вход
|
||||||
|
шире диффа. В цикле задачи это не доказывается ничем, а строки копятся и однажды
|
||||||
|
становятся поводом позвать глубокое ревью области; пересказанные своими словами,
|
||||||
|
они теряют оракул и перестают быть поводом.
|
||||||
|
|
||||||
|
**Сигнал «это изменение просит глубокого ревью»** приходит от `review-code` и
|
||||||
|
подтверждается `review-basics`. Он не команда и не стоп — строка доклада: когда
|
||||||
|
звать глубокий прогон, решает человек.
|
||||||
|
|
||||||
|
**Отчёт триажа сохрани вместе с change (`openspec/changes/<id>/review/`; шаг 6
|
||||||
|
унесёт его в архив вместе с change) — это обязательно, а не «если удобно».** По
|
||||||
|
нему потом видно, что было найдено и что из этого осталось в урожае. И это
|
||||||
|
единственный **независимый** артефакт о составе прогона: своей прозе здесь верить
|
||||||
|
нельзя — её написал тот, кто мог проход и пропустить.
|
||||||
|
|
||||||
|
### 6. Архивация и документы — отражение молча, новое по слову
|
||||||
|
|
||||||
|
Шаг идёт **в три такта**, и агент запускается в нём дважды. Причина одна: письмо
|
||||||
|
в документы бывает двух родов, а спрашивается только один.
|
||||||
|
|
||||||
|
**Копия.** Дом правила — раздел «Два рода правок» скилла `av-dev:doc-sync`.
|
||||||
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
|
<!-- копия: синк-род-правки из av-dev/skills/doc-sync/SKILL.md -->
|
||||||
|
|
||||||
|
- **Отражение** — документ уже описывает эту вещь, и без правки он **станет
|
||||||
|
ложным**: миграция написана, а `database.md` её не знает; компонент заведён, а
|
||||||
|
`architecture.md` перечисляет прежние. Такая правка ничего не решает, она
|
||||||
|
договаривает решённое на чекпоинте и уже стоящее в коде. **Пишется молча.**
|
||||||
|
- **Новое** — в каноне заводится запись или норма, которой не было: ADR, правило
|
||||||
|
в конвенциях, записка в `research/`, инвариант в `CLAUDE.md`, сдвиг периметра в
|
||||||
|
`security.md`, граница в `passport.md`, дефект в журнале `review.md`. Такая
|
||||||
|
запись переживёт задачу и свяжет следующие. **Пишется только по слову
|
||||||
|
человека.**
|
||||||
|
|
||||||
|
<!-- /копия: синк-род-правки -->
|
||||||
|
|
||||||
|
#### Такт первый — агент: архив и отражение
|
||||||
|
|
||||||
|
**Оба скилла уходят одному агенту, и это один запуск** (SKILL.md, «Кто пишет»).
|
||||||
|
Работа письменная от начала до конца: `opsx:archive` вливает дельты в актуальные
|
||||||
|
спеки, `av-dev:doc-sync` идёт по чек-листу и пишет отражение, и обе правки — по
|
||||||
|
чек-листам своих скиллов, а не по суждению оркестратора. Разнесённые по двум
|
||||||
|
запускам, они стоили бы двух заданий, двух возвратов и паузы между ними — при
|
||||||
|
том что второй читает ровно то, что оставил первый.
|
||||||
|
|
||||||
|
В задании: корень проекта, идентификатор change, база диффа и **порядок** —
|
||||||
|
сначала `opsx:archive` с `openspec validate --strict` перед ним, затем
|
||||||
|
`av-dev:doc-sync`.
|
||||||
|
|
||||||
|
**Вычитка и гейт идут последним тактом, в котором писали.** Вернул непустой
|
||||||
|
список предложений — оба ждут третьего такта; список пуст — этот такт последний,
|
||||||
|
и оба идут в нём. Гонять гейт дважды подряд по одному дереву незачем, а вычитывать
|
||||||
|
пачку, которая сейчас пополнится, — тем более. Вычитку зовёт сам
|
||||||
|
`av-dev:doc-sync` (агента `doc-wording` по пачке правленого), и правило живёт в
|
||||||
|
том скилле; гейт до зелёного доводит агент, потому что красный гейт остановил бы
|
||||||
|
коммит следующим шагом — документы у многих проектов он проверяет.
|
||||||
|
|
||||||
|
**Отложенное этим тактом обязано вернуться.** Оттого такт третий идёт **всякий
|
||||||
|
раз, когда была реплика** — в том числе когда человек не одобрил ничего: на нём
|
||||||
|
висят вычитка и гейт, которые первый такт с себя снял. Пропустить его на отказе
|
||||||
|
значило бы уехать в коммит с невычитанной правкой и непрогнанным гейтом.
|
||||||
|
|
||||||
|
**Возврат — чек-лист, адреса тронутого, исход валидации, строка сигнала сверки и
|
||||||
|
исход гейта, если он гонялся.**
|
||||||
|
Чек-лист уезжает в доклад целиком, и переписывать его своими словами нельзя —
|
||||||
|
это единственный след того, что каждый документ был назван.
|
||||||
|
|
||||||
|
**Правило, которое задаёт его форму, одно и оно жёсткое: принуждённое
|
||||||
|
отрицание.** Против **каждого** документа канона стоит одно из трёх — чем он
|
||||||
|
обновлён, что по нему предлагается, либо «не требуется, потому что…».
|
||||||
|
Нетронутые группируются одной строкой с общей причиной. Список триггеров прозой
|
||||||
|
уже проверен на живом проекте и дал 6 записей ADR на 43 изменения; работает
|
||||||
|
только обязательное отрицание. **Требование стоит в задании агента** — без него
|
||||||
|
возврат придёт перечнем тронутого, а тронутое без нетронутого не отличается от
|
||||||
|
невыполненного шага.
|
||||||
|
|
||||||
|
**Список документов и их триггеров здесь не дублируется** — он в чек-листе скилла
|
||||||
|
`av-dev:doc-sync`, и копия уже однажды разошлась с оригиналом, потеряв два
|
||||||
|
триггера.
|
||||||
|
|
||||||
|
**Документов канона в проекте нет** — синка нет вовсе: назови это исходом и
|
||||||
|
предложи завести канон скиллом `av-dev:canon`. Придумывать раскладку под
|
||||||
|
задачу нельзя: документ, заведённый мимо канона, окажется вторым домом ровно
|
||||||
|
тому, что канон потом заведёт своим.
|
||||||
|
|
||||||
|
#### Такт второй — одна реплика человеку на весь хвост
|
||||||
|
|
||||||
|
Покажи **одним списком** всё, что заводится нового:
|
||||||
|
|
||||||
|
- **предложения синка** — ADR, конвенция, записка в `research/`, инвариант,
|
||||||
|
периметр, дефект в журнал. Каждое строкой: что заведём, куда и на каком
|
||||||
|
основании;
|
||||||
|
- **урожай ревью с шага 5** — отложенные находки, из которых получаются задачи:
|
||||||
|
формулировка, оракул, откуда взялась.
|
||||||
|
|
||||||
|
Человек отвечает разом. **Нового нет — реплики нет**, и шаг кончился первым
|
||||||
|
тактом; у большинства задач так и выходит.
|
||||||
|
|
||||||
|
**Реплика одна, и делить её нельзя.** Спросить про ADR на синке, а про задачи
|
||||||
|
отдельно — значит взять с человека два переключения там, где решение одно: что из
|
||||||
|
найденного этой задачей переживёт её. Ровно поэтому вопрос про урожай и перенесён
|
||||||
|
сюда с шага 5.
|
||||||
|
|
||||||
|
**Спрашиваешь, а не советуешь по каждому пункту.** Основание уже названо строкой,
|
||||||
|
и второй абзац уговоров превращает реплику в чтение. Человек вправе ответить
|
||||||
|
«ничего» — это исход, а не потеря: находки остаются строками доклада.
|
||||||
|
|
||||||
|
#### Такт третий — задачи оркестратором, документы агентом
|
||||||
|
|
||||||
|
**Идёт всякий раз, когда была реплика**, и порядок в нём жёсткий.
|
||||||
|
|
||||||
|
**Сначала задачи — их заводишь ты, а не агент.** Человек сказал «заводим» — зови
|
||||||
|
Skill `av-dev:task-track`, у него на этот вход отдельный сценарий «задачи из ревью
|
||||||
|
и аудита»: своя нарезка, свой формат, свои правила дублей. Находка передаётся
|
||||||
|
**дословно, вместе с оракулом**. Согласован промоут находки в конвенцию — тем же
|
||||||
|
вызовом заводится **задача `chore` на механизацию правила**: шаг 2 промоута
|
||||||
|
(конфиг линтера, сканер, приведение кода к зелёному) в хвост чужой задачи не
|
||||||
|
помещается (`av-dev:code-review`, `references/promote.md`).
|
||||||
|
|
||||||
|
**Заведение задач агенту не отдаётся ни в одном сценарии** — по той же причине,
|
||||||
|
по какой ему не отдаются коммит и закрытие: оно правит индексы учёта, а перечень
|
||||||
|
работ ведёт человек. Правило и его дом — SKILL.md, «Кто пишет».
|
||||||
|
|
||||||
|
**Потом документы — их пишет тот же агент, что шёл тактом первым.** В задании:
|
||||||
|
|
||||||
|
- **одобренные записи дословно** — формулировка, источник, основание; сочинять
|
||||||
|
заново нельзя, ADR цитирует решение из архивного `design.md`, а не пересказывает
|
||||||
|
его. Человек не одобрил ничего — писать нечего, и это законный вход;
|
||||||
|
- **вычитка** `doc-wording` по всей пачке правленого — и первого такта, и этого;
|
||||||
|
- **гейт проекта до зелёного** после правок — он же увидит заведённые задачи,
|
||||||
|
потому они и заводятся раньше.
|
||||||
|
|
||||||
|
**Отвергнутое не пишется никуда.** Ни в один документ, ни отдельной записью «от
|
||||||
|
такого-то отказались»: журнала отвергнутого канон не держит, и заведение его
|
||||||
|
здесь было бы ровно тем новым, которого человек только что не заказал. Отказ
|
||||||
|
идёт строкой доклада.
|
||||||
|
|
||||||
|
### 7. Коммит
|
||||||
|
|
||||||
|
Коммить **в текущую ветку** (`git rev-parse --abbrev-ref HEAD`), сам ветку не
|
||||||
|
создавай и не переключай, ничего не пушь.
|
||||||
|
|
||||||
|
Сообщение — по-русски, **скиллом `av-dev-git:commit`**: форму сообщения держит
|
||||||
|
он, и здесь она не пересказывается. Вызов не разрешился — плагина в проекте нет:
|
||||||
|
напиши сообщение сам и скажи строкой доклада, что форму коммита не сверял никто.
|
||||||
|
Одна задача — один осмысленный коммит.
|
||||||
|
|
||||||
|
### 8. Закрыть задачу — **после коммита, не раньше**
|
||||||
|
|
||||||
|
**Вызови Skill `av-dev:task-track`** и попроси закрыть задачу как реализованную —
|
||||||
|
он владеет форматом и двигает строку индекса сам. Путь к его скрипту не выясняй и
|
||||||
|
индексы руками не правь: мост между плагинами — вызов скилла, а не путь.
|
||||||
|
|
||||||
|
**Порядок обязателен.** Закрытие удаляет файл задачи; сделанное до коммита оно
|
||||||
|
оставило бы задачу закрытой без единого следа работы, если шаг 7 упадёт.
|
||||||
|
|
||||||
|
**Закрытие тоже коммитится — вторым коммитом, тут же.** Удаление файла задачи и
|
||||||
|
правка индексов (их имена знает `av-dev:task-track`, не ты) — это правки в рабочем
|
||||||
|
дереве, и оставить их незакоммиченными нельзя: закрытие, не доехавшее до основной
|
||||||
|
ветки, оставит задачу открытой молча, а опора «индексы под git показывают, что и
|
||||||
|
когда закрыто» без коммита — пустые слова. Сообщение короткое, про учёт, а не про
|
||||||
|
работу: `закрыта задача <slug>`. Это второй коммит осознанно: правило «одна задача
|
||||||
|
— один осмысленный коммит» про работу, а учёт — не работа.
|
||||||
|
|
||||||
|
**Постановка пришла текстом — шага нет вовсе, и это не пропуск.** Записи не
|
||||||
|
существовало, закрывать нечего, а следом работы служат коммит и заархивированный
|
||||||
|
change. Заводить запись задним числом, чтобы её тут же закрыть, нельзя: учёт
|
||||||
|
получил бы задачу, которой никто не ставил, и закрытие без единой минуты
|
||||||
|
открытого состояния. Скажи это строкой и переходи к докладу.
|
||||||
|
|
||||||
|
Каталога задач в проекте нет — **ничего не выдумывай**: скажи в докладе, что
|
||||||
|
учёт задач остаётся за владельцем, и назови исход.
|
||||||
|
|
||||||
|
## Доклад решения
|
||||||
|
|
||||||
|
Общее ядро доклада — в SKILL.md; сверх него сценарий решения обязан назвать:
|
||||||
|
|
||||||
|
- **что было одобрено на чекпоинте и совпало ли с тем, что уехало в коммит** —
|
||||||
|
расхождение здесь называется прямо, даже если оно мелкое;
|
||||||
|
- ссылка на архивный change и хеш коммита;
|
||||||
|
- по каждому критерию приёмки, если они были: **оракул и наблюдаемый исход** —
|
||||||
|
это доклад приёмщику, а не отметка «принято»;
|
||||||
|
- **`Урожай`** — отложенные находки списком (формулировка, оракул, откуда
|
||||||
|
взялась) и **что человек по нему решил**: заведены задачи или список остался в
|
||||||
|
докладе;
|
||||||
|
- **что заведено нового в документах** — одобренное по именам записей, и **что
|
||||||
|
предложено и отвергнуто**, тоже по именам. Отказ виден только здесь: в
|
||||||
|
документы он не пишется;
|
||||||
|
- **сигнал сверки** — строка синка о том, сколько задач сделано с прошлого
|
||||||
|
прогона `av-dev:doc-healthcheck`, либо что сверки не было ни разу;
|
||||||
|
- **сколько находок ушло инлайном и сколько развилкой** — числом. По нему видно,
|
||||||
|
во что прогон обошёлся человеку;
|
||||||
|
- **одна строка границ покрытия**: какой режим гонялся, какие проходы не
|
||||||
|
запускались и что проверить было невозможно;
|
||||||
|
- **отложенное в `av-dev:code-deep-review`** — дословно из отчёта, либо «нечего». Доклад без неё сообщает
|
||||||
|
«проверено», не сообщая, что именно.
|
||||||
|
|
||||||
|
## Тонкости сценария
|
||||||
|
|
||||||
|
- Гейт блокирует: пока он красный, проходы с мнением не запускаются. Чинить и
|
||||||
|
перезапускать, а не «посмотреть заодно».
|
||||||
|
- Стиль правок — заточка под проект и конвенции, по размеру задачи, без
|
||||||
|
улучшений заодно.
|
||||||
|
- **Пропустить тему или проскочить чекпоинт — самый дешёвый способ «ускориться»,
|
||||||
|
и он же самый дорогой по последствиям.** Защита устроена так, что регулятора у
|
||||||
|
тебя нет: состав прогона постоянный и сокращению не подлежит, перечень тем
|
||||||
|
сверяется по исходу, непокрытое называется строкой, а расхождение с одобренным
|
||||||
|
— отдельным пунктом доклада.
|
||||||
|
- **Заведение задач из урожая ревью не идёт по умолчанию.** Отложенные находки
|
||||||
|
отдаются **списком**, и в задачи их превращает `av-dev:task-track` — по слову
|
||||||
|
человека и вызовом от тебя, а не от агента: перечень работ ведёт человек, а
|
||||||
|
индексы учёта правит тот же, кто коммитит. У скилла на этот вход отдельный
|
||||||
|
сценарий «задачи из ревью и аудита». Каталога задач в проекте нет — урожай
|
||||||
|
остаётся списком в докладе, и это говорится строкой.
|
||||||
|
- **Стопов у сценария два, и оба про решения человека, а не про ход работ.**
|
||||||
|
Чекпоинт шага 3 решает форму решения **до** кода; реплика шага 6 решает, что из
|
||||||
|
найденного переживёт задачу. Между ними прогон идёт сам: правки инлайном чинятся
|
||||||
|
молча, отражение в документах пишется молча. Третьего стопа заводить нельзя —
|
||||||
|
прогон, останавливающийся чаще, теряет ровно то время, ради которого короткие
|
||||||
|
итерации и выбраны.
|
||||||
|
- **Способ решения ты не выбираешь.** Он приходит известным: из постановки, из
|
||||||
|
разведки, от человека. Выбор между двумя подходами с разной ценой делается в
|
||||||
|
разведке, у своего чекпоинта, — не по ходу этого сценария.
|
||||||
@@ -0,0 +1,993 @@
|
|||||||
|
---
|
||||||
|
name: code-review
|
||||||
|
description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Состав прогона постоянный, метки у него нет: гейт (autotests), сверка со спекой (specs), разбор кода и конвенций (code), триаж; приёмник тем (basics) идёт, когда у проекта есть свои темы. Цикл задачи проверяет корректность и механику против записанного критерия — дельта-спеки, конвенции, инварианты CLAUDE.md, вывод инструментов. Темы риска и устройства — security, operations, architecture — закрыты в цикле только сверкой с записанными инвариантами: их разбор, доказательство запуском и суждение о форме решения живут в скилле av-dev:code-deep-review, который идёт по области кода и время от времени. Порядок прогона — граф зависимостей: гейт открывает проходы с мнением, триаж — единственный сток. Находки по умолчанию чинятся инлайн и молча; человеку уходит только необратимое, трогающее инвариант CLAUDE.md и меняющее дельта-спеки, а задачи из урожая заводятся по его слову. Проектная специфика приходит из документов канона проекта. Вызывается из скилла av-dev:code-resolve после apply. Второй вызов идёт от сценария обслуживания: без change, фиксированным планом (autotests, operations, плюс conventions, если тронут код)."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Конвейер ревью
|
||||||
|
|
||||||
|
Готовит ревью — **не заменяет его**. Потребитель отчёта — оркестратор, который
|
||||||
|
чинит код; человек читает только сводку, развилки и границы покрытия.
|
||||||
|
|
||||||
|
## Четыре правила, из которых всё следует
|
||||||
|
|
||||||
|
Если ситуация не покрыта инструкцией — решай по ним.
|
||||||
|
|
||||||
|
0. **Тема первична, проход вторичен.** Ревью проверяет **темы** — набор
|
||||||
|
направлений, который проект объявляет своими документами. Проход это только
|
||||||
|
способ закрыть тему на заданной глубине, и проходы меняются: переезжают в
|
||||||
|
другой скилл, сливаются, упраздняются. Если состав прогона считать списком
|
||||||
|
проходов, то уехавший проход уносит тему с собой **беззвучно** — отчёт честно
|
||||||
|
скажет «`ops` не запускался» и не скажет «эксплуатацию не смотрел никто», а
|
||||||
|
нужно второе. Проверено на живом переезде: `ops` и `adversary` ушли в
|
||||||
|
`av-dev:code-deep-review`, а темы `security` и `operations` остались в
|
||||||
|
конвейере — узко, сверкой с инвариантами внутри `code`, и это записано
|
||||||
|
строкой. Поэтому прогон описывается таблицей «тема → кто закрывает → против
|
||||||
|
чего», и таблица эта есть в каждом отчёте.
|
||||||
|
|
||||||
|
1. **Recall чек-листа равен длине чек-листа.** Проход, устроенный как «проверь
|
||||||
|
пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма
|
||||||
|
решения, «так не делают» — неперечислимо по определению: перечислимое уже
|
||||||
|
стало бы конвенцией. Отсюда деление проходов на **applicative** (применяют
|
||||||
|
заданный критерий) и **generative** (сперва порождают критерий или
|
||||||
|
альтернативу, потом сравнивают). Расширять чек-листы бесполезно; неявный слой
|
||||||
|
достают только generative-проходы.
|
||||||
|
2. **Ценность верификатора = наличие внешнего оракула × разведённость с
|
||||||
|
автором**, а не число ролей. Под всеми ролями одна модель с одними
|
||||||
|
априорными, вход у всех общий: седьмая роль почти не добавляет recall, но
|
||||||
|
линейно удорожает триаж. Иерархия надёжности: детерминированный инструмент >
|
||||||
|
агент, который его **запускает** и интерпретирует вывод > агент с чистым
|
||||||
|
мнением. Максимум работы переносим вниз.
|
||||||
|
3. **Отчёт без границ покрытия хуже отсутствия отчёта.** «Критичных проблем не
|
||||||
|
обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция
|
||||||
|
границ покрытия обязательна и не сокращается — в том числе в докладе человеку.
|
||||||
|
|
||||||
|
## Предпосылки
|
||||||
|
|
||||||
|
Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это
|
||||||
|
один раз, при установке плагина в проект:
|
||||||
|
|
||||||
|
- **OpenSpec — жёсткая предпосылка, а не опция.** Проход `review-specs` и
|
||||||
|
вызывающий скилл `av-dev:code-resolve` завязаны на дельта-спеки
|
||||||
|
(`openspec/changes/<id>/specs/*/spec.md`), на актуальные спеки
|
||||||
|
(`openspec/specs/`) и на `openspec validate --strict`. В проекте без OpenSpec
|
||||||
|
шаги, зовущие `opsx:explore` / `opsx:propose` / `opsx:apply` / `opsx:archive`,
|
||||||
|
упадут на «нет такого скилла», а `review-specs` останется без источника
|
||||||
|
требований. **Проект без OpenSpec этим конвейером не проверяется** — подключай
|
||||||
|
OpenSpec, а не понижай прогон: ветка деградации здесь не пишется, потому что
|
||||||
|
непроверенная ветка деградации хуже честного отказа. Заводить руками не надо:
|
||||||
|
этим владеет скилл `av-dev:code-openspec` — он заводит каталог и заменяет
|
||||||
|
пример в `config.yaml` настройкой. Его же зовут `av-dev:doc-init` на новом
|
||||||
|
проекте и `av-dev:canon` в режиме `adopt` — на переводимом.
|
||||||
|
**Предпосылка эта — про изменение поведения, а не про всякий прогон:**
|
||||||
|
сценарий обслуживания зовёт конвейер без change и без дельта-спек, и ни один
|
||||||
|
проход его плана на них не завязан. См. «Прогон без change».
|
||||||
|
- **Документы канона** — см. следующий раздел.
|
||||||
|
- **Проектные копии этих скиллов и агентов удаляются при установке.**
|
||||||
|
|
||||||
|
<!-- копия: проектные-копии из README.md -->
|
||||||
|
|
||||||
|
Голые имена в `.claude/skills/`: `resolve`, `review`, а у проектов прошлого
|
||||||
|
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`. С префиксом
|
||||||
|
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`. Агенты:
|
||||||
|
`.claude/agents/<проект>-review-*.md`.
|
||||||
|
|
||||||
|
Две копии одного скилла расходятся, и побеждает та, что **короче названа**:
|
||||||
|
короткое имя разрешится в устаревшую проектную копию молча и без признаков
|
||||||
|
подмены.
|
||||||
|
|
||||||
|
<!-- /копия: проектные-копии -->
|
||||||
|
|
||||||
|
### Чего может не быть
|
||||||
|
|
||||||
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
|
поведении.
|
||||||
|
|
||||||
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
|
|
||||||
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
|
<!-- /копия: отсутствие -->
|
||||||
|
|
||||||
|
Своих скиллов это касается ровно так же: `av-dev:code-review`,
|
||||||
|
`av-dev:code-resolve`, `av-dev:code-openspec` — подменяется короткое имя,
|
||||||
|
а не чужое.
|
||||||
|
|
||||||
|
## Темы, источники и процессные документы
|
||||||
|
|
||||||
|
Раньше здесь стояло плоское правило «каждый документ проекта — тема ревью». Оно
|
||||||
|
верно ровно наполовину, и потому вредно целиком: паспорт и схему хранилища
|
||||||
|
ревью читает, но темами они не являются, а журнал решений и журнал наблюдений
|
||||||
|
ревью изменения не нужны вовсе. Прогон, применявший правило буквально, обязан был
|
||||||
|
либо завести фантомные темы и продублировать ими работу настоящих, либо потерять
|
||||||
|
документ молча.
|
||||||
|
|
||||||
|
**Разрез один и проверяемый — тот же, что в каноне: можно ли по документу
|
||||||
|
сказать «в этом изменении сделано не так»?**
|
||||||
|
|
||||||
|
| Категория | Что конвейер с ней делает | Кто в ней |
|
||||||
|
|---|---|---|
|
||||||
|
| **тема** | заводит направление проверки и требует исполнителя | `conventions.*`, `security.*`, `architecture.*`, свои документы проекта |
|
||||||
|
| **источник темы** | читает как материал чужой темы, своей не порождает | `passport.*`, `database.*`, `CLAUDE.md`/`AGENTS.md`, `openspec/specs/` |
|
||||||
|
| **процессный документ** | не судит по нему изменение | `tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `.av-dev.toml` |
|
||||||
|
|
||||||
|
**Одна процессная запись всё же читается — `docs/review.*`.** В ней лежит
|
||||||
|
настройка самого конвейера: вопросы по темам, журнал дефектов, типовые узлы,
|
||||||
|
типовые ложноположительные. Проход, читающий её, читает **свою обвязку**, а не
|
||||||
|
критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не
|
||||||
|
открывает никто.
|
||||||
|
|
||||||
|
Дом канона этой раскладки — скилл `av-dev:canon`, раздел «Три категории
|
||||||
|
документов». Конвейер её **читатель**: категории и имена тем он берёт
|
||||||
|
оттуда и своих не заводит.
|
||||||
|
|
||||||
|
Отсюда то, ради чего правило и заведено: **`docs/` перестаёт быть просто
|
||||||
|
документацией и становится конфигурацией конвейера**. Проект настраивает ревью
|
||||||
|
тем, что пишет о себе, а не отдельным файлом настроек, который разошёлся бы с
|
||||||
|
документами. **Открыта при этом только категория `тема`** — две другие закрыты
|
||||||
|
и перечислены поимённо, поэтому документ, которого нет в раскладке канона,
|
||||||
|
однозначно своя тема проекта, а не «что-то непонятное».
|
||||||
|
|
||||||
|
Ядро — шесть тем, они есть у любого проекта, приведённого к канону. Форма дома
|
||||||
|
значения не имеет: `docs/security.md` и `docs/security/` — одна тема `security`.
|
||||||
|
|
||||||
|
| Тема | Дом | Вопрос темы |
|
||||||
|
|---|---|---|
|
||||||
|
| `requirements` | `openspec/specs/`, дельты change | делает ли код заказанное, и только его |
|
||||||
|
| `autotests` | `CLAUDE.md`: семантика гейта, команды | проверено ли машиной и хватает ли проверок |
|
||||||
|
| `conventions` | `docs/conventions.*` | написано ли так, как здесь пишут |
|
||||||
|
| `architecture` | `docs/architecture.*` + источник `passport.*` | цело ли устройство: понятия и границы |
|
||||||
|
| `security` | `docs/security.*` | что сделает недоверенный вход |
|
||||||
|
| `operations` | `docs/architecture.*`, раздел эксплуатации + источник `database.*` | что будет через неделю на проде |
|
||||||
|
|
||||||
|
**Три темы ядра дома в `docs/` не имеют, и это не пробел.** `requirements` живёт
|
||||||
|
в `openspec/`, `autotests` — в `CLAUDE.md`, `operations` — разделом внутри
|
||||||
|
`architecture.*`. Имя темы поэтому не выводится из имени файла, и обратно тоже:
|
||||||
|
`docs/passport.md` не заводит темы `passport`.
|
||||||
|
|
||||||
|
**`adr/` и `research/` прогон больше не открывает.** Раньше архитектурный проход
|
||||||
|
читал решения, а эксплуатационный и `specs` — числа. Цена решения записана в
|
||||||
|
каноне и повторяется здесь, потому что платит её конвейер: **расхождение
|
||||||
|
изменения с записанным решением прогоном не ловится**, это работа сверки
|
||||||
|
документации — скилл `av-dev:doc-healthcheck`.
|
||||||
|
Строка об этом обязательна в границах покрытия каждого прогона.
|
||||||
|
|
||||||
|
**Своя тема проекта бывает двух происхождений, и обе законны:** документ, который
|
||||||
|
проект положил в `docs/` и которого нет в раскладке канона, — и **тема, названная
|
||||||
|
директивой** `CLAUDE.md`/`AGENTS.md`, у которой документа нет вовсе. У второй дом
|
||||||
|
— сама директива; в остальном она ничем не отличается, и в раздаче идёт туда же.
|
||||||
|
Различать их приходится потому, что условие запуска приёмника тем звучит «есть ли
|
||||||
|
свои темы проекта», и тема без файла в `docs/` иначе не попала бы под это условие
|
||||||
|
никогда.
|
||||||
|
|
||||||
|
**Проектная тема закрывается `basics`**, и только она. Именных проходов конечное
|
||||||
|
число, а тем — сколько заведёт проект; приёмник обязателен, иначе открытость
|
||||||
|
списка была бы обещанием без механизма. Темы **ядра** он не держит **в цикле
|
||||||
|
задачи** — на прогоне обслуживания план сценария даёт ему `operations`, и это
|
||||||
|
единственное исключение (раздел «Прогон без change»). В цикле:
|
||||||
|
`requirements` закрывает `specs`, `conventions` и технику — `code`, а риск и
|
||||||
|
устройство — тот же `code` сверкой с инвариантами. Отсюда правило состава:
|
||||||
|
**`basics` запускается тогда и только тогда, когда ему есть что принимать** — см.
|
||||||
|
«Состав прогона».
|
||||||
|
|
||||||
|
**Тема без дома — законное состояние и отдельная строка.** «Тема `operations`
|
||||||
|
заявлена, `docs/database.md` нет» читается иначе, чем «не смотрели». Деградация
|
||||||
|
поразрядная: нет дома — падает глубина этой темы, и только её.
|
||||||
|
|
||||||
|
Что именно проход читает по каждой теме — [references/project-facts.md](references/project-facts.md).
|
||||||
|
Отдельного файла-брифа при этом нет: пути известны, посредник не нужен, а второй
|
||||||
|
дом для тех же фактов разошёлся бы и выглядел актуальным.
|
||||||
|
|
||||||
|
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
|
||||||
|
и предложи скилл `av-dev:canon`: одна операция на проект против деградации на
|
||||||
|
каждой задаче. Прогон при этом не останавливается.
|
||||||
|
|
||||||
|
## Что получает каждый проход
|
||||||
|
|
||||||
|
Задание собирается **по таблице тем** и состоит из шести вещей:
|
||||||
|
|
||||||
|
- **его темы** — какие темы он закрывает, у каждой **дом** (путь и раздел) и
|
||||||
|
**глубина**. Дом передаётся адресом, а не пересказом: проход, получивший
|
||||||
|
проинтерпретированный периметр, не заметит, что интерпретация неверна;
|
||||||
|
- **вопросы по его темам** из `docs/review.*`, если они там есть, — **дословно**.
|
||||||
|
Вопрос привязан к теме, а не к имени прохода, и потому переживает переезд
|
||||||
|
прохода между скиллами;
|
||||||
|
- **контракт находок** — путь к
|
||||||
|
[references/finding-contract.md](references/finding-contract.md) (в
|
||||||
|
установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/`);
|
||||||
|
- **изменение** — идентификатор change и путь к его дельта-спекам;
|
||||||
|
- **база диффа**;
|
||||||
|
- **его глубина и режим** прогона — чтобы проход знал, что писать в границы
|
||||||
|
покрытия.
|
||||||
|
|
||||||
|
**Ступень 1 получает сверх этого исход гейта, прогнанного до ревью** — сводку,
|
||||||
|
путь к логам шагов и отпечаток дерева, — если вызывающий скилл его дал. Зачем и
|
||||||
|
что происходит при расхождении — «Ступень 1 — Автотесты».
|
||||||
|
|
||||||
|
Чего проход **не** получает ни в каком режиме — выводов других проходов. См.
|
||||||
|
«Порядок прогона».
|
||||||
|
|
||||||
|
## Модель по проходу
|
||||||
|
|
||||||
|
Модель выбирается **по цене ошибки прохода, а не по его роду**. Признак рабочий и
|
||||||
|
проверяемый: находка со ссылкой на записанный источник — строку спеки, цель в
|
||||||
|
манифесте, значение в конфиге — опровергается открытием файла, и дешёвая модель
|
||||||
|
ошибается здесь проверяемо; находка-суждение опровергается рассуждением, а
|
||||||
|
рассуждение стоит триажа или человека. Второй род ошибки — **пропуск**: он не
|
||||||
|
стоит ничего сегодня и не виден вовсе, и проход, у которого дороже пропустить,
|
||||||
|
держится наверху, даже будучи applicative.
|
||||||
|
|
||||||
|
Модель задана во frontmatter каждого агента, менять её здесь не нужно.
|
||||||
|
|
||||||
|
| Модель | Цвет | Проходы | Почему |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `sonnet` | green | autotests, ops | вывод перечислим и сверяется механически |
|
||||||
|
| `opus` | yellow | specs, code, basics, triage, rubric, adversary, architecture | дорога ошибка — ложная либо пропущенная |
|
||||||
|
|
||||||
|
**В таблице стоят и проходы, которых в цикле нет.** `adversary`, `ops` и
|
||||||
|
`architecture` работают в скилле `av-dev:code-deep-review`, `rubric` зовут прямо
|
||||||
|
руками; раскладка «модель — цвет» общая для всех уставов плагина и проверяется
|
||||||
|
механически, поэтому дом у неё один, а не по скиллу.
|
||||||
|
|
||||||
|
**Цвет charter'а кодирует модель, а не роль прохода.** Это единственное
|
||||||
|
назначение цвета: список агентов читается взглядом, и по нему сразу видно, чем
|
||||||
|
платит прогон. Роль прохода из имени и так понятна, а цвет, розданный по ролям,
|
||||||
|
не отвечает ни на один вопрос, который задают во время прогона. Раскладка живёт
|
||||||
|
здесь и **проверяется механически** — цвет ставится один раз при заведении
|
||||||
|
charter'а, а модель потом двигает калибровка, и разъезжаются они молча.
|
||||||
|
|
||||||
|
**Моделей две, и верхняя из них — `opus`; выше неё конвейер не платит.** Замер:
|
||||||
|
на первом же прогоне самые ценные находки дали `opus`-проходы — сверка спек дала
|
||||||
|
13 находок с оракулами, а проход про идиоматичность (впоследствии упразднённый) —
|
||||||
|
три эксперимента против драйвера БД с воспроизведёнными числами. Разницы в пользу
|
||||||
|
модели **дороже** `opus` не обнаружилось ни на одном проходе, а прогон на ней
|
||||||
|
стоил заметно дольше и дороже — значит платить за неё не за что.
|
||||||
|
|
||||||
|
Четверо держатся наверху не за суждение, а по отдельным причинам, и их стоит
|
||||||
|
знать поимённо:
|
||||||
|
|
||||||
|
- `triage` — через него проходит всё, что оркестратор реализует **молча**:
|
||||||
|
ложноположительная находка становится кодом, потерянный `critical` — дефектом.
|
||||||
|
Ошибка триажа дороже ошибки любого отдельного прохода.
|
||||||
|
- `specs` — по устройству applicative, но направление `code → spec` требует
|
||||||
|
заметить **отсутствие**: тихий фолбэк, самодеятельный дефолт, проглоченную
|
||||||
|
ошибку. Здесь дорог пропуск, а не ложная находка.
|
||||||
|
- `code` — единственный, кто читает код **как код**, и с уходом тяжёлых проходов
|
||||||
|
он же единственный, кто смотрит на риск и устройство. Его пропуск это дефект в
|
||||||
|
проде, и он не оставляет следа ни в отчёте, ни в границах покрытия. По той же
|
||||||
|
причине, что `specs`, и это дороже всего в конвейере: проход идёт на каждой
|
||||||
|
задаче.
|
||||||
|
- `basics` — держит темы, которые проект завёл сам, то есть ровно те, о которых
|
||||||
|
плагин ничего не знает. Ошибиться на чужой теме дешёвой моделью проще всего:
|
||||||
|
критерий приходит текстом документа, а не перечнем.
|
||||||
|
|
||||||
|
**Самая дешёвая модель не используется ни на одном проходе, и это не экономия
|
||||||
|
наоборот.** Дешёвая модель на проходе с мнением даёт правдоподобные находки,
|
||||||
|
которые триаж обязан опровергать оракулом, — а это самая дорогая операция
|
||||||
|
конвейера. Механизируемая же работа здесь вынесена **ниже** модели: гейт,
|
||||||
|
покрытие диффа, карта проекта — это скрипты проекта, они стоят ноль токенов.
|
||||||
|
Дешёвому проходу просто не осталось работы.
|
||||||
|
|
||||||
|
Экономия достигается **не понижением модели, а тремя другими рычагами**, и все
|
||||||
|
три применяются к каждому проходу с мнением, а не к одному избранному.
|
||||||
|
|
||||||
|
1. **Непуск.** Тяжёлые проходы в цикле не запускаются вовсе — они живут в
|
||||||
|
`av-dev:code-deep-review`; приёмник тем не идёт, когда своих тем у проекта
|
||||||
|
нет. Что при этом перестаёт проверяться, названо поимённо и идёт в границы
|
||||||
|
покрытия.
|
||||||
|
2. **Вход.** `basics` идёт на верхней модели, но с узким входом: дифф и его
|
||||||
|
окрестности, без карты проекта. Карта проекта и вход шире диффа не даются в
|
||||||
|
цикле никому — это цена глубокого прогона, а не задачи.
|
||||||
|
3. **Потолок.** Он есть у каждого прохода с мнением и напечатан: `basics` — 4
|
||||||
|
находки; `code` — 4 конвенционных и 1 на все три темы риска и устройства
|
||||||
|
разом, у технической половины потолка нет; `specs` — потолка нет; триаж — 7 в
|
||||||
|
основном списке. Двум половинам его не ставят намеренно: пропуск дефекта и
|
||||||
|
пропуск расхождения со спекой стоят дороже длинного списка.
|
||||||
|
|
||||||
|
Все три раньше зависели от метки и потому на каждой задаче считались заново.
|
||||||
|
Теперь они постоянные, и проход знает свой потолок до того, как получил задание.
|
||||||
|
Проход без потолка выдаёт столько находок, сколько нашёл поверхностей, — а это
|
||||||
|
ровно тот механизм, из-за которого был снят проход независимой реализации:
|
||||||
|
**счёт определялся объёмом вывода**. Потолок ставится не ради краткости отчёта, а
|
||||||
|
против этого.
|
||||||
|
|
||||||
|
**Потолок обязан быть объявлен, когда он сработал.** Проход, срезавший находки
|
||||||
|
до потолка, говорит об этом строкой в своих границах покрытия: сколько осталось
|
||||||
|
за срезом и какого рода. Молчащий срез неотличим от «больше не нашлось» — это тот
|
||||||
|
же класс молчащего пропуска, что и непущенный проход.
|
||||||
|
|
||||||
|
## Состав прогона — постоянный
|
||||||
|
|
||||||
|
**Ступени нумерованы, и наружу выходит одна.** Прогон ревью один, и зовёт его
|
||||||
|
`av-dev:code-resolve` после того, как код написан; членение внутри прогона —
|
||||||
|
ступени, и знать их снаружи не нужно. Исключение единственное и названное:
|
||||||
|
**ступень 1**, автотесты, — на неё ссылаются снаружи, потому что она умеет
|
||||||
|
засчитать чужой прогон гейта по отпечатку дерева, и вызывающему надо знать, куда
|
||||||
|
этот отпечаток едет. Перечень осей процесса целиком —
|
||||||
|
[shared/axes.md](../../shared/axes.md).
|
||||||
|
|
||||||
|
**Состав не выводится ни из чего: он один и тот же на всякой задаче.** Гейт,
|
||||||
|
сверка со спекой, разбор кода, триаж; приёмник тем — когда у проекта есть свои
|
||||||
|
темы. Прежде состав выбирала **метка** `small`/`medium`/`large`, которую считал
|
||||||
|
отдельный проход по двум осям — размеру и сложности. Метки больше нет, и вместе с
|
||||||
|
ней ушли разметка, матрица выбора, правило «спорное решается вниз» и доли по
|
||||||
|
журналу.
|
||||||
|
|
||||||
|
**Цикл задачи проверяет корректность и механику, и это его определение.**
|
||||||
|
Вопрос «делает ли код заказанное и не сломается ли он сам» отвечается против
|
||||||
|
**записанного** критерия: дельта-спеки, конвенции, инварианты `CLAUDE.md`, вывод
|
||||||
|
инструментов. Вопрос «то ли это решение» здесь не задаётся вовсе: он стоит
|
||||||
|
человеку разговора, а место разговора назначено — чекпоинт до кода, где форму
|
||||||
|
решения одобряет человек, и скилл `av-dev:code-deep-review`, где находки
|
||||||
|
разбирают по одной.
|
||||||
|
|
||||||
|
Отсюда таблица тем — единственная и без вариантов:
|
||||||
|
|
||||||
|
<!-- дом: тема-глубина -->
|
||||||
|
|
||||||
|
| Тема | Кто закрывает | Против чего и как |
|
||||||
|
|---|---|---|
|
||||||
|
| `autotests` | `autotests` | запуск: гейт проекта и логи его шагов |
|
||||||
|
| `requirements` | `specs` | разбор: дельта-спеки change, сверка в обе стороны |
|
||||||
|
| `conventions` | `code` | разбор: дома конвенций проекта |
|
||||||
|
| техника | `code` | разбор: дефект, который сработает сам |
|
||||||
|
| `security`, `operations`, `architecture` | `code` | **сверка с записанными инвариантами `CLAUDE.md`** — и только |
|
||||||
|
| тема проекта | `basics` | разбор: дом темы против диффа |
|
||||||
|
|
||||||
|
<!-- /дом: тема-глубина -->
|
||||||
|
|
||||||
|
**Три темы риска и устройства закрыты узко, и это названо прямо.** Свойство,
|
||||||
|
которого нет в инвариантах, в цикле не спросит никто: ни сценарием, ни чтением
|
||||||
|
дома темы. Это самая крупная граница покрытия конвейера, она идёт строкой в
|
||||||
|
каждом отчёте, и снимает её не прогон задачи, а глубокое ревью области.
|
||||||
|
|
||||||
|
Весь процесс с исполнителями — одной схемой:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
propose["opsx:propose — change, дельта-спеки, tasks.md"]
|
||||||
|
checkpoint(["чекпоинт: форму решения одобряет человек"])
|
||||||
|
apply["opsx:apply — код, гейт зелёный"]
|
||||||
|
|
||||||
|
subgraph code["Ревью кода — состав постоянный"]
|
||||||
|
cGate["autotests — гейт, источник графа"]
|
||||||
|
cS["specs — requirements"]
|
||||||
|
cC["code — conventions, техника<br/>и сверка с инвариантами:<br/>security, operations, architecture"]
|
||||||
|
cB["basics — только свои темы проекта"]
|
||||||
|
cT["triage — единственный сток"]
|
||||||
|
end
|
||||||
|
|
||||||
|
propose --> checkpoint --> apply --> cGate
|
||||||
|
|
||||||
|
cGate -->|зелёный| cS
|
||||||
|
cGate -->|зелёный| cC
|
||||||
|
cGate -->|"зелёный, есть свои темы"| cB
|
||||||
|
|
||||||
|
cS --> cT
|
||||||
|
cC --> cT
|
||||||
|
cB --> cT
|
||||||
|
```
|
||||||
|
|
||||||
|
**Глубины две, и они не про старательность, а про способ доказательства.**
|
||||||
|
**Сверка** — открыть дом темы, открыть дифф, сравнить. **Разбор** — построить
|
||||||
|
сценарий рассуждением, ничего не запуская.
|
||||||
|
|
||||||
|
**Третья глубина — доказательство** (прогнать, померить, построить путь) — в
|
||||||
|
цикле задачи не производится вовсе. Она требует машины и стоит часов, и потому
|
||||||
|
живёт в скилле `av-dev:code-deep-review`, который идёт по названной области и
|
||||||
|
время от времени. Проход, которому в плане назначили доказательство, получил план
|
||||||
|
не от конвейера задачи.
|
||||||
|
|
||||||
|
**Необратимое изменение состава не меняет — оно меняет адресата находки.**
|
||||||
|
Миграция схемы и данных, формат на диске, публичный контракт, имя, разошедшееся
|
||||||
|
по кодовой базе, — всё, что после мерджа не откатывается обратной правкой. Раньше
|
||||||
|
это был отрицательный тест метки `small`: такое изменение поднимало метку и
|
||||||
|
получало лишний проход. Поднимать больше нечего, и правило работает иначе:
|
||||||
|
находка по необратимому месту помечается `Действие: развилка` и уходит человеку,
|
||||||
|
а не чинится молча, каким бы мелким ни был дифф. Цена ошибки тут не в размере
|
||||||
|
правки, а в том, что её не отменить.
|
||||||
|
|
||||||
|
**Состав сверяется до коммита — по таблице тем выше.** Она и есть реестр: тема,
|
||||||
|
кто закрывает, против чего. Это единственная защита от промаха, который уже
|
||||||
|
случился: пропуск **не отличим от прохода без находок** (гейт зелёный, спеки
|
||||||
|
сошлись, отчёт выглядит полным), а заметить его мог бы только триаж, который сам
|
||||||
|
заполняется тем, что ему подали. Непущенное идёт строкой «не запускался» с
|
||||||
|
причиной, а не отсутствует. Цена молчащего пропуска измерена: семь находок и
|
||||||
|
отдельная задача на их дозакрытие.
|
||||||
|
|
||||||
|
**Сверять теперь дешевле, и это главный выигрыш от снятия метки.** Реестр был
|
||||||
|
переменным — он приезжал планом разметки и на каждой задаче выглядел иначе;
|
||||||
|
пропущенную тему приходилось искать сверкой двух списков. Реестр постоянный
|
||||||
|
сверяется взглядом: против каждой строки таблицы либо отчёт, либо названная
|
||||||
|
причина, по которой проход не пущен.
|
||||||
|
|
||||||
|
## Порядок прогона — граф, а не очередь
|
||||||
|
|
||||||
|
Таблица тем отвечает «что и против чего проверяется», порядок — «что кого
|
||||||
|
ждёт». Ступени остаются единицей **состава**, но порядок задают **не их номера**:
|
||||||
|
между ступенями 2 и 3 настоящих зависимостей нет — ни один проход не читает вывод
|
||||||
|
другого, — и очередь между ними была бы платой ни за что.
|
||||||
|
|
||||||
|
Рёбер два вида, и они разной природы. Путать их нельзя: первое про
|
||||||
|
**осмысленность** (на красном гейте проходу с мнением не о чем судить), второе
|
||||||
|
про **железо**.
|
||||||
|
|
||||||
|
| Ребро | Смысл | Между кем |
|
||||||
|
|---|---|---|
|
||||||
|
| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все проходы с мнением; все проходы → триаж |
|
||||||
|
| **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» |
|
||||||
|
|
||||||
|
**Узла, который считает состав, у графа нет.** Раньше первым узлом каждого
|
||||||
|
прогона стояла разметка и ребро «разметка → все» шло отсюда; состав постоянный, и
|
||||||
|
считать его больше нечем и незачем.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
autotests["autotests<br/>(ступень 1, держит машину)"]
|
||||||
|
specs["specs"]
|
||||||
|
code["code"]
|
||||||
|
basics["basics<br/>(только свои темы проекта)"]
|
||||||
|
triage["triage — единственный сток"]
|
||||||
|
|
||||||
|
autotests -->|зелёный| specs
|
||||||
|
autotests -->|зелёный| code
|
||||||
|
autotests -->|"зелёный, есть свои темы"| basics
|
||||||
|
specs --> triage
|
||||||
|
code --> triage
|
||||||
|
basics --> triage
|
||||||
|
```
|
||||||
|
|
||||||
|
Читается граф так: **всё, у чего входящие рёбра закрыты, уходит одним
|
||||||
|
сообщением**. Источник графа — гейт: он один по построению и идёт первым. После
|
||||||
|
зелёного гейта уходят разом `specs` и `code`, а с ними `basics`, если у проекта
|
||||||
|
есть свои темы; триаж стартует, когда вернулся последний. Глубина графа — три
|
||||||
|
шага при любой задаче, и это же его худший случай.
|
||||||
|
|
||||||
|
**Схема здесь старше прозы.** Она не иллюстрация к тексту, а сам алгоритм
|
||||||
|
планировщика; проза ниже объясняет рёбра и называет их цену. Разошлись — прав
|
||||||
|
граф, а расхождение чинится правкой текста.
|
||||||
|
|
||||||
|
**Ребро значит «A закончил раньше, чем B стартовал», и ничего больше.** В обычном
|
||||||
|
графе задач ребро тянет за собой данные — здесь нет, и это не деталь реализации.
|
||||||
|
Проход **не видит** находок других проходов, в каком бы порядке их ни запустили.
|
||||||
|
Вся ценность конвейера держится на разведённости: под всеми ролями одна модель с
|
||||||
|
одними априорными, и стоит показать ей чужой вывод — она согласится. Согласие
|
||||||
|
нескольких проходов и так не повышает `confidence` (см. «Честный предел»);
|
||||||
|
согласие **наведённое** ещё и маскируется под независимое подтверждение.
|
||||||
|
Единственный, кто получает чужие выводы, — триаж, и это его работа.
|
||||||
|
|
||||||
|
### Кто держит машину
|
||||||
|
|
||||||
|
Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск.
|
||||||
|
Проходы, заявившие его, сериализуются между собой на любой ступени; порядок
|
||||||
|
внутри цепочки произволен.
|
||||||
|
|
||||||
|
| Проход | Держит машину | Почему |
|
||||||
|
|---|---|---|
|
||||||
|
| `autotests` | да | запускает инструменты проекта — но он источник графа и один по построению |
|
||||||
|
| `triage` | да | проверяет оракул `major` запуском — но он сток и тоже один |
|
||||||
|
| `specs`, `code`, `basics` | нет | читают и рассуждают, ничего не исполняют |
|
||||||
|
|
||||||
|
**В цикле задачи цепочки за машину нет.** Оба прохода, что её держали —
|
||||||
|
`adversary` и `ops`, — переехали в скилл `av-dev:code-deep-review`; там правило
|
||||||
|
действует целиком, и дом его остаётся здесь. Оставшиеся двое машину держат, но
|
||||||
|
каждый один по построению: один источник графа, другой сток.
|
||||||
|
|
||||||
|
**Правило про ресурс, а не про имена.** Раньше здесь стояло именованное
|
||||||
|
исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт
|
||||||
|
мерить или в проекте появится свой. Два прохода на одной машине соревнуются за
|
||||||
|
диск, CPU и за саму СУБД и выдают числа, которые не воспроизведутся, — а число,
|
||||||
|
снятое под конкурентную нагрузку, это находка с испорченным оракулом. Её
|
||||||
|
опровержение стоит дороже всего выигрыша от параллельности, и она хуже
|
||||||
|
отсутствующей: выглядит доказанной. Правило выведено из находок, целиком
|
||||||
|
державшихся на таких замерах; у каждого проекта они свои и лежат в журнале
|
||||||
|
`docs/review.md`.
|
||||||
|
|
||||||
|
Проект вправе пометить «держит машину» и другой проход — строкой в подразделе
|
||||||
|
**«Недоступно проверке»** файла `docs/review.md`: своего подраздела у пометки нет,
|
||||||
|
и заводить его канон не станет ради одного проекта. Читает её тот, кто строит
|
||||||
|
порядок прогона, то есть этот скилл. Снимать пометку с перечисленных нельзя.
|
||||||
|
|
||||||
|
### Находка «переделать форму» — прогон повторяется целиком
|
||||||
|
|
||||||
|
**Барьера стоимости в конвейере нет, и раннего выхода тоже.** Барьер существовал
|
||||||
|
ради независимой реализации — единственного прохода, чей счёт определялся объёмом
|
||||||
|
вывода, — и ушёл вместе с ней. Граф плоский, от гейта до триажа: защищать за
|
||||||
|
барьером нечего, а сериализация не бесплатна — она разводит по очереди то, что
|
||||||
|
могло идти разом.
|
||||||
|
|
||||||
|
Находка «**форму изменения** надо переделывать» ловится триажем, как и любая
|
||||||
|
другая; дальше правило одно. Находка чинится, и ревью кода запускается **заново с
|
||||||
|
гейта**, а не «доезжает» остатком по коду, которого через час не станет.
|
||||||
|
|
||||||
|
**Пересчитывать перед повтором нечего.** Состав постоянный, и второй прогон
|
||||||
|
идёт тем же составом, что первый; менять его нельзя даже «раз уж переделываем» —
|
||||||
|
конвейер, чей состав зависит от истории прогонов, не сверяется ни с чем.
|
||||||
|
Если прогон всё же остановлен на полпути, незапущенные проходы идут в границы
|
||||||
|
покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>»,
|
||||||
|
поимённо, а **триаж на половине прогона не запускается**: его отчёт выглядит
|
||||||
|
полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск,
|
||||||
|
что и в разделе «Состав прогона».
|
||||||
|
|
||||||
|
Находка, которая чинится в пределах существующей формы (`Действие: инлайн`),
|
||||||
|
прогон не останавливает: дешевле дособрать все находки и починить пачкой, чем
|
||||||
|
гонять конвейер дважды.
|
||||||
|
|
||||||
|
### Линеаризация — когда графа мало
|
||||||
|
|
||||||
|
Граф можно вытянуть в одну цепочку. Это отступление, и оно называется в отчёте:
|
||||||
|
|
||||||
|
1. **сказал оператор** — «гони линейно». Набора называть не надо: линейный прогон
|
||||||
|
ничего не портит, он только дольше, и домысливать тут нечего;
|
||||||
|
2. **машина занята, и знает об этом вызывающий.** Рядом идёт другая задача,
|
||||||
|
поднят сервис, гоняется дорогая проверка проекта. Сам конвейер занятости
|
||||||
|
машины не видит — её обязан назвать тот, кто запускает;
|
||||||
|
3. **разбор самого конвейера** — когда выясняется, почему проход чего-то не
|
||||||
|
нашёл, порядок и изоляция важнее скорости.
|
||||||
|
|
||||||
|
Обратное отступление — **слить цепочку ресурса** (пустить меряющие проходы
|
||||||
|
разом) — бывает только по прямому слову оператора, и тогда в границы покрытия
|
||||||
|
идёт строка: какие проходы шли одновременно и что замеры этого прогона как
|
||||||
|
оракул слабее.
|
||||||
|
|
||||||
|
Режим объявляется в отчёте отдельной строкой: **`по графу`** — одним словом,
|
||||||
|
**`линейно`** — с причиной (какой именно из трёх).
|
||||||
|
|
||||||
|
## Прогон без change — сценарий обслуживания
|
||||||
|
|
||||||
|
Второй вызывающий конвейера — сценарий обслуживания скилла `av-dev:code-resolve`
|
||||||
|
(тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит **без
|
||||||
|
change**: у работы, не меняющей поведения, дельта-спек нет по построению.
|
||||||
|
|
||||||
|
**Копия.** Дом оси — `shared/axes.md` в репозитории плагина: режим делят конвейер,
|
||||||
|
сценарий обслуживания и два устава, и ни один из них им не владеет. Правится дом,
|
||||||
|
а не этот файл.
|
||||||
|
|
||||||
|
<!-- копия: режим-прогона из av-dev/shared/axes.md -->
|
||||||
|
|
||||||
|
**Прогон ревью идёт в одном из двух режимов, и режим — не глубина.**
|
||||||
|
|
||||||
|
- **По change** — обычный прогон цикла задачи: есть дифф и дельта-спеки, состав
|
||||||
|
постоянный и живёт в конвейере.
|
||||||
|
- **Без change** — дельта-спек нет по построению, и вместе с ними нет темы
|
||||||
|
`requirements`. План фиксирован и назван вызывающим; так идёт сценарий
|
||||||
|
обслуживания.
|
||||||
|
|
||||||
|
**Режим правит состав, а не глубину.** Глубина темы стоит в таблице тем
|
||||||
|
конвейера, одна на все прогоны по change; на прогоне без change её называет план
|
||||||
|
сценария — иначе проход, чьей темы в плане нет, взял бы глубину наугад.
|
||||||
|
|
||||||
|
**Третьего режима у конвейера нет.** Скилл `av-dev:code-deep-review` конвейер не
|
||||||
|
зовёт вовсе: состав, глубина и вход у него свои, а общее с конвейером — уставы
|
||||||
|
проходов и контракт находок.
|
||||||
|
|
||||||
|
<!-- /копия: режим-прогона -->
|
||||||
|
|
||||||
|
**План приходит вызовом и фиксирован сценарием**, а не выводится здесь. **Он же
|
||||||
|
называет темы и глубину каждого прохода** — таблица тем конвейера описывает
|
||||||
|
прогон по change, и тема `requirements` в ней есть, а здесь её предмета нет:
|
||||||
|
|
||||||
|
<!-- копия: план-обслуживания из av-dev/skills/code-resolve/references/maintain.md -->
|
||||||
|
|
||||||
|
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда |
|
||||||
|
| `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда |
|
||||||
|
| `conventions` + технический разбор | `conventions.*` | `review-code` | как в цикле: дом конвенций целиком, потолок 4 конвенционных, у техники потолка нет; сверка с инвариантами `CLAUDE.md` по темам `security`, `operations`, `architecture`, потолок 1 | дифф трогает код, а не только оснастку |
|
||||||
|
|
||||||
|
<!-- /копия: план-обслуживания -->
|
||||||
|
|
||||||
|
Триаж обязателен и здесь — он единственный сток и единственный, кто сверяет план
|
||||||
|
с исходом; на его вход подаётся этот план вместо таблицы тем. Тема
|
||||||
|
`requirements` в плане отсутствует за отсутствием предмета; `security` и
|
||||||
|
`architecture` закрыты сверкой с записанными инвариантами внутри `code` — ровно
|
||||||
|
так же, как в цикле задачи. Все три обязаны быть названы в границах покрытия.
|
||||||
|
|
||||||
|
Дом плана — сценарий, а не этот скилл: `av-dev:code-resolve`,
|
||||||
|
`references/maintain.md`, раздел «Ревью — план фиксирован сценарием».
|
||||||
|
|
||||||
|
**Правило гейта на таком прогоне работает жёстче обычного.** Правка, которая
|
||||||
|
трогает сам гейт, проверяется гейтом же — инструмент проверяет себя, — поэтому
|
||||||
|
сверяется не только цвет, но и состав шагов. Что считается составом, объявляет
|
||||||
|
проект семантикой гейта в `CLAUDE.md`; не объявил — это строка границ покрытия, а
|
||||||
|
не догадка прохода.
|
||||||
|
|
||||||
|
## Ступень 1 — Автотесты (обязательна)
|
||||||
|
|
||||||
|
Агент `review-autotests`, тема `autotests`. Гонит команду гейта из семантики
|
||||||
|
гейта в `CLAUDE.md` — либо засчитывает прогон, сделанный до ревью, — и
|
||||||
|
интерпретирует вывод.
|
||||||
|
|
||||||
|
**Тема и проход названы одинаково намеренно, а «гейт» осталось именем команды.**
|
||||||
|
Раньше тема звалась `autotests`, а проход — `gate`: одна сущность под двумя
|
||||||
|
именами, и вопрос проекта, адресованный одному имени, к другому не приезжал.
|
||||||
|
Слово «гейт» теперь значит ровно одно — барьер, который проект запускает; тема
|
||||||
|
шире него ровно на «чего в гейте намеренно нет».
|
||||||
|
|
||||||
|
**Гейт, прогнанный до ревью, второй раз не гоняется.** Задача приходит на ревью
|
||||||
|
с зелёным гейтом: сценарий решения доводит его до зелёного шагом `opsx:apply`,
|
||||||
|
сценарий обслуживания — своим шагом гейта. Повтор на неизменившемся дереве
|
||||||
|
вернёт тот же вывод, а стоит он минут — то есть платит ими ни за что.
|
||||||
|
|
||||||
|
**Признак один и проверяемый — отпечаток рабочего дерева.** Его снимают дважды:
|
||||||
|
тот, кто прогнал гейт, сразу после прогона, и проход перед началом работы.
|
||||||
|
|
||||||
|
<!-- дом: отпечаток-дерева -->
|
||||||
|
|
||||||
|
```sh
|
||||||
|
{ git rev-parse HEAD; git status --porcelain -uall; git diff HEAD;
|
||||||
|
git ls-files -o --exclude-standard -z | xargs -0 -r git hash-object; } | sha1sum
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- /дом: отпечаток-дерева -->
|
||||||
|
|
||||||
|
Сводку прошлого прогона, путь к логам шагов и отпечаток проход получает
|
||||||
|
**заданием** — их передаёт вызывающий скилл. Отпечатки совпали — проход читает
|
||||||
|
готовую сводку и логи, команду не запускает. Разошлись, отпечатка в задании нет,
|
||||||
|
логи недоступны — проход гонит гейт сам и ни у кого не спрашивает.
|
||||||
|
|
||||||
|
**Отказ здесь безопасен по построению.** Лишний прогон стоит минут, а
|
||||||
|
засчитанный чужой — красноты, которой никто не увидел. Временный каталог проекта
|
||||||
|
из отпечатка выпадает сам: `--exclude-standard` отбрасывает игнорируемое, а логи
|
||||||
|
шагов гейт пишет именно туда. У проекта, держащего временный каталог под git,
|
||||||
|
отпечатки не совпадут никогда — и он получит честный прогон вместо тихого
|
||||||
|
засчитывания.
|
||||||
|
|
||||||
|
**Переиспользуется команда, а не проход.** Тема `autotests` закрывается целиком:
|
||||||
|
логи проход читает сам, находки об отсутствующей верификации выдаёт как обычно.
|
||||||
|
Переиспользование он объявляет строкой сводки и строкой границ покрытия — чем
|
||||||
|
гейт прогнан, когда и на каком отпечатке. Молчащее переиспользование неотличимо
|
||||||
|
от собственного прогона, а разница между ними в том, кто видел вывод своими
|
||||||
|
глазами.
|
||||||
|
|
||||||
|
**Пока гейт красный — проходы с мнением не запускаются.** Оркестратор чинит и
|
||||||
|
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
|
||||||
|
(гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не
|
||||||
|
блокирует.
|
||||||
|
|
||||||
|
Гейт возвращает не только «зелено/красно», но и находки класса **отсутствующая
|
||||||
|
верификация**: изменённые строки без покрытия, конкурентность без теста с
|
||||||
|
параллельным доступом, флаки-тест (не ниже `major`), недоступный инструмент.
|
||||||
|
|
||||||
|
Шаги выбираются по изменённым файлам: правка документации не гоняет тесты,
|
||||||
|
линтеры и детектор гонок. Пропуск при этом не молчит — он виден в сводке с
|
||||||
|
причиной и уезжает в границы покрытия, как и любой другой `SKIP`.
|
||||||
|
|
||||||
|
Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу
|
||||||
|
запрещено списывать такой отказ в мелочь.
|
||||||
|
|
||||||
|
## Ступень 2 — Сверка (обязательна)
|
||||||
|
|
||||||
|
Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра
|
||||||
|
между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со
|
||||||
|
ступенью 3, если она идёт.
|
||||||
|
|
||||||
|
- `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек
|
||||||
|
предлагаемого изменения**, а не из proposal, сообщения коммита или описания
|
||||||
|
задачи. Сверка двунаправленная; направление `code → spec` важнее.
|
||||||
|
- `review-code` закрывает тему `conventions` **и делает технический разбор
|
||||||
|
кода** — это две его половины. Первая ищет дефект, который сработает сам, на
|
||||||
|
обычном входе: необработанная ветка отказа, пустое значение, граница диапазона,
|
||||||
|
перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс
|
||||||
|
библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть,
|
||||||
|
которая **не выражается правилом**: механизируемое уже проверила ступень 1.
|
||||||
|
**Третья его обязанность узкая и постоянная** — сверить дифф с записанными
|
||||||
|
инвариантами `CLAUDE.md` по темам `security`, `operations` и `architecture`.
|
||||||
|
Потолок 1 находка на все три темы разом: это не разбор темы, а объявленный
|
||||||
|
минимум, и в границах покрытия он называется именно так.
|
||||||
|
|
||||||
|
**Вход обоих постоянный и полный:** `specs` читает дельта-спеки и затронутые
|
||||||
|
актуальные спеки, `code` — дом конвенций целиком, до чтения диффа. Прежде вход
|
||||||
|
сужала метка `small` до дельта-спеки и индекса конвенций; узкий вход ловит
|
||||||
|
нарушение записанного рода и пропускает то, ради чего конвенцию писали абзацем,
|
||||||
|
— то есть экономил ровно на той работе, ради которой проход и зовут.
|
||||||
|
|
||||||
|
**Потолки у половин `code` раздельные, и это не бюрократия.** Конвенционных
|
||||||
|
находок больше по построению — родов навигации в разы больше, чем классов
|
||||||
|
технического дефекта, — и в общем списке они вытесняют техническую половину, чей
|
||||||
|
пропуск дороже. Раздельный потолок делает вытеснение невозможным: конвенционных
|
||||||
|
4, инвариантных 1, у технической половины потолка нет.
|
||||||
|
|
||||||
|
**Технический разбор — не тема, а обязанность прохода, и он единственный.**
|
||||||
|
Остальные читают код как материал для своей оптики: `specs` — против требований,
|
||||||
|
`basics` — против отказов окружения проекта. «Здесь
|
||||||
|
ошибка в логике» не говорит больше никто, и до недавнего времени не говорил
|
||||||
|
никто вовсе: `code` был проходом только по конвенциям, а дефект ловился разве что
|
||||||
|
случайно. Это была самая крупная дыра конвейера, и стоила она дороже любой
|
||||||
|
недосмотренной темы.
|
||||||
|
|
||||||
|
Recall темы `conventions` равен длине конвенций проекта — это предел любой
|
||||||
|
сверки, и снимает его не цикл задачи, а глубокое ревью области.
|
||||||
|
|
||||||
|
**Оба прохода на верхней модели, и по одной причине — цене пропуска.** У `specs`
|
||||||
|
это направление `code → spec`: надо заметить **отсутствие** — тихий фолбэк,
|
||||||
|
самодеятельный дефолт, проглоченную ошибку. У `code` это пропущенный дефект,
|
||||||
|
который поедет в прод. Ни то ни другое не оставляет следа ни в отчёте, ни в
|
||||||
|
границах покрытия; прочие проходы с мнением держат `opus` из-за цены **ложных**
|
||||||
|
находок, эти двое — из-за цены пропущенных.
|
||||||
|
|
||||||
|
**Эта ступень и есть цикл задачи.** С уходом тяжёлых проходов на ней держится всё,
|
||||||
|
что прогон вообще проверяет по существу: заказанное против сделанного, дефект,
|
||||||
|
который сработает сам, и конвенции проекта. Отсюда и решение не ставить потолка
|
||||||
|
технической половине.
|
||||||
|
|
||||||
|
## Ступень 3 — Темы проекта (только когда они есть)
|
||||||
|
|
||||||
|
Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не
|
||||||
|
меряет — уходит одним сообщением вместе со ступенью 2, сразу после зелёного
|
||||||
|
гейта.
|
||||||
|
|
||||||
|
**Он приёмник проектных тем, и больше ничей.** Именных проходов конечное число, а
|
||||||
|
тем столько, сколько заведёт проект: без приёмника открытость списка тем была бы
|
||||||
|
обещанием без механизма. Темы **ядра** он больше не держит — риск и устройство
|
||||||
|
закрывает `code` сверкой с инвариантами, а разбор этих тем целиком уехал в
|
||||||
|
`av-dev:code-deep-review`.
|
||||||
|
|
||||||
|
**Запускается тогда и только тогда, когда ему есть что принимать.** Своих тем у
|
||||||
|
проекта нет — проход не идёт вовсе, и отчёт говорит об этом строкой: «свои темы
|
||||||
|
проекта не заведены, приёмник не запускался». Это единственное место, где состав
|
||||||
|
прогона зависит от проекта, и потому оно называется явно.
|
||||||
|
|
||||||
|
Глубина одна — **разбор**: построить сценарий рассуждением, дом темы против
|
||||||
|
диффа; потолок 4 находки. Прежде глубина приезжала планом разметки и на `small`
|
||||||
|
падала до сверки; плана нет, и падать ей больше неоткуда.
|
||||||
|
|
||||||
|
Чего он не делает ни на какой теме — замеров, эксперимента против драйвера,
|
||||||
|
построенного пути, карты проекта, границы домена. Всё это стоит машины или входа
|
||||||
|
шире диффа, то есть глубокого ревью области.
|
||||||
|
|
||||||
|
## Ступень 4 — Triage (обязательна)
|
||||||
|
|
||||||
|
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
|
||||||
|
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
|
||||||
|
стартует. Получает сырые выводы всех проходов, `git diff`, режим и **таблицу
|
||||||
|
тем**; возвращает финальный отчёт.
|
||||||
|
|
||||||
|
**На прогоне без change её место занимает план сценария** — см. «Прогон без
|
||||||
|
change»: сверять исход с планом триаж обязан и там, а другого перечня тем в том
|
||||||
|
прогоне не существует.
|
||||||
|
|
||||||
|
**Таблица тем на входе у триажа — не формальность, а сверка.** Он единственный,
|
||||||
|
кто видит и то, что заявлено, и то, что пришло: «тем шесть, отчёты покрывают
|
||||||
|
пять» — находка о самом прогоне, и заметить её больше некому. Раньше он получал
|
||||||
|
список запущенных проходов и потому мог сверить только состав; теперь сверяет
|
||||||
|
**темы**, а тема, оставшаяся без отчёта, — это то, чего список проходов никогда
|
||||||
|
не показывал. Таблица постоянная, и сверка потому дешевле прежней: сравнивать
|
||||||
|
приходится с одним и тем же реестром, а не с планом, который на каждой задаче
|
||||||
|
выглядел иначе.
|
||||||
|
|
||||||
|
Отсюда же правило, которое иначе выглядит придиркой: **триаж на неполном графе не
|
||||||
|
запускается**. Прогон, остановленный на полпути находкой «переделать форму», до
|
||||||
|
стока не доезжает — его отчёт агрегировал бы половину и выглядел бы полным.
|
||||||
|
|
||||||
|
Без триажа проходы дают порядка сорока замечаний при единицах существенных.
|
||||||
|
Потребитель здесь — оркестратор, который **молча реализует** всё, что прочитал:
|
||||||
|
цена нетриажированного отчёта — не потерянное время человека, а разросшийся от
|
||||||
|
вкусовщины код.
|
||||||
|
|
||||||
|
Порядок: дедупликация по причине → оракул для всего `critical`/`major` →
|
||||||
|
понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по
|
||||||
|
ущербу × вероятности → потолок 7 пунктов в основном списке.
|
||||||
|
|
||||||
|
**Разметку действия ставит он же, и умолчание у неё одно — `инлайн`.** Развилку
|
||||||
|
получает только то, что инлайном чинить нельзя, и оснований у неё три: правка
|
||||||
|
меняет дельта-спеки, находка сидит в необратимом месте, находка трогает инвариант
|
||||||
|
`CLAUDE.md`. Остальное чинится молча — см. «Что происходит с находками дальше».
|
||||||
|
|
||||||
|
**Он же собирает строки «отложено в `av-dev:code-deep-review`».** Проход, упёршийся
|
||||||
|
в предел цикла — нужен замер, нужен прогнанный путь, нужен вход шире диффа, —
|
||||||
|
пишет об этом в своих границах покрытия; триаж сводит такие строки в одну секцию
|
||||||
|
отчёта. Без сведения они растворяются по отчётам проходов, и повод позвать
|
||||||
|
глубокое ревью не накапливается нигде.
|
||||||
|
|
||||||
|
## Контракт находок
|
||||||
|
|
||||||
|
Единый для всех проходов — [references/finding-contract.md](references/finding-contract.md).
|
||||||
|
Коротко: заголовок через **последствие**, обязательные поля `Файл`, `Severity`,
|
||||||
|
`Confidence`, `Оракул`, `Последствие`, `Предложение`, `Найдено проходом`.
|
||||||
|
`critical` без оракула или построенного пути не существует. Находка без поля
|
||||||
|
«Последствие» не выводится вовсе.
|
||||||
|
|
||||||
|
Каждый проход завершает вывод блоком `## Coverage of this pass`.
|
||||||
|
|
||||||
|
## Что происходит с находками дальше
|
||||||
|
|
||||||
|
**Умолчание одно, и оно называется прямо: находку чинит агент, молча.** Цикл
|
||||||
|
задачи устроен так, чтобы человек читал сводку, а не разбирал список замечаний;
|
||||||
|
всё, что чинится в пределах одобренной формы решения, помечается `Действие:
|
||||||
|
инлайн`, уходит агенту дословно вместе с оракулом и логированию не подлежит.
|
||||||
|
Прогон, вернувший человеку десяток вопросов, свою работу не сделал.
|
||||||
|
|
||||||
|
Из умолчания два выхода, и оба узкие:
|
||||||
|
|
||||||
|
- **`Действие: развилка`** — вопросом с вариантами и ценой каждого туда, где
|
||||||
|
проект держит вопросы (это знает вызвавший скилл, а не конвейер ревью).
|
||||||
|
Помечается так **только** то, что инлайном чинить нельзя, и оснований ровно
|
||||||
|
три: находка по **необратимому** месту (миграция, формат на диске, публичный
|
||||||
|
контракт), находка, трогающая **инвариант** `CLAUDE.md`, и находка, чья правка
|
||||||
|
меняет **дельта-спеки** — то есть отменяет одобренное человеком.
|
||||||
|
|
||||||
|
По первым двум основаниям оркестратор **не останавливается**: он урезает
|
||||||
|
изменение до остатка и доводит его. Третье старше: правка, меняющая
|
||||||
|
дельта-спеки, отменяет одобрение, и оркестратор **возвращается на чекпоинт**
|
||||||
|
(`av-dev:code-resolve`, `references/solve.md`, шаг 5). Вопрос в запись при этом
|
||||||
|
остаётся, но возврата не заменяет — иначе одобренный дизайн переделывался бы
|
||||||
|
молча.
|
||||||
|
- **урожай** — находка реальная, но не для этого мерджа: отложенный `major`,
|
||||||
|
развилка, решённая «потом», пачка `nit`. Конвейер отдаёт её **списком** в
|
||||||
|
отчёте: формулировка, оракул, откуда взялась (какой проход, какой change).
|
||||||
|
|
||||||
|
**Задачи из урожая заводятся только по слову человека, и это правило, а не
|
||||||
|
вежливость.** Спрашивает не конвейер: список уезжает вызывающему и показывается
|
||||||
|
человеку **одной репликой на весь хвост задачи** — вместе с тем новым, что
|
||||||
|
предлагает записать синк документации (`av-dev:code-resolve`,
|
||||||
|
`references/solve.md`, шаг 6). Два вопроса про одно и то же — «что из найденного
|
||||||
|
переживёт задачу» — стоили бы двух переключений вместо одного. Сказал «заводим» —
|
||||||
|
зовётся `av-dev:task-track`, у него на этот вход отдельный сценарий «задачи из ревью и
|
||||||
|
аудита»: свой формат, кластеризация по причине, дедуп против беклога и кладбища.
|
||||||
|
Не сказал — урожай остаётся строками доклада, и это исход, а не потеря. Прогон,
|
||||||
|
заводящий задачи сам, наполняет беклог работой, которую никто не выбирал; на
|
||||||
|
проекте, где очередь работ ведёт один человек, это и есть главная цена лишней
|
||||||
|
находки. Каталога задач в проекте нет — урожай остаётся списком тем более, и
|
||||||
|
это говорится строкой.
|
||||||
|
|
||||||
|
Остальное не меняется:
|
||||||
|
|
||||||
|
- `Promote candidates` — по процедуре [references/promote.md](references/promote.md):
|
||||||
|
находка → конвенция → правило линтера → **удаление формулировки из конвенций**.
|
||||||
|
Третий шаг обязателен. **Сама конвенция заводится по слову человека** — той же
|
||||||
|
репликой, что и задачи из урожая: её строка станет входом каждого следующего
|
||||||
|
прогона, и из всего, что пишет хвост задачи, она связывает дальше всего.
|
||||||
|
- Дефект, проскочивший ревью и всплывший позже, идёт в журнал проекта
|
||||||
|
([references/review-journal.md](references/review-journal.md)) — сразу, не
|
||||||
|
ретроспективно: теряется именно то, почему дефект не поймали.
|
||||||
|
- **Отчёт триажа сохраняется вместе с изменением** — `openspec/changes/<id>/review/`.
|
||||||
|
Он единственное, по чему потом видно, что было найдено и что из этого не
|
||||||
|
заведено: нулевой урожай при непустом отчёте виден сразу.
|
||||||
|
**Вместе с изменением он и переезжает:** после `opsx:archive` его адрес —
|
||||||
|
`openspec/changes/archive/<id>/review/`. Кто ищет отчёт после архивации
|
||||||
|
(приёмщик на груминге `av-dev:task-groom`, разбор дефекта), смотрит **оба**
|
||||||
|
пути; «отчёта нет» объявляется, только когда пуст и архивный, иначе самый
|
||||||
|
дорогой сценарий «состав ревью неизвестен, гоняем заново» срабатывает на
|
||||||
|
каждой доведённой задаче.
|
||||||
|
|
||||||
|
## Честный предел
|
||||||
|
|
||||||
|
Модель воспроизводит медиану публичного кода, смещённую к популярному и
|
||||||
|
туториальному: отсюда тяга к интерфейсам ради интерфейсов, лишним мокам и
|
||||||
|
конфигурируемости, которую никто не просил. **«Идиоматично» и «распространено» —
|
||||||
|
разные вещи**; проходы обязаны различать их и опираться на поимённое положение
|
||||||
|
руководства, а не на ощущение частотности.
|
||||||
|
|
||||||
|
Согласие нескольких проходов — **не подтверждение**: это один источник,
|
||||||
|
высказавшийся несколько раз. Совпадение повышает приоритет, но не `confidence`.
|
||||||
|
|
||||||
|
Что недоступно **этому** проекту принципиально — перечисляет «Недоступно
|
||||||
|
проверке» в `docs/review.*`, по темам, и оба его подраздела целиком уезжают в
|
||||||
|
границы покрытия. **Тема, у которой нет дома, — тоже граница покрытия**, и она
|
||||||
|
объявляется на каждом прогоне, а не разово.
|
||||||
|
Независимо от проекта недоступно:
|
||||||
|
|
||||||
|
- поведение внешних систем в их будущих версиях;
|
||||||
|
- реальный профиль нагрузки и то, что на самом деле лежит в данных;
|
||||||
|
- завязка внешних потребителей на текущую форму ответа;
|
||||||
|
- суждение «этой функциональности не должно существовать».
|
||||||
|
|
||||||
|
Отдельно и честно: **поимённая сверка с положениями руководств по стилю языка не
|
||||||
|
задаётся ни одним проходом.** Проход про идиоматичность упразднён, его способные
|
||||||
|
части переселены (эксперимент против поведения библиотеки и драйвера — в `ops`,
|
||||||
|
«не изобретаем ли то, что уже есть в библиотеке» — в `architecture`, и оба теперь
|
||||||
|
живут в `av-dev:code-deep-review`), но различение «идиоматично против
|
||||||
|
распространено» не спрашивает никто. Класс обратимый — портит форму кода, не
|
||||||
|
данные, — и его надо признавать в границах покрытия, а не считать проверенным.
|
||||||
|
|
||||||
|
**Форму решения в цикле не судит никто, и это сознательное сужение.** Ни ревью
|
||||||
|
дизайна до кода, ни архитектурного прохода после — обоих сняли, и оба ушли по
|
||||||
|
одной причине: суждение о форме стоит разговора с человеком, а разговор внутри
|
||||||
|
задачи растягивает её в часы. Форму одобряет **человек на чекпоинте**, до кода, и
|
||||||
|
это единственное место цикла, где решение о ней принимается. Всё, что видно
|
||||||
|
только по написанному коду — второй способ делать уже делаемое, лишний слой,
|
||||||
|
интерфейс ради мока, — ловится глубоким ревью области, то есть позже и не всегда.
|
||||||
|
Класс идёт строкой в границы покрытия каждого прогона.
|
||||||
|
|
||||||
|
**Запуском в цикле не проверяется ничего сверх гейта, и это на всякой задаче.**
|
||||||
|
Формулировка «не запускается ничего» была бы короче и была бы ложью: гейт
|
||||||
|
запускает инструменты проекта, а триаж проверяет оракул `critical`/`major`
|
||||||
|
запуском — оба идут всегда. Не проверяется **проходом с мнением**: построенный
|
||||||
|
путь атаки (его надо прогнать), поведение библиотеки и драйвера в вырожденном
|
||||||
|
случае (достаётся только экспериментом), любое число — время удержания
|
||||||
|
блокировки, пик кучи, темп роста журнала, стоимость на годовой истории.
|
||||||
|
|
||||||
|
**Три темы риска и устройства смотрятся только против записанных инвариантов.**
|
||||||
|
Отдельная строка, и она обязательна в каждом отчёте: `security`, `operations` и
|
||||||
|
`architecture` закрывает `code` сверкой с `CLAUDE.md`, потолком 1 находка на все
|
||||||
|
три. Свойства, которого нет в инвариантах, в цикле не спросит никто. Это не
|
||||||
|
«глубина ниже» — это **другой дом темы**, куда более узкий, и путать одно с
|
||||||
|
другим нельзя.
|
||||||
|
|
||||||
|
**Ось времени в цикле не смотрит никто.** Обратима ли миграция, что станет с
|
||||||
|
записями новой версии после отката, как узел ведёт себя через неделю роста —
|
||||||
|
раньше эти вопросы задавал приёмник тем на метке `medium`, теперь метки нет, а
|
||||||
|
приёмник держит только свои темы проекта. Взамен работает адресация: находка по
|
||||||
|
необратимому месту идёт человеку развилкой, а не чинится молча. **Это не
|
||||||
|
равноценная замена, и подменять одно другим нельзя:** развилка срабатывает,
|
||||||
|
только если находку кто-то сделал, а по оси времени в цикле её теперь делает
|
||||||
|
разве что инвариант.
|
||||||
|
|
||||||
|
**Решения и измеренные числа проекта прогон не читает вовсе.** `adr.*` и
|
||||||
|
`research.*` — процессные документы. Отсюда две строки в границы покрытия каждого
|
||||||
|
прогона: расхождение изменения с записанным решением ловится не здесь, а сверкой
|
||||||
|
документации; число, на которое опирается находка, обязано быть снято **на этом
|
||||||
|
прогоне**, иначе находка не поднимается выше гипотезы. Раньше числа брались из
|
||||||
|
`docs/research/`, и находка выглядела доказанной чужим замером неизвестной
|
||||||
|
свежести.
|
||||||
|
|
||||||
|
**Темы при этом названы все — но закрыты они по-разному, и это надо читать
|
||||||
|
буквально.** «Тема `security`, глубина сверка» не значит «безопасность
|
||||||
|
проверена»: значит, что дом темы открыли, дифф посмотрели и сравнили.
|
||||||
|
|
||||||
|
**Доказательства в цикле задачи нет, и это самая крупная его граница.** Класс
|
||||||
|
дефектов, который виден только построенным путём и снятым числом — гонка,
|
||||||
|
деградация под нагрузкой, исчерпание ресурса, откат бинаря поверх новой схемы, —
|
||||||
|
здесь не ловится ничем.
|
||||||
|
|
||||||
|
Это сознательная сделка, а не пробел в устройстве: тяжёлые проходы оплачивались
|
||||||
|
на каждой задаче, где запускались, а получались на немногих. Теперь они живут в
|
||||||
|
`av-dev:code-deep-review` и оплачиваются тогда, когда их решают получить.
|
||||||
|
Проверяется сделка не рассуждением, а двумя следами: **строками «отложено»** в
|
||||||
|
отчётах — если по одному месту повторяется один и тот же неснятый замер, глубокий
|
||||||
|
прогон просрочен, — и **журналом дефектов**: класс, всплывающий после мерджа,
|
||||||
|
значит, что прогон надо звать чаще.
|
||||||
|
|
||||||
|
Так же честно и про упразднённый проход: **«не знаю, чего не знаю» больше
|
||||||
|
не достаёт никто.** Проход независимой реализации писал свою версию узла, не
|
||||||
|
открывая существующую, и диффил по решениям — декомпозиция, владение данными,
|
||||||
|
модель конкурентности, форма решения там, где спека выбора не сделала. Он снят по
|
||||||
|
решению оператора о **стоимости** — счёт определялся объёмом вывода, и на прогон
|
||||||
|
он тратил больше всех остальных проходов вместе, — а не по замеру, который
|
||||||
|
[calibration.md](references/calibration.md) требует перед удалением. Значит и
|
||||||
|
записывается это как сознательное сужение, а не как «класс оказался пустым».
|
||||||
|
Класс идёт строкой в границы покрытия каждого прогона — там же, где проект
|
||||||
|
перечисляет своё в подразделе «перестали проверять сознательно».
|
||||||
|
|
||||||
|
Это и есть причина, по которой конвейер готовит ревью, а не заменяет его.
|
||||||
|
|
||||||
|
## Ссылки
|
||||||
|
|
||||||
|
- [references/project-facts.md](references/project-facts.md) — что нужно проходу
|
||||||
|
и где это лежит в документах проекта; таблица поразрядной деградации.
|
||||||
|
- Skill `av-dev:code-deep-review` — глубокое ревью области кода: там живут
|
||||||
|
`review-adversary`, `review-ops` и `review-architecture`, там же единственное
|
||||||
|
место процесса, где находка доказывается прогоном и замером, а форма решения
|
||||||
|
вообще обсуждается.
|
||||||
|
- Skill `av-dev:canon` — приведение проекта к канону документов.
|
||||||
|
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
||||||
|
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
|
||||||
|
- [references/calibration.md](references/calibration.md) — калибровка инъекцией, вердикты keep/retune/drop.
|
||||||
|
- [references/review-journal.md](references/review-journal.md) — журнал проскочивших дефектов.
|
||||||
+6
-6
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
stateDiagram-v2
|
stateDiagram-v2
|
||||||
state "проход в составе метки" as live
|
state "проход в составе прогона" as live
|
||||||
state "retune №1 — правка charter'а" as r1
|
state "retune №1 — правка charter'а" as r1
|
||||||
state "retune №2 — последняя попытка" as r2
|
state "retune №2 — последняя попытка" as r2
|
||||||
state "проход удалён" as dead
|
state "проход удалён" as dead
|
||||||
@@ -55,7 +55,7 @@ stateDiagram-v2
|
|||||||
поведения библиотеки и драйвера**, — и находка, воспроизведённая числом, отменила
|
поведения библиотеки и драйвера**, — и находка, воспроизведённая числом, отменила
|
||||||
решение, принятое по ощущению.
|
решение, принятое по ощущению.
|
||||||
|
|
||||||
## Состав проходов принадлежит плагину, а не проекту
|
## Состав проходов принадлежит скиллу, а не проекту
|
||||||
|
|
||||||
Проходы общие. Проект не может удалить проход — он может **не звать** его, и
|
Проходы общие. Проект не может удалить проход — он может **не звать** его, и
|
||||||
тогда это идёт строкой «не запускался» в границы покрытия, как любой другой
|
тогда это идёт строкой «не запускался» в границы покрытия, как любой другой
|
||||||
@@ -67,7 +67,7 @@ stateDiagram-v2
|
|||||||
- **правка charter'а — правка для всех проектов.** Прежде чем сужать
|
- **правка charter'а — правка для всех проектов.** Прежде чем сужать
|
||||||
формулировку под свою боль, проверь, не место ли ей в документах проекта: предмет проверки
|
формулировку под свою боль, проверь, не место ли ей в документах проекта: предмет проверки
|
||||||
живёт там, метод — в charter'е;
|
живёт там, метод — в charter'е;
|
||||||
- **удаление прохода из плагина требует замера на двух проектах**, а не на одном:
|
- **удаление прохода из конвейера требует замера на двух проектах**, а не на одном:
|
||||||
класс, не всплывший здесь, мог быть единственным работающим там.
|
класс, не всплывший здесь, мог быть единственным работающим там.
|
||||||
|
|
||||||
## Пробы дефектов по проходам
|
## Пробы дефектов по проходам
|
||||||
@@ -79,7 +79,6 @@ stateDiagram-v2
|
|||||||
|
|
||||||
| Проход | Класс дефекта для инъекции | Заготовка пробы |
|
| Проход | Класс дефекта для инъекции | Заготовка пробы |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `review-scope` | пропущенная тема | положить в `docs/` новый документ и проверить, попал ли он в план темой |
|
|
||||||
| `review-autotests` | отсутствующая верификация | убрать тест на изменённую ветку, оставить код рабочим |
|
| `review-autotests` | отсутствующая верификация | убрать тест на изменённую ветку, оставить код рабочим |
|
||||||
| `review-specs` | поведение вне спеки | добавить незаказанный фолбэк-дефолт на пустом входе |
|
| `review-specs` | поведение вне спеки | добавить незаказанный фолбэк-дефолт на пустом входе |
|
||||||
| `review-code` | нарушение прозаической конвенции | увести штатный отказ мимо единой точки трансляции ошибки |
|
| `review-code` | нарушение прозаической конвенции | увести штатный отказ мимо единой точки трансляции ошибки |
|
||||||
@@ -88,8 +87,9 @@ stateDiagram-v2
|
|||||||
| `review-basics` | отказ, видимый чтением | убрать обработку ошибки записи так, чтобы отказ считался успехом |
|
| `review-basics` | отказ, видимый чтением | убрать обработку ошибки записи так, чтобы отказ считался успехом |
|
||||||
| `review-basics` | своя тема проекта | нарушить правило из документа, у которого нет именного прохода |
|
| `review-basics` | своя тема проекта | нарушить правило из документа, у которого нет именного прохода |
|
||||||
| `review-architecture` | второй способ | завести вторую точку генерации id мимо единой |
|
| `review-architecture` | второй способ | завести вторую точку генерации id мимо единой |
|
||||||
|
| `review-code` | инвариант проекта | нарушить записанный в `CLAUDE.md` запрет по темам `security`, `operations` или `architecture` |
|
||||||
| `review-adversary` | построенный путь | принять внешний идентификатор без разбора до запроса в хранилище |
|
| `review-adversary` | построенный путь | принять внешний идентификатор без разбора до запроса в хранилище |
|
||||||
| `review-ops` | деградация окружения | убрать обработку недоступности внешней зависимости в фоновом цикле |
|
| `review-ops` | ось времени | убрать обработку недоступности внешней зависимости в фоновом цикле |
|
||||||
| `review-triage` | шум | подать 20 находок, из них 15 вкусовщина и 3 дубля — проверить потолок и дедуп |
|
| `review-triage` | шум | подать 20 находок, из них 15 вкусовщина и 3 дубля — проверить потолок и дедуп |
|
||||||
|
|
||||||
Метрик сверх этого не заводим. Precision, корреляция между проходами, стоимость
|
Метрик сверх этого не заводим. Precision, корреляция между проходами, стоимость
|
||||||
@@ -101,7 +101,7 @@ stateDiagram-v2
|
|||||||
|
|
||||||
## Когда калибровать
|
## Когда калибровать
|
||||||
|
|
||||||
- при заведении нового прохода — **до** включения в состав метки по умолчанию;
|
- при заведении нового прохода — **до** включения в состав прогона;
|
||||||
- при правке charter'а существующего — иначе непонятно, правка помогла или нет;
|
- при правке charter'а существующего — иначе непонятно, правка помогла или нет;
|
||||||
- при появлении записи в журнале проскочивших дефектов — калибруем тот проход,
|
- при появлении записи в журнале проскочивших дефектов — калибруем тот проход,
|
||||||
который должен был поймать;
|
который должен был поймать;
|
||||||
+20
-12
@@ -43,6 +43,8 @@
|
|||||||
|
|
||||||
## Шкала severity
|
## Шкала severity
|
||||||
|
|
||||||
|
Severity — ось процесса; перечень осей — [shared/axes.md](../../../shared/axes.md).
|
||||||
|
|
||||||
| Severity | Что это | Пример |
|
| Severity | Что это | Пример |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `critical` | нарушение инварианта проекта, потеря или порча данных, утечка секрета, построенный путь к отказу | запись потеряна при слиянии; тело пользовательской выгрузки в поле лога |
|
| `critical` | нарушение инварианта проекта, потеря или порча данных, утечка секрета, построенный путь к отказу | запись потеряна при слиянии; тело пользовательской выгрузки в поле лога |
|
||||||
@@ -73,19 +75,23 @@
|
|||||||
1. `Блокирует мердж` (≤3, каждая с оракулом);
|
1. `Блокирует мердж` (≤3, каждая с оракулом);
|
||||||
2. `Стоит исправить сейчас` (≤4);
|
2. `Стоит исправить сейчас` (≤4);
|
||||||
3. `Гипотезы без доказательства` — что понижено и почему;
|
3. `Гипотезы без доказательства` — что понижено и почему;
|
||||||
4. `Promote candidates` — кандидаты в конвенцию или правило линтера;
|
4. `Урожай` — реальные находки не для этого мерджа: формулировка, оракул,
|
||||||
5. `Границы покрытия` — сводная, обязательная.
|
происхождение. Задачи из них заводит человек своим словом, не отчёт;
|
||||||
|
5. `Отложено в av-dev:code-deep-review` — что доказывается только запуском,
|
||||||
|
замером или входом шире диффа: тема, место, чем проверяется;
|
||||||
|
6. `Promote candidates` — кандидаты в конвенцию или правило линтера;
|
||||||
|
7. `Границы покрытия` — сводная, обязательная.
|
||||||
|
|
||||||
Перед секциями — сводка для человека: размер, сложность, метка и режим
|
Перед секциями — сводка для человека: режим прогона, состояние гейта, **перечень
|
||||||
прогона, состояние гейта, **план разметки задачи с исходом по каждой теме**,
|
тем с исходом по каждой**, сколько находок пришло на вход и сколько осталось,
|
||||||
сколько находок пришло на вход и сколько осталось.
|
сколько из них помечено `инлайн` и сколько `развилка`.
|
||||||
|
|
||||||
**Реестр сводки — темы, а не проходы, и это не оформление.** Перечень запущенных
|
**Реестр сводки — темы, а не проходы, и это не оформление.** Перечень запущенных
|
||||||
проходов отвечает «все, кто должен был, отработали» и молчит о том, что именно
|
проходов отвечает «все, кто должен был, отработали» и молчит о том, что именно
|
||||||
осталось непроверенным: уехавший в старшую метку проход уносит тему с собой
|
осталось непроверенным: уехавший в другой скилл проход уносит тему с собой
|
||||||
беззвучно. План же называет тему, её дом, глубину и исполнителя — и тема,
|
беззвучно. Перечень тем называет тему, её дом, глубину и исполнителя — и тема,
|
||||||
оставшаяся без отчёта, видна сразу. Перечень проходов из сводки не исчезает, но
|
оставшаяся без отчёта, видна сразу. Перечень проходов из сводки не исчезает, но
|
||||||
идёт **внутри** плана, колонкой «кто закрывает».
|
идёт **внутри** него, колонкой «кто закрывает».
|
||||||
|
|
||||||
Каждая находка в секциях 1–2 несёт дополнительное поле:
|
Каждая находка в секциях 1–2 несёт дополнительное поле:
|
||||||
|
|
||||||
@@ -93,10 +99,12 @@
|
|||||||
- Действие: инлайн | развилка
|
- Действие: инлайн | развилка
|
||||||
```
|
```
|
||||||
|
|
||||||
`инлайн` — оркестратор чинит сам, не спрашивая и не логируя. `развилка` — цена
|
`инлайн` — оркестратор чинит сам, не спрашивая и не логируя, **и это
|
||||||
исправления сопоставима с переработкой, либо выбор меняет scope, либо решение
|
умолчание**. `развилка` — узкий выход с тремя основаниями: правка меняет
|
||||||
трогает инвариант: уезжает вопросом с вариантами и ценой каждого туда, где
|
дельта-спеки, находка сидит в необратимом месте (миграция, формат на диске,
|
||||||
проект держит вопросы, а работа продолжается на остатке.
|
публичный контракт), находка трогает инвариант. Она уезжает вопросом с вариантами
|
||||||
|
и ценой каждого туда, где проект держит вопросы, а работа продолжается на
|
||||||
|
остатке.
|
||||||
|
|
||||||
Потребитель отчёта — оркестратор, который **реализует прочитанное**. Поэтому
|
Потребитель отчёта — оркестратор, который **реализует прочитанное**. Поэтому
|
||||||
потолок в 7 пунктов — не забота о внимании читателя, а защита кодовой базы от
|
потолок в 7 пунктов — не забота о внимании читателя, а защита кодовой базы от
|
||||||
+25
-21
@@ -4,17 +4,17 @@
|
|||||||
нельзя нарушать, чем краснеет гейт и сколько данных реально идёт через узел,
|
нельзя нарушать, чем краснеет гейт и сколько данных реально идёт через узел,
|
||||||
выдаёт правдоподобные общие места: их дорого опровергать и нечем подтверждать.
|
выдаёт правдоподобные общие места: их дорого опровергать и нечем подтверждать.
|
||||||
|
|
||||||
Отдельного файла-брифа **нет**. Проектная конкретика живёт в документах канона
|
Отдельного файла-брифа **нет**. Проектная конкретика живёт в документах канона,
|
||||||
`av-dev-docs`, и проход читает их напрямую: пути жёсткие, посредник не нужен, а
|
и проход читает их напрямую: пути жёсткие, посредник не нужен, а
|
||||||
второй дом для тех же фактов разошёлся бы и выглядел актуальным.
|
второй дом для тех же фактов разошёлся бы и выглядел актуальным.
|
||||||
|
|
||||||
Определение канона держит скилл `av-dev-docs:canon`. Здесь только карта «тема →
|
Определение канона держит скилл `av-dev:canon`. Здесь только карта «тема →
|
||||||
её дом → что оттуда берётся».
|
её дом → что оттуда берётся».
|
||||||
|
|
||||||
## Карта тем
|
## Карта тем
|
||||||
|
|
||||||
**Дом бывает файлом или каталогом** — `docs/security.md` и `docs/security/`
|
**Дом бывает файлом или каталогом** — `docs/security.md` и `docs/security/`
|
||||||
называют одну и ту же тему. Форму дома называет план разметки задачи; проход её не
|
называют одну и ту же тему. Форму дома называет задание прохода; проход её не
|
||||||
угадывает.
|
угадывает.
|
||||||
|
|
||||||
| Тема | Дом | Что оттуда берётся |
|
| Тема | Дом | Что оттуда берётся |
|
||||||
@@ -34,10 +34,11 @@
|
|||||||
второй — `operations` и `requirements`; обе строки убраны, и цена этого названа в
|
второй — `operations` и `requirements`; обе строки убраны, и цена этого названа в
|
||||||
`SKILL.md`, раздел «Честный предел».
|
`SKILL.md`, раздел «Честный предел».
|
||||||
|
|
||||||
**Дом темы зависит ещё и от метки.** На `small` темы `security`, `operations` и
|
**Дом темы зависит от того, кто её закрывает.** В цикле задачи темы `security`,
|
||||||
`architecture` смотрятся не против домов из этой таблицы, а против **инвариантов
|
`operations` и `architecture` смотрятся не против домов из этой таблицы, а против
|
||||||
`CLAUDE.md`**, и закрывает их `code`. Таблица описывает полный дом темы; сколько
|
**инвариантов `CLAUDE.md`**, и закрывает их `code`. Полные дома открывает скилл
|
||||||
из него открыто на этом прогоне, говорит план разметки задачи.
|
`av-dev:code-deep-review` своими проходами. Таблица описывает полный дом темы;
|
||||||
|
что из него открыто на этом прогоне, говорит состав прогона.
|
||||||
|
|
||||||
Сквозное, не привязанное к теме:
|
Сквозное, не привязанное к теме:
|
||||||
|
|
||||||
@@ -45,12 +46,12 @@
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| инварианты **с severity рядом с формулировкой** | `CLAUDE.md` (и `AGENTS.md`, если он рядом), раздел инвариантов |
|
| инварианты **с severity рядом с формулировкой** | `CLAUDE.md` (и `AGENTS.md`, если он рядом), раздел инвариантов |
|
||||||
| что запускать запрещено, с путями; `testdata`; куда писать временное; имя основной ветки | `CLAUDE.md` |
|
| что запускать запрещено, с путями; `testdata`; куда писать временное; имя основной ветки | `CLAUDE.md` |
|
||||||
| типовые узлы, типовые ложноположительные, **вопросы по темам**, триггеры метки, недоступно проверке | `docs/review.*`, раздел настройки |
|
| типовые узлы, типовые ложноположительные, **вопросы по темам**, недоступно проверке | `docs/review.*`, раздел настройки |
|
||||||
| прецеденты: воспроизведённые дефекты с оракулом | `docs/review.*`, журнал |
|
| прецеденты: воспроизведённые дефекты с оракулом | `docs/review.*`, журнал |
|
||||||
|
|
||||||
**Вопросы проекта привязаны к теме, а не к имени прохода.** Раньше блок в
|
**Вопросы проекта привязаны к теме, а не к имени прохода.** Раньше блок в
|
||||||
`docs/review.md` адресовался поимённо (`ops: <вопрос>`), и когда проход уехал в
|
`docs/review.md` адресовался поимённо (`ops: <вопрос>`), и когда проход уехал в
|
||||||
старшую метку, вопрос перестал задаваться молча. Тема переезд прохода
|
другой скилл, вопрос перестал задаваться молча. Тема переезд прохода
|
||||||
переживает.
|
переживает.
|
||||||
|
|
||||||
## Сшивать обязаны проходы
|
## Сшивать обязаны проходы
|
||||||
@@ -65,7 +66,8 @@
|
|||||||
«запись лежит сжатой и распаковывается целиком»; «блокировка удерживалась
|
«запись лежит сжатой и распаковывается целиком»; «блокировка удерживалась
|
||||||
5.019 с» — гарантированный отказ соседа только рядом с известным таймаутом
|
5.019 с» — гарантированный отказ соседа только рядом с известным таймаутом
|
||||||
занятости. **Число проход снимает сам, на этом прогоне**, настройки берёт из
|
занятости. **Число проход снимает сам, на этом прогоне**, настройки берёт из
|
||||||
`docs/database.md`, и сшивают их `ops` и `adversary`. Раньше числа брались из
|
`docs/database.md`, и сшивает их `ops` в глубоком ревью — в цикле задачи не
|
||||||
|
снимает чисел никто. Раньше числа брались из
|
||||||
`docs/research/`; теперь этот документ процессный, и замер неизвестной свежести
|
`docs/research/`; теперь этот документ процессный, и замер неизвестной свежести
|
||||||
больше не выдаёт себя за оракул.
|
больше не выдаёт себя за оракул.
|
||||||
- **инвариант + обратимость.** severity берётся из `CLAUDE.md`; если её там
|
- **инвариант + обратимость.** severity берётся из `CLAUDE.md`; если её там
|
||||||
@@ -75,15 +77,16 @@
|
|||||||
**У `basics` стыков нет, и это не упущение.** Он не меряет, поэтому сшивать число
|
**У `basics` стыков нет, и это не упущение.** Он не меряет, поэтому сшивать число
|
||||||
с настройкой ему нечего; единственное его основание для `critical` — инвариант из
|
с настройкой ему нечего; единственное его основание для `critical` — инвариант из
|
||||||
`CLAUDE.md`, всё остальное он формулирует условиями и оставляет гипотезой. Его
|
`CLAUDE.md`, всё остальное он формулирует условиями и оставляет гипотезой. Его
|
||||||
вход намеренно узкий: дома тем из плана плюс инварианты и журнал. Широкий вход —
|
вход намеренно узкий: дома тем из задания плюс инварианты и журнал. Широкий вход
|
||||||
это метка `large`, и там он есть у `architecture`. Греп по базе ему разрешён
|
есть только у `architecture`, а он работает в глубоком ревью. Греп по базе ему разрешён
|
||||||
точечный — «есть ли второй вызывающий», — но обход всей базы и инвентарь
|
точечный — «есть ли второй вызывающий», — но обход всей базы и инвентарь
|
||||||
концепций не его работа.
|
концепций не его работа.
|
||||||
|
|
||||||
**У `scope` стыков нет по другой причине: он не читает содержимого.** Его дело —
|
**Дома передаются адресом, а не пересказом, и это правило пережило проход,
|
||||||
найти дома и раздать темы, а не пересказать написанное. Пересказ сделал бы его
|
который его исполнял.** Прежде темы раздавал `review-scope`: он находил дома и
|
||||||
посредником между документом и проходом, а посредник расходится с источником и при
|
называл их путём с разделом, ничего не пересказывая. Прохода нет, состав
|
||||||
этом выглядит актуальным.
|
постоянный, но правило то же — проход, получивший проинтерпретированный периметр,
|
||||||
|
не заметит, что интерпретация неверна.
|
||||||
|
|
||||||
## Деградация — поразрядная
|
## Деградация — поразрядная
|
||||||
|
|
||||||
@@ -94,8 +97,9 @@
|
|||||||
|
|
||||||
**Кто какой документ читает — из документа не выводится, а назначается планом.**
|
**Кто какой документ читает — из документа не выводится, а назначается планом.**
|
||||||
Документ питает тему (это записано на стороне канона, таблица «Роли документов и
|
Документ питает тему (это записано на стороне канона, таблица «Роли документов и
|
||||||
темы ревью»), а тему на этом прогоне закрывает тот, кого назвала разметка задачи; вся
|
темы ревью»), а тему на этом прогоне закрывает тот, кто назван в составе прогона; вся
|
||||||
раскладка «тема → проход → глубина» — в `SKILL.md` этого скилла и больше нигде.
|
раскладка «тема → кто закрывает → против чего» — в `SKILL.md` этого скилла и
|
||||||
|
больше нигде.
|
||||||
**Списка читателей не ведёт никто, и это не пробел.** Он жил бы на стороне
|
**Списка читателей не ведёт никто, и это не пробел.** Он жил бы на стороне
|
||||||
канона, а документ живёт дольше, чем раскладка проходов: список разошёлся бы с
|
канона, а документ живёт дольше, чем раскладка проходов: список разошёлся бы с
|
||||||
конвейером молча и при этом выглядел актуальным. Однажды уже разошёлся.
|
конвейером молча и при этом выглядел актуальным. Однажды уже разошёлся.
|
||||||
@@ -118,14 +122,14 @@
|
|||||||
строка неотличима от «мы просто не стали» и перестаёт читаться на третьей задаче.
|
строка неотличима от «мы просто не стали» и перестаёт читаться на третьей задаче.
|
||||||
|
|
||||||
**Документов канона нет вовсе** — проект не приведён к канону. Это не повод
|
**Документов канона нет вовсе** — проект не приведён к канону. Это не повод
|
||||||
работать вслепую: скажи об этом строкой и предложи `av-dev-docs:canon`. Одна
|
работать вслепую: скажи об этом строкой и предложи `av-dev:canon`. Одна
|
||||||
операция на проект против деградации на каждой задаче.
|
операция на проект против деградации на каждой задаче.
|
||||||
|
|
||||||
## Правило чтения
|
## Правило чтения
|
||||||
|
|
||||||
- **Читай в источнике, не по памяти.** Документы правятся по ходу работы, в том
|
- **Читай в источнике, не по памяти.** Документы правятся по ходу работы, в том
|
||||||
числе этой же задачей.
|
числе этой же задачей.
|
||||||
- **Число без провенанса — условие, а не утверждение.** Число, чей источник по
|
- **Число без происхождения — условие, а не утверждение.** Число, чей источник по
|
||||||
ссылке не подтвердился, читается как условие и **называется расходящимся**, а
|
ссылке не подтвердился, читается как условие и **называется расходящимся**, а
|
||||||
не подменяется догадкой.
|
не подменяется догадкой.
|
||||||
- **Пустое, названное пустым, — это факт.** «Внешних зависимостей нет — смотри
|
- **Пустое, названное пустым, — это факт.** «Внешних зависимостей нет — смотри
|
||||||
+13
@@ -52,6 +52,13 @@ flowchart TD
|
|||||||
тема относится к поведению системы, а не к тому, как мы пишем код, — это не
|
тема относится к поведению системы, а не к тому, как мы пишем код, — это не
|
||||||
конвенция, а требование: заводится дельта-спека обычным путём.
|
конвенция, а требование: заводится дельта-спека обычным путём.
|
||||||
|
|
||||||
|
**Конвенция заводится по слову человека, и это не формальность.** Одна её строка
|
||||||
|
становится входом каждого следующего прогона ревью и критерием для всех будущих
|
||||||
|
задач — из всего, что пишет хвост задачи, конвенция связывает дальше всего.
|
||||||
|
В цикле задачи она поэтому **предлагается**, а не заводится: строка предложения
|
||||||
|
называет проверяемое свойство и проход, который его нашёл, и по этой паре человек
|
||||||
|
решает (`av-dev:code-resolve`, `references/solve.md`, шаг 6, такт второй).
|
||||||
|
|
||||||
Промоут идёт **тем же путём, что change → spec**: правка попадает в тот же
|
Промоут идёт **тем же путём, что change → spec**: правка попадает в тот же
|
||||||
коммит, что и исправление кода, с пометкой в сообщении — история промоутов
|
коммит, что и исправление кода, с пометкой в сообщении — история промоутов
|
||||||
остаётся видна в `git log` по файлу конвенций.
|
остаётся видна в `git log` по файлу конвенций.
|
||||||
@@ -75,6 +82,12 @@ flowchart TD
|
|||||||
хук блокирует любой коммит, и правило снимут первым же раздражённым движением.
|
хук блокирует любой коммит, и правило снимут первым же раздражённым движением.
|
||||||
Приводить код в соответствие — часть шага 2, отдельным коммитом.
|
Приводить код в соответствие — часть шага 2, отдельным коммитом.
|
||||||
|
|
||||||
|
**Отсюда и место шага 2: он не помещается в хвост чужой задачи.** Конфиг,
|
||||||
|
сканер и приведение кода к зелёному — это работа размером с задачу, и сделанная
|
||||||
|
попутно она удваивает прогон, который человек заводил ради другого. Согласованный
|
||||||
|
промоут даёт **строку конвенции сейчас** и **задачу `chore` на механизацию**;
|
||||||
|
задачу заводит `av-dev:task-track` тем же словом, что и саму конвенцию.
|
||||||
|
|
||||||
## Шаг 3. Удаление из конвенций и из промптов
|
## Шаг 3. Удаление из конвенций и из промптов
|
||||||
|
|
||||||
**Шаг, который пропускают чаще всего, и единственный, ради которого затевались
|
**Шаг, который пропускают чаще всего, и единственный, ради которого затевались
|
||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
# Журнал дефектов
|
# Журнал дефектов
|
||||||
|
|
||||||
Артефакт проекта, а не плагина: файл живёт в репозитории — **`docs/review.md`**,
|
Артефакт проекта, а не плагина: файл живёт в репозитории — **`docs/review.md`**,
|
||||||
слот канона `av-dev-docs`. Здесь описано, зачем он и какой формы, потому что без
|
слот канона документов. Здесь описано, зачем он и какой формы, потому что без
|
||||||
него конвейер не учится: находки закрываются, а почему их не поймали — забывается,
|
него конвейер не учится: находки закрываются, а почему их не поймали — забывается,
|
||||||
и один и тот же класс проскакивает второй раз.
|
и один и тот же класс проскакивает второй раз.
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
и `docs/adr/`.
|
и `docs/adr/`.
|
||||||
|
|
||||||
Отдельно сюда попадают **решения о составе прогонов**: перестали звать проход,
|
Отдельно сюда попадают **решения о составе прогонов**: перестали звать проход,
|
||||||
понизили метку правилом, сузили класс проверяемого. Не потому, что это промах,
|
переселили его в другой скилл, сузили класс проверяемого. Не потому, что это промах,
|
||||||
а потому, что здесь лежит цена: если что-то теперь проскочит, первый вопрос —
|
а потому, что здесь лежит цена: если что-то теперь проскочит, первый вопрос —
|
||||||
«не тот ли это класс, который мы перестали проверять».
|
«не тот ли это класс, который мы перестали проверять».
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
## Форма записи
|
## Форма записи
|
||||||
|
|
||||||
**Это дом формы, и у него есть копия.** Скелет `docs/review.md`, который кладёт
|
**Это дом формы, и у него есть копия.** Скелет `docs/review.md`, который кладёт
|
||||||
в проект `av-dev-docs:canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы
|
в проект `av-dev:canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы
|
||||||
здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе
|
здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе
|
||||||
проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет.
|
проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет.
|
||||||
Дословность сверяет `scripts/copies.py` маркетплейса по маркерам ниже — но
|
Дословность сверяет `scripts/copies.py` маркетплейса по маркерам ниже — но
|
||||||
@@ -81,8 +81,8 @@
|
|||||||
что необратимо и какой шаг гейта красит безусловно → `CLAUDE.md`; периметр и
|
что необратимо и какой шаг гейта красит безусловно → `CLAUDE.md`; периметр и
|
||||||
недоверенный вход → `docs/security.*`. **Вопрос по теме**, если промах лечится
|
недоверенный вход → `docs/security.*`. **Вопрос по теме**, если промах лечится
|
||||||
не фактом, а заданным вопросом, → раздел «Вопросы по темам» того же
|
не фактом, а заданным вопросом, → раздел «Вопросы по темам» того же
|
||||||
`docs/review.*`; адресуй теме, а не имени прохода — проход уедет между
|
`docs/review.*`; адресуй теме, а не имени прохода — проход уедет в другой
|
||||||
метками, тема останется. Самый частый адрес и самый дешёвый. Прежде чем
|
скилл, тема останется. Самый частый адрес и самый дешёвый. Прежде чем
|
||||||
править charter, проверь, не хватит ли факта или вопроса: charter общий для
|
править charter, проверь, не хватит ли факта или вопроса: charter общий для
|
||||||
всех проектов, документ — про этот.
|
всех проектов, документ — про этот.
|
||||||
- **в конвенции или в правило линтера** — если свойство выражается
|
- **в конвенции или в правило линтера** — если свойство выражается
|
||||||
@@ -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). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Прогон оставляет след — ключ [docs] healthcheck_last в .av-dev.toml, — и по нему синк документации считает, сколько задач сделано с прошлой сверки, и выдаёт сигнал строкой. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию раскладки проверяет скилл av-dev:canon, язык документов — агент doc-wording."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Здоровье документации
|
# Здоровье документации
|
||||||
@@ -20,7 +20,10 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
- **с прошлой сверки сделан десяток задач.** Документы протухают ровно от
|
- **с прошлой сверки сделан десяток задач.** Документы протухают ровно от
|
||||||
сделанной работы: переименованная цель сборки, ушедшая зависимость, второй
|
сделанной работы: переименованная цель сборки, ушедшая зависимость, второй
|
||||||
способ делать то, что обзор объявил единственным, факт, дописанный в
|
способ делать то, что обзор объявил единственным, факт, дописанный в
|
||||||
`architecture.md` и уже живущий в `CLAUDE.md`;
|
`architecture.md` и уже живущий в `CLAUDE.md`. **Этот признак считается, а не
|
||||||
|
вспоминается**: счёт ведёт синк документации по следу прошлого прогона и
|
||||||
|
выдаёт строкой на каждой сделанной задаче (`av-dev:doc-sync`, раздел «Сигнал
|
||||||
|
сверки»);
|
||||||
- **вернулись к проекту после перерыва** — прежде чем опираться на написанное;
|
- **вернулись к проекту после перерыва** — прежде чем опираться на написанное;
|
||||||
- **перед тем как опереться на документ в решении**, если оно дорогое;
|
- **перед тем как опереться на документ в решении**, если оно дорогое;
|
||||||
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `canon` сам.
|
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `canon` сам.
|
||||||
@@ -35,39 +38,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: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`, когда находка тянет на задачу. Его нет —
|
||||||
находки остаются списком в докладе, и это говорится строкой.
|
находки остаются списком в докладе, и это говорится строкой.
|
||||||
|
|
||||||
## Пачка — весь канон, и это не расточительство
|
## Пачка — весь канон, и это не расточительство
|
||||||
@@ -83,7 +92,7 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
|
|
||||||
| Агент | Что смотрит | Читает | Модель |
|
| Агент | Что смотрит | Читает | Модель |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `doc-consistency` | смысловой дубль, прямое противоречие между документами, поведение в `architecture.md` вместо спек, ADR без ссылки и парного статуса, число без провенанса, заглушка вместо честной строки | `docs/`, `openspec/` | `opus` |
|
| `doc-consistency` | смысловой дубль, прямое противоречие между документами, поведение в `architecture.md` вместо спек, ADR без ссылки и парного статуса, число без происхождения, заглушка вместо честной строки | `docs/`, `openspec/` | `opus` |
|
||||||
| `doc-code-drift` | протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки проекта, capability | весь репозиторий | `sonnet` |
|
| `doc-code-drift` | протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки проекта, capability | весь репозиторий | `sonnet` |
|
||||||
|
|
||||||
**`doc-code-drift` обязан получить раздел запретов `CLAUDE.md`.** Он гоняет
|
**`doc-code-drift` обязан получить раздел запретов `CLAUDE.md`.** Он гоняет
|
||||||
@@ -109,14 +118,48 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
1. **Строка на замену** — правь документ сразу. Формулировка уже готова, спорить
|
1. **Строка на замену** — правь документ сразу. Формулировка уже готова, спорить
|
||||||
не с чем, и откладывание превращает её в задачу дороже самой правки.
|
не с чем, и откладывание превращает её в задачу дороже самой правки.
|
||||||
2. **Работа больше чем на абзац** — задача типа `chore`. Заводит её **не этот
|
2. **Работа больше чем на абзац** — задача типа `chore`. Заводит её **не этот
|
||||||
скилл**: вызови Skill `av-dev-tasks:tasks`, у него свой формат, дедупликация
|
скилл**: вызови Skill `av-dev:task-track`, у него свой формат, дедупликация
|
||||||
против беклога и кладбища. Плагина нет — отдай списком в докладе и скажи это
|
против беклога и кладбища. Каталога задач в проекте нет — отдай списком в
|
||||||
строкой.
|
докладе и скажи это строкой.
|
||||||
3. **Не находка** — агент ошибся, документ прав. Скажи это прямо: неразобранная
|
3. **Не находка** — агент ошибся, документ прав. Скажи это прямо: неразобранная
|
||||||
находка и отклонённая различаются, и вторая экономит время на следующем
|
находка и отклонённая различаются, и вторая экономит время на следующем
|
||||||
прогоне. Класс ошибок, который повторяется, идёт в `docs/review.*`, раздел
|
прогоне. Класс ошибок, который повторяется, идёт в `docs/review.*`, раздел
|
||||||
настройки, — там дом типовых ложноположительных.
|
настройки, — там дом типовых ложноположительных.
|
||||||
|
|
||||||
|
## След прогона
|
||||||
|
|
||||||
|
**Последним шагом прогон правит `.av-dev.toml`** — ключ `healthcheck_last` в
|
||||||
|
секции `[docs]`: хеш коммита `HEAD` и дата комментарием рядом. Состав ключей —
|
||||||
|
[канон](../canon/references/canon.md), раздел `.av-dev.toml`; правится **строка**,
|
||||||
|
а не файл целиком.
|
||||||
|
|
||||||
|
**Секцию и имя ключа не выбирай сам.** Неизвестный ключ `.av-dev.toml` — отказ
|
||||||
|
кодом 3, а не пропуск: ключ, заведённый мимо константы скрипта-владельца, роняет
|
||||||
|
`docs.py`, `tasks.py` и гейт проекта разом. Этот ключ там уже назван
|
||||||
|
(`DOCS_KEYS` в `av-dev/skills/canon/scripts/docs.py`), а любой другой пришлось бы
|
||||||
|
заводить правкой скрипта.
|
||||||
|
|
||||||
|
**Без следа признак «десяток задач» не считается никем.** Так и было: сверку
|
||||||
|
звали по памяти, то есть не звали — тот же прозаический триггер, что дал 6
|
||||||
|
записей ADR на 43 изменения. След превращает признак в число, которое
|
||||||
|
`av-dev:doc-sync` считает командой
|
||||||
|
`git rev-list --count <last>..HEAD -- openspec/changes/archive` и говорит вслух
|
||||||
|
на каждой задаче.
|
||||||
|
|
||||||
|
Ключ **необязательный и заводится сам** — первым же прогоном сверки; проекту для
|
||||||
|
этого делать нечего. Его отсутствие значит «сверки не было ни разу», и синк
|
||||||
|
говорит это отдельной строкой.
|
||||||
|
|
||||||
|
**Правку следа коммитит тот, кто позвал прогон.** Своего коммита у скилла нет:
|
||||||
|
он правит документы, заводит задачи и ставит след — всё это уезжает одним
|
||||||
|
коммитом разбора, и `last` в нём указывает на **прежний** `HEAD`, то есть на
|
||||||
|
состояние, которое сверяли. Оставить правку незакоммиченной нельзя: счёт пойдёт
|
||||||
|
от коммита, которого в истории нет.
|
||||||
|
|
||||||
|
**Позвал одного агента из двух — след всё равно ставится, но в докладе назван
|
||||||
|
неполным.** Иначе следующая сверка отсчитывалась бы от прогона, который смотрел
|
||||||
|
половину.
|
||||||
|
|
||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
- **Кого позвал** — обоих или одного, и почему одного.
|
- **Кого позвал** — обоих или одного, и почему одного.
|
||||||
@@ -126,18 +169,21 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
идёт из его собственного отчёта — перечень фактов у него закрытый, и он
|
идёт из его собственного отчёта — перечень фактов у него закрытый, и он
|
||||||
называет, какие из них проверить было нечем.
|
называет, какие из них проверить было нечем.
|
||||||
- Канона в проекте нет вовсе — это исход, а не пустой прогон: скажи строкой и
|
- Канона в проекте нет вовсе — это исход, а не пустой прогон: скажи строкой и
|
||||||
предложи `av-dev-docs:canon`.
|
предложи `av-dev:canon`.
|
||||||
|
|
||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
- **Не проверяет раскладку, версию и ссылки** — это `canon check`, там машина.
|
- **Не проверяет раскладку, версию и ссылки** — это `canon check`, там машина.
|
||||||
- **Не судит язык** документов: залог, англицизмы, жаргон, термин без дома — это
|
- **Не судит язык** документов: залог, англицизмы, жаргон, термин без дома — это
|
||||||
агент `doc-wording`, и зовут его отдельно, по пачке правленных документов.
|
агент `doc-wording`, и зовут его отдельно, по пачке правленных документов.
|
||||||
Звонящие у него названные — последний шаг синка в `av-dev-docs:docs`, шаг 9
|
Звонящие у него названные — последний заход синка в `av-dev:doc-sync`, шаг
|
||||||
`av-dev-docs:init` и шаг вычитки в обоих режимах `canon`, — просто ни один из
|
вычитки сценария разведки (`av-dev:code-resolve`), шаг 9 `av-dev:doc-init` и
|
||||||
них не здесь. У него другой ритм: он нужен там, где текст только что писали, а
|
шаг вычитки в обоих режимах `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 — каталог принадлежит конвейеру. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл canon."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Заведение нового проекта
|
# Заведение нового проекта
|
||||||
@@ -26,16 +26,17 @@ 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»: «архитектуры пока нет: кода нет,
|
||||||
заводится первой задачей». Проход читает её как факт.
|
заводится первой задачей». Проход читает её как факт.
|
||||||
|
|
||||||
**`tasks/ROADMAP.md` в таблице нет намеренно.** Первые цели `init` собирает
|
**`tasks/BACKLOG.md` в таблице нет намеренно.** Первый план работ `init`
|
||||||
интервью (блок 6), но записывает их не он: каталогом задач и формой целей владеет
|
собирает интервью (блок 6), но записывает его не он: каталогом задач владеет
|
||||||
`av-dev-tasks:tasks`, и это шаг 7. Плагина нет — цели остаются списком в докладе,
|
`av-dev:task-track`, и это шаг 7. Человек от каталога задач отказался — план
|
||||||
роадмапа в проекте не появляется, и это говорится строкой.
|
остаётся списком в докладе, беклога в проекте не появляется, и это говорится
|
||||||
|
строкой.
|
||||||
|
|
||||||
## Порядок интервью — зависимость, а не удобство
|
## Порядок интервью — зависимость, а не удобство
|
||||||
|
|
||||||
@@ -53,9 +54,11 @@ description: "Завести новый проект — сессия вопро
|
|||||||
проекте нельзя откатить — деплой, выкладка наружу, перезапись данных.
|
проекте нельзя откатить — деплой, выкладка наружу, перезапись данных.
|
||||||
5. **Чем краснеет гейт.** Какие проверки обязательны; что красит безусловно;
|
5. **Чем краснеет гейт.** Какие проверки обязательны; что красит безусловно;
|
||||||
чего в гейте намеренно не будет и кто тогда это гоняет.
|
чего в гейте намеренно не будет и кто тогда это гоняет.
|
||||||
6. **Первые цели.** Возможности приложения, а не задачи: три-пять целей в
|
6. **Первые шаги стройки.** Новый проект по определению начинается со стадии
|
||||||
`Запланировано`, каждая — ответ на «что приложение будет уметь», с
|
`build`: приложения ещё нет. Собери **список от базы к деталям** — что нужно
|
||||||
обоснованием очереди прозой.
|
сделать, чтобы приложение заработало, — и обоснуй **порядок**: он значит
|
||||||
|
зависимость, а не важность. Пять-десять шагов достаточно: план дописывается
|
||||||
|
по ходу стройки, и это законно.
|
||||||
|
|
||||||
### Как вести
|
### Как вести
|
||||||
|
|
||||||
@@ -69,66 +72,74 @@ 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: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. Заведи скелет остальных по [скелетам](../canon/references/skeletons.md) —
|
||||||
каждый с честной строкой.
|
каждый с честной строкой.
|
||||||
7. Каталог задач и первые цели — **вызови скилл `av-dev-tasks:tasks`**: он владеет
|
7. Каталог задач и первые шаги — **вызови скилл `av-dev:task-track`**: он владеет
|
||||||
форматом целей и задач. Не разрешился — учёт задач остаётся владельцу, и это
|
форматом задач и стадией (`init --stage build`). Не разрешился — учёт задач
|
||||||
тоже строка доклада.
|
остаётся владельцу, и это тоже строка доклада.
|
||||||
8. `docs.py check` из скилла `canon` — до отсутствия дрейфа. Замечания о
|
8. `docs.py check` из скилла `canon` — до отсутствия дрейфа. Замечания о
|
||||||
незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа.
|
незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа.
|
||||||
9. **Вычитай написанное — агент `doc-wording`**, по пачке заполненных документов
|
9. **Вычитай написанное — агент `doc-wording`**, по пачке заполненных документов
|
||||||
@@ -142,7 +153,7 @@ description: "Завести новый проект — сессия вопро
|
|||||||
|
|
||||||
## Что дальше
|
## Что дальше
|
||||||
|
|
||||||
- Содержимое канона по ходу разработки ведёт скилл `docs`.
|
- Содержимое канона по ходу разработки ведёт скилл `doc-sync`.
|
||||||
- Раскладку проверяет `canon check`.
|
- Раскладку проверяет `canon check`.
|
||||||
- Первую задачу берёт конвейер проекта; `architecture.md` и `conventions/`
|
- Первую задачу берёт конвейер проекта; `architecture.md` и `conventions/`
|
||||||
наполняются его шагом синка, а не заранее.
|
наполняются его шагом синка, а не заранее.
|
||||||
@@ -0,0 +1,357 @@
|
|||||||
|
---
|
||||||
|
name: doc-sync
|
||||||
|
description: "Вести содержимое документов канона по ходу разработки. Правки двух родов, и спрашивается один: отражение сделанного (вливание дельт, миграция в database.md, компонент в architecture.md) пишется молча, а новая запись и новая норма (ADR, правило в conventions, записка в research, инвариант CLAUDE.md, периметр security.md, граница passport.md, дефект в review.md) только предлагается — пишет её второй запуск после слова человека. Построчный отчёт по каждому документу остаётся: каждый назван либо правкой, либо предложением, либо отрицанием с причиной. ADR и записка разведки — промоут цитатой из архивного design.md или записки, а не второе сочинение. Синк же считает и выдаёт строкой сигнал сверки: сколько задач сделано с прошлого прогона av-dev:doc-healthcheck, читая след в ключе [docs] healthcheck_last. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл av-dev:canon."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Ведение содержимого канона
|
||||||
|
|
||||||
|
Скилл владеет **содержимым** документов канона; раскладкой владеет `canon`.
|
||||||
|
Определение канона и роли документов — [канон](../canon/references/canon.md),
|
||||||
|
здесь не пересказывается.
|
||||||
|
|
||||||
|
Главный вызывающий — **шаг синка документации в конвейере задачи**: скилл
|
||||||
|
`av-dev:code-resolve` зовёт этот по имени. Задачу ведут не конвейером —
|
||||||
|
документация ведётся тем же скиллом вручную.
|
||||||
|
|
||||||
|
## Правило, из которого всё следует
|
||||||
|
|
||||||
|
**Принуждённое отрицание.** Синк обязан назвать **каждый** документ канона: либо
|
||||||
|
чем он обновлён, либо «не требуется, потому что…». Нетронутые группируются одной
|
||||||
|
строкой с общей причиной.
|
||||||
|
|
||||||
|
Причина, по которой правило именно такое, измерена: у ADR был список триггеров
|
||||||
|
прозой — и он дал **6 записей на 43 изменения**. Прозаический триггер, который
|
||||||
|
некому проверить, не срабатывает. Отличить «не написал» от «написал, что не
|
||||||
|
требуется» можно только тогда, когда отрицание обязательно.
|
||||||
|
|
||||||
|
Это тот же приём, что «границы покрытия» в отчёте ревью и «пустое называется
|
||||||
|
пустым» в каноне.
|
||||||
|
|
||||||
|
## Два рода правок, и спрашивается один
|
||||||
|
|
||||||
|
Второе правило, поперёк первого: **пройти по всем документам обязан ты, а
|
||||||
|
завести новое — человек**. Признак проверяемый и читается одним вопросом: **что
|
||||||
|
станет с документом, если правку не сделать**.
|
||||||
|
|
||||||
|
<!-- дом: синк-род-правки -->
|
||||||
|
|
||||||
|
- **Отражение** — документ уже описывает эту вещь, и без правки он **станет
|
||||||
|
ложным**: миграция написана, а `database.md` её не знает; компонент заведён, а
|
||||||
|
`architecture.md` перечисляет прежние. Такая правка ничего не решает, она
|
||||||
|
договаривает решённое на чекпоинте и уже стоящее в коде. **Пишется молча.**
|
||||||
|
- **Новое** — в каноне заводится запись или норма, которой не было: ADR, правило
|
||||||
|
в конвенциях, записка в `research/`, инвариант в `CLAUDE.md`, сдвиг периметра в
|
||||||
|
`security.md`, граница в `passport.md`, дефект в журнале `review.md`. Такая
|
||||||
|
запись переживёт задачу и свяжет следующие. **Пишется только по слову
|
||||||
|
человека.**
|
||||||
|
|
||||||
|
<!-- /дом: синк-род-правки -->
|
||||||
|
|
||||||
|
**Показывается новое одной репликой и одним списком.** Каждый пункт — строкой:
|
||||||
|
что заведём, куда и на каком основании. Человек отвечает разом, и одобренное
|
||||||
|
пишет **следующий заход синка** — в цикле задачи это третий такт шага 6
|
||||||
|
(`av-dev:code-resolve`, `references/solve.md`). **Нового нет — реплики нет**, и
|
||||||
|
это обычный исход: у большинства задач хвост состоит из одного отражения.
|
||||||
|
|
||||||
|
**«По слову» — это по слову, а не вторым вопросом.** Человек уже сказал в этом
|
||||||
|
прогоне «заведи ADR», сам решил сузить проверки, сам одобрил формулировку
|
||||||
|
конвенции — слово сказано, и переспрашивать нечего: запись идёт как одобренная, а
|
||||||
|
в докладе стоит, чьим решением. Предложение существует ради нового, которое
|
||||||
|
заметил ты, а не ради ритуала.
|
||||||
|
|
||||||
|
**Отказ человека — строка доклада и всё.** В документы он не пишется: журнала
|
||||||
|
отвергнутых ADR и снятых конвенций канон не держит, и заведение такого журнала
|
||||||
|
здесь было бы ровно тем новым, которого никто не заказывал.
|
||||||
|
|
||||||
|
**Отрицание от этого не ослабло.** Документ, по которому нечего предложить,
|
||||||
|
по-прежнему обязан быть назван — просто раньше отрицание читал отчёт, а теперь
|
||||||
|
человек, и читает он его **до** того, как что-то написано. Обязанность та же:
|
||||||
|
пропуск неотличим от «не требуется», пока отрицание не сказано вслух.
|
||||||
|
|
||||||
|
## Чек-лист синка
|
||||||
|
|
||||||
|
Идёт сверху вниз; каждая строка попадает в доклад.
|
||||||
|
|
||||||
|
| Документ | Род | Обновляется, когда | Проверка |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `openspec/specs/` | отражение | всегда при изменении поведения | вливает `opsx:archive` |
|
||||||
|
| `database.md` | отражение | тронуты миграции | `docs.py check --base` |
|
||||||
|
| `architecture.md` | отражение | новый компонент, граница, внешняя зависимость, изменилось окружение | `docs.py`: capability без упоминания |
|
||||||
|
| `adr/` | новое | сработал триггер канона (перечень — [canon.md](../canon/references/canon.md#adr)) | нет — только этот чек-лист |
|
||||||
|
| `research/` | новое | узнали новое о внешнем формате или данных | нет |
|
||||||
|
| `security.md` | новое | новый недоверенный вход, токен, путь наружу, сдвиг периметра | нет |
|
||||||
|
| `conventions/` | новое | находка принята и не специфична для одного места | промоут |
|
||||||
|
| `review.md` | новое | дефект воспроизведён; сузили или расширили проверку | нет |
|
||||||
|
| `passport.md` | новое | новый потребитель, сдвиг границы «чем не является» | нет |
|
||||||
|
| `CLAUDE.md` | новое | изменился инвариант, гейт, запрет, необратимое | нет |
|
||||||
|
|
||||||
|
**Разрез в таблице не произволен.** Ложным без правки становится ровно тот
|
||||||
|
документ, который описывает **состояние системы**, — потому отражений в чек-листе
|
||||||
|
и мало. Остальные задают норму или хранят память: им не с чем разойтись, пока в
|
||||||
|
них не написано новое.
|
||||||
|
|
||||||
|
Пример доклада:
|
||||||
|
|
||||||
|
```
|
||||||
|
Синк документации.
|
||||||
|
Отражено, записано:
|
||||||
|
- openspec/specs/ — влиты дельты change add-bucket-reindex
|
||||||
|
- architecture.md — добавлен воркер свёртки, ссылка на capability reindex
|
||||||
|
- database.md — миграция 00006, таблица bucket
|
||||||
|
Предложено, жду слова:
|
||||||
|
- adr/ — отказ от внешней очереди в пользу таблицы; источник: архивный
|
||||||
|
design.md; триггер: намеренный отказ от очевидного подхода
|
||||||
|
Не требуется: research, security, conventions, review, passport, CLAUDE.md —
|
||||||
|
периметр не двигался, инварианты те же, новое о внешних данных не узнано.
|
||||||
|
Сверка документов: с прошлой (a1b2c3d, 2026-07-30) сделано 11 задач — пора
|
||||||
|
звать av-dev:doc-healthcheck.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Сверка — не здесь, а в `av-dev:doc-healthcheck`
|
||||||
|
|
||||||
|
Синк правит документы поодиночке, а расходятся они **между собой**: факт,
|
||||||
|
дописанный в `architecture.md`, уже живёт в `CLAUDE.md`; периметр в
|
||||||
|
`security.md` не знает про новый эндпоинт. Поймать это на своей же правке нельзя,
|
||||||
|
и судит это агент `doc-consistency`.
|
||||||
|
|
||||||
|
**Но синк его не зовёт.** Обоими судьями документов владеет скилл
|
||||||
|
`av-dev:doc-healthcheck`, и зовут их на весь канон разом, а не на пачку,
|
||||||
|
отобранную работой. Причина в цене: `doc-consistency` на `opus` по каждой
|
||||||
|
сделанной задаче — самая дорогая церемония процесса, а `doc-code-drift` хоть и на
|
||||||
|
`sonnet`, но читает репозиторий целиком. К тому же расхождение между двумя
|
||||||
|
документами по определению требует двух документов, а на большинстве задач синк
|
||||||
|
правит один.
|
||||||
|
|
||||||
|
Что теряется: привязка находки к задаче, которая её породила. Что выигрывается,
|
||||||
|
кроме денег: синк перестаёт отбирать пачку, и в неё попадают документы,
|
||||||
|
которых работа не касалась, — расхождение, внесённое правкой в одном месте, там
|
||||||
|
и живёт.
|
||||||
|
|
||||||
|
## Вычитка — наоборот, здесь
|
||||||
|
|
||||||
|
**Язык правленого вычитывается тем же прогоном, который его написал, и зовёшь
|
||||||
|
агента `doc-wording` ты.** Довод обратный доводу про судей: он читает **только
|
||||||
|
названную пачку**, стоит дёшево и ищет ровно то, что портится в момент письма, —
|
||||||
|
залог, оценку без факта, жаргон, термин без ввода. Ждать `doc-healthcheck` здесь
|
||||||
|
нечего: через месяц никто уже не помнит, какую фразу имел в виду автор.
|
||||||
|
|
||||||
|
Позови его **последним шагом правки, до коммита**, отдав список файлов, которых
|
||||||
|
она коснулась, — и назови этот список в промпте: по нему же он судит, известен ли
|
||||||
|
термин. Находки он отдаёт готовыми формулировками, подставляешь их ты.
|
||||||
|
|
||||||
|
**Синк бывает в два захода, и вычитка идёт последним из них.** Вернул непустой
|
||||||
|
список предложений — правка ещё не кончилась: человек ответит, и второй заход
|
||||||
|
допишет одобренное. Вычитывать пачку, которая сейчас пополнится, значит платить
|
||||||
|
за неё дважды. Значит: **предложения есть — вычитку откладываешь до второго
|
||||||
|
захода; предложений нет — этот заход последний, и вычитка идёт в нём.** Отказ
|
||||||
|
человека второго захода не отменяет: письма в нём не будет, а вычитка и гейт
|
||||||
|
будут — иначе правка первого захода уедет в коммит невычитанной.
|
||||||
|
|
||||||
|
**Условие вызова — правка, а не синк.** Синк самый частый вызывающий, но не
|
||||||
|
единственный: разведка (`av-dev:code-resolve`, сценарий разведки) пишет ответ по
|
||||||
|
одному адресу и синком себя не считает намеренно — вычитка ей нужна ровно та же.
|
||||||
|
Признак один и читается буквально: **документы правились — зови, ничего не правил
|
||||||
|
— не зови**.
|
||||||
|
|
||||||
|
## ADR — промоут, а не второе сочинение
|
||||||
|
|
||||||
|
Обоснование уже написано: `opsx:propose` кладёт `design.md` в каждый change, и
|
||||||
|
после архивации он лежит в `openspec/changes/archive/<id>/design.md` с разделами
|
||||||
|
`Context` / `Goals / Non-Goals` / `Decisions` / `Risks / Trade-offs`.
|
||||||
|
|
||||||
|
**ADR цитирует решение оттуда и ссылается на источник.** Не пересказывает и не
|
||||||
|
сочиняет заново.
|
||||||
|
|
||||||
|
**Второй законный источник — записка разведки**, и приходит он от скилла
|
||||||
|
`av-dev:code-resolve`, сценарий разведки: решение, принятое разведкой (намеренный отказ, выбор
|
||||||
|
подхода, «проверили и не делаем»), `design.md` не имеет по построению — change по
|
||||||
|
нему не будет никогда. Промоут при этом тот же: цитата и ссылка, но на записку.
|
||||||
|
Перечень источников закрыт и живёт в [каноне](../canon/references/canon.md),
|
||||||
|
раздел `adr/`.
|
||||||
|
|
||||||
|
**Триггер заведения, форма имени и правило замены — в
|
||||||
|
[каноне](../canon/references/canon.md), раздел `adr/`.** Здесь они не
|
||||||
|
повторяются: копия правила расходится с оригиналом на первой же смене версии
|
||||||
|
канона, а расходится незаметно.
|
||||||
|
|
||||||
|
Твоя часть — **применить триггер к этой задаче и сказать вслух, сработал он или
|
||||||
|
нет**. Строка «adr/ — не требуется: решение рутинное» и есть то, ради чего
|
||||||
|
чек-лист существует; её отсутствие неотличимо от «забыл посмотреть».
|
||||||
|
|
||||||
|
**Запись — новое, и заводится она по слову** (раздел «Два рода правок»).
|
||||||
|
Сработавший триггер даёт не файл, а строку предложения: какое решение, из какого
|
||||||
|
источника, каким из трёх триггеров прошло. Своей записи ADR не стоит ничего, а
|
||||||
|
каталог решений читают как список того, что в проекте всерьёз, — и разбавленный
|
||||||
|
рутиной он перестаёт им быть.
|
||||||
|
|
||||||
|
Порядок работы после «да»: открой источник — архивный `design.md` change либо
|
||||||
|
записку разведки, — найди в нём решение, проходящее триггер, процитируй его и
|
||||||
|
причину, сошлись на источник, добавь строку в индекс `docs/adr/README.md`
|
||||||
|
сверху.
|
||||||
|
|
||||||
|
## Чистка `architecture.md`
|
||||||
|
|
||||||
|
Обзор не держит поведение — его нормативный дом `openspec/specs/`. **Форма
|
||||||
|
маркера долга и правило «гейт от них не краснеет» — в
|
||||||
|
[каноне](../canon/references/canon.md), раздел `architecture.md`.**
|
||||||
|
|
||||||
|
Разбирается порциями: раздел вычищает та задача, которая его касается.
|
||||||
|
Содержимое не выбрасывается, а переезжает — требования в дельта-спеку change,
|
||||||
|
обоснование в ADR, обзор остаётся строкой со ссылкой на capability.
|
||||||
|
|
||||||
|
## Запись в `research/`
|
||||||
|
|
||||||
|
Наблюдение о внешнем мире: что реально шлёт источник, чем документация формата
|
||||||
|
расходится с практикой. **Требование происхождения и правило про расходящееся
|
||||||
|
число — в [каноне](../canon/references/canon.md), раздел `research/`.**
|
||||||
|
|
||||||
|
Твоя часть — заметить, что по ходу задачи узналось новое о внешних данных, и не
|
||||||
|
дать этому остаться в контексте. Признак: ты правил разбор, опираясь на то, чего
|
||||||
|
нет ни в одном документе.
|
||||||
|
|
||||||
|
## Чего может не быть
|
||||||
|
|
||||||
|
Два раздела ниже — запись в `review.md` и промоут в конвенции — берут форму у
|
||||||
|
конвейера ревью: она принадлежит ему, а не канону. Берут **вызовом скилла**, а не
|
||||||
|
чтением файла по пути.
|
||||||
|
|
||||||
|
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||||||
|
Правится дом, а не этот файл.
|
||||||
|
|
||||||
|
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||||||
|
|
||||||
|
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||||||
|
живут порознь; каждая узнаётся своим следом:
|
||||||
|
|
||||||
|
| Чего нет | Как видно | Чего теперь не делает никто |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||||||
|
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||||||
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
|
поведении.
|
||||||
|
|
||||||
|
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||||||
|
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||||||
|
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||||||
|
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||||||
|
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||||||
|
сам.
|
||||||
|
|
||||||
|
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||||||
|
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||||||
|
сделанного. Выдумывать обходной путь нельзя тоже.
|
||||||
|
|
||||||
|
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||||||
|
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||||||
|
|
||||||
|
<!-- /копия: отсутствие -->
|
||||||
|
|
||||||
|
Здесь это значит: документов канона может не быть вовсе — тогда синка нет, и
|
||||||
|
это исход, а не повод раскладывать документы по своему усмотрению.
|
||||||
|
|
||||||
|
## Запись в `review.md`
|
||||||
|
|
||||||
|
Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку
|
||||||
|
конвейера. **Что в каком и в какой форме — в
|
||||||
|
[каноне](../canon/references/canon.md), раздел `review.md`**; подробности формы
|
||||||
|
записи и выбор адреса, куда она ведёт, — у конвейера ревью: `Skill
|
||||||
|
av-dev:code-review`, его `references/review-journal.md`.
|
||||||
|
|
||||||
|
Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим».
|
||||||
|
Со временем теряется не факт, а то, почему дефект не поймали, — единственное,
|
||||||
|
ради чего журнал есть.
|
||||||
|
|
||||||
|
«Сразу» и «по слову» здесь не спорят: запись — новое, и она идёт предложением, но
|
||||||
|
**предложением этого прогона**, а не следующего. Отложить её до «когда починим»
|
||||||
|
нельзя ни с чьего согласия: чинится дефект, а теряется причина промаха.
|
||||||
|
|
||||||
|
**Решение сузить проверки** (перестали звать проход, переселили его в другой
|
||||||
|
скилл) обязано попасть в раздел настройки, а не остаться в отчёте ревью. Второй
|
||||||
|
раз оно не спрашивается: такое решение принимает человек по определению, и слово
|
||||||
|
по нему уже сказано — сказано тогда, когда проверку сузили.
|
||||||
|
|
||||||
|
## Промоут в конвенции
|
||||||
|
|
||||||
|
Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком
|
||||||
|
принадлежит конвейеру ревью — его `references/promote.md`, читается через
|
||||||
|
`Skill av-dev:code-review`; роль каталога конвенций — в
|
||||||
|
[каноне](../canon/references/canon.md). **Прогон идёт вне конвейера**
|
||||||
|
(находку принесли руками) — три шага всё равно твои, просто без его процедуры:
|
||||||
|
сформулируй правило,
|
||||||
|
поищи, чем оно механизируется, и вычеркни прозу, если механизировалось.
|
||||||
|
|
||||||
|
Твоя часть — **третий шаг, который пропускают чаще всего**: правило заработало,
|
||||||
|
а формулировка осталась в прозе, и проход продолжает проверять уже проверенное.
|
||||||
|
На синке это отдельная строка: «conventions/ — правило X механизировано,
|
||||||
|
формулировка удалена» либо «не требуется».
|
||||||
|
|
||||||
|
**Конвенция — самое дорогое из нового, и на синке она только предлагается.**
|
||||||
|
Одна её строка становится входом каждого следующего прогона ревью и критерием
|
||||||
|
для всех будущих задач; находка, доехавшая до конвенции по инерции хвоста, потом
|
||||||
|
годами разменивается на внимание прохода. Предложение называет **проверяемое
|
||||||
|
свойство и проход, который его нашёл**, — по этой паре человек и решает.
|
||||||
|
|
||||||
|
**Шаг 2 в хвост задачи не помещается.** Механизация правила — конфиг линтера или
|
||||||
|
сканер, плюс приведение кода к зелёному — это работа размером с задачу, и делать
|
||||||
|
её попутно значит удваивать чужой прогон. Согласованный промоут даёт строку
|
||||||
|
конвенции сейчас и **задачу типа `chore`** на механизацию — заводит её
|
||||||
|
`av-dev:task-track`, и заводится она тем же словом человека, что и сама
|
||||||
|
конвенция.
|
||||||
|
|
||||||
|
## Сигнал сверки — строка, а не вызов
|
||||||
|
|
||||||
|
Сверку документов (`av-dev:doc-healthcheck`) зовёт человек по признаку **«с
|
||||||
|
прошлой сверки сделан десяток задач»**. Признак наблюдаемый, но считать его было
|
||||||
|
нечем: следа у сверки не оставалось, и «десяток» держался в чьей-то памяти. Это
|
||||||
|
ровно тот прозаический триггер, который дал 6 записей ADR на 43 изменения, — и
|
||||||
|
здесь он не срабатывал по той же причине.
|
||||||
|
|
||||||
|
**След оставляет сама сверка** — ключ `healthcheck_last` в секции `[docs]`
|
||||||
|
файла `.av-dev.toml` (состав ключей — [canon.md](../canon/references/canon.md),
|
||||||
|
раздел `.av-dev.toml`). **Считает синк**, и вот чем:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git rev-list --count <last>..HEAD -- openspec/changes/archive <каталог задач>
|
||||||
|
```
|
||||||
|
|
||||||
|
Считаются коммиты, тронувшие **архив change или каталог задач** (его путь — ключ
|
||||||
|
`[tasks] dir`). Оба пути выбраны потому, что доведённая до конца задача оставляет
|
||||||
|
след хотя бы в одном: решение архивирует change, а обслуживание и разведка change
|
||||||
|
не заводят вовсе и видны только закрытием — правкой индексов учёта. Считать один
|
||||||
|
архив значило бы не считать `chore` и `research`, то есть на проекте с их
|
||||||
|
перевесом говорить «звать рано» вечно.
|
||||||
|
|
||||||
|
Ни `openspec`, ни каталога задач в проекте нет — считай коммиты
|
||||||
|
(`git rev-list --count <last>..HEAD`) и **скажи, что считал коммиты**: число
|
||||||
|
другого рода, и молчаливая подмена сделала бы признак вдвое чувствительнее.
|
||||||
|
Постановка, пришедшая текстом, следа не оставляет ни там ни там — такие задачи в
|
||||||
|
счёт не входят, и это тоже говорится строкой, когда прогон шёл текстом.
|
||||||
|
|
||||||
|
Строка доклада обязательна всегда, и вариантов у неё три:
|
||||||
|
|
||||||
|
- **счёт меньше десятка** — «с прошлой сверки N задач, звать рано»;
|
||||||
|
- **счёт от десятка** — «с прошлой сверки N задач, пора звать
|
||||||
|
`av-dev:doc-healthcheck`»;
|
||||||
|
- **ключа нет** — «сверка документов не проводилась ни разу», и это самый
|
||||||
|
сильный из трёх сигналов, а не отсутствие данных.
|
||||||
|
|
||||||
|
**Сам не зовёшь.** Прогон сверки идёт по всему канону и держит `opus`; решение о
|
||||||
|
таких часах принимает тот, кто их оплачивает. Синк, позвавший её сам, превратил
|
||||||
|
бы самую дорогую проверку процесса в церемонию хвоста задачи — против чего она и
|
||||||
|
вынесена в отдельный скилл.
|
||||||
|
|
||||||
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
|
- **Не проверяет раскладку** — это `canon`.
|
||||||
|
- **Не заводит недостающие документы** — их скелет кладёт `canon adopt` или
|
||||||
|
`doc-init`.
|
||||||
|
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
|
||||||
|
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.
|
||||||
|
- **Не заводит новое молча** — ни ADR, ни конвенцию, ни записку. Молча идёт
|
||||||
|
только отражение, и признак у него один: без правки документ станет ложным.
|
||||||
|
- **Не зовёт сверку документов** — считает и говорит строкой; зовёт человек.
|
||||||
@@ -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,15 +21,15 @@ description: "Груминг беклога — интерактивный ра
|
|||||||
рекомендацией. Что решается фактом (сделано, отменено, дублируется), решается
|
рекомендацией. Что решается фактом (сделано, отменено, дублируется), решается
|
||||||
без вопросов и показывается списком.
|
без вопросов и показывается списком.
|
||||||
|
|
||||||
Форматом и содержимым записей владеет скилл `tasks` — груминг зовёт его
|
Форматом и содержимым записей владеет скилл `task-track` — груминг зовёт его
|
||||||
операции, а не правит файлы руками. Выполнением задачи — конвейер проекта.
|
операции, а не правит файлы руками. Выполнением задачи — конвейер проекта.
|
||||||
|
|
||||||
## Три правила, из которых всё следует
|
## Три правила, из которых всё следует
|
||||||
|
|
||||||
1. **Порядок назначает человек, машина его не выводит.** Ни давность, ни тип, ни
|
1. **Порядок назначает человек, машина его не выводит.** Ни давность, ни тип, ни
|
||||||
число задач под целью приоритетом не являются. Единственное место в очереди,
|
размер секции приоритетом не являются. Единственное место в очереди,
|
||||||
назначенное не человеком, — конец секции у сырья, и оно из очереди изъято
|
назначенное не человеком, — конец секции у сырья, и оно из очереди изъято
|
||||||
(`tasks`, правило 4).
|
(`task-track`, правило 4).
|
||||||
2. **Порция важнее охвата.** Тридцать задач за заход — это усталость и
|
2. **Порция важнее охвата.** Тридцать задач за заход — это усталость и
|
||||||
штамповка: последние десять получат «оставить» не потому, что живы, а потому,
|
штамповка: последние десять получат «оставить» не потому, что живы, а потому,
|
||||||
что разбор затянулся. Лучше две честные порции, чем один полный проход.
|
что разбор затянулся. Лучше две честные порции, чем один полный проход.
|
||||||
@@ -37,16 +37,48 @@ description: "Груминг беклога — интерактивный ра
|
|||||||
`--reason` у закрытия и переноса, ответ в теле задачи, строка в докладе.
|
`--reason` у закрытия и переноса, ответ в теле задачи, строка в докладе.
|
||||||
Решение, оставшееся в переписке, будет принято заново через месяц.
|
Решение, оставшееся в переписке, будет принято заново через месяц.
|
||||||
|
|
||||||
|
## Груминг — операция доработки
|
||||||
|
|
||||||
|
**Стадия проекта решает, применим ли груминг вообще** (дом стадии —
|
||||||
|
[`task-track`, «Две стадии»](../task-track/SKILL.md#две-стадии); посмотреть —
|
||||||
|
`tasks.py stage`).
|
||||||
|
|
||||||
|
На **доработке** он и есть основная гигиена: беклог пополняется извне и
|
||||||
|
вразнобой, порядок значит важность, и назначить её может только человек.
|
||||||
|
|
||||||
|
На **стройке** оба вопроса скилла отвечены заранее. «Что сейчас самое важное» —
|
||||||
|
первая строка плана, и назначил её не приоритет, а зависимость: переставить её
|
||||||
|
значит сломать стройку. «Что перестало быть важным» возникает не порциями, а
|
||||||
|
разом — когда меняется замысел, — и тогда пересматривается **план целиком**, а
|
||||||
|
не 5–8 задач из середины. Порционный разбор здесь вреден: он вынимает шаги из
|
||||||
|
списка, порядок которого и есть его содержание.
|
||||||
|
|
||||||
|
Поэтому на стройке скилл говорит это строкой и **отсылает к другой работе**:
|
||||||
|
[пересмотр плана целиком](../task-track/SKILL.md#пересмотр-плана-стройки) —
|
||||||
|
сценарий скилла `task-track`, гигиена полей — тоже его, а исчерпанный беклог
|
||||||
|
значит переход (`tasks.py stage support`). Четыре вещи он делает и на стройке,
|
||||||
|
потому что от стадии они не зависят: `tasks.py check --fix`, разбор
|
||||||
|
накопившихся вопросов, закрытие сделанного попутно и **возврат неудавшейся
|
||||||
|
приёмки** (`reopen`).
|
||||||
|
|
||||||
|
**Возврат приёмки от стадии не зависит вовсе, и это надо сказать отдельно.**
|
||||||
|
Приёмщик и исполнитель у нас совпадают, и опор против этого две: независимый
|
||||||
|
отчёт ревью и `reopen`. Вторая привязана к грумингу только по привычке — заметил,
|
||||||
|
что закрытая задача сделана не тем, чем обещала, возвращай сразу, на любой
|
||||||
|
стадии и в любой момент.
|
||||||
|
|
||||||
## Когда груминг созрел
|
## Когда груминг созрел
|
||||||
|
|
||||||
**Зовёт человек.** Скилл сам себя не назначает, но обязан **напоминать**, и
|
**Зовёт человек.** Скилл сам себя не назначает, но обязан **напоминать**, и
|
||||||
признак наблюдаемый, а не календарный:
|
признак наблюдаемый, а не календарный:
|
||||||
|
|
||||||
- в беклоге появились записи, которых человек ещё не видел (заведены интейком по
|
- в беклоге появились записи, которых человек ещё не видел (заведены по ходу
|
||||||
ходу работы, урожаем ревью, разбором находок);
|
работы, урожаем ревью, разбором находок);
|
||||||
- на верхних строках очереди есть задача с открытым вопросом — очередь
|
- на верхних строках очереди есть задача с открытым вопросом — очередь
|
||||||
показывает то, что взять нельзя;
|
показывает то, что взять нельзя;
|
||||||
- `tasks.py check` печатает «готово к взятию: 0 из N» — брать сегодня нечего.
|
- `tasks.py check` печатает «готово к взятию: 0 из N» — брать сегодня нечего.
|
||||||
|
**На стройке этот признак читается иначе**: он значит «план ещё не дописан», а
|
||||||
|
не «пора грумить».
|
||||||
|
|
||||||
Порога в неделях нет намеренно: счётчик простоя пришлось бы вести руками, а
|
Порога в неделях нет намеренно: счётчик простоя пришлось бы вести руками, а
|
||||||
решает всё равно человек. Признак — **очередь перестала быть твоей**: взялся
|
решает всё равно человек. Признак — **очередь перестала быть твоей**: взялся
|
||||||
@@ -122,7 +154,7 @@ flowchart TD
|
|||||||
**3. Что перестало быть важным.** Порциями по 5–8. Сперва то, что решается
|
**3. Что перестало быть важным.** Порциями по 5–8. Сперва то, что решается
|
||||||
фактом и не требует ничьего суждения (сделано попутно, отменено решением,
|
фактом и не требует ничьего суждения (сделано попутно, отменено решением,
|
||||||
дублируется, симптомы одного дефекта), затем то, что решает человек (жива ли,
|
дублируется, симптомы одного дефекта), затем то, что решает человек (жива ли,
|
||||||
та ли цель, задача ли это ещё).
|
задача ли это ещё).
|
||||||
|
|
||||||
**4. Что важно сейчас.** Расстановка порядка — `move --after <слаг>` и
|
**4. Что важно сейчас.** Расстановка порядка — `move --after <слаг>` и
|
||||||
`move --first`. Разбирается **не весь беклог, а верх очереди**: первые три-пять
|
`move --first`. Разбирается **не весь беклог, а верх очереди**: первые три-пять
|
||||||
@@ -146,26 +178,26 @@ flowchart TD
|
|||||||
кодом стоит меньше, чем та же работа через квартал;
|
кодом стоит меньше, чем та же работа через квартал;
|
||||||
- **что дорожает от ожидания** — данные копятся, миграция усложняется, внешний
|
- **что дорожает от ожидания** — данные копятся, миграция усложняется, внешний
|
||||||
срок приближается;
|
срок приближается;
|
||||||
- **цель, которую человек назвал следующей.**
|
- **то, что человек назвал следующим.**
|
||||||
|
|
||||||
Довод **записывается причиной** (`move --after <слаг> --reason …`). Порядок без
|
Довод **записывается причиной** (`move --after <слаг> --reason …`). Порядок без
|
||||||
причины — это порядок, который на следующем груминге назначат заново с нуля.
|
причины — это порядок, который на следующем груминге назначат заново с нуля.
|
||||||
|
|
||||||
**Цель и приоритет — независимые оси.** Очередь может идти поперёк целей, и это
|
**Тема и приоритет — независимые оси.** Очередь может идти поперёк полок, и это
|
||||||
законно: задачи одной цели не обязаны стоять подряд. Но если под целью годами
|
законно: задачи одной темы не обязаны стоять подряд. Но если из одной полки
|
||||||
ничего не поднимается наверх — это разговор про цель, а не про очередь, и он
|
годами ничего не поднимается наверх — это разговор про саму работу, а не про
|
||||||
идёт на шаге 3.
|
очередь, и он идёт на шаге 3.
|
||||||
|
|
||||||
## Документы устаревают тем же ходом работы
|
## Документы устаревают тем же ходом работы
|
||||||
|
|
||||||
Груминг судит **задачи**, а не документы, и агентов канона не зовёт: они
|
Груминг судит **задачи**, а не документы, и агентов канона не зовёт: они
|
||||||
принадлежат плагину `av-dev-docs`, и когда их звать — решает он.
|
принадлежат скиллам документации, и когда их звать — решают они.
|
||||||
|
|
||||||
Но повод назвать это здесь есть: беклог и документы протухают от одного и того
|
Но повод назвать это здесь есть: беклог и документы протухают от одного и того
|
||||||
же — от сделанной работы. Пришёл на груминг и видишь, что с прошлого раза сделан
|
же — от сделанной работы. Пришёл на груминг и видишь, что с прошлого раза сделан
|
||||||
десяток задач, — скажи строкой, что документы стоит сверить
|
десяток задач, — скажи строкой, что документы стоит сверить
|
||||||
(`av-dev-docs:healthcheck`), и иди дальше. Плагина в проекте нет — сверять нечем,
|
(`av-dev:doc-healthcheck`), и иди дальше. Документов канона в проекте нет —
|
||||||
и это тоже строка.
|
сверять нечем, и это тоже строка.
|
||||||
|
|
||||||
## Интерактив
|
## Интерактив
|
||||||
|
|
||||||
@@ -190,12 +222,12 @@ 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`** — закрытие не окончательно. Заметил, что закрытая
|
||||||
что закрытая задача сделана не тем, чем обещала, — возвращай, это штатная
|
задача сделана не тем, чем обещала, — возвращай, это штатная операция, а не
|
||||||
операция, а не скандал. Индексы под git: `git log -p` по беклогу показывает,
|
скандал, и ждать груминга она не требует (на стройке его и не будет). Индексы под git: `git log -p` по беклогу показывает,
|
||||||
что и когда закрыто, потому что закрытие коммитится отдельным коммитом учёта.
|
что и когда закрыто, потому что закрытие коммитится отдельным коммитом учёта.
|
||||||
|
|
||||||
Известные обходы:
|
Известные обходы:
|
||||||
@@ -209,7 +241,7 @@ flowchart TD
|
|||||||
- **Расставить порядок молча**, без доводов: тогда через месяц он неотличим от
|
- **Расставить порядок молча**, без доводов: тогда через месяц он неотличим от
|
||||||
случайного. Защита: причина у каждого движения и строка доклада.
|
случайного. Защита: причина у каждого движения и строка доклада.
|
||||||
- **Разобрать много и мелко** вместо немногого и важного: тридцать полей гигиены
|
- **Разобрать много и мелко** вместо немногого и важного: тридцать полей гигиены
|
||||||
вместо трёх решений о важности. Защита: гигиена — работа скилла `tasks` и
|
вместо трёх решений о важности. Защита: гигиена — работа скилла `task-track` и
|
||||||
побочный продукт здесь; доклад называет **решения**, а не правки.
|
побочный продукт здесь; доклад называет **решения**, а не правки.
|
||||||
|
|
||||||
## Слоты проекта
|
## Слоты проекта
|
||||||
@@ -218,7 +250,7 @@ flowchart TD
|
|||||||
|
|
||||||
1. **Что считается сломанным** — какая красная проверка обгоняет развитие.
|
1. **Что считается сломанным** — какая красная проверка обгоняет развитие.
|
||||||
Не названо — спрашиваем человека, а не решаем сами.
|
Не названо — спрашиваем человека, а не решаем сами.
|
||||||
2. **Необратимое** — что спрашивается всегда (тот же слот, что у скилла `tasks`;
|
2. **Необратимое** — что спрашивается всегда (тот же слот, что у скилла `task-track`;
|
||||||
дом один).
|
дом один).
|
||||||
3. **Ориентир по размеру порции**, если он замерялся. Умолчание — 5–8 задач, и
|
3. **Ориентир по размеру порции**, если он замерялся. Умолчание — 5–8 задач, и
|
||||||
это **ориентир, а не закон**.
|
это **ориентир, а не закон**.
|
||||||
@@ -231,17 +263,17 @@ flowchart TD
|
|||||||
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
|
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
|
||||||
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
|
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
|
||||||
- **Что перестало быть важным**: удалено как реализованное (со ссылками), ушло
|
- **Что перестало быть важным**: удалено как реализованное (со ссылками), ушло
|
||||||
без реализации (с причинами), понижено до сырья, слито, сменило тип или цель.
|
без реализации (с причинами), понижено до сырья, слито, сменило тип.
|
||||||
- **Что важно сейчас**: верх очереди по каждой секции — слаги в порядке, и по
|
- **Что важно сейчас**: верх очереди по каждой секции — слаги в порядке, и по
|
||||||
каждому движению довод одной строкой.
|
каждому движению довод одной строкой.
|
||||||
- **Границы покрытия**: сколько задач не трогали и какие именно секции, теги или
|
- **Границы покрытия**: сколько задач не трогали и какие именно секции или теги
|
||||||
цели остались — иначе доклад читается как «беклог разобран».
|
остались — иначе доклад читается как «беклог разобран».
|
||||||
- `tasks.py check` после правок — результат строкой.
|
- `tasks.py check` после правок — результат строкой.
|
||||||
|
|
||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
Не пишет код и не выполняет задачи. Не заводит и не переоформляет записи сам по
|
Не пишет код и не выполняет задачи. Не заводит и не переоформляет записи сам по
|
||||||
себе — формат и содержимое ведёт `tasks` (груминг зовёт его операции). Не решает
|
себе — формат и содержимое ведёт `task-track` (груминг зовёт его операции). Не решает
|
||||||
за человека, что важно: он готовит развилки и рекомендует. Не принимает
|
за человека, что важно: он готовит развилки и рекомендует. Не принимает
|
||||||
закрытые задачи отдельным ритуалом — `reopen` есть, момента у него нет. Не судит
|
закрытые задачи отдельным ритуалом — `reopen` есть, момента у него нет. Не судит
|
||||||
документы проекта — это плагин `av-dev-docs`.
|
документы проекта — это скиллы `av-dev:canon` и `av-dev:doc-healthcheck`.
|
||||||
+17
-24
@@ -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).
|
||||||
|
|
||||||
**Вопросы на верхних строках очереди разбираются вне очереди порции** — здесь
|
**Вопросы на верхних строках очереди разбираются вне очереди порции** — здесь
|
||||||
же, даже если сама задача в порцию переоценки не попала. Иначе правило «задача с
|
же, даже если сама задача в порцию переоценки не попала. Иначе правило «задача с
|
||||||
@@ -41,8 +41,8 @@
|
|||||||
проверки на нужность. Свежесть меряется git'ом, как и залежалость, — датой
|
проверки на нужность. Свежесть меряется git'ом, как и залежалость, — датой
|
||||||
появления файла в истории;
|
появления файла в истории;
|
||||||
2. дальше **по залежалости** — `list --stale`;
|
2. дальше **по залежалости** — `list --stale`;
|
||||||
3. по потребности — одна секция целиком, один тег (партия ревью), одна цель
|
3. по потребности — одна секция целиком, один тег (партия ревью), список от
|
||||||
(`--goal`), список от человека.
|
человека.
|
||||||
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
||||||
Между порциями — промежуточный доклад.
|
Между порциями — промежуточный доклад.
|
||||||
|
|
||||||
@@ -64,14 +64,14 @@
|
|||||||
решение>"`. Задача закрывается не только коммитом.
|
решение>"`. Задача закрывается не только коммитом.
|
||||||
3. **Проверь пересечения.** Две задачи об одном — содержимое в одну, вторую
|
3. **Проверь пересечения.** Две задачи об одном — содержимое в одну, вторую
|
||||||
`close <slug> --reason "слита с <другой-слаг>"`. Смотри **шире порции**:
|
`close <slug> --reason "слита с <другой-слаг>"`. Смотри **шире порции**:
|
||||||
интейк дедуплицирует новое против существующего, но никогда не пересматривает
|
заведение сверяет новое против уже лежащего, но никогда не пересматривает
|
||||||
уже лежащее, и две задачи с одной причиной могут лежать рядом месяцами.
|
уже лежащее, и две задачи с одной причиной могут лежать рядом месяцами.
|
||||||
4. **Пере-кластеризуй по общей причине.** Несколько задач, оказавшихся симптомами
|
4. **Пере-кластеризуй по общей причине.** Несколько задач, оказавшихся симптомами
|
||||||
одного дефекта, сливаются в одну — это находка, которую интейк дать не мог.
|
одного дефекта, сливаются в одну — это находка, которую заведение дать не могло.
|
||||||
5. **Гигиена полей** — протухшее «зачем», вопрос в прозе, снятый ответ, свойство
|
5. **Гигиена полей** — протухшее «зачем», вопрос в прозе, снятый ответ, свойство
|
||||||
репозитория в рамках, предписание процесса в теле, тип, разошедшийся с
|
репозитория в рамках, предписание процесса в теле, тип, разошедшийся с
|
||||||
задачей, границы вместо реализации в разделе «Затрагивает». Список и правила —
|
задачей, границы вместо реализации в разделе «Затрагивает». Список и правила —
|
||||||
в скилле `tasks`. **Груминг — то самое место, где беклог добирает тип и
|
в скилле `task-track`. **Груминг — то самое место, где беклог добирает тип и
|
||||||
разделы его схемы:** требовать их на входе значило бы выгонять в заметки то,
|
разделы его схемы:** требовать их на входе значило бы выгонять в заметки то,
|
||||||
что должно лежать задачей, а к взятию в работу они уже обязательны (`ready`).
|
что должно лежать задачей, а к взятию в работу они уже обязательны (`ready`).
|
||||||
Блок здоровья `check` печатает, сколько записей готово к взятию, — по этому
|
Блок здоровья `check` печатает, сколько записей готово к взятию, — по этому
|
||||||
@@ -84,20 +84,14 @@
|
|||||||
|
|
||||||
6. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
|
6. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
|
||||||
сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
|
сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
|
||||||
7. **Та ли цель — и нужна ли она вообще.** `feature`, которой не находится цель,
|
7. **Тот ли тип.** Заводилась починкой, а после разбора оказалось, что
|
||||||
— кандидат на выход: новая возможность вне цели это возможность, которой никто
|
поведение никогда и не было заявлено, — это `feature`. Тип, оставшийся от
|
||||||
не заказывал. Операционной задаче (`fix`, `chore`, `research`) цель не нужна,
|
прошлой формулировки, врёт ровно там, где по нему отбирают, и требует не тех
|
||||||
и выдумывать её здесь не надо.
|
разделов.
|
||||||
|
|
||||||
**Отменяется и сама цель** — когда замысел оказался неверен, а не когда
|
|
||||||
задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую
|
|
||||||
либо закрыть своей причиной, либо перевесить на другую цель, и только потом
|
|
||||||
закрыть цель. Порядок и почему он такой —
|
|
||||||
[task-goal.md](../../tasks/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель).
|
|
||||||
8. **Задача ли это по-прежнему.** Не проходит `ready` по существу, а не по
|
8. **Задача ли это по-прежнему.** Не проходит `ready` по существу, а не по
|
||||||
недописанным разделам → `edit <slug> --type research` и опустошённый раздел
|
недописанным разделам → `edit <slug> --type research` и опустошённый раздел
|
||||||
«Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач под
|
«Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач,
|
||||||
той же целью, дальше декомпозиция.
|
дальше декомпозиция.
|
||||||
9. **Не подешевела ли она.** Сделанная с прошлого раза работа меняет цену
|
9. **Не подешевела ли она.** Сделанная с прошлого раза работа меняет цену
|
||||||
**других** задач: рядом с только что тронутым кодом та же работа стоит меньше.
|
**других** задач: рядом с только что тронутым кодом та же работа стоит меньше.
|
||||||
Это довод и на шаге 4 — задача, внезапно подешевевшая, поднимается в очереди
|
Это довод и на шаге 4 — задача, внезапно подешевевшая, поднимается в очереди
|
||||||
@@ -111,7 +105,7 @@
|
|||||||
нигде не хранится.
|
нигде не хранится.
|
||||||
|
|
||||||
Задача из верхних строк `--stale`, которую и этот заход оставляет без изменений,
|
Задача из верхних строк `--stale`, которую и этот заход оставляет без изменений,
|
||||||
**либо двигается (меняет цель, поднимается в очереди, уходит с причиной), либо
|
**либо двигается (меняет полку, поднимается в очереди, уходит с причиной), либо
|
||||||
остаётся с явно записанной причиной**, почему её держим (`move <slug> --reason
|
остаётся с явно записанной причиной**, почему её держим (`move <slug> --reason
|
||||||
…` — без `--section` секция берётся текущая). Молчаливое «оставить как есть» на
|
…` — без `--section` секция берётся текущая). Молчаливое «оставить как есть» на
|
||||||
давно неподвижной задаче — это решение не принимать решение; запись причины
|
давно неподвижной задаче — это решение не принимать решение; запись причины
|
||||||
@@ -123,9 +117,8 @@
|
|||||||
Разбирается **верх очереди**, а не весь беклог: первые три-пять строк каждой
|
Разбирается **верх очереди**, а не весь беклог: первые три-пять строк каждой
|
||||||
секции. Ниже пятой строки порядок всё равно перестаёт что-либо значить.
|
секции. Ниже пятой строки порядок всё равно перестаёт что-либо значить.
|
||||||
|
|
||||||
1. **Покажи текущий верх** — `list --index backlog`, по секциям, в том порядке,
|
1. **Покажи текущий верх** — `list`, по секциям, в том порядке, в каком строки
|
||||||
в каком строки лежат. Плюс состояние проекта из роадмапа: секция `Готово`
|
лежат.
|
||||||
отвечает на «где мы», `Запланировано` — на «куда шли».
|
|
||||||
2. **Спрашивай сравнением, а не оценкой.** «Что из этих двух делают раньше»
|
2. **Спрашивай сравнением, а не оценкой.** «Что из этих двух делают раньше»
|
||||||
имеет проверяемый ответ, «насколько важна эта задача» — нет. Веди попарно и
|
имеет проверяемый ответ, «насколько важна эта задача» — нет. Веди попарно и
|
||||||
сверху: что первое, что после него.
|
сверху: что первое, что после него.
|
||||||
@@ -133,7 +126,7 @@
|
|||||||
или `move <slug> --first --reason …`. Довод берётся из перечня в
|
или `move <slug> --first --reason …`. Довод берётся из перечня в
|
||||||
[SKILL.md](../SKILL.md#приоритет-как-его-расставляют): сломано сейчас,
|
[SKILL.md](../SKILL.md#приоритет-как-его-расставляют): сломано сейчас,
|
||||||
разблокирует остальное, дешевеет от сделанного, дорожает от ожидания,
|
разблокирует остальное, дешевеет от сделанного, дорожает от ожидания,
|
||||||
названная цель.
|
названо человеком.
|
||||||
4. **Проверь верх на готовность** — `tasks.py ready <слаг> …` по первым строкам.
|
4. **Проверь верх на готовность** — `tasks.py ready <слаг> …` по первым строкам.
|
||||||
Задача, стоящая первой и не проходящая `ready`, — это очередь, которая врёт:
|
Задача, стоящая первой и не проходящая `ready`, — это очередь, которая врёт:
|
||||||
взять её нельзя. Либо дописывается здесь же, либо уступает место.
|
взять её нельзя. Либо дописывается здесь же, либо уступает место.
|
||||||
@@ -1,133 +1,97 @@
|
|||||||
---
|
---
|
||||||
name: tasks
|
name: task-track
|
||||||
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Он же повышает каталог до текущей версии формата по своему журналу версий, когда tasks.py check говорит, что каталог отстал. Расстановка приоритетов и разбор накопившегося — скилл groom. Не реализует задачи — этим занимается скилл решения задачи.
|
description: Ведение задач как каталога markdown-файлов (одна задача = один файл в items/ + строка в BACKLOG.md). У каждой задачи есть тип (feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. У проекта есть стадия (build — беклог это план стройки, порядок строк значит зависимость; support — очередь правок, порядок значит важность). Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей, смена стадии и проверка согласованности индекса. Использовать, когда просят добавить задачу или идею, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат, объявить стадию или проверить беклог. Каталог отстал от версии раскладки — это скажет tasks.py check, а повышает проект скилл av-dev:canon по общему журналу версий. Расстановка приоритетов и разбор накопившегося — скилл av-dev:task-groom. Не реализует задачи — этим занимается скилл решения задачи.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Задачи
|
# Задачи
|
||||||
|
|
||||||
Задачи — каталог markdown-файлов. Одна запись = один файл `items/<slug>.md` плюс
|
Задачи — каталог markdown-файлов. Одна задача = один файл `items/<slug>.md` плюс
|
||||||
строка **ровно в одном** индексе. Скилл владеет **форматом и содержимым**:
|
строка в `BACKLOG.md`. Скилл владеет **форматом и содержимым**: заводит,
|
||||||
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
|
редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
|
||||||
|
|
||||||
Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть
|
Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть
|
||||||
важным, решает скилл `groom`, а этот скилл лишь даёт ему операции; и выполнением
|
важным, решает скилл `task-groom`, а этот скилл лишь даёт ему операции; и выполнением
|
||||||
задачи — это конвейер проекта.
|
задачи — это конвейер проекта.
|
||||||
|
|
||||||
## Шесть правил, из которых всё следует
|
## Шесть правил, из которых всё следует
|
||||||
|
|
||||||
Ситуация не покрыта инструкцией — решай по ним.
|
Ситуация не покрыта инструкцией — решай по ним.
|
||||||
|
|
||||||
0. **Цель — возможность приложения, задача — шаг к ней.** Цель отвечает на «что
|
0. **Стадия решает, что значит порядок строк.** Проект живёт в одной из двух
|
||||||
приложение будет уметь», её «Завершение» — наблюдаемый признак того, что уже
|
стадий, и обе ведут один и тот же беклог, но читают его по-разному.
|
||||||
умеет; задача отвечает на «что для этого нужно сделать». Оценивается проект
|
На **стройке** (`build`) беклог это план от базы к деталям: порядок —
|
||||||
по **поведению**, а не по внутреннему устройству, поэтому и роадмап отвечает
|
зависимость, «раньше нельзя». На **доработке** (`support`) беклог это очередь
|
||||||
не «сколько работ осталось», а «что уже умеет и чего ещё не умеет».
|
правок: порядок — важность, «раньше лучше». Из этого следует остальное —
|
||||||
Свойство поведения — тоже возможность: «сообщает о своём состоянии»,
|
сколько у беклога секций, как его пополняют, что значит его опустошение и
|
||||||
«исход слияния не зависит от порядка доставки» — законные цели.
|
нужен ли груминг. Стадия объявлена ключом `[tasks] stage`; молчание ответом
|
||||||
1. **Беклог гниёт с той стороны, где его пополняют.** Заведение — самая частая
|
не считается, и `check` без неё отказывает.
|
||||||
операция и с худшим отказом: из одного разговора рождается пять файлов, а
|
1. **Беклог гниёт с той стороны, где его пополняют — на доработке.** Заведение
|
||||||
переоценка потом разгребает то, чего не надо было заводить. Дедупликация и
|
там самая частая операция и с худшим отказом: из одного разговора рождается
|
||||||
фильтр на входе дешевле любой чистки. Заводим только то, что **не делаем
|
пять файлов, а переоценка потом разгребает то, чего не надо было заводить.
|
||||||
сейчас** и о потере чего пожалеем.
|
Дедупликация и фильтр на входе дешевле любой чистки: заводим только то, что
|
||||||
2. **Файл — источник истины, индексы производны.** Разошлись — неправы индексы.
|
**не делаем сейчас** и о потере чего пожалеем.
|
||||||
|
|
||||||
|
**На стройке правило не применяется**, и это не послабление. Список стройки
|
||||||
|
пишется вперёд целиком — он и есть замысел, — а фильтр «не заводи то, чего не
|
||||||
|
делаешь сейчас» запретил бы написать план дальше первого шага. Дедуп остаётся
|
||||||
|
в обеих стадиях: две записи об одном плохи всегда.
|
||||||
|
2. **Файл — источник истины, индекс производен.** Разошлись — неправ индекс.
|
||||||
Согласованность механизируема и проверяется командой, а не вниманием: всё,
|
Согласованность механизируема и проверяется командой, а не вниманием: всё,
|
||||||
что ловит `tasks.py check`, не должно попадать ни в чек-лист, ни в промпт.
|
что ловит `tasks.py check`, не должно попадать ни в чек-лист, ни в промпт.
|
||||||
Поэтому **«зачем» живёт в мете файла**, а строка индекса его лишь
|
Поэтому **«зачем» живёт в мете файла**, а строка индекса его лишь
|
||||||
повторяет: пока поле лежало только в индексе, восстановление пропавшей
|
повторяет: пока поле лежало только в индексе, восстановление пропавшей
|
||||||
строки теряло его молча и навсегда. Единственное исключение намеренное: **в каком
|
строки теряло его молча и навсегда. Единственное исключение намеренное:
|
||||||
индексе лежит запись, знают индексы** — поля-состояния в файле нет. И
|
**порядок строк в беклоге** — он свойство списка, а не задачи, и в файле ему
|
||||||
**порядок строк в беклоге**: приоритет это свойство очереди, а не задачи, и в
|
места нет (правило 4).
|
||||||
файле ему места нет (правило 4).
|
|
||||||
3. **Причина переживает запись.** Выкинутая без причины задача вернётся через
|
3. **Причина переживает запись.** Выкинутая без причины задача вернётся через
|
||||||
квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не
|
квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не
|
||||||
оставляет ничего, поэтому у неё есть `REJECTED.md`.
|
оставляет ничего, поэтому у неё есть `REJECTED.md`.
|
||||||
4. **Приоритет — это порядок строк, а цель есть не у всякой задачи.** Очередь
|
4. **Порядок строк — единственное, чего в файле нет.** Он значим в обеих
|
||||||
внутри секции беклога значима: **первая строка — то, что делают следующим**.
|
стадиях, и назначает его человек: на стройке — раскладывая шаги по
|
||||||
Приоритет назначает человек на груминге, машина его не выводит и не угадывает.
|
зависимости, на доработке — на груминге. Машина порядок не выводит и не
|
||||||
|
угадывает; всё, что она делает сама, — ставит машинную позицию в **конец**
|
||||||
|
секции и говорит об этом вслух.
|
||||||
|
|
||||||
Прежде здесь стояло «порядка нет, есть цель», и обосновано это было тем, что
|
**Дом порядка — индекс, а не файл.** Положи его в файл числом, и два соседних
|
||||||
на «что делать дальше» отвечает **набор спринта**. Набора больше нет, а
|
файла смогли бы утверждать одно и то же место, а строка индекса —
|
||||||
вопрос остался — и без порядка отвечать на него стало нечем.
|
противоречить обоим.
|
||||||
|
|
||||||
**Дом приоритета — индекс, а не файл.** Это то же исключение из правила 2,
|
|
||||||
что и «в каком индексе лежит запись»: приоритет — свойство очереди. Положи он
|
|
||||||
в файл числом, и два соседних файла смогли бы утверждать одно и то же место,
|
|
||||||
а строка индекса — противоречить обоим.
|
|
||||||
|
|
||||||
Цель обязательна там, где она и есть содержание работы, — у **новой
|
|
||||||
возможности** (`feature`). Починка, техдолг и разведка служат
|
|
||||||
работоспособности, а не направлению, и живут без цели законно. Придуманная им
|
|
||||||
цель — то же враньё, от которого спасает тип. **Цель и приоритет —
|
|
||||||
независимые оси:** очередь может идти поперёк целей, и это законно.
|
|
||||||
|
|
||||||
Одно место в очереди назначено **не человеком, а типом**: **сырьё**
|
Одно место в очереди назначено **не человеком, а типом**: **сырьё**
|
||||||
(`research` без раздела «Вопрос») стоит в конце своей категории. Его не берут,
|
(`research` без раздела «Вопрос») стоит в конце своей секции. Его не берут,
|
||||||
и между берущимся оно каждый раз требует открыть файл, чтобы это понять. Раз
|
и между берущимся оно каждый раз требует открыть файл, чтобы это понять. Раз
|
||||||
это выводится, проверяет и чинит это машина.
|
это выводится, проверяет и чинит это машина.
|
||||||
5. **Тип решает, что с записью можно делать.** Тип — единственная ось и первое
|
5. **Тип решает, что с задачей можно делать.** Тип — вторая ось и первое
|
||||||
поле меты: от него зависит, какие разделы обязательны в теле, нужна ли цель,
|
поле меты: от него зависит, какие разделы обязательны в теле и берётся ли
|
||||||
берётся ли запись в работу и в каком индексе живёт её строка. Словарь закрыт;
|
запись в работу. Словарь закрыт; ни один тип не подошёл — значит, в записи их
|
||||||
ни один тип не подошёл — значит, в записи их два, и её надо разделить.
|
два, и её надо разделить.
|
||||||
|
|
||||||
## Раскладка
|
## Раскладка
|
||||||
|
|
||||||
Каталог задач — **`tasks/` в корне репозитория, жёстко.** Он принадлежит этому
|
Каталог задач — **`tasks/` в корне репозитория, жёстко.** Он принадлежит этому
|
||||||
плагину, а не канону документов: `docs/` ведёт другой плагин (`av-dev-docs`), и
|
скиллу, а не канону документов: учёт работ ведут и в проекте, который к канону
|
||||||
проект, поставивший учёт работ без него, каталога `docs/` не имеет вовсе. Внутри
|
не приведён, и каталога `docs/` там нет вовсе. Внутри
|
||||||
`docs/` задачи лежали до версии канона 11; непереехавший проект скрипт
|
`docs/` задачи лежали до версии канона 11; непереехавший проект скрипт
|
||||||
по-прежнему находит, но новый заводит только в корне.
|
по-прежнему находит, но новый заводит только в корне.
|
||||||
|
|
||||||
```
|
```
|
||||||
tasks/
|
tasks/
|
||||||
items/ задачи и цели файлами, <slug>.md, слаги английские
|
items/ задачи файлами, <slug>.md, слаги английские
|
||||||
ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет
|
BACKLOG.md что можно взять. Порядок строк в секции значим,
|
||||||
BACKLOG.md что можно взять — только задачи, целей здесь нет.
|
и значит он разное на разных стадиях
|
||||||
Порядок строк в секции значим: это очередь
|
|
||||||
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
|
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
|
||||||
```
|
```
|
||||||
|
|
||||||
Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `ROADMAP.md` — то,
|
**Индекс один.** `REJECTED.md` индексом не считается: он не говорит, где запись
|
||||||
подо что берут.** Цель в работу взять нельзя — берут её задачи, — поэтому в
|
числится, — это кладбище ушедшего.
|
||||||
списке берущихся ей не место.
|
|
||||||
|
|
||||||
**Четыре секции роадмапа, и последняя отвечает на половину вопроса:**
|
**Секции беклога называет проект**, и `check` проверяет у них ровно две вещи:
|
||||||
|
что секция есть хоть одна и что на стройке она **одна**. Смысла секции не несут
|
||||||
|
— это полки домена (`Ядро`, `Инфра`), — и подгонять их имена под свой вкус
|
||||||
|
скрипт права не имеет. Поле меты, называющее полку, зовётся **Категория**.
|
||||||
|
|
||||||
| Секция | Англ. | Что в ней |
|
**Заголовок секции отбит пустой строкой с обеих сторон и написан с прописной**;
|
||||||
| --- | --- | --- |
|
отбивку правит `check --fix`. Написание секции в мете файлов он тоже правит: имя
|
||||||
| `Запланировано` | `Planned` | очередь значима и обосновывается прозой рядом |
|
секции принадлежит заголовку индекса, файл на неё только ссылается.
|
||||||
| `Направления` | `Directions` | очереди нет, тянутся долго |
|
|
||||||
| `Сопровождение` | `Operations` | чем держат проект: инструмент, процесс, эксплуатация — не возможности приложения, и потому отдельно |
|
|
||||||
| `Готово` | `Done` | достигнутые цели — строкой с датой, **без ссылки на файл**: файл удалён, поведение живёт в спеках |
|
|
||||||
|
|
||||||
**Порядок тоже канонический, и `Готово` стоит последним не из скромности.**
|
|
||||||
Достигнутое **копится**: через год этой секции больше, чем всех остальных
|
|
||||||
вместе. Стоя первой, она отодвигает за экран ровно то, ради чего роадмап
|
|
||||||
открывают чаще всего, — что делается сейчас и что дальше. Порядок проверяет
|
|
||||||
`check`, переставляет `check --fix`.
|
|
||||||
|
|
||||||
**Секции роадмапа канонические, категории беклога — нет**, и разница не в любви к
|
|
||||||
единообразию. У каждой секции роадмапа свой смысл, в достигнутое пишет сам `close`, и
|
|
||||||
роадмап, названный по-своему, читался бы только своим автором. Категории беклога
|
|
||||||
(`Ядро`, `Инфра`) смысла не несут — это полки домена, и остаются делом проекта.
|
|
||||||
Отсюда и разные имена поля меты: у цели **Секция** (часть роадмапа — состояние
|
|
||||||
очереди), у задачи **Категория** (полка домена, на которой она лежит).
|
|
||||||
|
|
||||||
Отсюда четыре правила, которые проверяет `tasks.py check`: **состав закреплён**
|
|
||||||
(чужая секция — ошибка, а не вольность), **все четыре обязаны быть** (нет
|
|
||||||
секции — нет ответа на её часть вопроса), **язык один на весь индекс**, **порядок
|
|
||||||
канонический**. `--roadmap-sections` у `init` нет: выбирать нечего.
|
|
||||||
|
|
||||||
**Заголовок секции отбит пустой строкой с обеих сторон и написан с прописной.**
|
|
||||||
Во всех индексах одинаково, включая категории беклога, которые проект называет сам.
|
|
||||||
Написание канонических секций правит `check --fix` (заодно и ссылку на секцию в
|
|
||||||
мете файлов: имя секции принадлежит заголовку индекса, файл на неё только
|
|
||||||
ссылается); отбивку и порядок он правит везде.
|
|
||||||
|
|
||||||
Оговорка про `Сопровождение`: слово `окружение` сюда не годится — в
|
|
||||||
`architecture.md` оно уже значит боевое окружение приложения, и одно слово в двух
|
|
||||||
смыслах развело бы документы канона. А `Разработка`, стоявшая тут раньше,
|
|
||||||
называла слишком много: роадмап **весь** про разработку, и секция с таким именем
|
|
||||||
не отличалась от остальных ничем.
|
|
||||||
|
|
||||||
**Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (работа не может
|
**Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (работа не может
|
||||||
продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и
|
продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и
|
||||||
@@ -138,53 +102,31 @@ tasks/
|
|||||||
который переезжает с такой секцией, её надо удалить** — это единственное место,
|
который переезжает с такой секцией, её надо удалить** — это единственное место,
|
||||||
где это сказано.
|
где это сказано.
|
||||||
|
|
||||||
**Запись живёт в одном индексе за раз.** Индексов два, и выбирает между ними
|
**Порядок строк — единственное, чего в файле нет** (правило 4). Отсюда следствие
|
||||||
тип: цель в роадмапе, задача в беклоге. Сменился тип — строка переезжает
|
для всякой машинной правки индекса: восстановленная или перенесённая строка
|
||||||
(`edit --type`). Файл в `items/` при этом **не двигается**: он и есть запись,
|
встаёт **в конец своей секции**, и скрипт об этом говорит. Молчаливая вставка
|
||||||
индексы лишь показывают, где она числится и в каком порядке стоит.
|
выдала бы машинную позицию за решение человека — а решение это его.
|
||||||
|
|
||||||
**Порядок строк в беклоге — приоритет**, и он единственное, чего в файле нет
|
|
||||||
(правило 4). Отсюда следствие для всякой машинной правки индекса:
|
|
||||||
восстановленная или перенесённая строка встаёт **в конец своей секции**, и
|
|
||||||
скрипт об этом говорит. Молчаливая вставка выдала бы машинную позицию за
|
|
||||||
решение человека — а решение это его.
|
|
||||||
|
|
||||||
**У сделанной задачи записи не остаётся** — файл и строка удаляются (`close
|
**У сделанной задачи записи не остаётся** — файл и строка удаляются (`close
|
||||||
--implemented`). Ей хватает коммита и документации проекта; вторая запись была
|
--implemented`). Ей хватает коммита и документации проекта; вторая запись была
|
||||||
бы вторым домом для того же факта. Вопрос «что было сделано и когда» отвечается
|
бы вторым домом для того же факта. Вопрос «что было сделано и когда» отвечается
|
||||||
даром: индексы лежат под git, а закрытие коммитится отдельным коммитом учёта —
|
даром: индекс лежит под git, а закрытие коммитится отдельным коммитом учёта —
|
||||||
`git log -p tasks/BACKLOG.md` отдаёт историю без отдельного журнала.
|
`git log -p tasks/BACKLOG.md` отдаёт историю без отдельного журнала.
|
||||||
|
|
||||||
**У достигнутой цели запись остаётся, и это единственное исключение.** Файл
|
|
||||||
удаляется так же, а строка переезжает в секцию `Готово` с датой. Причина в том,
|
|
||||||
что цель — не работа, а **возможность**: «что приложение умеет» это половина
|
|
||||||
вопроса, ради которого роадмап и открывают, и стирать её вместе с файлом значит
|
|
||||||
оставить инструмент, отвечающий только «что осталось». Вторым домом это не
|
|
||||||
становится: поведение живёт в `openspec/specs/`, а роадмап отвечает **когда и в
|
|
||||||
каком порядке оно появилось** — другой вопрос. Ссылки на файл в строке нет
|
|
||||||
намеренно: файл удалён, а битая ссылка — законная ошибка `check`.
|
|
||||||
|
|
||||||
Куда запись может переехать и какой командой — весь набор переходов:
|
Куда запись может переехать и какой командой — весь набор переходов:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
stateDiagram-v2
|
stateDiagram-v2
|
||||||
state "BACKLOG.md — что берут" as B
|
state "BACKLOG.md — что берут" as B
|
||||||
state "ROADMAP.md — подо что берут" as P
|
|
||||||
state "REJECTED.md — ушла без реализации" as R
|
state "REJECTED.md — ушла без реализации" as R
|
||||||
state "записи нет — реализована" as D
|
state "записи нет — реализована" as D
|
||||||
state "ROADMAP.md, «умеет» — цель достигнута" as A
|
|
||||||
|
|
||||||
[*] --> B: add --type feature|fix|chore|research
|
[*] --> B: add --type feature|fix|chore|research
|
||||||
[*] --> P: add --type goal
|
B --> B: move --after | --first | --section
|
||||||
B --> P: edit --type goal --section
|
|
||||||
P --> B: edit --type feature|fix|chore|research --section
|
|
||||||
B --> D: close --implemented
|
B --> D: close --implemented
|
||||||
P --> A: close --implemented
|
|
||||||
B --> R: close --reason
|
B --> R: close --reason
|
||||||
P --> R: close --reason
|
|
||||||
D --> B: reopen --reason
|
D --> B: reopen --reason
|
||||||
R --> B: reopen --reason
|
R --> B: reopen --reason
|
||||||
A --> P: reopen --reason
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Состояния здесь — **где числится строка**, а не где лежит файл: файл
|
Состояния здесь — **где числится строка**, а не где лежит файл: файл
|
||||||
@@ -195,86 +137,92 @@ stateDiagram-v2
|
|||||||
Схема — **сводка**: условия и оговорки живут в тексте разделов, и при
|
Схема — **сводка**: условия и оговорки живут в тексте разделов, и при
|
||||||
расхождении прав текст.
|
расхождении прав текст.
|
||||||
|
|
||||||
## Цели
|
## Две стадии
|
||||||
|
|
||||||
**Цель — возможность приложения.** Такой же файл в `items/`, тип `goal` (🎯),
|
**Стадия проекта — ось, и решает она, что значит порядок строк беклога.**
|
||||||
перечисленный в `ROADMAP.md`. Формулируется ответом на вопрос **«что приложение
|
Значения два, дом — ключ `[tasks] stage` в `.av-dev.toml`.
|
||||||
будет уметь»**, а не названием области работ: не «Работа с чтением», а «Чтение
|
|
||||||
данных клиентами»; не «Рефакторинг слияния», а «Исход слияния не зависит от
|
|
||||||
порядка доставки».
|
|
||||||
|
|
||||||
**Свойство поведения — тоже возможность.** «Наблюдаемость» это «приложение
|
| | `build` — стройка | `support` — доработка |
|
||||||
сообщает о своём состоянии»; «прочность слияния» это «исход не зависит от
|
| --- | --- | --- |
|
||||||
порядка». Такие цели законны и переформулировки в функцию не требуют — требуют
|
| Порядок строк | зависимость: раньше **нельзя** | важность: раньше **лучше** |
|
||||||
только, чтобы формулировка отвечала на «что приложение делает», а не на «какую
|
| Секции | ровно одна: список от базы к деталям | полки домена, сколько нужно |
|
||||||
часть кода мы трогаем».
|
| Заведение | список пишется вперёд целиком | по одной, по мере появления |
|
||||||
|
| Пустой беклог | план исчерпан, стройка окончена | нормальное состояние |
|
||||||
|
| Груминг | не применяется; замысел сменился — план пересматривается целиком | основная гигиена, порциями по 5–8 |
|
||||||
|
| Залежалость | не считается: шаг ждёт своей очереди законно | считается, `list --stale` |
|
||||||
|
|
||||||
**Целью не становится работа, которой держат проект.** Состав перечислен
|
**Стадия называется явно, и молчание ответом не считается.** Без неё порядок
|
||||||
[в словаре сопровождения](references/operations.md);
|
строк нечем прочитать: переставить строку значит на стройке сломать план, а на
|
||||||
на вопрос «что приложение будет уметь» ничто из него не отвечает. Им отведена отдельная секция роадмапа,
|
доработке — принять решение о важности, и это разные действия. `init --stage`
|
||||||
чтобы они были видны в том же экране и при этом не читались как возможности
|
обязателен, `check` без ключа отказывает, `check --fix` его не подставляет:
|
||||||
продукта.
|
какая стадия у проекта, знает человек, а подставленное умолчание соврало бы ровно
|
||||||
|
там, где по нему принимают решение.
|
||||||
|
|
||||||
**Граница проходит по тому, кто наблюдает, а не по теме.** «Приложение сообщает
|
**Секций на стройке одна, и это не педантизм.** Порядок там — зависимость, и
|
||||||
о своём состоянии» — возможность: наблюдает пользователь сервиса, и цели место
|
разложенный по полкам список перестаёт быть планом: два шага из разных секций
|
||||||
среди прочих. «Дежурный видит состояние на одном экране» — сопровождение:
|
уже не сравнить. На доработке полки законны — правки независимы, и очередь
|
||||||
наблюдаем мы. Одна и та же наблюдаемость попадает в разные секции, и это верно —
|
внутри полки самостоятельна.
|
||||||
секции отвечают на разные вопросы.
|
|
||||||
|
|
||||||
**Сопровождение и эксплуатация — целое и часть**, а не синонимы, и та же тема
|
**Переход — событие, а не настройка.** `tasks.py stage support` переносит остаток
|
||||||
живёт ещё в двух местах: разделе «Эксплуатация» в `architecture.md` и теме ревью
|
беклога в первую новую секцию, правит «Категорию» в файлах и говорит, что порядок
|
||||||
`operations`. Словарь у всех трёх общий, дом у него один — `shared/operations.md`
|
с этого момента значит другое. Датой ему служит коммит: отдельного журнала ради
|
||||||
в репозитории плагинов, — а здесь лежит дословная копия:
|
одной строки не заводится. Признак созревания наблюдаемый — беклог стройки
|
||||||
[references/operations.md](references/operations.md). Пересказывать его своими
|
исчерпан, и `check` об этом напоминает; **запретить переход раньше скрипт не
|
||||||
словами нельзя: три перечня «чем держат проект» уже разъезжались на «метриках и
|
берётся**: «приложение построено» решает человек, а не счётчик строк.
|
||||||
логах» против «мониторинга».
|
|
||||||
|
|
||||||
Секция выбирается так: очередь значима и обоснована прозой — `Запланировано`;
|
Обратный переход (`stage build`) разрешён и устроен так же. Он редок — проект
|
||||||
тянется долго и очереди не имеет — `Направления`; не про приложение, а про то,
|
уходит на стройку заново разве что при переделке замысла целиком, — но
|
||||||
чем его держат, — `Сопровождение`; в `Готово` кладёт сам `close`.
|
запрещать его было бы запретом на то, что иногда и правда случается.
|
||||||
|
|
||||||
- **Список задач цели выводится, а не хранится.** В теле цели — зачем она и что
|
## Чего у задач больше нет
|
||||||
считается её завершением; перечня задач там нет. Он был бы третьим индексом и
|
|
||||||
поехал бы на первой же закрытой задаче, а `check` про него не знает. Связь
|
**Тип `goal` и `ROADMAP.md` упразднены.** Цель была зонтиком над параллельными
|
||||||
однонаправленна: задача несёт тег `goal:<слаг>`, перечень даёт
|
направлениями: она нужна там, где список работ нельзя выстроить в один порядок,
|
||||||
`tasks.py list --goal <слаг>`.
|
и очередь идёт поперёк направлений. У проекта, который ведёт один человек, такого
|
||||||
- **Статус цели выводится.** Цель достигнута, когда у неё не осталось открытых
|
не бывает — на стройке список линеен по зависимости, на доработке правки
|
||||||
задач; `[x]`/`[~]` руками не ведутся, а `close` цели с живыми задачами
|
независимы, — и зонтик не стоял ни над чем.
|
||||||
скрипт запретит. Достижение — `close <цель> --implemented`: файл удаляется,
|
|
||||||
строка с датой переезжает в `Готово`. Ошиблись — `reopen` вернёт файл и
|
Роадмап при этом отвечал на свой вопрос наполовину: «чего ещё не умеет» — это
|
||||||
**снимет строку достигнутого**, чтобы роадмап не утверждал того, чего нет. Единственная оговорка: цель без задач неотличима — «ещё не
|
«что осталось в беклоге», то есть пересказ второго индекса. Вторая половина, «что
|
||||||
разобрана» или «всё закрыто». Различает **тег `decomposed`** в мете
|
уже умеет», живёт в двух домах и без него: нормативное поведение — в
|
||||||
цели: он ставится, когда цель разложена на задачи. Тег, а не строка в теле —
|
`openspec/specs/`, а когда и в каком порядке оно появилось — в `git log` индекса
|
||||||
потому что проверяется механически: `check` **напоминает** о нём у пустой цели
|
и коммитах задач.
|
||||||
(замечанием, не ошибкой — неразобранная цель это законное состояние), а `check
|
|
||||||
--fix` сам проставляет его цели, у которой задачи есть.
|
Вместе с целью ушли: секция `Готово` (её ответ дают спеки и история), теги
|
||||||
- **Тип `[epic]` упразднён.** Он был зонтиком между целью и задачами — «задача,
|
`goal:<слаг>` и `decomposed`, поле меты `Секция` (осталась `Категория`), раздел
|
||||||
которая не мерджится целиком». Зонтик теперь цель, а слишком крупный шаг просто
|
`Завершение`, ключи `[tasks] roadmap` и `[tasks] completion_heading`, флаги
|
||||||
дробится на шаги помельче под той же целью, и промежуточному типу места не
|
`add --goal`, `list --goal`, `list --index`, `edit --goal`, `edit --section` и
|
||||||
осталось. Замер подтвердил: ноль употреблений на 97 записей двух живых
|
`init --roadmap`. Встретились в проекте —
|
||||||
проектов. Встретился в чужом беклоге — это цель либо набор задач, и `check`
|
`check` назовёт их поимённо, `check --fix` снимет теги, а запись типа `goal`
|
||||||
назовёт его неизвестным типом.
|
оставит человеку: во что она превращается — в задачу или в ничто, — машина не
|
||||||
|
решает.
|
||||||
|
|
||||||
|
**Тип `[epic]` упразднён раньше и не вернулся.** Слишком крупный шаг дробится на
|
||||||
|
шаги помельче, стоящие в списке подряд.
|
||||||
|
|
||||||
## Тип записи
|
## Тип записи
|
||||||
|
|
||||||
**Тип — единственная ось, и он решает, что с записью можно делать.** Дом типа —
|
**Тип решает, что с задачей можно делать.** Вторая ось скилла — первая стадия.
|
||||||
|
Перечень осей всего процесса и того, чего каждая **не** решает, —
|
||||||
|
[shared/axes.md](../../shared/axes.md). Дом типа —
|
||||||
**поле меты `Тип` первой строкой**; эмодзи в заголовке H1 от него производна, её
|
**поле меты `Тип` первой строкой**; эмодзи в заголовке H1 от него производна, её
|
||||||
ставит `add` и чинит `check --fix`.
|
ставит `add` и чинит `check --fix`.
|
||||||
|
|
||||||
| Тип | Обязательные разделы | Цель | В работу | Устав |
|
| Тип | Обязательные разделы | Устав |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 🎯 `goal` | `Завершение` | — | нет | [task-goal.md](references/task-goal.md) |
|
| ✨ `feature` | `Затрагивает`, `Критерии приёмки` | [task-feature.md](references/task-feature.md) |
|
||||||
| ✨ `feature` | `Затрагивает`, `Критерии приёмки` | **обязательна** | да | [task-feature.md](references/task-feature.md) |
|
| 🐞 `fix` | `Воспроизведение`, `Затрагивает`, `Критерии приёмки` | [task-fix.md](references/task-fix.md) |
|
||||||
| 🐞 `fix` | `Воспроизведение`, `Затрагивает`, `Критерии приёмки` | необязательна | да | [task-fix.md](references/task-fix.md) |
|
| 🧹 `chore` | `Затрагивает`, `Критерии приёмки` | [task-chore.md](references/task-chore.md) |
|
||||||
| 🧹 `chore` | `Затрагивает`, `Критерии приёмки` | нет | да | [task-chore.md](references/task-chore.md) |
|
| 🔬 `research` | `Вопрос`, `Куда ляжет ответ` | [task-research.md](references/task-research.md) |
|
||||||
| 🔬 `research` | `Вопрос`, `Куда ляжет ответ` | нет | да | [task-research.md](references/task-research.md) |
|
|
||||||
|
Берутся в работу все четыре: записи, которую нельзя взять, больше не существует.
|
||||||
|
|
||||||
Сверх обязательных у любой задачи допустимы `Рамки` и `Вопросы`. Раздел не из
|
Сверх обязательных у любой задачи допустимы `Рамки` и `Вопросы`. Раздел не из
|
||||||
схемы своего типа — **замечание, а не ошибка**: свой раздел законная вольность
|
схемы своего типа — **замечание, а не ошибка**: свой раздел законная вольность
|
||||||
проекта, но `Воспроизведение` у `chore` почти всегда значит, что тип проставлен
|
проекта, но `Воспроизведение` у `chore` почти всегда значит, что тип проставлен
|
||||||
не тот, и сказать об этом стоит, не запрещая.
|
не тот, и сказать об этом стоит, не запрещая.
|
||||||
|
|
||||||
**Осей было две, и ортогональность у них была фальшивой.** Тип записи
|
**Прежних осей было две, и ортогональность у них была фальшивой.** Тип записи
|
||||||
(`goal`/`idea`/`task`) и род работы (`kind:<род>` тегом) давали двенадцать клеток
|
(`goal`/`idea`/`task`) и род работы (`kind:<род>` тегом) давали двенадцать клеток
|
||||||
произведения, из которых законны были шесть: у цели род запрещён, у задачи
|
произведения, из которых законны были шесть: у цели род запрещён, у задачи
|
||||||
обязателен, у идеи пуст. Плюс алгоритм работы крепится не к `task`, а к `fix` и
|
обязателен, у идеи пуст. Плюс алгоритм работы крепится не к `task`, а к `fix` и
|
||||||
@@ -296,22 +244,32 @@ stateDiagram-v2
|
|||||||
**напоминает** — беклог, заведённый до появления типа, законен, и переоформлять
|
**напоминает** — беклог, заведённый до появления типа, законен, и переоформлять
|
||||||
его «заодно» здесь не просят.
|
его «заодно» здесь не просят.
|
||||||
|
|
||||||
**Тип не выбирает метку ревью и вообще ничего не предписывает конвейеру.**
|
**Тип не выбирает состав ревью и глубину проверки — и не выбирает их больше
|
||||||
Профиль выбирается по факту изменения, а не по типу задачи: `chore` бывает
|
никто.** Состав прогона постоянный: он один и тот же на всякой задаче
|
||||||
миграцией схемы, `fix` — правкой публичного контракта. Правило «предписание
|
(`av-dev:code-review`, «Состав прогона»). Прежде состав считала метка `small` ·
|
||||||
процесса в теле задачи снимается» типом не отменяется, а подтверждается: он
|
`medium` · `large`, и тогда эта строка отвечала на живой вопрос «не задаёт ли её
|
||||||
описывает работу, а не то, как её проверять.
|
тип»; метки нет, и вопрос снят вместе с ней. Правило «предписание процесса в теле
|
||||||
|
задачи снимается» типом не отменяется, а подтверждается: он описывает работу, а
|
||||||
|
не то, как её проверять. **Стадия проекта состава тоже не выбирает**: изменение
|
||||||
|
на стройке ничем не проще того же изменения на доработке.
|
||||||
|
|
||||||
|
**Одно исполнителю тип всё же говорит — каким сценарием работу вести, и то не
|
||||||
|
один.** Скилл `av-dev:code-resolve` выбирает сценарий связкой из двух
|
||||||
|
признаков: тип **предлагает** (`chore` — обслуживание, `research` — разведка),
|
||||||
|
а подтверждает его предмет работы — есть ли что менять в спеках. Признаки
|
||||||
|
разошлись — работа останавливается, и тип меняется здесь, командой `edit --type`,
|
||||||
|
а не переклеивается исполнителем по ходу. Состава ревью это по-прежнему не
|
||||||
|
задаёт: он постоянный, а на прогоне без change его называет сам сценарий.
|
||||||
|
|
||||||
## Как написана задача
|
## Как написана задача
|
||||||
|
|
||||||
Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы
|
Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы
|
||||||
задачу можно было **оценить, не открывая код**.
|
задачу можно было **оценить, не открывая код**.
|
||||||
|
|
||||||
**Заголовок отвечает на вопрос своего типа.** Вопросов три, поэтому и форм три:
|
**Заголовок отвечает на вопрос своего типа.** Вопросов два, поэтому и форм две:
|
||||||
|
|
||||||
| Тип | Отвечает на | Пример |
|
| Тип | Отвечает на | Пример |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 🎯 `goal` | что приложение будет уметь | Соперником может быть компьютер |
|
|
||||||
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | Печатать поле одним куском кода |
|
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | Печатать поле одним куском кода |
|
||||||
| 🔬 `research` | о чём разведка | Подсказка следующего хода |
|
| 🔬 `research` | о чём разведка | Подсказка следующего хода |
|
||||||
|
|
||||||
@@ -324,10 +282,6 @@ stateDiagram-v2
|
|||||||
исход знание, а что делать — ещё неизвестно, и заголовок-действие обещал бы
|
исход знание, а что делать — ещё неизвестно, и заголовок-действие обещал бы
|
||||||
решённость, которой нет.
|
решённость, которой нет.
|
||||||
|
|
||||||
Из этого же правила растёт разница индексов: роадмап — список возможностей,
|
|
||||||
беклог — список работ, и если заголовки перепутать формами, каждый из них
|
|
||||||
начинает читаться как другой.
|
|
||||||
|
|
||||||
`check` считает заголовки не в форме действия и печатает **число** в блоке
|
`check` считает заголовки не в форме действия и печатает **число** в блоке
|
||||||
здоровья, не замечанием на файл: проверка эвристическая (первое слово на
|
здоровья, не замечанием на файл: проверка эвристическая (первое слово на
|
||||||
`-ть`/`-ти`/`-чь`), а беклог, заведённый до правила, не переоформляют «заодно».
|
`-ть`/`-ти`/`-чь`), а беклог, заведённый до правила, не переоформляют «заодно».
|
||||||
@@ -346,11 +300,10 @@ stateDiagram-v2
|
|||||||
**Предметно, но без усложнения.** Текст задачи читает человек, который решает,
|
**Предметно, но без усложнения.** Текст задачи читает человек, который решает,
|
||||||
брать её или нет, и делает это по строке индекса и одному экрану тела.
|
брать её или нет, и делает это по строке индекса и одному экрану тела.
|
||||||
|
|
||||||
Язык — общий для всех проектных текстов, и дом у него один,
|
Язык — общий для всех проектных текстов, и дом у него один:
|
||||||
`shared/language.md` в репозитории плагинов; здесь лежит дословная копия:
|
[shared/language.md](../../shared/language.md) — информационный стиль,
|
||||||
[references/language.md](references/language.md) (информационный стиль,
|
|
||||||
применённый к задачам и документам канона; там же таблицы англицизмов и жаргона
|
применённый к задачам и документам канона; там же таблицы англицизмов и жаргона
|
||||||
и то, что из стиля отброшено намеренно). Задаче он даёт четыре требования,
|
и то, что из стиля отброшено намеренно. Задаче он даёт четыре требования,
|
||||||
которые нарушаются чаще прочих:
|
которые нарушаются чаще прочих:
|
||||||
|
|
||||||
- **глагол вместо отглагольного существительного**: «обработчик не проверяет
|
- **глагол вместо отглагольного существительного**: «обработчик не проверяет
|
||||||
@@ -373,63 +326,75 @@ 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` стоит в примерах намеренно: вызов из
|
||||||
подкаталога — обычное дело.
|
подкаталога — обычное дело.
|
||||||
|
|
||||||
```
|
```
|
||||||
python3 $tk check --dir D # согласованность индексов + здоровье
|
python3 $tk check --dir D # согласованность индекса + здоровье
|
||||||
python3 $tk check --dir D --fix # + починить дрейф (тип, эмодзи, место, заголовок, дубли, «зачем», форма меты)
|
python3 $tk check --dir D --fix # + починить дрейф (тип, эмодзи, место, заголовок, дубли, «зачем», форма меты)
|
||||||
python3 $tk list --dir D [--stale] [--section S] [--type T] [--tag a,b] [--goal S] [--raw] [--index …] [--questions]
|
python3 $tk list --dir D [--stale] [--section S] [--type T] [--tag a,b] [--raw] [--questions]
|
||||||
python3 $tk add --dir D --slug S --title T --type goal|feature|fix|chore|research [--section S] [--goal G] [--why «зачем»] [--tag a,b]
|
python3 $tk add --dir D --slug S --title T --type feature|fix|chore|research [--section S] [--why «зачем»] [--tag a,b]
|
||||||
python3 $tk edit S --dir D [--title T] [--why «зачем»] [--type T] [--goal G] [--add-tag a,b] [--rm-tag c]
|
python3 $tk edit S --dir D [--title T] [--why «зачем»] [--type T] [--add-tag a,b] [--rm-tag c]
|
||||||
python3 $tk move S --dir D [--section S] [--reason R] [--after S | --first] # без --section — текущая секция
|
python3 $tk move S --dir D [--section S] [--reason R] [--after S | --first] # без --section — текущая секция
|
||||||
python3 $tk close S --dir D --reason R # в REJECTED.md + удалить (ушла без реализации)
|
python3 $tk close S --dir D --reason R # в REJECTED.md + удалить (ушла без реализации)
|
||||||
python3 $tk close S --dir D --implemented # просто удалить (реализована и закоммичена)
|
python3 $tk close S --dir D --implemented # просто удалить (реализована и закоммичена)
|
||||||
python3 $tk reopen S --dir D --reason R # вернуть закрытую: приёмка не сошлась
|
python3 $tk reopen S --dir D --reason R # вернуть закрытую: приёмка не сошлась
|
||||||
python3 $tk ready S… --dir D # схема типа выполнена — можно брать в работу
|
python3 $tk ready S… --dir D # схема типа выполнена — можно брать в работу
|
||||||
python3 $tk init --dir D [--sections …] [--items …] [--backlog …] …
|
python3 $tk stage --dir D # показать стадию
|
||||||
python3 $tk adopt scan --from … | apply --plan … # разовая адаптация, references/adopt.md
|
python3 $tk stage support --dir D [--sections …] # сменить стадию: секции и смысл порядка
|
||||||
|
python3 $tk init --dir D --stage build|support [--sections …] [--items …] …
|
||||||
|
python3 $tk adopt scan --from … --stage S | apply --plan … # разовая адаптация, references/adopt.md
|
||||||
```
|
```
|
||||||
|
|
||||||
**Коды выхода — единый словарь; на нём ветвятся скиллы, а не на тексте вывода:**
|
**Копия.** Дом словаря — `shared/axes.md` в репозитории плагина: словарь общий
|
||||||
|
для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл.
|
||||||
|
|
||||||
| Код | Что случилось | Что делать |
|
<!-- копия: коды-выхода из av-dev/shared/axes.md -->
|
||||||
| --- | --- | --- |
|
|
||||||
| 0 | сошлось / сделано | дальше по сценарию |
|
|
||||||
| 1 | **только `check`:** найден дрейф индексов и файлов | `check --fix`, остаток разобрать |
|
|
||||||
| 2 | ошибка употребления: аргументы или нарушенное правило | читать сообщение, это отказ по существу |
|
|
||||||
| 3 | окружение: каталог не найден, конфиг битый или мимо диска | чинится путём или `<каталог задач>/.tasks.json`, повтор не поможет |
|
|
||||||
| 4 | внутренний сбой | дефект скрипта, доложить |
|
|
||||||
|
|
||||||
Различать 1 и 3 обязательно: «дрейф в беклоге» — рабочая ситуация, «каталога
|
**Коды выхода — общий словарь всех скриптов `av-dev`. Ветвись на коде, а не на
|
||||||
нет» — нерабочая, и одинаковая реакция на них была бы неверна в обоих случаях.
|
тексте вывода.**
|
||||||
|
|
||||||
Тип — английское ключевое слово `goal` / `feature` / `fix` / `chore` /
|
| Код | Что случилось |
|
||||||
`research` (как и прочие токены команд), у `add` **обязательное**: без него
|
| --- | --- |
|
||||||
неизвестно, какой шаблон тела класть. Текст задачи при этом русский, а эмодзи в
|
| 0 | сошлось |
|
||||||
заголовке ставит скрипт.
|
| 1 | дрейф: рабочая ситуация, чинится |
|
||||||
|
| 2 | ошибка употребления: аргументы или нарушенное правило |
|
||||||
|
| 3 | окружение: не тот каталог, битый конфиг, нет инструмента |
|
||||||
|
| 4 | внутренний сбой — дефект скрипта, доложить |
|
||||||
|
|
||||||
**Мутации правят файл и индексы заодно** — руками строку индекса или мету
|
**Различать 1 и 3 обязательно.** «Дрейф» — рабочая ситуация, и чинится она
|
||||||
не пиши, зови `add`/`edit`/`move`/`close`/`reopen`. Смена заголовка, «зачем», типа,
|
правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет.
|
||||||
цели и **тегов** — это `edit`: он держит H1 (вместе с эмодзи), мету и индекс
|
Одинаковая реакция на них неверна в обоих случаях.
|
||||||
|
|
||||||
|
<!-- /копия: коды-выхода -->
|
||||||
|
|
||||||
|
Здесь это значит: код 1 приходит **только от `check`** — найден дрейф индекса и
|
||||||
|
файлов, чинится `check --fix`, остаток разбирается руками. Код 3 — каталог не
|
||||||
|
найден, конфиг битый или мимо диска: чинится путём или `.av-dev.toml` в корне.
|
||||||
|
|
||||||
|
Тип — английское ключевое слово `feature` / `fix` / `chore` / `research` (как и
|
||||||
|
прочие токены команд), у `add` **обязательное**: без него неизвестно, какой
|
||||||
|
шаблон тела класть. Стадия — такое же слово, `build` / `support`, и у `init` она
|
||||||
|
обязательна по той же причине: без неё неизвестно, что писать в шапке беклога и
|
||||||
|
сколько заводить секций. Текст задачи при этом русский, а эмодзи в заголовке
|
||||||
|
ставит скрипт.
|
||||||
|
|
||||||
|
**Мутации правят файл и индекс заодно** — руками строку индекса или мету
|
||||||
|
не пиши, зови `add`/`edit`/`move`/`close`/`reopen`. Смена заголовка, «зачем», типа
|
||||||
|
и **тегов** — это `edit`: он держит H1 (вместе с эмодзи), мету и индекс
|
||||||
согласованными. Снятие тега — `--rm-tag` (после ответа на вопрос снимается
|
согласованными. Снятие тега — `--rm-tag` (после ответа на вопрос снимается
|
||||||
`question`), смена цели — `--goal`, типа — `--type`; оба заменяют прежнее
|
`question`), смена типа — `--type`; оба заменяют прежнее значение, а не
|
||||||
значение, а не добавляют второе.
|
добавляют второе.
|
||||||
|
|
||||||
**Переезд между индексами — следствие смены типа, а не отдельная команда.**
|
**Секцию меняет только `move`, и он пишет причину**: смена полки без причины и
|
||||||
`edit <slug> --type goal --section <часть роадмапа>` переносит строку из
|
есть тот дрейф, который потом никто не объяснит.
|
||||||
`BACKLOG.md` в `ROADMAP.md` (и обратно — задачным типом плюс
|
|
||||||
`--section <категория беклога>`);
|
|
||||||
`move` двигает только внутри одного индекса и пишет причину. `--section` у
|
|
||||||
`edit` работает **только** при таком переезде — иначе он отсылает к `move`,
|
|
||||||
потому что смена секции без причины и есть тот дрейф, который потом никто не
|
|
||||||
объяснит.
|
|
||||||
|
|
||||||
**`move --after <слаг>` и `move --first` — это и есть расстановка приоритета.**
|
**`move --after <слаг>` и `move --first` — это и есть расстановка порядка.**
|
||||||
Порядок строк в секции значим (правило 4), и двигают его только этой командой:
|
Порядок строк в секции значим (правило 4), и двигают его только этой командой:
|
||||||
руками поправленная строка не оставляет причины, а причина здесь и есть половина
|
руками поправленная строка не оставляет причины, а причина здесь и есть половина
|
||||||
решения.
|
решения. Что именно этот порядок значит, говорит стадия: на стройке `--after`
|
||||||
|
называет зависимость, на доработке — приоритет.
|
||||||
|
|
||||||
Тело задачи скрипт не трогает:
|
Тело задачи скрипт не трогает:
|
||||||
`add` кладёт заголовок, мета-блок и шаблон с подсказками, тело дописываешь
|
`add` кладёт заголовок, мета-блок и шаблон с подсказками, тело дописываешь
|
||||||
@@ -440,18 +405,23 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
правки**, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся
|
правки**, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся
|
||||||
чини `check --fix` — он детерминированно правит то, где истина однозначна (тип в
|
чини `check --fix` — он детерминированно правит то, где истина однозначна (тип в
|
||||||
своё поле, эмодзи заголовка, имя поля места, секция, заголовок, дубли, «зачем» из
|
своё поле, эмодзи заголовка, имя поля места, секция, заголовок, дубли, «зачем» из
|
||||||
индекса в файл, старая форма меты, пометка `decomposed` у цели с задачами, сырьё
|
индекса в файл, старая форма меты, снятые теги упразднённых целей, сырьё
|
||||||
в конец категории), а неоднозначное (задача сразу в двух индексах, нечего
|
в конец секции), а неоднозначное (нечего восстанавливать, **тип, которого
|
||||||
восстанавливать, **тип, которого неоткуда взять**) печатает отдельной пометкой
|
неоткуда взять**, запись типа `goal`) печатает отдельной пометкой
|
||||||
`НЕОДНОЗНАЧНО` — это тебе, и это идёт строкой доклада. **Ссылка на исчезнувший
|
`НЕОДНОЗНАЧНО` — это тебе, и это идёт строкой доклада. **Ссылка на исчезнувший
|
||||||
файл в пометку не попадает:** `--fix` её просто не трогает, и она остаётся
|
файл в пометку не попадает:** `--fix` её просто не трогает, и она остаётся
|
||||||
`ОШИБКА` обычного `check` — то есть видна, но в докладе её надо назвать отдельно.
|
`ОШИБКА` обычного `check` — то есть видна, но в докладе её надо назвать отдельно.
|
||||||
|
|
||||||
`--fix` правит **и файлы** — там, где источник ровно один и выбирать не из чего:
|
`--fix` правит **и файлы** — там, где источник ровно один и выбирать не из чего:
|
||||||
тип переезжает из прежнего дома (тег `kind:`, префикс `[goal]`/`[idea]`) в поле
|
тип переезжает из прежнего дома (тег `kind:`, префикс `[goal]`/`[idea]`) в поле
|
||||||
меты, заголовок получает эмодзи, поле места — имя по типу, «зачем», оставшееся
|
меты, заголовок получает эмодзи, поле места зовётся «Категория», «зачем»,
|
||||||
только в индексе, переезжает в мету, цель с задачами получает `decomposed`.
|
оставшееся только в индексе, переезжает в мету, теги `goal:` и `decomposed`
|
||||||
Каждый случай печатается поимённо.
|
снимаются. Каждый случай печатается поимённо.
|
||||||
|
|
||||||
|
**Ни стадию, ни состав секций `--fix` не трогает.** Стадию он подставить не
|
||||||
|
может — это решение человека; секции стройки не сливает — в каком порядке пойдут
|
||||||
|
строки слитых полок, знает тоже только человек, а порядок здесь и есть
|
||||||
|
содержание.
|
||||||
|
|
||||||
**Тип, который не выводится ниоткуда, `--fix` не угадывает.** `feature` от
|
**Тип, который не выводится ниоткуда, `--fix` не угадывает.** `feature` от
|
||||||
`chore` машина не отличает, и подставленное наугад значение врало бы ровно там,
|
`chore` машина не отличает, и подставленное наугад значение врало бы ровно там,
|
||||||
@@ -462,7 +432,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
работу — там, где по ней принимают решение; `check` поимённо о ней не говорит, а
|
работу — там, где по ней принимают решение; `check` поимённо о ней не говорит, а
|
||||||
считает: строка здоровья **«схема типа не выполнена: N из M»** называет число и
|
считает: строка здоровья **«схема типа не выполнена: N из M»** называет число и
|
||||||
первые слаги, строка **«готово к взятию»** — сколько задач беклога пройдут
|
первые слаги, строка **«готово к взятию»** — сколько задач беклога пройдут
|
||||||
`ready` целиком (схема плюс цель плюс отсутствие открытого вопроса). Это две
|
`ready` целиком (схема плюс отсутствие открытого вопроса). Это две
|
||||||
разные строки, и совпадение их чисел — совпадение. У каждой части своя глубина:
|
разные строки, и совпадение их чисел — совпадение. У каждой части своя глубина:
|
||||||
|
|
||||||
- **тип** — жёстко: назван и из закрытого словаря;
|
- **тип** — жёстко: назван и из закрытого словаря;
|
||||||
@@ -470,7 +440,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
(меньше двух отказ, больше пяти замечание), наличие оракула **эвристикой** по
|
(меньше двух отказ, больше пяти замечание), наличие оракула **эвристикой** по
|
||||||
слову «оракул» в пункте;
|
слову «оракул» в пункте;
|
||||||
- **прочие разделы схемы** (`Затрагивает`, `Воспроизведение`, `Вопрос`,
|
- **прочие разделы схемы** (`Затрагивает`, `Воспроизведение`, `Вопрос`,
|
||||||
`Куда ляжет ответ`, `Завершение`) — только **наличие непустого**. Содержимое
|
`Куда ляжет ответ`) — только **наличие непустого**. Содержимое
|
||||||
машине не видно: границу, которую забыли назвать, она от отсутствующей не
|
машине не видно: границу, которую забыли назвать, она от отсутствующей не
|
||||||
отличает, а шаги, по которым ничего не воспроизводится, — от годных.
|
отличает, а шаги, по которым ничего не воспроизводится, — от годных.
|
||||||
|
|
||||||
@@ -479,53 +449,43 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
разделов своего типа и число критериев, годность оракулов и полнота границ —
|
разделов своего типа и число критериев, годность оракулов и полнота границ —
|
||||||
глазами».
|
глазами».
|
||||||
|
|
||||||
Формат записи, меты, слага, индексов и `REJECTED.md` —
|
Формат записи, меты, слага, индекса и `REJECTED.md` —
|
||||||
[references/task-format.md](references/task-format.md); там же тест «готова к
|
[references/task-format.md](references/task-format.md); там же тест «готова к
|
||||||
взятию». Схема и алгоритм каждого типа — по файлу на тип:
|
взятию». Схема и алгоритм каждого типа — по файлу на тип:
|
||||||
[goal](references/task-goal.md) · [feature](references/task-feature.md) ·
|
[feature](references/task-feature.md) ·
|
||||||
[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), сверяет их
|
журнал версий — [журнал скилла `canon`](../canon/references/changelog.md),
|
||||||
`tasks.py check`: отстало — строка расхождения, ушло вперёд — устарел плагин.
|
сверяет их `tasks.py check`: отстало — строка расхождения, ушло вперёд — устарел
|
||||||
|
плагин. Обратной совместимости нет: есть «приведён» и «не приведён».
|
||||||
|
|
||||||
**Версия своя, а не канона документов.** Плагин ставится в одиночку: проект,
|
**Версия одна на всю раскладку — и на документы, и на задачи.** Своя у каталога
|
||||||
взявший учёт работ без `av-dev-docs`, каталога `docs/` не имеет вовсе, а значит
|
задач была, пока плагинов было три и ставились они порознь: проект мог взять
|
||||||
не имеет и версии канона — сверять было бы не с чем. Обратной совместимости у
|
учёт работ без канона документов, и общее число оказалось бы домом, которого у
|
||||||
формата нет: есть «приведён» и «не приведён».
|
половины проектов нет. Плагин один — довод ушёл, а два числа оставляли бы
|
||||||
|
вопрос, по какому журналу повышать.
|
||||||
|
|
||||||
**`upgrade` — повысить каталог до текущего формата:**
|
**Повышает проект скилл `av-dev: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` и вправе сказать «позови этот скилл», но не двигать версию
|
|
||||||
формата задач: две версии, ходящие по одному журналу, разъедутся на первом же
|
|
||||||
проекте, где стоит один плагин без другого.
|
|
||||||
|
|
||||||
## Сценарии
|
## Сценарии
|
||||||
|
|
||||||
### Завести запись из диалога
|
### Завести запись из диалога
|
||||||
|
|
||||||
1. **Фильтр.** Делаем прямо сейчас — не заводим. Не пожалеем о потере — не
|
0. **Посмотри стадию** — `stage`. От неё зависят шаг 1 и место новой строки: на
|
||||||
заводим. Родилось три кандидата — покажи их и спроси, какие заводить: молча
|
доработке беклог пополняют по одной и с фильтром, на стройке пишут планом.
|
||||||
заведённая пачка и есть тот самый отказ из правила 1.
|
1. **Фильтр — на доработке.** Делаем прямо сейчас — не заводим. Не пожалеем о
|
||||||
|
потере — не заводим. Родилось три кандидата — покажи их и спроси, какие
|
||||||
|
заводить: молча заведённая пачка и есть тот самый отказ из правила 1.
|
||||||
|
**На стройке фильтра нет**: план пишется вперёд целиком, и «этого мы сейчас
|
||||||
|
не делаем» — не довод против шага, а описание всякого шага, кроме первого.
|
||||||
2. **Дедуп.** `list` плюс поиск по слагам, полю «зачем» и телам (`grep -ril`),
|
2. **Дедуп.** `list` плюс поиск по слагам, полю «зачем» и телам (`grep -ril`),
|
||||||
**включая `REJECTED.md`**. Нашлось среди живых — **дописываем в существующий
|
**включая `REJECTED.md`**. Нашлось среди живых — **дописываем в существующий
|
||||||
файл**, а не заводим соседний. Нашлось в `REJECTED.md` — покажи пользователю
|
файл**, а не заводим соседний. Нашлось в `REJECTED.md` — покажи пользователю
|
||||||
@@ -534,7 +494,6 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
переоценки.
|
переоценки.
|
||||||
3. **Тип** — `--type` обязателен, и он же первое содержательное решение:
|
3. **Тип** — `--type` обязателен, и он же первое содержательное решение:
|
||||||
|
|
||||||
- возможность приложения, а не шаг к ней → `goal`;
|
|
||||||
- снаружи появляется то, чего не было → `feature`;
|
- снаружи появляется то, чего не было → `feature`;
|
||||||
- поведение расходится с заявленным и **воспроизводится** → `fix`
|
- поведение расходится с заявленным и **воспроизводится** → `fix`
|
||||||
(не воспроизводится → `research`);
|
(не воспроизводится → `research`);
|
||||||
@@ -543,12 +502,12 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
Не подходит ни один — в записи их два, разбирай. Не проходит тест готовности
|
Не подходит ни один — в записи их два, разбирай. Не проходит тест готовности
|
||||||
(см. task-format) — это **сырьё**: `--type research`, раздел «Вопрос» пока
|
(см. task-format) — это **сырьё**: `--type research`, раздел «Вопрос» пока
|
||||||
пуст, место в конце категории. Не делается одним заходом — это не эпик, а
|
пуст, место в конце секции. Не делается одним заходом — дроби на шаги
|
||||||
несколько задач под одной целью: дроби сразу.
|
помельче и ставь их в списке подряд.
|
||||||
4. **Цель — если тип её требует.** У `feature` должен быть `--goal <слаг>`:
|
4. **Место в списке.** `add` кладёт строку в конец секции всегда. На стройке это
|
||||||
новая возможность и есть содержание цели. Подходящей нет — либо она
|
почти наверняка не то место: порядок там зависимость, и новый шаг чаще всего
|
||||||
заводится (`--type goal`), либо перед тобой не `feature`. У `fix`, `chore` и
|
встаёт в середину — `move <слаг> --after <слаг>`. На доработке конец списка
|
||||||
`research` цели может не быть вовсе, и придумывать её не надо.
|
законен: место в очереди назначает груминг, а не заведение.
|
||||||
5. `add …`, затем допиши тело редактором **по схеме своего типа** — шаблон её
|
5. `add …`, затем допиши тело редактором **по схеме своего типа** — шаблон её
|
||||||
уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем
|
уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем
|
||||||
нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый
|
нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый
|
||||||
@@ -562,18 +521,46 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
пять файлов из одной мысли, а сорок файлов из сорока сырых находок. Защита та
|
пять файлов из одной мысли, а сорок файлов из сорока сырых находок. Защита та
|
||||||
же, что в самом ревью: кластеризация по причине, дедуп против живых и
|
же, что в самом ревью: кластеризация по причине, дедуп против живых и
|
||||||
`REJECTED.md`, находка без свидетельства → сырьё (`research`), а не задача, и карта кластеров
|
`REJECTED.md`, находка без свидетельства → сырьё (`research`), а не задача, и карта кластеров
|
||||||
пользователю до создания файлов. Порядок, отображение серьёзности и привязка к
|
пользователю до создания файлов. **Стадию смотри и здесь**, тем же нулевым
|
||||||
целям — [references/from-review.md](references/from-review.md).
|
шагом: от неё зависит, куда ляжет тяжёлая находка — наверх очереди или после
|
||||||
|
своей зависимости. Порядок и отображение серьёзности —
|
||||||
|
[references/from-review.md](references/from-review.md).
|
||||||
|
|
||||||
### Прийти в репозиторий, где задачи уже как-то ведутся
|
### Прийти в репозиторий, где задачи уже как-то ведутся
|
||||||
|
|
||||||
Разовая операция: вывести каталог задач из старой раскладки беклога, `TODO.md`,
|
Разовая операция: вывести каталог задач из старой раскладки беклога, `TODO.md`,
|
||||||
заметок или списка шагов роадмапа — [references/adopt.md](references/adopt.md).
|
заметок или списка шагов плана — [references/adopt.md](references/adopt.md).
|
||||||
Сюда же относится переименование транслитных слагов в английские: оно делается
|
Сюда же относится переименование транслитных слагов в английские: оно делается
|
||||||
**одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу.
|
**одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу.
|
||||||
|
|
||||||
Если переводить надо не только задачи, а весь `docs/` — это скилл
|
Если переводить надо не только задачи, а весь `docs/` — это скилл
|
||||||
`av-dev-docs:canon`, и он зовёт этот сценарий сам на своём шаге.
|
`av-dev:canon`, и он зовёт этот сценарий сам на своём шаге.
|
||||||
|
|
||||||
|
### Пересмотр плана стройки
|
||||||
|
|
||||||
|
Операция стадии `build`, и на доработке её нет: там переоценка идёт порциями и
|
||||||
|
называется грумингом. **Повод один — сменился замысел**, а не «давно не
|
||||||
|
смотрели»: план стройки протухает не по частям, а целиком, потому что порядок в
|
||||||
|
нём — зависимость, и одна изменившаяся посылка переставляет всё, что ниже.
|
||||||
|
|
||||||
|
Порционный разбор здесь вреден, и это не вкус: вынуть пять шагов из середины
|
||||||
|
списка, порядок которого и есть его содержание, — значит получить план, про
|
||||||
|
который никто уже не скажет, почему он такой.
|
||||||
|
|
||||||
|
1. **Назови, что изменилось в замысле.** Одной фразой, и она уедет причиной в
|
||||||
|
каждое движение. Не находится — значит повода нет, и пересмотр не нужен.
|
||||||
|
2. **Прочитай список целиком**, сверху вниз, и по каждой строке ответь одно из
|
||||||
|
трёх: остаётся как есть, переезжает (`move --after` с причиной), уходит
|
||||||
|
(`close --reason`). Дописанное новое встаёт туда, куда велит зависимость, а
|
||||||
|
не в конец.
|
||||||
|
3. **Покажи человеку весь новый список**, а не отдельные решения: план читается
|
||||||
|
только целиком. `AskUserQuestion` с готовым порядком и доводом на каждое
|
||||||
|
движение.
|
||||||
|
4. `check` и доклад: сколько строк тронуто из скольких, что ушло и почему.
|
||||||
|
|
||||||
|
**Границу с грумингом держи твёрдо.** Если хочется пересмотреть план «потому что
|
||||||
|
накопилось» — это не пересмотр, а признак того, что стройка кончилась: беклог
|
||||||
|
перестал быть планом и стал очередью. Проверь `stage`.
|
||||||
|
|
||||||
### Декомпозиция и штурм сырья
|
### Декомпозиция и штурм сырья
|
||||||
|
|
||||||
@@ -582,9 +569,9 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
**каждая давать видимую пользу**, а у штурма исход «выкинуть» — полноправный.
|
**каждая давать видимую пользу**, а у штурма исход «выкинуть» — полноправный.
|
||||||
|
|
||||||
Там же **шов**: где резать, когда допустимых мест несколько. Коротко — по
|
Там же **шов**: где резать, когда допустимых мест несколько. Коротко — по
|
||||||
границе, которая одна поднимает метку ревью выше остальных; и не резать, когда
|
границе, где **меняется род работы**; и резать пореже, потому что костяк ревью
|
||||||
обе половины остаются в одной метке, потому что несокращаемый костяк проверок
|
разрез удваивает **всегда** — состав прогона постоянный и от размера половин не
|
||||||
платится за каждую задачу отдельно.
|
зависит. Выигрыш даёт не проверка, а то, что половина доводится и мерджится сама.
|
||||||
|
|
||||||
### Вычитка: два прохода, а не один
|
### Вычитка: два прохода, а не один
|
||||||
|
|
||||||
@@ -594,16 +581,15 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
| Проход | Что смотрит | Над чем работает |
|
| Проход | Что смотрит | Над чем работает |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `task-form` | заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса, связь со строкой «Завершения» цели | только `items/`, **открывает файл цели** |
|
| `task-form` | заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса | только `items/` |
|
||||||
| `task-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индексов; документы проекта — только как словарь |
|
| `task-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индекса; документы проекта — только как словарь |
|
||||||
|
|
||||||
Разделены они не по охвату, а **по глубине**. Язык проверяется по словам и
|
Разделены они не по охвату, а **по глубине**. Язык проверяется по словам и
|
||||||
фразам, поштучно; форма записи требует понять, что задача делает, и открыть
|
фразам, поштучно; форма записи требует понять, что задача делает. Слитый проход
|
||||||
цель, на которую она ссылается. Слитый проход одну половину делает дорогой, а
|
одну половину делает дорогой, а вторую — поверхностной.
|
||||||
вторую — поверхностной.
|
|
||||||
|
|
||||||
Модель у обоих одна, `sonnet`, и это не отменяет разреза. Оба судят по
|
Модель у обоих одна, `sonnet`, и это не отменяет разреза. Оба судят по
|
||||||
**записанному правилу** — семь пунктов формы против правил языка, — а их находка
|
**записанному правилу** — шесть пунктов формы против правил языка, — а их находка
|
||||||
приходит готовой формулировкой, которую читает и отклоняет человек, а не молча
|
приходит готовой формулировкой, которую читает и отклоняет человек, а не молча
|
||||||
реализует оркестратор. Ошибка здесь стоит строки чтения, и платить за неё верхней
|
реализует оркестратор. Ошибка здесь стоит строки чтения, и платить за неё верхней
|
||||||
моделью не за что.
|
моделью не за что.
|
||||||
@@ -617,7 +603,8 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
вычитывать до того, как он переписан.
|
вычитывать до того, как он переписан.
|
||||||
|
|
||||||
Зовутся они **пачкой, а не на каждую запись**: после заведения нескольких задач,
|
Зовутся они **пачкой, а не на каждую запись**: после заведения нескольких задач,
|
||||||
после разбора находок ревью и на переоценке. Передаётся список файлов и — если
|
после разбора находок ревью, после того как чужая работа уточнила записи (так
|
||||||
|
делает разведка в `av-dev:code-resolve`), и на переоценке. Передаётся список файлов и — если
|
||||||
есть — паспорт, архитектура и конвенции проекта: по ним отличается неизвестный
|
есть — паспорт, архитектура и конвенции проекта: по ним отличается неизвестный
|
||||||
термин от известного.
|
термин от известного.
|
||||||
|
|
||||||
@@ -648,9 +635,10 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
- **свойство репозитория в рамках** — номер миграции, хеш, версия зависимости:
|
- **свойство репозитория в рамках** — номер миграции, хеш, версия зависимости:
|
||||||
в лежалой задаче протухает молча и становится ложной рамкой. Снимается;
|
в лежалой задаче протухает молча и становится ложной рамкой. Снимается;
|
||||||
снимок берётся при постановке, а не при заведении;
|
снимок берётся при постановке, а не при заведении;
|
||||||
- **предписание процесса в теле** — «делать с такой-то меткой ревью», «взять
|
- **предписание процесса в теле** — «прогнать глубоким ревью», «взять такой-то
|
||||||
такой-то агент»: это второй дом для правила выбора и путь понизить требования
|
агент», «этой задаче хватит короткой проверки»: это второй дом для правила
|
||||||
решением, принятым до проектирования. Снимается;
|
выбора и путь понизить требования решением, принятым до проектирования.
|
||||||
|
Снимается;
|
||||||
- **тип, разошедшийся с задачей** — задача заводилась починкой, а после разбора
|
- **тип, разошедшийся с задачей** — задача заводилась починкой, а после разбора
|
||||||
оказалось, что поведение никогда и не было заявлено: это `feature`, а не `fix`.
|
оказалось, что поведение никогда и не было заявлено: это `feature`, а не `fix`.
|
||||||
Правится `edit <slug> --type …`; тип, оставшийся от прошлой формулировки, врёт
|
Правится `edit <slug> --type …`; тип, оставшийся от прошлой формулировки, врёт
|
||||||
@@ -677,25 +665,25 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
- **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда:
|
- **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда:
|
||||||
раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога
|
раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога
|
||||||
нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект
|
нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект
|
||||||
действительно новый, а перевод чужой раскладки делает `av-dev-docs:canon`.
|
действительно новый, а перевод чужой раскладки делает `av-dev:canon`.
|
||||||
У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и
|
У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и
|
||||||
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
|
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
|
||||||
- **Версия формата и настройки живут в `<каталог задач>/.tasks.json`** — свой
|
- **Версия и настройки живут в `.av-dev.toml` в корне репозитория** — версия
|
||||||
файл у своего плагина: ключ `tasks` с версией формата плюс **имена** файлов и
|
ключом `version`, настройки каталога секцией `[tasks]`: `dir` — где каталог
|
||||||
заголовков, и последние — только если отличаются от умолчания. Неизвестный
|
лежит, `stage` — стадия проекта, плюс **имена** файлов и заголовков, и
|
||||||
ключ — код 3 на любой команде, так что лишнее слово в этом объекте
|
последние только если отличаются от умолчания. Неизвестный ключ в секции — код
|
||||||
останавливает работу с задачами целиком.
|
3 на любой команде, так что лишнее слово останавливает работу с задачами
|
||||||
|
целиком.
|
||||||
|
|
||||||
Дом именно свой, а не `docs/.docs.json`, потому что `docs/` принадлежит
|
Дом в корне, а не внутри каталога задач, по двум причинам: настройка, лежащая
|
||||||
плагину канона: проект, поставивший учёт работ без него, каталога `docs/` не
|
внутри настраиваемого каталога, не смогла бы сказать, **где он**; и версия
|
||||||
имеет вовсе. Прежний ключ `tasks` в `docs/.pm.json` читается, **только когда
|
одна на весь плагин, а корень есть и у проекта без `docs/`. Прежние
|
||||||
своего файла нет** — для проектов, заведённых до раскола плагинов; скрипт при
|
`<каталог задач>/.tasks.json` и `docs/.docs.json` не читаются — увидев их,
|
||||||
этом говорит замечанием, куда его перенести. Есть оба — побеждает свой, и об
|
скрипт говорит «прежняя раскладка» и зовёт `upgrade`.
|
||||||
этом тоже говорится вслух: молча выбранный из двух конфиг это дрейф. Версию
|
- **Секции беклога** берутся из заголовков `##` индекса как есть; их названия —
|
||||||
прежний дом не знает и знать не может — она читается только из своего файла.
|
дело проекта (умолчание `План` на стройке, `Ядро` / `Инфра` на доработке), а
|
||||||
- **Секции беклога** берутся из заголовков `##` индекса как есть; их количество
|
количество ограничено стадией: на стройке секция одна. **В конфиге секций
|
||||||
и названия — дело проекта (умолчание `Ядро` / `Инфра`). **В конфиге их нет** —
|
нет** — второй список разошёлся бы с заголовками молча.
|
||||||
второй список разошёлся бы с заголовками молча.
|
|
||||||
|
|
||||||
### Вызов из другого плагина
|
### Вызов из другого плагина
|
||||||
|
|
||||||
@@ -704,13 +692,13 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
переменной не дотянутся. Мост — **вызов скилла через пространство имён**, а не
|
переменной не дотянутся. Мост — **вызов скилла через пространство имён**, а не
|
||||||
путь:
|
путь:
|
||||||
|
|
||||||
> Чужой контекст зовёт `Skill av-dev-tasks:tasks` и называет, что нужно сделать
|
> Чужой контекст зовёт `Skill av-dev:task-track` и называет, что нужно сделать
|
||||||
> («закрой задачу `<слаг>`, реализована»). Скилл разрешает свой
|
> («закрой задачу `<слаг>`, реализована»). Скилл разрешает свой
|
||||||
> `$CLAUDE_PLUGIN_ROOT` сам. Путь наружу не выносится вовсе.
|
> `$CLAUDE_PLUGIN_ROOT` сам. Путь наружу не выносится вовсе.
|
||||||
|
|
||||||
Плагина в проекте нет — вызов не разрешится, и вызывающий **не выдумывает путь и
|
Каталога задач в проекте нет — вызывающий **не выдумывает путь и не правит
|
||||||
не правит индекс руками**, а сообщает в докладе, что учёт задач остаётся за
|
индекс руками**, а сообщает в докладе, что учёт остаётся за владельцем. Скилл
|
||||||
владельцем.
|
при этом разрешится: он в том же плагине, что и вызывающий.
|
||||||
|
|
||||||
## Слоты проекта
|
## Слоты проекта
|
||||||
|
|
||||||
@@ -732,12 +720,14 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
- **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным
|
- **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным
|
||||||
предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть,
|
предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть,
|
||||||
под какую цель отнести, какая рамка разведки верна — решение пользователя. Слаг,
|
какая рамка разведки верна, пора ли менять стадию — решение пользователя. Слаг
|
||||||
формулировка, порядок строк в индексе — механика, делаем сами.
|
и формулировка — механика, делаем сами. **Порядок строк механикой не
|
||||||
|
считается** ни на одной стадии: на стройке он зависимость, на доработке
|
||||||
|
приоритет, и оба называет человек.
|
||||||
- **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа;
|
- **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа;
|
||||||
решений больше — веди **несколько итераций** диалога по ≤3, а не один
|
решений больше — веди **несколько итераций** диалога по ≤3, а не один
|
||||||
перегруженный запрос. Между итерациями применяй уже решённое.
|
перегруженный запрос. Между итерациями применяй уже решённое.
|
||||||
- **Границы покрытия в отчёте.** Любая сессия разбора, штурма или интейка
|
- **Границы покрытия в отчёте.** Любая сессия разбора, штурма или заведения записей
|
||||||
заканчивается строкой «просмотрено N из M, не трогали — …». Отчёт без неё
|
заканчивается строкой «просмотрено N из M, не трогали — …». Отчёт без неё
|
||||||
сообщает «беклог разобран», не сообщая, какая его часть осталась нетронутой.
|
сообщает «беклог разобран», не сообщая, какая его часть осталась нетронутой.
|
||||||
- **Ничего не удаляем молча.** Файл исчезает только через `close` — `--reason`
|
- **Ничего не удаляем молча.** Файл исчезает только через `close` — `--reason`
|
||||||
@@ -749,6 +739,6 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в
|
Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в
|
||||||
работу — этим занимается конвейер проекта. **Не ведёт очередь:** что делать
|
работу — этим занимается конвейер проекта. **Не ведёт очередь:** что делать
|
||||||
следующим и что перестало быть важным — скилл `groom`, а этот даёт ему операции.
|
следующим и что перестало быть важным — скилл `task-groom`, а этот даёт ему операции.
|
||||||
Не решает за пользователя, что важно. Не
|
Не решает за пользователя, что важно. Не
|
||||||
переоформляет существующие задачи «заодно»: правится то, чего касается операция.
|
переоформляет существующие задачи «заодно»: правится то, чего касается операция.
|
||||||
+54
-47
@@ -1,24 +1,24 @@
|
|||||||
# Адаптация каталога задач
|
# Адаптация каталога задач
|
||||||
|
|
||||||
Проект, где задачи уже как-то ведутся, и из имеющегося материала **выводится**
|
Проект, где задачи уже как-то ведутся, и из имеющегося материала **выводится**
|
||||||
заполненный каталог задач: цели, задачи, кладбище, индексы. Операция разовая —
|
заполненный каталог задач: задачи, кладбище, индекс. Операция разовая —
|
||||||
после неё проект живёт скиллами `tasks` и `groom`.
|
после неё проект живёт скиллами `task-track` и `task-groom`.
|
||||||
|
|
||||||
**Это часть приведения проекта к канону.** Раскладку `docs/` целиком ведёт скилл
|
**Это часть приведения проекта к канону.** Раскладку `docs/` целиком ведёт скилл
|
||||||
`av-dev-docs:canon`; он же зовёт этот сценарий на шаге «каталог задач», потому что
|
`av-dev:canon`; он же зовёт этот сценарий на шаге «каталог задач», потому что
|
||||||
форматом задач владеет `tasks`, а не `canon`. Отдельно сценарий вызывается,
|
форматом задач владеет `task-track`, а не `canon`. Отдельно сценарий вызывается,
|
||||||
когда переводить надо **только** задачи.
|
когда переводить надо **только** задачи.
|
||||||
|
|
||||||
Вход какой угодно: старая раскладка `av-dev-backlog` (индекс `README.md`,
|
Вход какой угодно: старая раскладка `av-dev-backlog` (индекс `README.md`,
|
||||||
кладбище `CLOSED.md`, приоритеты секциями, транслитные слаги, файлы рядом с
|
кладбище `CLOSED.md`, приоритеты секциями, транслитные слаги, файлы рядом с
|
||||||
индексом), `TODO.md`, россыпь заметок, раздел «планы» в `README.md`, список
|
индексом), `TODO.md`, россыпь заметок, раздел «планы» в `README.md`, список
|
||||||
шагов роадмапа проекта.
|
шагов плана проекта.
|
||||||
|
|
||||||
## Три правила, из которых всё следует
|
## Три правила, из которых всё следует
|
||||||
|
|
||||||
1. **Сперва карта, потом файлы.** Человеку показывается, что найдено, как
|
1. **Сперва карта, потом файлы.** Человеку показывается, что найдено, в каком
|
||||||
разложилось по целям и **что не разложилось**, — и только после подтверждения
|
порядке разложилось и **что не разложилось**, — и только после подтверждения
|
||||||
пишется хоть один файл. Это то же правило, что у интейка находок ревью:
|
пишется хоть один файл. Это то же правило, что у заведения задач из ревью:
|
||||||
массовое заведение записей без подтверждения — самый дорогой отказ, потому
|
массовое заведение записей без подтверждения — самый дорогой отказ, потому
|
||||||
что разгребает его потом переоценка.
|
что разгребает его потом переоценка.
|
||||||
2. **Ничего не терять.** Исходный текст переезжает в тело, «зачем» и причина
|
2. **Ничего не терять.** Исходный текст переезжает в тело, «зачем» и причина
|
||||||
@@ -35,10 +35,10 @@
|
|||||||
«машина умеет / не умеет»:
|
«машина умеет / не умеет»:
|
||||||
|
|
||||||
```
|
```
|
||||||
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 # только чтение
|
--stage build --target tasks --out tasks-adopt-plan.json # только чтение
|
||||||
python3 $tk adopt apply --plan tasks-adopt-plan.json \
|
python3 $tk adopt apply --plan tasks-adopt-plan.json \
|
||||||
--refs docs openspec CLAUDE.md README.md # запись
|
--refs docs openspec CLAUDE.md README.md # запись
|
||||||
```
|
```
|
||||||
@@ -53,56 +53,61 @@ python3 $tk adopt apply --plan tasks-adopt-plan.json \
|
|||||||
- **английские слаги.** Перевести `taj-brejk-pri-ravnoj-polnote` в
|
- **английские слаги.** Перевести `taj-brejk-pri-ravnoj-polnote` в
|
||||||
`tie-break-equal-completeness` может только тот, кто понимает смысл. `scan`
|
`tie-break-equal-completeness` может только тот, кто понимает смысл. `scan`
|
||||||
честно говорит: проверить надо **все** слаги, признаки транслита — эвристика;
|
честно говорит: проверить надо **все** слаги, признаки транслита — эвристика;
|
||||||
- **цели.** Шаги роадмапа — готовые цели в **`Запланировано`** (очередь и
|
- **стадия.** `--stage` называет, чем этот беклог будет: планом стройки или
|
||||||
обоснование у них уже есть); тематические скопления задач — цели в
|
очередью правок. Машине это не выводится — она видит список пунктов, а не то,
|
||||||
**`Направления`** («прочность слияния»,
|
построено приложение или нет;
|
||||||
«журнал и пересборка»). Предлагаешь ты, назначает человек;
|
- **порядок.** Нумерованные шаги источника `scan` сохраняет по номерам, прочие
|
||||||
|
ставит следом. Дальше порядок — твоё суждение и подтверждение человека: на
|
||||||
|
стройке это зависимость, на доработке важность;
|
||||||
- **что вообще не задача.** Обоснование порядка шагов, абзац прозой, заголовок
|
- **что вообще не задача.** Обоснование порядка шагов, абзац прозой, заголовок
|
||||||
раздела — это не пункты беклога, и они уходят в «не разложилось» с причиной.
|
раздела — это не пункты беклога, и они уходят в «не разложилось» с причиной.
|
||||||
|
|
||||||
## Порядок
|
## Порядок
|
||||||
|
|
||||||
1. **Осмотрись.** Где лежат задачи, роадмап, заметки. Каталог задач по канону —
|
1. **Осмотрись и назови стадию.** Где лежат задачи, план, заметки; построено
|
||||||
всегда `tasks`. Секции беклога (`--sections`) — по умолчанию
|
приложение или строится. Каталог задач по канону — всегда `tasks`. Секции
|
||||||
`Ядро,Инфра`; если у проекта деление другое по существу, оно называется
|
беклога (`--sections`) — на стройке ровно одна (умолчание `План`), на
|
||||||
здесь, а не подгоняется под умолчание, и становится **заголовками `##`
|
доработке сколько нужно (умолчание `Ядро,Инфра`); если у проекта деление
|
||||||
индекса** — их единственным домом. В `.tasks.json` секции не пишутся: там
|
другое по существу, оно называется здесь, а не подгоняется под умолчание, и
|
||||||
версия формата и имена частей, а второй список секций разошёлся бы с
|
становится **заголовками `##` индекса** — их единственным домом. В
|
||||||
заголовками молча.
|
`.av-dev.toml` секции не пишутся: там версия, стадия и имена частей, а второй
|
||||||
|
список секций разошёлся бы с заголовками молча.
|
||||||
2. **`adopt scan`** по всем источникам разом. Один прогон, одна карта: два
|
2. **`adopt scan`** по всем источникам разом. Один прогон, одна карта: два
|
||||||
прохода дадут два несогласованных состояния.
|
прохода дадут два несогласованных состояния.
|
||||||
3. **Заполни карту**: `slug` (английский), `section`, `goal` у каждой записи;
|
3. **Заполни карту**: `slug` (английский), `type` и `section` у каждой записи, и
|
||||||
список `goals` — из шагов роадмапа и из тем. Закрытый шаг целью не
|
**порядок `items`** — он уедет в индекс как есть. Пункт, помеченный
|
||||||
заводится. Пустой `goal` законен у `fix`, `chore` и `research` — они служат
|
закрытым, не переносится вовсе.
|
||||||
работоспособности, а не направлению; у `feature` цель обязательна.
|
|
||||||
4. **Покажи человеку карту** через `AskUserQuestion`, ≤3 вопроса за итерацию,
|
4. **Покажи человеку карту** через `AskUserQuestion`, ≤3 вопроса за итерацию,
|
||||||
рекомендация первым вариантом. Показывается: сколько записей, предлагаемые
|
рекомендация первым вариантом. Показывается: сколько записей, предлагаемый
|
||||||
цели (порядок и темы) с обоснованием, спорные отнесения, список «не
|
порядок с обоснованием, спорные отнесения, список «не разложилось». Слаги не
|
||||||
разложилось». Массовые механические решения (слаги, порядок строк) не
|
выносятся — это механика; **порядок выносится всегда**, потому что механикой
|
||||||
выносятся — это механика.
|
он не является ни на одной стадии.
|
||||||
5. **`adopt apply`.** `--refs` перечисляет **всё**, где могут стоять ссылки на
|
5. **`adopt apply`.** `--refs` перечисляет **всё**, где могут стоять ссылки на
|
||||||
слаги: документация, архив изменений, `CLAUDE.md`, `README.md`. Скрипт
|
слаги: документация, архив изменений, `CLAUDE.md`, `README.md`. Скрипт
|
||||||
посчитает и покажет, сколько ссылок поправлено и по каким слагам.
|
посчитает и покажет, сколько ссылок поправлено и по каким слагам.
|
||||||
6. **`tasks.py check`** и доклад.
|
6. **`tasks.py check`** и доклад.
|
||||||
|
|
||||||
`apply` отказывается писать поверх живого каталога и проверяет карту целиком
|
`apply` отказывается писать поверх живого каталога и проверяет карту целиком
|
||||||
**до** первой записи: неверная секция, дубль слага, цель, которой нет в карте —
|
**до** первой записи: неверная секция, дубль слага, неназванный тип, две секции
|
||||||
всё это отказ до того, как на диске появился хотя бы один файл.
|
при стадии `build` — всё это отказ до того, как на диске появился хотя бы один
|
||||||
|
файл.
|
||||||
|
|
||||||
## Переходное состояние — объявляется, а не заминается
|
## Переходное состояние — объявляется, а не заминается
|
||||||
|
|
||||||
Сразу после адаптации задачи в большинстве своём **не готовы к взятию**: у них
|
Сразу после адаптации задачи в большинстве своём **не готовы к взятию**: у них
|
||||||
нет критериев приёмки, а у части может не быть цели. Это нормально, но обязано
|
нет критериев приёмки. Это нормально, но обязано быть названо, иначе следующий
|
||||||
быть названо, иначе следующий агент примет пустой беклог за поломку.
|
агент примет пустой беклог за поломку.
|
||||||
|
|
||||||
`apply` печатает состояние по факту: сколько задач без цели (это **ошибки**
|
`apply` печатает состояние по факту: сколько задач не собрало разделы своего типа
|
||||||
`check`) и сколько не собрало разделы своего типа (для `check` это не ошибка, а
|
(для `check` это не ошибка, а строка здоровья, но `ready` такую задачу не
|
||||||
строка здоровья, но `ready` такую задачу не пропустит). Закрывается это
|
пропустит). Закрывается это **порциями по 5–8 задач**: превратить «готово,
|
||||||
**порциями груминга** — скилл
|
когда» в критерии с оракулами, вынуть вопросы из прозы в раздел «Вопросы». На
|
||||||
`groom`, 5–8 задач за порцию: проставить цели, превратить «готово, когда» в
|
доработке это груминг (скилл `av-dev:task-groom`), на стройке — гигиена полей
|
||||||
критерии с оракулами, вынуть вопросы из прозы в раздел «Вопросы». Там же
|
этого скилла: груминга там нет.
|
||||||
беклогу впервые назначается **порядок**: после адаптации его нет вовсе, а
|
|
||||||
очередь и есть то, ради чего каталог заводят.
|
**Порядок строк проверяется глазами отдельно.** На стройке он выведен из
|
||||||
|
нумерации источника, и там, где её не было, он случаен. На доработке машина
|
||||||
|
важности не знает вовсе — очередь расставляется первым же грумингом.
|
||||||
|
|
||||||
Готовность к первой задаче — не «`check` зелёный», а «`ready` пропускает хотя бы
|
Готовность к первой задаче — не «`check` зелёный», а «`ready` пропускает хотя бы
|
||||||
верхние строки очереди».
|
верхние строки очереди».
|
||||||
@@ -113,18 +118,20 @@ python3 $tk adopt apply --plan tasks-adopt-plan.json \
|
|||||||
дело человека, удалять чужое молча нельзя. В доклад идёт готовая команда.
|
дело человека, удалять чужое молча нельзя. В доклад идёт готовая команда.
|
||||||
- **Не переписывает подписи ссылок.** `[docs/backlog](tasks/BACKLOG.md)` —
|
- **Не переписывает подписи ссылок.** `[docs/backlog](tasks/BACKLOG.md)` —
|
||||||
цель поправлена, текст остался; это правится глазами, и таких мест немного.
|
цель поправлена, текст остался; это правится глазами, и таких мест немного.
|
||||||
- **Не сочиняет критерии приёмки и не придумывает цели**, которых в материале
|
- **Не сочиняет критерии приёмки** и не додумывает пользы, которой в материале
|
||||||
нет. Придуманная цель хуже отсутствующей: под неё заведут задачи.
|
нет.
|
||||||
|
- **Не выводит стадию.** Список пунктов одинаково выглядит и планом стройки, и
|
||||||
|
очередью правок; отвечает `--stage`, а называет его человек.
|
||||||
- **Не трогает историю.** В коммитах старые слаги остаются, и это нормально.
|
- **Не трогает историю.** В коммитах старые слаги остаются, и это нормально.
|
||||||
|
|
||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
- Источники и что в каждом распознано (раскладка, индекс, кладбище, секции).
|
- Источники и что в каждом распознано (раскладка, индекс, кладбище, секции).
|
||||||
- Сколько записей перенесено, сколько целей заведено (порядок / темы) и откуда
|
- Стадия и сколько записей перенесено; откуда взялся порядок (нумерация
|
||||||
каждая выведена.
|
источника или суждение).
|
||||||
- **Переименования**: сколько слагов, сколько ссылок поправлено и в скольких
|
- **Переименования**: сколько слагов, сколько ссылок поправлено и в скольких
|
||||||
файлах — числом, а не «поправлены ссылки».
|
файлах — числом, а не «поправлены ссылки».
|
||||||
- **Не разложилось**: поимённо, с причиной.
|
- **Не разложилось**: поимённо, с причиной.
|
||||||
- Переходное состояние: сколько задач без цели, сколько без критериев, чем и за
|
- Переходное состояние: сколько задач без критериев, чем и за сколько порций
|
||||||
сколько порций закрывается.
|
закрывается.
|
||||||
- `tasks.py check` — результат строкой.
|
- `tasks.py check` — результат строкой.
|
||||||
+45
-29
@@ -2,21 +2,26 @@
|
|||||||
|
|
||||||
Ревью и аудиты — код-ревью, архитектурный проход, аудит безопасности, любой
|
Ревью и аудиты — код-ревью, архитектурный проход, аудит безопасности, любой
|
||||||
разбор другим агентом — порождают находки, часть которых становится задачами.
|
разбор другим агентом — порождают находки, часть которых становится задачами.
|
||||||
Это отдельный интейк со своей опасностью, **зеркальной** интейку из диалога.
|
Это отдельное **заведение записей** со своей опасностью, **зеркальной**
|
||||||
|
заведению из диалога. Операция зовётся по источнику, потому что источник и
|
||||||
|
задаёт опасность.
|
||||||
|
|
||||||
- Интейк из диалога грешит переполнением: из одной мысли рождается пять файлов.
|
- Заведение из диалога грешит переполнением: из одной мысли рождается пять
|
||||||
- Интейк из ревью грешит сваливанием: сорок сырых находок превращаются в сорок
|
файлов.
|
||||||
|
- Заведение из ревью грешит сваливанием: сорок сырых находок превращаются в сорок
|
||||||
файлов. Беклог раздувается, а следующая переоценка склеивает их обратно.
|
файлов. Беклог раздувается, а следующая переоценка склеивает их обратно.
|
||||||
|
|
||||||
Защита от сваливания — та же, что в самом ревью: **кластеризация по причине, а
|
Защита от сваливания — та же, что в самом ревью: **кластеризация по причине, а
|
||||||
не файл-на-находку.** Если у ревью был триаж — половина работы уже сделана, бери
|
не файл-на-находку.** Если у ревью был триаж — половина работы уже сделана, бери
|
||||||
его выход. Если нет — триажируй сам, прежде чем заводить.
|
его выход. Если нет — триажируй сам, прежде чем заводить.
|
||||||
|
|
||||||
**Штатный отправитель — `av-dev-code:review`** (и `av-dev-code:resolve`, который
|
**Штатных отправителя два.** Первый — `av-dev:code-review` (и зовущий его
|
||||||
его вызывает): задач он не заводит сам, а отдаёт отложенные находки **списком
|
`av-dev:code-resolve`): задач он не заводит сам, а отдаёт отложенные находки
|
||||||
урожая** — формулировка, оракул, провенанс — и хранит отчёт триажа вместе с
|
**списком урожая** — формулировка, оракул, откуда взялась — и хранит отчёт триажа
|
||||||
изменением. Приходит и любой другой разбор, вплоть до пересказа человеком; тогда
|
вместе с изменением. Второй — `av-dev:code-deep-review`, и он зовёт этот сценарий
|
||||||
триажа нет и шаг 1 порядка делается руками.
|
напрямую, передавая согласованные с человеком находки дословно. Приходит и любой
|
||||||
|
другой разбор, вплоть до пересказа человеком; тогда триажа нет и шаг 1 порядка
|
||||||
|
делается руками.
|
||||||
|
|
||||||
## Находка агента — не задача
|
## Находка агента — не задача
|
||||||
|
|
||||||
@@ -50,22 +55,25 @@
|
|||||||
заведённое и уже выкинутое. Нашлось среди живых — дописываем находку в
|
заведённое и уже выкинутое. Нашлось среди живых — дописываем находку в
|
||||||
существующий файл. Нашлось в `REJECTED.md` — это сигнал: причина отказа могла
|
существующий файл. Нашлось в `REJECTED.md` — это сигнал: причина отказа могла
|
||||||
устареть, выноси пользователю, а не заводи молча заново.
|
устареть, выноси пользователю, а не заводи молча заново.
|
||||||
4. **Разложи по целям — там, где цель нужна.** Большинство находок ревью это
|
4. **Проставь типы.** Большинство находок ревью это `fix` и `chore`. Находка,
|
||||||
`fix` и `chore`, и **цель им не требуется**: они служат работоспособности, а
|
оказавшаяся **новой возможностью** (`feature`), — отдельный случай: нашлось
|
||||||
не направлению. Придуманная им цель —
|
поведение, которого никто не заказывал, и решение тут не «завести задачу», а
|
||||||
ровно то враньё, от которого спасает тип.
|
«заказать или убрать». Выноси такую пользователю отдельно от прочих.
|
||||||
|
|
||||||
Цель обязательна у находки, которая оказалась **новой возможностью**
|
|
||||||
(`feature`): нашлось поведение, которого никто не заказывал, и его надо
|
|
||||||
либо заказать целью, либо убрать. Подходящей цели нет — заведи её
|
|
||||||
(`add --type goal --section Направления`) в том же проходе.
|
|
||||||
5. **Покажи карту до создания файлов.** Кластер → задача / сырьё / строка в
|
5. **Покажи карту до создания файлов.** Кластер → задача / сырьё / строка в
|
||||||
пакетный файл / уже заведено / отброшено, и под какую цель — пачкой через
|
пакетный файл / уже заведено / отброшено — пачкой через
|
||||||
`AskUserQuestion`. Это тот же барьер, что и «три кандидата» в интейке из
|
`AskUserQuestion`. Это тот же барьер, что и «три кандидата» при заведении
|
||||||
диалога: массовое заведение файлов без подтверждения — ровно тот отказ, ради
|
из диалога: массовое заведение файлов без подтверждения — ровно тот отказ,
|
||||||
которого интейк из ревью и выделен. Дешёвая мелочь по явному согласию может
|
ради которого заведение из ревью и выделено. Дешёвая мелочь по явному согласию может
|
||||||
заводиться и без поштучного вопроса — но карта пользователю предъявляется
|
заводиться и без поштучного вопроса — но карта пользователю предъявляется
|
||||||
всё равно.
|
всё равно.
|
||||||
|
|
||||||
|
**Барьер снимается ровно в одном случае — когда его уже прошли.** В хвосте
|
||||||
|
задачи (`av-dev:code-resolve`, шаг 6; в обслуживании — шаг 5) человек одной
|
||||||
|
репликой сказал, что из урожая заводится, и третьего стопа у прогона не будет:
|
||||||
|
там карта идёт **строкой доклада**, а не вопросом. Признак читается буквально:
|
||||||
|
**список находок уже был показан человеку и получил ответ**. Не был — карта
|
||||||
|
предъявляется вопросом, и это обычный случай прямого вызова и вызова из
|
||||||
|
`av-dev:code-deep-review`, где находки разбирались по одной, а нарезка — нет.
|
||||||
6. **Заводи утверждённое** через `tasks.py add`, с тремя добавками:
|
6. **Заводи утверждённое** через `tasks.py add`, с тремя добавками:
|
||||||
- **тег партии** — `--tag review-ГГГГ-ММ-ДД` (или `audit-<slug>`), чтобы весь
|
- **тег партии** — `--tag review-ГГГГ-ММ-ДД` (или `audit-<slug>`), чтобы весь
|
||||||
заход разбора поднимался одной командой `list --tag …`;
|
заход разбора поднимался одной командой `list --tag …`;
|
||||||
@@ -75,7 +83,7 @@
|
|||||||
`research`. Тип, розданный оптом, врёт ровно там, где по нему потом
|
`research`. Тип, розданный оптом, врёт ровно там, где по нему потом
|
||||||
отбирают, **и требует не тех разделов**: каждому `fix` придётся заполнить
|
отбирают, **и требует не тех разделов**: каждому `fix` придётся заполнить
|
||||||
`Воспроизведение`, а у находки без свидетельства его нет;
|
`Воспроизведение`, а у находки без свидетельства его нет;
|
||||||
- **провенанс в теле** — кто нашёл, каким проходом, с каким свидетельством.
|
- **откуда взялась — в теле**: кто нашёл, каким проходом, с каким свидетельством.
|
||||||
Без него через месяц не отличить проверенную находку от догадки.
|
Без него через месяц не отличить проверенную находку от догадки.
|
||||||
7. `tasks.py check`.
|
7. `tasks.py check`.
|
||||||
|
|
||||||
@@ -84,12 +92,20 @@
|
|||||||
Уровня серьёзности в записи нет — но **выкидывать её нельзя**: серьёзность
|
Уровня серьёзности в записи нет — но **выкидывать её нельзя**: серьёзность
|
||||||
отображается **в позицию в очереди**, потому что приоритет и есть порядок строк
|
отображается **в позицию в очереди**, потому что приоритет и есть порядок строк
|
||||||
в беклоге (правило 4 [SKILL.md](../SKILL.md)). Отображается через довод, а не
|
в беклоге (правило 4 [SKILL.md](../SKILL.md)). Отображается через довод, а не
|
||||||
напрямую: своей шкалы у интейка нет, доводы расстановки перечислены в
|
напрямую: своей шкалы у заведения нет, доводы расстановки перечислены в
|
||||||
[скилле груминга](../../groom/SKILL.md#приоритет-как-его-расставляют), и
|
[скилле груминга](../../task-groom/SKILL.md#приоритет-как-его-расставляют), и
|
||||||
серьёзность попадает ровно в один из них.
|
серьёзность попадает ровно в один из них.
|
||||||
|
|
||||||
- **тяжёлая находка со свидетельством о сломанном сейчас** → задача под ту цель,
|
**Всё это — про доработку.** На стройке порядок строк значит зависимость, и
|
||||||
которой она угрожает, и **первой строкой секции**: `move <слаг> --first
|
`--first` там означает «ни от чего не зависит», а не «важнее всех»: находка,
|
||||||
|
поднятая наверх, встанет перед собственной зависимостью. Место находке на стройке
|
||||||
|
называет зависимость — `move --after <шаг, после которого её можно делать>`, — а
|
||||||
|
серьёзность идёт **причиной в мете** и разбирается ближайшим пересмотром плана
|
||||||
|
(`task-track`, «Пересмотр плана стройки»). Груминга там нет, и откладывать «до
|
||||||
|
него» некуда.
|
||||||
|
|
||||||
|
- **тяжёлая находка со свидетельством о сломанном сейчас** → задача
|
||||||
|
**первой строкой секции**: `move <слаг> --first
|
||||||
--reason «сломано сейчас: …»`. Это довод «что сломано сейчас» из перечня
|
--reason «сломано сейчас: …»`. Это довод «что сломано сейчас» из перечня
|
||||||
груминга — единственный, который не требует сравнения с соседями по очереди,
|
груминга — единственный, который не требует сравнения с соседями по очереди,
|
||||||
потому что сломанное дорожает само. Позицию всё равно назначает человек, и
|
потому что сломанное дорожает само. Позицию всё равно назначает человек, и
|
||||||
@@ -100,7 +116,7 @@
|
|||||||
(`--reason`). Позицию назначит человек на ближайшем груминге, сравнив её с
|
(`--reason`). Позицию назначит человек на ближайшем груминге, сравнив её с
|
||||||
верхом очереди; без записанного довода сравнивать он будет с нуля;
|
верхом очереди; без записанного довода сравнивать он будет с нуля;
|
||||||
- **находка, которая не ждёт груминга вовсе** (необратимый ущерб, покраснела
|
- **находка, которая не ждёт груминга вовсе** (необратимый ущерб, покраснела
|
||||||
проверка, которую проект назвал сломанным), — не интейк: это работа прямо
|
проверка, которую проект назвал сломанным), — не заведение записи: это работа прямо
|
||||||
сейчас, а в беклог она падает, только если ждать всё-таки можно;
|
сейчас, а в беклог она падает, только если ждать всё-таки можно;
|
||||||
- **низкая уверенность или нет свидетельства** → сырьё (`research` с пустым
|
- **низкая уверенность или нет свидетельства** → сырьё (`research` с пустым
|
||||||
разделом «Вопрос»): его место в очереди производно от типа — конец секции;
|
разделом «Вопрос»): его место в очереди производно от типа — конец секции;
|
||||||
@@ -112,7 +128,7 @@
|
|||||||
|
|
||||||
## Поимённая сверка
|
## Поимённая сверка
|
||||||
|
|
||||||
Интейк считается выполненным, только если **каждая** находка триажа получила
|
Заведение считается выполненным, только если **каждая** находка триажа получила
|
||||||
исход: слаг заведённой задачи, ссылку на существующую, строку пакетного файла
|
исход: слаг заведённой задачи, ссылку на существующую, строку пакетного файла
|
||||||
или запись «не заведена: причина». Нулевой урожай при непустом отчёте триажа
|
или запись «не заведена: причина». Нулевой урожай при непустом отчёте триажа
|
||||||
виден сразу — и это единственный способ отличить «находок не было» от «не стал
|
виден сразу — и это единственный способ отличить «находок не было» от «не стал
|
||||||
@@ -124,7 +140,7 @@
|
|||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
- Источник (какое ревью/аудит, сколько находок на входе).
|
- Источник (какое ревью/аудит, сколько находок на входе).
|
||||||
- Свёрнуто в задачи: N кластеров из M находок, со слагами, целями и тегом партии.
|
- Свёрнуто в задачи: N кластеров из M находок, со слагами и тегом партии.
|
||||||
- Что не заведено и почему: починено инлайн, уже заведено, стало сырьём, ушло в
|
- Что не заведено и почему: починено инлайн, уже заведено, стало сырьём, ушло в
|
||||||
`REJECTED.md`.
|
`REJECTED.md`.
|
||||||
- Поимённая сверка: находок на входе N, исход есть у N.
|
- Поимённая сверка: находок на входе N, исход есть у N.
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
# Декомпозиция и мозговой штурм
|
||||||
|
|
||||||
|
Обе операции превращают одну запись в несколько (или в ноль). Разница во входе:
|
||||||
|
декомпозиция дробит **слишком крупную задачу**, штурм прорабатывает **идею**,
|
||||||
|
которая ещё не задача.
|
||||||
|
|
||||||
|
## Тест декомпозиции
|
||||||
|
|
||||||
|
Задачу можно дробить, только если части удовлетворяют **обоим** условиям:
|
||||||
|
|
||||||
|
1. **Каждая мерджится сама по себе.** Часть, после которой дерево не собирается
|
||||||
|
или поведение сломано до прихода соседней, — не часть, а половина.
|
||||||
|
2. **Каждая — самостоятельный шаг.** Часть, осмысленная только в комплекте с
|
||||||
|
другой, — не задача. Проверяй тестом «готова к взятию» (task-format): свои
|
||||||
|
критерии приёмки у неё есть или нет.
|
||||||
|
|
||||||
|
**Порядок между частями законен на стройке и подозрителен на доработке**, и это
|
||||||
|
единственное, что стадия здесь меняет. Беклог стройки **весь** состоит из
|
||||||
|
упорядоченных зависимостью шагов: «сперва А, потом Б» — не повод не дробить, а
|
||||||
|
описание того, как этот список устроен, и части просто встают подряд. На
|
||||||
|
доработке правки независимы, и обнаруженный порядок «иначе не собрать» чаще
|
||||||
|
всего значит, что перед тобой не декомпозиция, а план реализации: шаги остаются
|
||||||
|
**внутри одного файла**.
|
||||||
|
|
||||||
|
Не проходит хотя бы одно — **не дроби**. Ложная декомпозиция плодит файлы,
|
||||||
|
которые нельзя взять поодиночке, и переоценка потом склеивает их обратно.
|
||||||
|
|
||||||
|
## Где резать, если резать можно
|
||||||
|
|
||||||
|
Тест выше говорит, **допустим** ли разрез. Где его провести из нескольких
|
||||||
|
допустимых мест — отвечает шов.
|
||||||
|
|
||||||
|
**Шов — там, где меняется род работы.** Раздел «Затрагивает» перечисляет
|
||||||
|
границы; если одна строка перечня стоит особняком от остальных — трогает другой
|
||||||
|
слой, переносит ответственность, вводит новое понятие, — эта часть и режется
|
||||||
|
отдельно. Пример: задача перекладывает несколько узлов разом и заодно добавляет
|
||||||
|
два поля в существующий ответ; переложенная часть и добавленные поля проверяются
|
||||||
|
по-разному человеком, хотя конвейером — одинаково.
|
||||||
|
|
||||||
|
**Ревью на цену разреза больше не влияет.** Состав прогона постоянный: гейт,
|
||||||
|
спеки, код, триаж плюс приёмник тем, — и каждая половина платит его целиком.
|
||||||
|
Значит, разрез удваивает костяк ревью **всегда**, а не только когда обе половины
|
||||||
|
остаются в одной метке; выигрыш он даёт не в проверке, а в том, что каждая
|
||||||
|
половина доводится и мерджится сама по себе. Прежде здесь стояло правило «резать,
|
||||||
|
когда разрез снимает дорогой проход с большей части диффа» — снимать больше
|
||||||
|
нечего.
|
||||||
|
|
||||||
|
**Это планирование, а не предписание процесса.** Как проверять изменение, решает
|
||||||
|
конвейер, увидев его; в тело задачи это не пишется — строка «делать вот так» и
|
||||||
|
есть тот второй дом правила, который гигиена полей снимает.
|
||||||
|
|
||||||
|
## Что делать с родителем
|
||||||
|
|
||||||
|
После разделения родитель **не остаётся** третьей висящей строкой:
|
||||||
|
|
||||||
|
части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
|
||||||
|
`REJECTED.md` здесь — не «выкинули», а именно тот след, что переживает запись:
|
||||||
|
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
|
||||||
|
наследников, а не археологией git.
|
||||||
|
|
||||||
|
**Зонтика над частями нет никакого.** Тип `epic` упразднён, цель, игравшая его
|
||||||
|
роль после него, — тоже. Если частям нужен общий заголовок, у них общее место в
|
||||||
|
списке: они встают подряд, и соседство и есть тот ответ, ради которого заводили
|
||||||
|
зонтик.
|
||||||
|
|
||||||
|
## Когда декомпозиция случается посреди работы
|
||||||
|
|
||||||
|
Задача, которая **оказалась крупнее задачи**, распознаётся до того, как под неё
|
||||||
|
заведено предложение об изменении: иначе его придётся выбрасывать. Она выходит
|
||||||
|
из работы на декомпозицию, а её строка возвращается в беклог с причиной
|
||||||
|
(`move … --reason "крупнее задачи"`). **Место в списке частям назначает
|
||||||
|
человек**: машина поставит их в конец секции, а на стройке место наследуется от
|
||||||
|
родителя (`move --after`), да и на доработке крупная задача редко распадается на
|
||||||
|
что-то менее срочное, чем была сама.
|
||||||
|
|
||||||
|
## Мозговой штурм сырья
|
||||||
|
|
||||||
|
Сырьё — запись типа `research`, у которой раздел «Вопрос» пуст: она не проходит
|
||||||
|
тест «готова к взятию», потому что неясно, что именно делаем. Штурм проясняет —
|
||||||
|
и это **generative-операция, а не applicative**.
|
||||||
|
|
||||||
|
Исход штурма и есть заполненный «Вопрос» (тогда разведку можно брать в работу)
|
||||||
|
или набор задач с типами, которые из ответа следуют. Третий законный исход —
|
||||||
|
`close --reason`.
|
||||||
|
|
||||||
|
Applicative-штурм («перечисли задачи, следующие из идеи») выдаёт очевидное:
|
||||||
|
перечисляется то, что уже видно в формулировке. Ценное — на уровень выше.
|
||||||
|
|
||||||
|
1. **Сперва — формы, а не задачи.** Предложи **три разные постановки** идеи и
|
||||||
|
назови **компромисс каждой**: что она даёт, чем платит, что оставляет за
|
||||||
|
бортом. Если получилась одна постановка — штурм не состоялся, это
|
||||||
|
applicative.
|
||||||
|
2. **Вынеси формы пользователю** через `AskUserQuestion` с компромиссами. Рамку
|
||||||
|
выбирает он: это продуктовое решение, не механика.
|
||||||
|
3. **Назови пользу.** Выбранная форма отвечает на «что станет наблюдаемо иначе».
|
||||||
|
Идея, для которой такого ответа не находится, скорее всего уезжает в
|
||||||
|
`REJECTED.md`, а не заводится задачей.
|
||||||
|
4. **Только выбранную форму** дроби по тесту декомпозиции выше и проставь
|
||||||
|
критерии приёмки: без них наследники останутся идеями под другим именем.
|
||||||
|
|
||||||
|
**«Выкинуть» — полноправный исход штурма, а не его неудача.** Проработка, честно
|
||||||
|
показавшая, что пользы нет или она несоразмерна цене, — это результат: идея
|
||||||
|
уезжает с этой самой причиной, и та причина гасит её повторное появление.
|
||||||
|
|
||||||
|
## Доклад
|
||||||
|
|
||||||
|
- Идея/задача на входе, выбранная рамка (для штурма), задачи-наследники со
|
||||||
|
слагами, секциями и местом в списке.
|
||||||
|
- Судьба родителя: удалён / выкинут с причиной.
|
||||||
|
- `tasks.py check` после правок.
|
||||||
|
- Границы покрытия: какие постановки рассмотрены и какие сознательно отброшены —
|
||||||
|
чтобы штурм не пришлось повторять с нуля.
|
||||||
+18
-5
@@ -14,7 +14,6 @@
|
|||||||
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
|
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
|
||||||
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
||||||
| Поле места | **Категория** — полка домена беклога |
|
| Поле места | **Категория** — полка домена беклога |
|
||||||
| Цель (`goal:<слаг>`) | нет: цель — это возможность, а здесь её не появляется |
|
|
||||||
| Индекс | `BACKLOG.md` |
|
| Индекс | `BACKLOG.md` |
|
||||||
| Берётся в работу | да |
|
| Берётся в работу | да |
|
||||||
|
|
||||||
@@ -33,10 +32,17 @@
|
|||||||
не `chore`. Тип, оставшийся от первой формулировки, врёт ровно там, где по нему
|
не `chore`. Тип, оставшийся от первой формулировки, врёт ровно там, где по нему
|
||||||
отбирают.
|
отбирают.
|
||||||
|
|
||||||
|
**Обнаружилось это уже в работе — запись переформулируется, а не дорешивается.**
|
||||||
|
Исполнитель останавливается, называет тип, которым задача оказалась (`fix` —
|
||||||
|
поведение расходится с заявленным, `feature` — снаружи появляется то, чего не
|
||||||
|
было), и человек решает: сменить тип и решать процессом того типа — либо
|
||||||
|
прекратить. Тип меняет этот скилл, а не исполнитель по ходу: у нового типа своя
|
||||||
|
схема разделов, и `ready` проверит её заново.
|
||||||
|
|
||||||
## Алгоритм
|
## Алгоритм
|
||||||
|
|
||||||
1. **Проверить, что поведение не меняется.** Меняется — это `feature` или `fix`,
|
1. **Проверить, что поведение не меняется.** Меняется — это `feature` или `fix`,
|
||||||
и у неё другие требования (цель, воспроизведение).
|
и у последнего другие требования (воспроизведение).
|
||||||
2. **Назвать, что перестанет мешать** — одной фразой, адресуясь разработчику.
|
2. **Назвать, что перестанет мешать** — одной фразой, адресуясь разработчику.
|
||||||
«Прибраться в модуле X» — не ответ: непонятно, что изменится.
|
«Прибраться в модуле X» — не ответ: непонятно, что изменится.
|
||||||
3. **Назвать границы** в `Затрагивает`. У обслуживания они часто не в коде:
|
3. **Назвать границы** в `Затрагивает`. У обслуживания они часто не в коде:
|
||||||
@@ -48,9 +54,16 @@
|
|||||||
5. **Проверить, что это не «заодно».** Обслуживание любит склеиваться в пачку
|
5. **Проверить, что это не «заодно».** Обслуживание любит склеиваться в пачку
|
||||||
(«обновить зависимости и переписать сборку и убрать мёртвый код»). Не
|
(«обновить зависимости и переписать сборку и убрать мёртвый код»). Не
|
||||||
мерджится порознь — это несколько задач ([split.md](split.md)).
|
мерджится порознь — это несколько задач ([split.md](split.md)).
|
||||||
6. **Цель не проставлять.** `chore` служит работоспособности, а не направлению.
|
|
||||||
Работа по сопровождению проекта при этом видна в роадмапе — секцией
|
## Кто такую задачу решает
|
||||||
`Сопровождение`, но целью не становится.
|
|
||||||
|
Решает её конвейер проекта — скилл `av-dev:code-resolve`,
|
||||||
|
**сценарий обслуживания**: он не заводит change и не пишет требований, потому что
|
||||||
|
у работы, не меняющей поведения, дельта-спек нет по построению. Тип записи там
|
||||||
|
только **предлагает** сценарий, а подтверждает его отсутствие дельт: разошлись —
|
||||||
|
работа останавливается, и это тот самый случай, когда тип, оставшийся от первой
|
||||||
|
формулировки, врёт. Задачу ведут не этим процессом — она решается как проект
|
||||||
|
привык, а этот скилл её только заводит и закрывает.
|
||||||
|
|
||||||
## Что видит машина, а что человек
|
## Что видит машина, а что человек
|
||||||
|
|
||||||
+9
-15
@@ -15,18 +15,13 @@
|
|||||||
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
|
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
|
||||||
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
||||||
| Поле места | **Категория** — полка домена беклога |
|
| Поле места | **Категория** — полка домена беклога |
|
||||||
| Цель (`goal:<слаг>`) | **обязательна** |
|
|
||||||
| Индекс | `BACKLOG.md` |
|
| Индекс | `BACKLOG.md` |
|
||||||
| Берётся в работу | да |
|
| Берётся в работу | да |
|
||||||
|
|
||||||
**Цель обязательна, и это единственный тип, у которого так.** Новая возможность
|
|
||||||
и есть содержание цели: подходящей нет — либо она заводится, либо перед тобой не
|
|
||||||
`feature`. `ready` без цели откажет.
|
|
||||||
|
|
||||||
## Алгоритм
|
## Алгоритм
|
||||||
|
|
||||||
1. **Найти цель или завести её.** Задача без цели, названная функцией, — самый
|
1. **Проверить, что возможность и правда новая.** Поведение расходится с уже
|
||||||
частый способ пронести в беклог работу, которой никто не заказывал.
|
заявленным — это `fix`, а не `feature`, и требования у него другие.
|
||||||
2. **Назвать границы** в разделе `Затрагивает`: эндпоинт или команда, таблица и
|
2. **Назвать границы** в разделе `Затрагивает`: эндпоинт или команда, таблица и
|
||||||
миграция, формат на диске, публичный тип пакета, внешний сервис. Названы
|
миграция, формат на диске, публичный тип пакета, внешний сервис. Названы
|
||||||
**границы, а не замысел**: «переписать хранилище на новый драйвер» — замысел,
|
**границы, а не замысел**: «переписать хранилище на новый драйвер» — замысел,
|
||||||
@@ -35,13 +30,12 @@
|
|||||||
3. **Написать критерии приёмки** — 2–5 проверяемых утверждений списком, у
|
3. **Написать критерии приёмки** — 2–5 проверяемых утверждений списком, у
|
||||||
каждого назван оракул. Не «работает корректно», а «повторный прогон даёт тот
|
каждого назван оракул. Не «работает корректно», а «повторный прогон даёт тот
|
||||||
же отпечаток — оракул: команда сверки».
|
же отпечаток — оракул: команда сверки».
|
||||||
4. **Сказать, какую строку «Завершения» цели задача двигает.** Одной строкой в
|
4. **Поставить её на место в списке.** На стройке место называет зависимость:
|
||||||
теле. Это защита от задачи «отрефакторить X»: она проваливается не потому,
|
`move <слаг> --after <шаг, без которого нельзя>`. На доработке место в
|
||||||
что невидима снаружи, а потому, что не находит строки, к которой относится.
|
очереди назначает груминг, и конец списка законен.
|
||||||
5. **Проверить, что задача одна.** Отвечается всё, но задача не делается одним
|
5. **Проверить, что задача одна.** Отвечается всё, но задача не делается одним
|
||||||
заходом и не мерджится целиком — это несколько задач под одной целью, дроби
|
заходом и не мерджится целиком — это несколько задач, дроби сразу
|
||||||
сразу ([split.md](split.md)). Промежуточного зонтика между целью и задачей
|
([split.md](split.md)) и ставь их в списке подряд.
|
||||||
нет.
|
|
||||||
6. **Реализация** — дело конвейера проекта, не этого скилла. Закрывается
|
6. **Реализация** — дело конвейера проекта, не этого скилла. Закрывается
|
||||||
`close <слаг> --implemented`: файл и строка удаляются, суть переезжает в
|
`close <слаг> --implemented`: файл и строка удаляются, суть переезжает в
|
||||||
`openspec/specs/` и документацию.
|
`openspec/specs/` и документацию.
|
||||||
@@ -49,8 +43,8 @@
|
|||||||
## Что видит машина, а что человек
|
## Что видит машина, а что человек
|
||||||
|
|
||||||
Схему типа судит `ready` на входе в работу: **наличие непустого** раздела
|
Схему типа судит `ready` на входе в работу: **наличие непустого** раздела
|
||||||
`Затрагивает`, **число** критериев (меньше двух — отказ, больше пяти —
|
`Затрагивает` и **число** критериев (меньше двух — отказ, больше пяти —
|
||||||
замечание) и цель. Наличие оракула проверяется **эвристикой** — словом «оракул»
|
замечание). Наличие оракула проверяется **эвристикой** — словом «оракул»
|
||||||
в пункте. `check` этого поимённо не говорит, а считает строкой здоровья
|
в пункте. `check` этого поимённо не говорит, а считает строкой здоровья
|
||||||
(`SKILL.md`, «Что механизировано, а что нет»).
|
(`SKILL.md`, «Что механизировано, а что нет»).
|
||||||
|
|
||||||
+1
-4
@@ -16,7 +16,6 @@
|
|||||||
| Обязательные разделы | **`Воспроизведение`**, `Затрагивает`, `Критерии приёмки` |
|
| Обязательные разделы | **`Воспроизведение`**, `Затрагивает`, `Критерии приёмки` |
|
||||||
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
||||||
| Поле места | **Категория** — полка домена беклога |
|
| Поле места | **Категория** — полка домена беклога |
|
||||||
| Цель (`goal:<слаг>`) | необязательна |
|
|
||||||
| Индекс | `BACKLOG.md` |
|
| Индекс | `BACKLOG.md` |
|
||||||
| Берётся в работу | да |
|
| Берётся в работу | да |
|
||||||
|
|
||||||
@@ -54,9 +53,7 @@
|
|||||||
почти всегда есть парный критерий: **прежнее поведение не сломалось**
|
почти всегда есть парный критерий: **прежнее поведение не сломалось**
|
||||||
(«ввод `а1` принимается по-прежнему»). Без него починка чинит одно и ломает
|
(«ввод `а1` принимается по-прежнему»). Без него починка чинит одно и ломает
|
||||||
соседнее.
|
соседнее.
|
||||||
6. **Цель не выдумывать.** `fix` служит работоспособности, а не направлению.
|
6. **Записать дефект в журнал** `docs/review.md` с пометкой «проскочил / пойман
|
||||||
Придуманная цель — то же враньё, от которого спасает тип.
|
|
||||||
7. **Записать дефект в журнал** `docs/review.md` с пометкой «проскочил / пойман
|
|
||||||
ревью». Проскочившие — проверочный набор для калибровки конвейера; пойманные с
|
ревью». Проскочившие — проверочный набор для калибровки конвейера; пойманные с
|
||||||
оракулом — лучшая опора для прохода ревью: проектные, воспроизводимые,
|
оракулом — лучшая опора для прохода ревью: проектные, воспроизводимые,
|
||||||
однажды оказавшиеся правдой.
|
однажды оказавшиеся правдой.
|
||||||
+58
-120
@@ -1,4 +1,4 @@
|
|||||||
# Формат записей и индексов
|
# Формат записей и индекса
|
||||||
|
|
||||||
Заголовок, мета-блок и строку индекса ставит `tasks.py add` — руками их не
|
Заголовок, мета-блок и строку индекса ставит `tasks.py add` — руками их не
|
||||||
пишут. Этот файл описывает **общую форму** любой записи и то, что проверяет
|
пишут. Этот файл описывает **общую форму** любой записи и то, что проверяет
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
| Тип | Файл | Одной строкой |
|
| Тип | Файл | Одной строкой |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 🎯 `goal` | [task-goal.md](task-goal.md) | возможность приложения |
|
|
||||||
| ✨ `feature` | [task-feature.md](task-feature.md) | снаружи появляется то, чего не было |
|
| ✨ `feature` | [task-feature.md](task-feature.md) | снаружи появляется то, чего не было |
|
||||||
| 🐞 `fix` | [task-fix.md](task-fix.md) | поведение расходится с заявленным |
|
| 🐞 `fix` | [task-fix.md](task-fix.md) | поведение расходится с заявленным |
|
||||||
| 🧹 `chore` | [task-chore.md](task-chore.md) | обслуживание, поведение не меняется |
|
| 🧹 `chore` | [task-chore.md](task-chore.md) | обслуживание, поведение не меняется |
|
||||||
@@ -25,7 +24,6 @@
|
|||||||
- **Тип:** fix
|
- **Тип:** fix
|
||||||
- **Категория:** Ядро — вернулась из работы: остаток писал нерешённое в журнал
|
- **Категория:** Ядро — вернулась из работы: остаток писал нерешённое в журнал
|
||||||
- **Зачем:** ввод «а1б2» ходит в a1 — игрок не видит, что ошибся, и винит игру
|
- **Зачем:** ввод «а1б2» ходит в a1 — игрок не видит, что ошибся, и винит игру
|
||||||
- **Теги:** goal:merge-robustness
|
|
||||||
|
|
||||||
Разбор хода читает первые два символа и молча выбрасывает остаток строки.
|
Разбор хода читает первые два символа и молча выбрасывает остаток строки.
|
||||||
|
|
||||||
@@ -55,8 +53,8 @@
|
|||||||
**эмодзи типа**, и она **производна**: её ставит `add` и чинит `check --fix`
|
**эмодзи типа**, и она **производна**: её ставит `add` и чинит `check --fix`
|
||||||
по полю меты. Второго дома у типа нет — эмодзи это его отображение, как
|
по полю меты. Второго дома у типа нет — эмодзи это его отображение, как
|
||||||
строка индекса это отображение файла.
|
строка индекса это отображение файла.
|
||||||
- **Форма заголовка — по типу.** Цель отвечает на «что приложение будет уметь»;
|
- **Форма заголовка — по типу.** `feature`, `fix` и `chore` отвечают на «что
|
||||||
`feature`, `fix` и `chore` — на «что нужно сделать», глаголом в неопределённой
|
нужно сделать», глаголом в неопределённой
|
||||||
форме, перед ним допускается «не»; `research` называет предмет разведки и
|
форме, перед ним допускается «не»; `research` называет предмет разведки и
|
||||||
формы действия **не несёт намеренно**. Почему так — SKILL.md, «Как написана
|
формы действия **не несёт намеренно**. Почему так — SKILL.md, «Как написана
|
||||||
задача». `check` считает заголовки не в форме действия и печатает число в
|
задача». `check` считает заголовки не в форме действия и печатает число в
|
||||||
@@ -67,9 +65,8 @@
|
|||||||
теги необязательны. Нераспознанные поля сохраняются: скрипт правит свои и не
|
теги необязательны. Нераспознанные поля сохраняются: скрипт правит свои и не
|
||||||
трогает чужие.
|
трогает чужие.
|
||||||
- **Тип — первым полем.** Он решает, что у записи вообще может быть: какие
|
- **Тип — первым полем.** Он решает, что у записи вообще может быть: какие
|
||||||
разделы обязательны, нужна ли цель, берётся ли она в работу, — и читается
|
разделы обязательны и берётся ли она в работу, — и читается раньше всего
|
||||||
раньше всего остального. Словарь **закрыт**: `goal` | `feature` | `fix` |
|
остального. Словарь **закрыт**: `feature` | `fix` | `chore` | `research`. Не подходит ни один — это сигнал, что в записи их два и
|
||||||
`chore` | `research`. Не подходит ни один — это сигнал, что в записи их два и
|
|
||||||
её надо разделить.
|
её надо разделить.
|
||||||
- **«Зачем» отвечает на «зачем нужна эта задача»** — состояние, остаток, боль.
|
- **«Зачем» отвечает на «зачем нужна эта задача»** — состояние, остаток, боль.
|
||||||
Не пересказ задачи: пересказ уже есть по ссылке. Живёт здесь, а не только в
|
Не пересказ задачи: пересказ уже есть по ссылке. Живёт здесь, а не только в
|
||||||
@@ -86,19 +83,16 @@
|
|||||||
Тело — не план реализации и не спецификация: принятое и реализованное переезжает
|
Тело — не план реализации и не спецификация: принятое и реализованное переезжает
|
||||||
в документацию проекта, а файл задачи удаляется.
|
в документацию проекта, а файл задачи удаляется.
|
||||||
|
|
||||||
### Поле места: «Категория» и «Секция»
|
### Поле места: «Категория»
|
||||||
|
|
||||||
Поле называет, **где числится строка**, и имя у него **зависит от типа**:
|
Поле называет **секцию беклога, в которой числится строка** — полку домена
|
||||||
|
(`Ядро`, `Инфра`, …), куда задачу положили и куда вернут, если она уйдёт в работу
|
||||||
|
и вернётся. **На стройке секция одна**, и поле называет её же: различать ей
|
||||||
|
нечего, но производность от заголовка индекса сохраняется и там.
|
||||||
|
|
||||||
| Тип | Поле | Значения | Что это |
|
Прежнее имя поля — **«Секция»**: так оно называлось у целей, указывая на часть
|
||||||
| --- | --- | --- | --- |
|
роадмапа. Разбор его по-прежнему принимает, `check` называет дрейфом, `check
|
||||||
| `goal` | **Секция** | `Запланировано`, `Направления`, `Сопровождение` | часть роадмапа: состояние очереди |
|
--fix` переименовывает.
|
||||||
| прочие | **Категория** | секции беклога проекта (`Ядро`, `Инфра`, …) | полка домена, на которой задача лежит |
|
|
||||||
|
|
||||||
Разные имена потому, что это **разные вещи**. У задачи это полка: куда её
|
|
||||||
положили и куда вернут, если она уйдёт в работу и вернётся. У цели оно называет
|
|
||||||
не полку, а место в очереди работ. Одно имя на два смысла их и смешивало; `check` называет
|
|
||||||
несовпадение дрейфом, `check --fix` переименовывает.
|
|
||||||
|
|
||||||
Имя самого места принадлежит **заголовку индекса** — файл на него лишь
|
Имя самого места принадлежит **заголовку индекса** — файл на него лишь
|
||||||
ссылается, и принадлежность сверяется по нижнему регистру.
|
ссылается, и принадлежность сверяется по нижнему регистру.
|
||||||
@@ -111,14 +105,18 @@
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| префикс `[goal]` / `[idea]` в H1 | поле **Тип** + эмодзи в H1; `[idea]` → `research` |
|
| префикс `[goal]` / `[idea]` в H1 | поле **Тип** + эмодзи в H1; `[idea]` → `research` |
|
||||||
| тег `kind:<род>` | поле **Тип** (род работы стал типом) |
|
| тег `kind:<род>` | поле **Тип** (род работы стал типом) |
|
||||||
| поле **Секция** у задачи | поле **Категория** |
|
| поле **Секция** | поле **Категория** |
|
||||||
|
| теги `goal:<слаг>` и `decomposed` | сняты: целей больше нет |
|
||||||
| поле **Хук** | поле **Зачем** |
|
| поле **Хук** | поле **Зачем** |
|
||||||
| мета одной строкой через `·` | мета списком, поле на строку |
|
| мета одной строкой через `·` | мета списком, поле на строку |
|
||||||
|
|
||||||
Единственное, чего `--fix` не делает сам, — **проставить тип записи, у которой
|
Чего `--fix` не делает сам — **решает за человека, каким быть типу**. Случаев
|
||||||
его неоткуда взять**: `feature` от `chore` машина не отличает, и подставленное
|
три, и все три уезжают пометкой `НЕОДНОЗНАЧНО`: тип, которого неоткуда взять
|
||||||
наугад значение врало бы ровно там, где по нему принимают решение. Такие записи
|
(`feature` от `chore` машина не отличает); тип вне словаря; и запись типа `goal`
|
||||||
он называет поимённо пометкой `НЕОДНОЗНАЧНО`.
|
— целей больше нет, а во что превращается эта, в задачу или в ничто, машина не
|
||||||
|
знает. Подставленное наугад значение врало бы ровно там, где по нему принимают
|
||||||
|
решение. Мёртвые теги и имя поля места при этом снимаются у **любой** записи,
|
||||||
|
включая ту, чей тип остался неразобранным.
|
||||||
|
|
||||||
### Затрагивает
|
### Затрагивает
|
||||||
|
|
||||||
@@ -147,8 +145,8 @@
|
|||||||
забыли назвать, она от отсутствующей не отличает. Раздела нет — отказ во взятии:
|
забыли назвать, она от отсутствующей не отличает. Раздела нет — отказ во взятии:
|
||||||
оценивать нечем.
|
оценивать нечем.
|
||||||
|
|
||||||
**У `goal` и `research` раздела нет** — у первой границы называют её задачи, у
|
**У `research` раздела нет** — её границы становятся известны, когда из разведки
|
||||||
второй они становятся известны, когда из разведки родятся задачи.
|
родятся задачи.
|
||||||
|
|
||||||
### Критерии приёмки
|
### Критерии приёмки
|
||||||
|
|
||||||
@@ -166,8 +164,7 @@
|
|||||||
что проверено больше проверенного, хуже, чем не проверять вовсе.
|
что проверено больше проверенного, хуже, чем не проверять вовсе.
|
||||||
|
|
||||||
**У `research` критериев нет** — её приёмка это записанный ответ, и описывается
|
**У `research` критериев нет** — её приёмка это записанный ответ, и описывается
|
||||||
она разделами «Вопрос» и «Куда ляжет ответ». **У `goal` их заменяет
|
она разделами «Вопрос» и «Куда ляжет ответ».
|
||||||
«Завершение».**
|
|
||||||
|
|
||||||
**Критерии — пол, но расхождение с ними есть дефект критериев.** Если приёмщик
|
**Критерии — пол, но расхождение с ними есть дефект критериев.** Если приёмщик
|
||||||
видит, что критерии закрыты, а суть задачи не достигнута, он **правит критерии и
|
видит, что критерии закрыты, а суть задачи не достигнута, он **правит критерии и
|
||||||
@@ -210,44 +207,6 @@
|
|||||||
на снятый тег при непустом разделе посоветует тег вернуть. Снять тег, не
|
на снятый тег при непустом разделе посоветует тег вернуть. Снять тег, не
|
||||||
опустошив раздел, — значит закольцевать себя между двумя советами.
|
опустошив раздел, — значит закольцевать себя между двумя советами.
|
||||||
|
|
||||||
## Файл цели
|
|
||||||
|
|
||||||
Форма та же, разделы и алгоритм — [task-goal.md](task-goal.md).
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# 🎯 Исход слияния не зависит от порядка доставки
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Направления
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Ради чего: точки из разных доставок сходятся в один часовой объект, и сегодня
|
|
||||||
исход столкновения зависит от порядка доставки, а не от содержания.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
- повторная доставка тех же точек в другом порядке даёт то же состояние;
|
|
||||||
- накопительная метрика за сутки не уменьшается после повторной доставки;
|
|
||||||
- в логе видно, какая из двух точек выиграла и почему.
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Задачи цели здесь не перечисляются.** Перечень даёт
|
|
||||||
`tasks.py list --goal <слаг>`; хранимый список стал бы третьим индексом и
|
|
||||||
поехал бы на первой же закрытой задаче.
|
|
||||||
- **Тег `decomposed`** отличает «цель ещё не разобрана» от «все её задачи
|
|
||||||
закрыты» — два состояния, у которых снаружи один и тот же признак: задач нет.
|
|
||||||
Пометка именно **тегом**, а не строкой в теле: только так она проверяется.
|
|
||||||
`check` напоминает о нём у цели без задач замечанием — неразобранная цель
|
|
||||||
законна и зелёного прогона не ломает; `check --fix` сам ставит его цели, у
|
|
||||||
которой задачи есть, а цель с тегом и без задач — прямое приглашение закрыть.
|
|
||||||
- Цель живёт в `ROADMAP.md` и **никогда** — в `BACKLOG.md`.
|
|
||||||
- **Достигнутая цель не исчезает.** `close <слаг> --implemented` удаляет файл и
|
|
||||||
переносит строку в секцию `Готово` с датой:
|
|
||||||
`- 2026-08-04 \`merge-order\` — Исход слияния не зависит от порядка доставки. …`
|
|
||||||
Ссылки на файл в ней нет — файл удалён, а битая ссылка это ошибка `check`.
|
|
||||||
Поведение живёт в спеках проекта; роадмап отвечает, **когда и в каком порядке**
|
|
||||||
оно появилось.
|
|
||||||
|
|
||||||
## Слаг
|
## Слаг
|
||||||
|
|
||||||
Латиница и цифры, kebab-case, без ведущих, хвостовых и двойных дефисов
|
Латиница и цифры, kebab-case, без ведущих, хвостовых и двойных дефисов
|
||||||
@@ -261,9 +220,9 @@
|
|||||||
проходом по всем местам, где слаг упомянут, иначе останутся битые ссылки,
|
проходом по всем местам, где слаг упомянут, иначе останутся битые ссылки,
|
||||||
которых никто не проверяет.
|
которых никто не проверяет.
|
||||||
|
|
||||||
## Индексы
|
## Индекс
|
||||||
|
|
||||||
Строка везде одной формы:
|
Строка одной формы:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
- [🐞 Заголовок дословно](items/slug.md) — зачем
|
- [🐞 Заголовок дословно](items/slug.md) — зачем
|
||||||
@@ -276,52 +235,47 @@
|
|||||||
|
|
||||||
| Файл | Что отвечает | Секции |
|
| Файл | Что отвечает | Секции |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `ROADMAP.md` | что приложение уже умеет и чего ещё не умеет | канонические и в этом порядке: `Запланировано`, `Направления`, `Сопровождение`, `Готово` (англ. `Planned`, `Directions`, `Operations`, `Done`) |
|
| `BACKLOG.md` | что **можно взять**, в значимом порядке | называет проект; на стройке ровно одна (умолчание `План`), на доработке сколько нужно (умолчание `Ядро`/`Инфра`) |
|
||||||
| `BACKLOG.md` | что **можно взять** — только задачи, **в порядке очереди** | категории проекта (по умолчанию Ядро/Инфра) |
|
|
||||||
| `REJECTED.md` | что ушло без реализации и почему | — |
|
| `REJECTED.md` | что ушло без реализации и почему | — |
|
||||||
|
|
||||||
Секции — **единственные заголовки `##` в индексе**: любой другой `##` в
|
Секции — **единственные заголовки `##` в индексе**: любой другой `##` в
|
||||||
преамбуле проверка сочтёт секцией.
|
преамбуле проверка сочтёт секцией.
|
||||||
|
|
||||||
**Порядок строк внутри секции беклога значим: это очередь.** Первая строка — то,
|
**Порядок строк внутри секции значим, и стадия решает, что он значит:** на
|
||||||
что делают следующим; назначает порядок человек на груминге, и двигают его
|
стройке зависимость, на доработке важность (SKILL.md, «Две стадии»). Назначает
|
||||||
`move --after` и `move --first`. Одно место из очереди изъято и **производно от
|
его человек — раскладывая шаги или на груминге, — и двигают его `move --after`
|
||||||
типа и заполненности**: **сырьё** (`research` без раздела «Вопрос») стоит в конце
|
и `move --first`. Одно место из очереди изъято и **производно от типа и
|
||||||
своей секции, потому что его не берут, и между берущимся оно каждый раз требует
|
заполненности**: **сырьё** (`research` без раздела «Вопрос») стоит в конце своей
|
||||||
|
секции, потому что его не берут, и между берущимся оно каждый раз требует
|
||||||
открыть файл, чтобы это понять. Проверяет `check`, переставляет `check --fix`,
|
открыть файл, чтобы это понять. Проверяет `check`, переставляет `check --fix`,
|
||||||
и человек этот порядок не назначает — иначе он был бы приоритетом, которого
|
и человек этот порядок не назначает — иначе он был бы решением, которого здесь
|
||||||
здесь нет.
|
нет.
|
||||||
|
|
||||||
**Секции «блокеры» среди них нет.** Блокер — состояние, а не полка: он живёт до
|
**Секции «блокеры» среди них нет.** Блокер — состояние, а не полка: он живёт до
|
||||||
ответа человека, а следы остаются вопросами в файлах задач.
|
ответа человека, а следы остаются вопросами в файлах задач.
|
||||||
Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила
|
Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила
|
||||||
бы правилу «эскалируем немедленно», поэтому `init` её не заводит, а `check`
|
бы правилу «эскалируем немедленно», поэтому `init` её не заводит, а `check`
|
||||||
говорит о ней в чужом беклоге. Переезжаешь с такой секцией — удали её. В секции
|
говорит о ней в чужом беклоге. Переезжаешь с такой секцией — удали её.
|
||||||
**`Запланировано`** очередь значима и обосновывается прозой; двигают строку
|
|
||||||
`move <slug> --section Запланировано --after <другой>`. В секции **`Готово`**
|
|
||||||
строки не той формы, что у прочих индексов: дата, слаг, заголовок — как в
|
|
||||||
`REJECTED.md`, и по той же причине (файла уже нет, ссылаться некуда).
|
|
||||||
|
|
||||||
**Секции роадмапа закреплены** — состав, полнота, единство языка и **порядок**
|
**Имена секций проект выбирает сам, а количество ограничено стадией:** на
|
||||||
проверяются `check`; категории беклога проект называет сам. Почему так —
|
стройке секция одна, потому что порядок там зависимость, и разложенный по полкам
|
||||||
SKILL.md. Порядок закреплён потому, что `Готово` копится: стоя первым,
|
список перестаёт быть планом. Проверяет `check`; слить секции сам он не берётся —
|
||||||
достигнутое отодвигает за экран то, ради чего роадмап открывают чаще всего.
|
в каком порядке пойдут строки слитых полок, знает только человек.
|
||||||
|
|
||||||
**Заголовок секции пишется с прописной и отбивается пустой строкой с обеих
|
**Заголовок секции пишется с прописной и отбивается пустой строкой с обеих
|
||||||
сторон** — во всех индексах, включая категории беклога, имена которых выбирает
|
сторон.** Отбивку правит `check --fix`; он же сводит написание места в мете файла
|
||||||
проект. Написание канонических секций и отбивку правит `check --fix`; он же
|
с заголовком индекса.
|
||||||
сводит написание места в мете файла с заголовком индекса.
|
|
||||||
|
|
||||||
Индексы **производны**: расходятся с файлом — правим индексы (`check --fix`).
|
Индекс **производен**: расходится с файлом — правим индекс (`check --fix`).
|
||||||
Строку руками не пишут.
|
Строку руками не пишут.
|
||||||
|
|
||||||
Отсюда же ответ на «а если оборвётся посередине». Мутация сперва проверяет всё
|
Отсюда же ответ на «а если оборвётся посередине». Мутация сперва проверяет всё
|
||||||
и складывает правки, и только потом пишет: сначала все временные файлы, потом
|
и складывает правки, и только потом пишет: сначала все временные файлы, потом
|
||||||
переименования подряд. Полной транзакции на несколько файлов файловая система не
|
переименования подряд. Полной транзакции на несколько файлов файловая система не
|
||||||
даёт, но окно сжато до цепочки переименований, а **всё, что в нём может
|
даёт, но окно сжато до цепочки переименований, а **всё, что в нём может
|
||||||
разъехаться, — производное**: файлы целы, индексы восстанавливает `check --fix`.
|
разъехаться, — производное**: файлы целы, индекс восстанавливает `check --fix`.
|
||||||
Поэтому отказ на второй задаче из пяти не оставляет первую переписанной при
|
Поэтому отказ на второй задаче из пяти не оставляет первую переписанной при
|
||||||
нетронутых индексах.
|
нетронутом индексе.
|
||||||
|
|
||||||
## `REJECTED.md`
|
## `REJECTED.md`
|
||||||
|
|
||||||
@@ -346,27 +300,24 @@ SKILL.md. Порядок закреплён потому, что `Готово`
|
|||||||
Разметка сверх типа. Тип полем, потому что он один и обязателен; теги — потому
|
Разметка сверх типа. Тип полем, потому что он один и обязателен; теги — потому
|
||||||
что их много и `list --tag` уже умеет отбирать по ним порцию разбора.
|
что их много и `list --tag` уже умеет отбирать по ним порцию разбора.
|
||||||
|
|
||||||
- `goal:<слаг>` — цель, которой служит задача. Обязателен **у `feature`**:
|
|
||||||
новая возможность и есть содержание цели. У `fix`, `chore` и `research` его
|
|
||||||
может не быть — они служат работоспособности, а не направлению.
|
|
||||||
- `question` — в файле есть неразобранный раздел «Вопросы».
|
- `question` — в файле есть неразобранный раздел «Вопросы».
|
||||||
- `decomposed` — на цели: разложена на задачи (см. «Файл цели»).
|
|
||||||
|
|
||||||
Тега `kind:<род>` больше нет: род работы стал типом. Оставшийся в файле `check`
|
Тегов `kind:<род>`, `goal:<слаг>` и `decomposed` больше нет: род работы стал
|
||||||
называет дрейфом, а `check --fix` снимает, перенеся значение в поле «Тип».
|
типом, а цели упразднены. Оставшиеся в файле `check` называет дрейфом, а `check
|
||||||
|
--fix` снимает (значение `kind:` при этом переезжает в поле «Тип»).
|
||||||
|
|
||||||
Отбор — `list --tag a,b`: перечисленные через запятую теги требуются **все
|
Отбор — `list --tag a,b`: перечисленные через запятую теги требуются **все
|
||||||
сразу** (это И, не ИЛИ). Тег, которого нет ни у одной задачи, `list` называет
|
сразу** (это И, не ИЛИ). Тег, которого нет ни у одной задачи, `list` называет
|
||||||
вслух: молчаливый ноль читается как «таких задач нет», а чаще это опечатка.
|
вслух: молчаливый ноль читается как «таких задач нет», а чаще это опечатка.
|
||||||
|
|
||||||
Свои теги проект заводит свободно (партия ревью `review-ГГГГ-ММ-ДД`, тема,
|
Свои теги проект заводит свободно (партия ревью `review-ГГГГ-ММ-ДД`, тема,
|
||||||
источник) — словарь не фиксирован. В индексы теги не выносим: индексы
|
источник) — словарь не фиксирован. В индекс теги не выносим: он
|
||||||
производны, отбор делает `list --tag`, а не глаза.
|
производен, отбор делает `list --tag`, а не глаза.
|
||||||
|
|
||||||
## Тест «готова к взятию»
|
## Тест «готова к взятию»
|
||||||
|
|
||||||
Задача готова, если из файла отвечаются четыре вопроса. Первый и четвёртый —
|
Задача готова, если из файла отвечаются три вопроса. Первый общий, второй и
|
||||||
общие, второй и третий у каждого типа свои и перечислены в его файле.
|
третий у каждого типа свои и перечислены в его файле.
|
||||||
|
|
||||||
1. **Что станет наблюдаемо иначе**, когда она сделана — снаружи: пользователю,
|
1. **Что станет наблюдаемо иначе**, когда она сделана — снаружи: пользователю,
|
||||||
владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет
|
владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет
|
||||||
@@ -379,26 +330,13 @@ SKILL.md. Порядок закреплён потому, что `Готово`
|
|||||||
`chore` — `Затрагивает`.
|
`chore` — `Затрагивает`.
|
||||||
3. **По чему видно, что закончено** — критерии приёмки с оракулами;
|
3. **По чему видно, что закончено** — критерии приёмки с оракулами;
|
||||||
у `research` вместо них `Куда ляжет ответ`.
|
у `research` вместо них `Куда ляжет ответ`.
|
||||||
4. **Какую часть «Завершения» своей цели она двигает** — у задачи с целью.
|
Не отвечается любой из трёх → это ещё не задача, а **сырьё**: тип `research` без
|
||||||
Строкой: «двигает пункт 2 «Завершения» — накопительная метрика перестаёт
|
раздела «Вопрос», место — конец секции, работа над ним — штурм.
|
||||||
уменьшаться». Это и есть защита от задачи «отрефакторить X»: она проваливает
|
|
||||||
тест не потому, что невидима снаружи, а потому, что не находит строки, к
|
|
||||||
которой относится. Заодно видно обратное — достаточен ли набор задач для
|
|
||||||
цели: строка «Завершения», к которой не относится ни одна задача, это
|
|
||||||
незакрытая часть возможности.
|
|
||||||
|
|
||||||
**У задачи без цели** (`fix`, `chore`, `research`) вопрос не задаётся: они
|
|
||||||
служат работоспособности, а не направлению.
|
|
||||||
|
|
||||||
Не отвечается первый, второй или третий вопрос → это ещё не задача, а **сырьё**:
|
|
||||||
тип `research` без раздела «Вопрос», место — конец секции, работа над ним —
|
|
||||||
штурм. Не отвечается четвёртый у `feature` → либо цель есть и не проставлена,
|
|
||||||
либо это не новая возможность.
|
|
||||||
|
|
||||||
Отвечается всё, но задача не делается одним заходом и не мерджится целиком →
|
Отвечается всё, но задача не делается одним заходом и не мерджится целиком →
|
||||||
это **несколько задач под одной целью**, дроби сразу. Промежуточного зонтика
|
это **несколько задач**, дроби сразу и ставь их в списке подряд. Промежуточного
|
||||||
между целью и задачей нет: тип `[epic]` упразднён, потому что зонтиком стала
|
зонтика между планом и задачей нет: тип `[epic]` упразднён, и цель, ставшая
|
||||||
сама цель.
|
зонтиком после него, упразднена тоже.
|
||||||
|
|
||||||
Тест применяется при заведении и при переоценке. К старым задачам, которых
|
Тест применяется при заведении и при переоценке. К старым задачам, которых
|
||||||
операция не касается, задним числом не применяется — беклог не переоформляют
|
операция не касается, задним числом не применяется — беклог не переоформляют
|
||||||
+5
-6
@@ -14,7 +14,6 @@
|
|||||||
| Обязательные разделы | `Вопрос`, `Куда ляжет ответ` |
|
| Обязательные разделы | `Вопрос`, `Куда ляжет ответ` |
|
||||||
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
||||||
| Поле места | **Категория** — полка домена беклога |
|
| Поле места | **Категория** — полка домена беклога |
|
||||||
| Цель (`goal:<слаг>`) | нет |
|
|
||||||
| Индекс | `BACKLOG.md` |
|
| Индекс | `BACKLOG.md` |
|
||||||
| Берётся в работу | да — **но только с заполненным «Вопросом»** |
|
| Берётся в работу | да — **но только с заполненным «Вопросом»** |
|
||||||
|
|
||||||
@@ -43,7 +42,8 @@
|
|||||||
| Место в секции беклога | **конец**, `check --fix` сносит туда сам | среди прочих |
|
| Место в секции беклога | **конец**, `check --fix` сносит туда сам | среди прочих |
|
||||||
| `tasks.py list --raw` | показывает | нет |
|
| `tasks.py list --raw` | показывает | нет |
|
||||||
|
|
||||||
Порядок строк в беклоге назначает человек — это приоритет (правило 4 скилла).
|
Порядок строк в беклоге назначает человек, и стадия решает, что он значит:
|
||||||
|
зависимость на стройке, важность на доработке (правило 4 скилла).
|
||||||
Место сырья **из него изъято**: оно производно от типа и заполненности, а не от
|
Место сырья **из него изъято**: оно производно от типа и заполненности, а не от
|
||||||
чьего-то решения, и потому его проверяет и чинит машина. Приоритетом оно не
|
чьего-то решения, и потому его проверяет и чинит машина. Приоритетом оно не
|
||||||
становится: сырьё не берут вовсе, и место в конце говорит именно это.
|
становится: сырьё не берут вовсе, и место в конце говорит именно это.
|
||||||
@@ -64,11 +64,10 @@
|
|||||||
3. **Ограничить рамками**, если разведка может утечь: сколько времени, какие
|
3. **Ограничить рамками**, если разведка может утечь: сколько времени, какие
|
||||||
источники, что заведомо вне.
|
источники, что заведомо вне.
|
||||||
4. **Провести разведку** и **записать ответ по названному адресу**. Числа — с
|
4. **Провести разведку** и **записать ответ по названному адресу**. Числа — с
|
||||||
провенансом: с командой или условиями, которыми получены. Число без источника
|
происхождением: с командой или условиями, которыми получены. Число без источника
|
||||||
проход ревью обязан читать как условие, а не как замер. Проводит её конвейер
|
проход ревью обязан читать как условие, а не как замер. Проводит её конвейер
|
||||||
проекта — в плагине `av-dev-code` это скилл `resolve`, сценарий разведки;
|
проекта — скилл `av-dev:code-resolve`, сценарий разведки; этот скилл её
|
||||||
плагина нет — разведка ведётся как проект привык, а этот скилл её только
|
только заводит и закрывает.
|
||||||
заводит и закрывает.
|
|
||||||
5. **Разложить исход на задачи** — если он их родил. Разведка кончается одним из
|
5. **Разложить исход на задачи** — если он их родил. Разведка кончается одним из
|
||||||
трёх: заведены задачи, записано знание, отказ. **Отказ — полноправный исход**:
|
трёх: заведены задачи, записано знание, отказ. **Отказ — полноправный исход**:
|
||||||
«проверили, не проблема» экономит работу.
|
«проверили, не проблема» экономит работу.
|
||||||
+1013
-940
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,86 @@
|
|||||||
|
# 1. Статус OpenSpec (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
OpenSpec несёт оба проекта: healthlog — 5 capability, 3530 строк спек, 9
|
||||||
|
архивных change за две недели; jellybit — 11 capability, 3895 строк, 43 архивных
|
||||||
|
change. При этом в трёх местах плагина написана ветка «проект без OpenSpec»
|
||||||
|
(`task-pipeline` предпосылки, `review-pipeline` предпосылки, `task-batch`
|
||||||
|
предпосылки) — и **не исполнялась ни разу**.
|
||||||
|
|
||||||
|
Проектные факты живут в пяти домах: `CLAUDE.md`, `docs/architecture.md`,
|
||||||
|
`openspec/specs/`, `openspec/config.yaml` → `context`, и планируется шестой —
|
||||||
|
`docs/review-brief.md`.
|
||||||
|
|
||||||
|
Расхождение измерено: у healthlog раздел «Хранилище» в `docs/architecture.md` —
|
||||||
|
950 строк (377–1328) против `openspec/specs/storage/spec.md` на 1337 строк. Два
|
||||||
|
описания одного поведения, никем не сверяемые. У jellybit того же нет:
|
||||||
|
`docs/specs/architecture.md` — 300 строк обзора, детали в 11 спеках. **Проект с
|
||||||
|
43 изменениями держит архитектуру втрое короче проекта с 9.**
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р1. OpenSpec — жёсткая предпосылка `av-dev-pipeline`.** Ветки деградации
|
||||||
|
удаляются, вместо них объявленная зависимость и проверка на старте. Зависимость
|
||||||
|
на уровне **плагина, а не процесса**: `av-dev-tasks`, `av-dev-git` и будущий
|
||||||
|
плагин документов от OpenSpec не зависят и работают на python/ansible-проектах.
|
||||||
|
|
||||||
|
*Причина:* непроверенная ветка деградации хуже честной строки «требуется
|
||||||
|
OpenSpec» — она даёт ложную уверенность, что проект без спек поедет.
|
||||||
|
|
||||||
|
**Р2. Нормативный дом поведения — `openspec/specs/`.** `architecture.md`
|
||||||
|
переопределяется как **обзор**: принципы, компоненты со ссылками на capability,
|
||||||
|
внешние форматы данных, раскладка, деплой, открытые вопросы. Поведения он не
|
||||||
|
описывает.
|
||||||
|
|
||||||
|
*Причина:* `opsx:archive` вливает дельты именно в `openspec/specs/` — любой
|
||||||
|
другой нормативный дом обязан синхронизироваться руками и разойдётся. Форма
|
||||||
|
jellybit это уже подтвердила на 43 изменениях.
|
||||||
|
|
||||||
|
**Р3. `openspec/config.yaml` → `context` держит только нужды генерации.** Язык,
|
||||||
|
правила именования capability, придирки валидатора RFC 2119 — и ссылки. Правило
|
||||||
|
ревью, пересказ конвенций и инварианты оттуда вычищаются: у них есть свои дома.
|
||||||
|
|
||||||
|
*Причина:* блок «Ревью (процесс, не артефакт)» в обоих `config.yaml` дословно
|
||||||
|
повторяет шаги 4 и 7 `task-pipeline`. Это второй дом для правила, которым владеет
|
||||||
|
плагин, и он разойдётся на первой же правке.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
Из Р1:
|
||||||
|
|
||||||
|
**С1.** Три места с веткой деградации переписываются на объявленную предпосылку
|
||||||
|
плюс проверку на старте (есть `openspec/`, разрешаются `opsx:*`) и внятный
|
||||||
|
отказ: `task-pipeline` предпосылки, `review-pipeline` предпосылки, `task-batch`
|
||||||
|
предпосылки.
|
||||||
|
|
||||||
|
**С2.** Описание `av-dev-pipeline` в маркетплейсе получает строку «требует
|
||||||
|
OpenSpec».
|
||||||
|
|
||||||
|
**С3. Факт для темы «объединять ли tasks и pipeline»:** объединение потянуло бы
|
||||||
|
зависимость от OpenSpec на управление задачами, которой там сейчас нет.
|
||||||
|
|
||||||
|
Из Р2:
|
||||||
|
|
||||||
|
**С4.** Правило «поведение — в спеку, устройство и границы — в архитектуру»
|
||||||
|
становится контрактом плагина документов и правилом шага «синк документации» в
|
||||||
|
`task-pipeline`.
|
||||||
|
|
||||||
|
**С5.** healthlog чистится **не разом**: раздел вычищается той задачей, которая
|
||||||
|
его касается. Нужен способ не потерять остаток — иначе 950 строк «Хранилища»
|
||||||
|
останутся навсегда.
|
||||||
|
|
||||||
|
**С6. Дыра, которую решение открывает:** «почему» после архивации. Сегодня
|
||||||
|
`CLAUDE.md` healthlog велит писать причину решения в `architecture.md` — а мы её
|
||||||
|
оттуда выселяем. Спеки нормативны и «почему» не держат; `design.md` живёт внутри
|
||||||
|
change и уезжает в архив. Либо ADR (как у jellybit), либо явное правило «почему
|
||||||
|
живёт в архивных change». **Первый вопрос следующей темы.**
|
||||||
|
|
||||||
|
Из Р3:
|
||||||
|
|
||||||
|
**С7.** `av-dev-pipeline` даёт образец `openspec/config.yaml` отдельным
|
||||||
|
reference — он владеет связью с OpenSpec. Заполняется при старте проекта и при
|
||||||
|
`adopt`.
|
||||||
|
|
||||||
|
**С8.** У обоих проектов из `config.yaml` вычищается блок «Ревью (процесс, не
|
||||||
|
артефакт)», пересказ конвенций и инвариантов.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# 2. Канон документов проекта (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Измерено по обоим проектам:
|
||||||
|
|
||||||
|
- **«Почему» не теряется — оно не находится.** `design.md` пишется почти всегда
|
||||||
|
(jellybit 39 из 43 архивных change, healthlog 9 из 9 — ≈285 КБ за две недели)
|
||||||
|
и имеет секции `Context` / `Goals / Non-Goals` / `Decisions` /
|
||||||
|
`Risks / Trade-offs`, то есть является ADR по структуре. Против этого ADR
|
||||||
|
руками: **6 записей у jellybit, четыре из них 13 июня — в день старта**; между
|
||||||
|
15 июня и 23 июля прошло ~40 изменений и ноль ADR. У healthlog ADR нет вовсе,
|
||||||
|
а настоящее ADR-рассуждение (отказ от DuckDB) лежит в разделе «Открытые
|
||||||
|
вопросы» файла `architecture.md`, потому что больше некуда.
|
||||||
|
- **Два плана.** `docs/plan.md` healthlog («порядок и его обоснование», 11 шагов)
|
||||||
|
и `<tasks>/PLAN.md` из `av-dev-tasks` («цели с обоснованием очереди прозой»)
|
||||||
|
— один артефакт под двумя именами.
|
||||||
|
- **Дубли спек у jellybit.** Из шести файлов `docs/specs/` три (`recognition`,
|
||||||
|
`review-ux`, `workflow`) описывают поведение, уже покрытое capability в
|
||||||
|
`openspec/specs/`.
|
||||||
|
- **`docs/drafts/` раскладывается без остатка:** `roadmap.md` → цели в «порядок»,
|
||||||
|
`conventions-backlog.md` → задачи `[idea]`, `logical-title-model.md` (293
|
||||||
|
строки, итог «сущность `title` не вводим») → намеренный отказ, то есть ADR.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р4. «Почему» — ADR как промоут поверх архива.** Обоснование по-прежнему пишет
|
||||||
|
`design.md`; ADR — короткая запись, цитирующая решение и ссылающаяся на архивный
|
||||||
|
`design.md`. Заводит её **шаг «синк документации» пайплайна по названному
|
||||||
|
триггеру** (дорогой откат / намеренный отказ от очевидного / пересмотр прежнего
|
||||||
|
решения), а не человек по вдохновению.
|
||||||
|
|
||||||
|
*Причина:* ручной ритуал эмпирически не выжил — 6 записей на 52 изменения.
|
||||||
|
Автоматический (`opsx:propose` пишет `design.md` всегда) работает и производит на
|
||||||
|
порядок больше. Чинить надо не дом, а индекс и критерий промоута.
|
||||||
|
|
||||||
|
**Р5. `docs/plan.md` растворяется в `<tasks>/PLAN.md`.** Файл удаляется, 11
|
||||||
|
шагов становятся целями в «порядке», ссылки в `CLAUDE.md` и паспорте
|
||||||
|
переводятся.
|
||||||
|
|
||||||
|
**Р6. Пути жёсткие, оба проекта приводятся к одному виду.** Плагин знает
|
||||||
|
раскладку поимённо; указателя вида `.docs.json` нет.
|
||||||
|
|
||||||
|
*Причина (словами владельца):* «так проще ориентироваться во множестве проектов,
|
||||||
|
а не видеть слегка похожую, но разную структуру в каждом. Все проекты малого и
|
||||||
|
среднего размера, проще подогнать их под одну структуру. Кроме того, у OpenSpec
|
||||||
|
тоже структура строгая». Цена принята сознательно: плагин перестаёт быть
|
||||||
|
переносимым на чужой репозиторий, а `adopt` из «поправь указатели» превращается в
|
||||||
|
«перенеси файлы».
|
||||||
|
|
||||||
|
**Р7. Конвенции и разведка — каталогами с README-индексом.** `docs/conventions/`
|
||||||
|
и `docs/research/`: путь жёсткий, нарезка внутри свободна. Схема хранилища —
|
||||||
|
**отдельный** `docs/database.md` (своя каденция: меняется миграцией, а не
|
||||||
|
архитектурным решением; гейт healthlog уже сверяет миграции с документацией).
|
||||||
|
Конвенции идентификаторов и именования — не схема, они в `conventions/`.
|
||||||
|
|
||||||
|
**Р8. Слота для черновиков нет.** Идея → задача `[idea]`; намеренный отказ →
|
||||||
|
ADR; порядок работ → `PLAN.md`; незрелое размышление → `opsx:explore` внутри
|
||||||
|
change.
|
||||||
|
|
||||||
|
## Канон
|
||||||
|
|
||||||
|
```
|
||||||
|
CLAUDE.md памятка агенту: что это, стек, инварианты, команды, слоты
|
||||||
|
docs/
|
||||||
|
passport.md зачем и для кого; чем НЕ является; сценарии; референсы
|
||||||
|
architecture.md как сложено — обзор: принципы, компоненты со ссылками
|
||||||
|
на capability, внешние границы, раскладка, деплой
|
||||||
|
database.md схема хранилища (там, где есть БД)
|
||||||
|
conventions/README.md + <тема>.md как пишем код; README держит правило промоута
|
||||||
|
research/README.md + <тема>.md что показала реальность: чужие форматы, живые данные
|
||||||
|
adr/README.md + template.md + ADR-*.md почему — промоут поверх архивных design.md
|
||||||
|
review-journal.md промахи конвейера ревью ← уточнено в теме 3
|
||||||
|
review-brief.md предмет ревью — см. тему 3 ← отменено в теме 3
|
||||||
|
tasks/ av-dev-tasks: items/, PLAN.md, BACKLOG.md, SPRINT.md, REJECTED.md
|
||||||
|
openspec/
|
||||||
|
config.yaml только нужды генерации + ссылки
|
||||||
|
specs/<capability>/spec.md что система делает — нормативно
|
||||||
|
changes/archive/ журнал изменений с design.md — сырьё для ADR
|
||||||
|
```
|
||||||
|
|
||||||
|
Слотов **нет** у: `docs/drafts/`, `docs/specs/`, `docs/plan.md`, `BRIEF.md`,
|
||||||
|
`docs/backlog/`, `docs/review/journal.md`.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С9. Переезд healthlog:** `architecture.md` 1611 → обзор (поведение уезжает в
|
||||||
|
`openspec/specs` по разделу за задачу); `conventions.md` →
|
||||||
|
`conventions/README.md`; `local-research.md` 1829 → `research/`; `plan.md` →
|
||||||
|
`docs/tasks/PLAN.md`; `backlog/` → `docs/tasks/`; завести `docs/adr/`.
|
||||||
|
|
||||||
|
**С10. Переезд jellybit:** `BRIEF.md` → `docs/passport.md` (заодно обновить — не
|
||||||
|
трогался с 13 июня); `docs/specs/architecture.md` → `docs/architecture.md`;
|
||||||
|
`docs/specs/database.md` → `docs/database.md`; `docs/specs/jellyfin-layout.md` →
|
||||||
|
`docs/research/`; `docs/specs/{recognition,review-ux,workflow}.md` сверить с
|
||||||
|
capability и удалить как дубли; `docs/review/journal.md` →
|
||||||
|
`docs/review-journal.md`; `drafts/` растворить по H; `docs/backlog/` →
|
||||||
|
`docs/tasks/`.
|
||||||
|
|
||||||
|
**С11. `adopt` меняет природу** — теперь он переносит файлы, а не правит
|
||||||
|
указатели. Разбирается в теме про старт проекта.
|
||||||
|
|
||||||
|
**С12. Открыто до [темы 6](06-docs-upkeep.md) (поддержание):** точная
|
||||||
|
формулировка триггера промоута в ADR; нужен ли механический `check` раскладки
|
||||||
|
документов, раз пути жёсткие; как не потерять остаток при постепенной чистке
|
||||||
|
`architecture.md`.
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# 3. Брифа ревью нет — бриф это и есть канон (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Контракт брифа — 413 строк, 13 разделов, отдельный файл `docs/review-brief.md`,
|
||||||
|
который каждый проход читает как истину. Заполнение на обоих проектах дало 841 и
|
||||||
|
734 строки, и `REMAINING.md` уже отметил, что часть разделов вырождается в
|
||||||
|
пересказ.
|
||||||
|
|
||||||
|
Разбор по разделам после решения [Р6](02-project-doc-canon.md) (жёсткие пути)
|
||||||
|
показал: **посредник между агентом и файлом не нужен, когда путь известен**.
|
||||||
|
Восемь из тринадцати разделов дублируют канон или снимаются жёсткими путями.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р9. Отдельного файла-брифа нет.** Проектную конкретику проходам дают документы
|
||||||
|
канона напрямую, по жёстким путям. Формулировка владельца: «артефакты в `docs` и
|
||||||
|
должны стать частями брифа, а для ревью достаточно дать ссылки на эти
|
||||||
|
артефакты».
|
||||||
|
|
||||||
|
*Причина:* один факт — один дом. Бриф был вторым домом для паспорта, инвариантов
|
||||||
|
и карты, а разошедшийся бриф хуже отсутствующего: он выглядит актуальным.
|
||||||
|
|
||||||
|
**Р10. Заводится `docs/security.md`.** Периметр **первой строкой** (целевой и
|
||||||
|
сегодняшний, если контур не развёрнут), недоверенный вход и его каналы, из чего
|
||||||
|
строятся пути и ключи, что разграничивает доступ, что чувствительнее чего, что
|
||||||
|
вне модели. Материал уже есть, но рассыпан: у healthlog — раздел
|
||||||
|
«Аутентификация» в `architecture.md` и строка про секреты в `CLAUDE.md`, у
|
||||||
|
jellybit — секреты в `conventions/config.md`. **Периметра нет ни у одного**, а
|
||||||
|
без него враждебный проход не выбирает между «открыт наружу» и «контур
|
||||||
|
доверенный».
|
||||||
|
|
||||||
|
**Р11. `review-journal.md` → `docs/review.md`:** журнал дефектов плюс настройка
|
||||||
|
конвейера под проект. Туда садится остаток брифа, который фактом о проекте не
|
||||||
|
является — типовые узлы, типовые ложноположительные, вопросы к проходам,
|
||||||
|
недоступно проверке.
|
||||||
|
|
||||||
|
*Причина:* все четыре — производные калибровки, и журнал им источник. `##
|
||||||
|
Вопросы к проходам` сам называет журнал главным источником; `### Перестали
|
||||||
|
проверять сознательно` требует ссылки на его запись.
|
||||||
|
|
||||||
|
**Р12. Журнал расширяется до всех воспроизведённых дефектов** с пометкой
|
||||||
|
«проскочил / пойман ревью». Проверочный набор для калибровки — выборка по
|
||||||
|
пометке.
|
||||||
|
|
||||||
|
*Причина:* пойманные дефекты с оракулом (768 МиБ пика, 5.019 с удержания
|
||||||
|
блокировки) сегодня не сохраняются нигде, кроме отчётов триажа в архиве change, а
|
||||||
|
они и есть лучшая опора для прохода — проектные, воспроизводимые, однажды
|
||||||
|
оказавшиеся правдой.
|
||||||
|
|
||||||
|
**Р13. Семантика гейта — в `CLAUDE.md`, расширением раздела «Команды».** Чем
|
||||||
|
краснеет безусловно и почему, где логи, что означает исход, чего в гейте
|
||||||
|
намеренно нет, **кто и когда обязан гонять дорогое вне гейта**, что запускать
|
||||||
|
запрещено (с путями). Гейт краснеет не только в ревью — это факт о проекте.
|
||||||
|
|
||||||
|
**Р14. Severity инвариантов дописывается в `CLAUDE.md`** рядом с формулировкой.
|
||||||
|
Контракт брифа сам называл это лучшим исходом; жёсткие пути делают возможным.
|
||||||
|
Оговорка «выведена по обратимости» исчезает вместе с пересказом.
|
||||||
|
|
||||||
|
## Канон после темы 3
|
||||||
|
|
||||||
|
```
|
||||||
|
CLAUDE.md что это, стек, инварианты с severity, команды,
|
||||||
|
семантика гейта, запреты, слоты
|
||||||
|
docs/
|
||||||
|
passport.md зачем и для кого; чем НЕ является; сценарии; референсы
|
||||||
|
architecture.md как сложено — обзор; окружение, внешние зависимости,
|
||||||
|
наблюдатель, характер потока
|
||||||
|
database.md схема хранилища; представление данных и настройки
|
||||||
|
с числовым значением (таймаут занятости, лимит тела,
|
||||||
|
режим журналирования, ретеншен)
|
||||||
|
security.md периметр первой строкой; недоверенный вход; из чего
|
||||||
|
строятся пути и ключи; разграничение; что вне модели
|
||||||
|
conventions/README.md + <тема>.md
|
||||||
|
research/README.md + <тема>.md наблюдения и измеренные числа с провенансом
|
||||||
|
adr/README.md + template.md + ADR-*.md
|
||||||
|
review.md настройка конвейера под проект + журнал дефектов
|
||||||
|
tasks/ av-dev-tasks
|
||||||
|
openspec/
|
||||||
|
config.yaml, specs/<capability>/spec.md, changes/archive/
|
||||||
|
```
|
||||||
|
|
||||||
|
Слотов **нет** у: `docs/review-brief.md`, `docs/drafts/`, `docs/specs/`,
|
||||||
|
`docs/plan.md`, `BRIEF.md`, `docs/backlog/`, `docs/review-journal.md`.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С13. Скилл `project-brief` растворяется.** Заведение недостающих документов
|
||||||
|
канона — часть скилла старта/адаптации ([тема 5](05-project-start-lifecycle.md),
|
||||||
|
требование [Т1](README.md)).
|
||||||
|
|
||||||
|
**С14. Девять charter'ов переписываются второй раз.** Сейчас каждый читает «из
|
||||||
|
раздела `## X` брифа»; станет — из файла канона. **Цена названа вслух:** первая
|
||||||
|
переписка (вынос в плагин) осталась незамеренной — `REMAINING.md`, пункт 1.
|
||||||
|
Вторая делает замер по четырём реальным находкам healthlog **обязательным, а не
|
||||||
|
желательным**: два неизмеренных изменения подряд в том самом месте, где
|
||||||
|
присваивается severity.
|
||||||
|
|
||||||
|
**С15. Теряется соседство фактов, и charter обязан сшивать.** Контракт
|
||||||
|
настаивал, что замер становится находкой только рядом с настройкой: «768 МиБ
|
||||||
|
пика» — аномалия, лишь если известно, что запись лежит сжатой и распаковывается
|
||||||
|
целиком; «5.019 с удержания блокировки» — отказ соседа, лишь если известен
|
||||||
|
таймаут занятости. Теперь это `research/` и `database.md`, и charter'ы `ops`,
|
||||||
|
`adversary`, `reimpl` обязаны прямо говорить «собери из этих двух», иначе проход
|
||||||
|
снимет верное число и честно понизит находку до гипотезы.
|
||||||
|
|
||||||
|
**С16. Деградация становится поразрядной** — и это лучше прежнего «нет брифа →
|
||||||
|
деградирует всё». Нет `security.md` — деградирует `adversary`; нет `research/` —
|
||||||
|
числа неизвестны `ops`, `adversary` и `reimpl`; нет `passport.md` —
|
||||||
|
архитектурный проход теряет границу домена. Каждый проход пишет свою строку в
|
||||||
|
границы покрытия.
|
||||||
|
|
||||||
|
**С17. Открытый вопрос из `REMAINING.md` закрыт:** раздел `## Триггеры`
|
||||||
|
удаляется вместе с брифом. Правило выбора профиля остаётся в скилле конвейера;
|
||||||
|
проектная конкретизация, если понадобится, — в `docs/review.md`.
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# 4. Границы плагинов (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Связь `tasks` ↔ `pipeline` уже сделана **ролями, а не именами**: скиллы говорят
|
||||||
|
«пайплайн проекта», «владелец спринта», «тот, кто ведёт задачи». Жёсткая ссылка
|
||||||
|
по имени ровно одна — `task-pipeline:112` на канонический текст правила про
|
||||||
|
остаток внутри `session`, и рядом обработан случай «плагин не подключён».
|
||||||
|
|
||||||
|
Слоты `CLAUDE.md` при этом дублировались уже внутри одного плагина: шесть у
|
||||||
|
`tasks`, семь у `session`, три пары — одно и то же. Темы 2–3 растворили ещё
|
||||||
|
часть: «куда переезжает суть» отвечает канон, «оракулы» — семантика гейта
|
||||||
|
(решение [Р13](03-review-brief-is-canon.md)), «где живёт разбор процесса» —
|
||||||
|
`docs/review.md` (решение [Р11](03-review-brief-is-canon.md)). Из тринадцати
|
||||||
|
остаётся около четырёх.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р15. Три плагина: `av-dev-pm`, `av-dev-pipeline`, `av-dev-git`.**
|
||||||
|
|
||||||
|
- **`av-dev-pm`** (бывший `av-dev-tasks`) — управление продуктом: канон
|
||||||
|
документов, задачи, цели, спринты, старт и адаптация проекта. Владеет всем
|
||||||
|
`docs/`, включая `docs/tasks/`.
|
||||||
|
- **`av-dev-pipeline`** — исполнение: SDD-цикл, конвейер ревью, девять агентов.
|
||||||
|
- **`av-dev-git`** — стиль коммитов; работает в любом репозитории.
|
||||||
|
|
||||||
|
*Причина (словами владельца):* «пайплайн можно и переиспользовать в других
|
||||||
|
проектах с более простым подходом к управлению». Это подтверждается разбором:
|
||||||
|
пайплайн зависит от **файлов канона и от OpenSpec, а не от плагина** `av-dev-pm`.
|
||||||
|
В чужом проекте нужных файлов нет — включается поразрядная деградация (следствие
|
||||||
|
16), и это штатный режим, а не поломка.
|
||||||
|
|
||||||
|
*Имя:* `pm` = product management, «объединение всех операций по управлению
|
||||||
|
продуктом», и согласуется с `av-dev-git`.
|
||||||
|
|
||||||
|
**Р16. Граница «пайплайн не закрывает задачу» снимается.** Закрывает задачу и
|
||||||
|
двигает строки между `SPRINT.md` / `BACKLOG.md` / `REJECTED.md` **агент-
|
||||||
|
оркестратор** — `task-pipeline` и `task-batch`, а не сабагенты внутри них. Зовёт
|
||||||
|
он `tasks.py` через слот «Команда учёта задач» в `CLAUDE.md`.
|
||||||
|
|
||||||
|
Слот, следовательно, **не исчезает, а становится мостом между плагинами** — и
|
||||||
|
заодно тем, чего в чужом проекте нет, отчего пайплайн там работает как прежде:
|
||||||
|
докладывает исход, записей учёта не трогает.
|
||||||
|
|
||||||
|
**Р17. `av-dev-backlog` помечается устаревшим и остаётся** до перевода jellybit.
|
||||||
|
*(заменено на [тему 30](30-av-dev-backlog-removed.md): плагин удалён раньше
|
||||||
|
этого срока — условие пережило свою причину.)* Описание переписывается так,
|
||||||
|
чтобы не ловить триггер «добавь задачу в беклог» — иначе агент выбирает между
|
||||||
|
ним и `av-dev-pm` случайно.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С18. Переименование `av-dev-tasks` → `av-dev-pm`** тянет `plugin.json`,
|
||||||
|
`marketplace.json` и пространство имён скиллов: `av-dev-tasks:session` →
|
||||||
|
`av-dev-pm:session`, включая ссылку из `task-pipeline:112`.
|
||||||
|
|
||||||
|
**С19. Раздел «Стимулы, которые процесс создаёт» в `session` переписывается.**
|
||||||
|
Снятая граница выбила механическую опору у трёх защит: «сжать задачу до
|
||||||
|
остатка», «занизить урожай», «занизить критерии приёмки» — во всех трёх приёмщик
|
||||||
|
и исполнитель теперь совпадают. Остаются: **отчёт триажа** в
|
||||||
|
`openspec/changes/<id>/review/` (независимый артефакт, `task-batch` уже сверяет
|
||||||
|
полноту ревью по нему, а не по прозе исполнителя), **`SPRINT.md` под git** с
|
||||||
|
видимой историей и **`reopen <slug> --reason`** — закрытие не окончательно,
|
||||||
|
приёмка человеком на сессии его отменяет. Раздел обязан назвать их поимённо,
|
||||||
|
иначе обещает защиту, которой нет.
|
||||||
|
|
||||||
|
**С20. Конфликт владения `docs/tasks/` снят** — канон и задачи теперь в одном
|
||||||
|
плагине.
|
||||||
|
|
||||||
|
**С21. Скилл `adopt` из `av-dev-tasks` поглощается** скиллом адаптации проекта
|
||||||
|
уровня канона (требование [Т1](README.md)). Разбирается в [теме
|
||||||
|
5](05-project-start-lifecycle.md).
|
||||||
|
|
||||||
|
**С22. Состав `av-dev-pm`:** `tasks`, `session` (есть), `docs` — ведение канона,
|
||||||
|
`project` — старт, adopt, check, upgrade ([тема
|
||||||
|
5](05-project-start-lifecycle.md)).
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# 5. Старт проекта и жизненный цикл под каноном (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Требование [Т1](README.md): прийти в любой старый проект и перевести на текущие
|
||||||
|
рельсы; канон сам меняется, значит уже приведённые проекты тоже повышаются.
|
||||||
|
|
||||||
|
Существующий `adopt` (уровень задач) даёт готовую форму: **`scan` — только
|
||||||
|
чтение, карта → суждение человека → `apply` — запись одним проходом**, с отказом
|
||||||
|
до первой записи при неверной карте и с обязательным разделом «не разложилось»
|
||||||
|
поимённо. Форма переносится на уровень канона как есть.
|
||||||
|
|
||||||
|
Четыре операции различаются не поровну: `adopt`, `check` и `upgrade` — одна
|
||||||
|
машина сравнения с разными исходами, а `init` — принципиально другой режим,
|
||||||
|
разговор, а не сверка.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р18. Два скилла: `av-dev-pm:init` и `av-dev-pm:canon`.** `init` — интервью по
|
||||||
|
входному брифу для нового проекта. `canon` — привести к канону: `check`,
|
||||||
|
`adopt`, `upgrade` одной машиной.
|
||||||
|
|
||||||
|
**Р19. Скелет канона заводится целиком, незаполненное называется пустым.** Все
|
||||||
|
файлы канона есть с первого дня, но незаполненный держит **одну честную
|
||||||
|
информативную строку**: «наблюдений на живых данных нет — внешний источник один,
|
||||||
|
формат документирован», «прецедентов не накоплено», «внешних зависимостей нет,
|
||||||
|
смотри на диск и на СУБД».
|
||||||
|
|
||||||
|
*Причина:* это тот же принцип, что был в контракте брифа, поднятый на уровень
|
||||||
|
файлов. Проход читает такую строку **как факт**, а не как пробел, и не тратит
|
||||||
|
обязательный вопрос впустую. Отсутствие файла он прочитать не может никак.
|
||||||
|
|
||||||
|
**Защита от вырождения в заглушки** берётся у `tasks.py`: пока на месте стоит
|
||||||
|
плейсхолдер шаблона, `check` о нём напоминает. Строка «TBD» — это не «пустое
|
||||||
|
названо пустым», и `check` обязан их различать.
|
||||||
|
|
||||||
|
**Р20. Скрипт `docs.py` плюс версия канона в `docs/.pm.json`.** Отдельный
|
||||||
|
скрипт, не расширение `tasks.py`: рефакторинг 2421 работающей строки ради
|
||||||
|
удобства вызова не окупается. `docs.py check` зовёт `tasks.py check` для своей
|
||||||
|
части.
|
||||||
|
|
||||||
|
**Граница механизируемого объявляется вслух — иначе `check` соврёт.**
|
||||||
|
|
||||||
|
| Проверяет `docs.py` | Судит агент |
|
||||||
|
| --- | --- |
|
||||||
|
| отсутствующие пути канона | смысловой дубль (`docs/specs/recognition.md` против capability) |
|
||||||
|
| файлы в `docs/` вне канона | поведение, оставшееся в `architecture.md` |
|
||||||
|
| битые относительные ссылки | протухший факт, разошедшийся с кодом |
|
||||||
|
| версия канона и её отставание | достаточность честной строки в пустом слоте |
|
||||||
|
| нетронутый плейсхолдер шаблона | |
|
||||||
|
|
||||||
|
`check`, отчитавшийся «канон соблюдён» на проекте, где из шести файлов три
|
||||||
|
лишние, хуже отсутствующего.
|
||||||
|
|
||||||
|
## Порядок интервью `init` — зависимость, а не удобство
|
||||||
|
|
||||||
|
Цель и потребители → чем это **не** является и мера успеха → периметр и что
|
||||||
|
недоверенное → стек, хранилище, необратимое → чем краснеет гейт → первые цели в
|
||||||
|
`PLAN.md`. Каждый блок опирается на ответ предыдущего.
|
||||||
|
|
||||||
|
Вход — свободный текст «что мне нужно и почему» (образец формы: `BRIEF.md`
|
||||||
|
jellybit, 6 КБ). После `init` его дом — `passport.md`; отдельным файлом он не
|
||||||
|
остаётся.
|
||||||
|
|
||||||
|
**`init` физически не производит полный канон.** В новом репозитории нет кода, а
|
||||||
|
`architecture.md`, `database.md`, `conventions/` и `research/` выводятся из него.
|
||||||
|
Они заводятся скелетом с честной строкой («архитектуры пока нет: кода нет,
|
||||||
|
заводится первой задачей») и наполняются шагом синка документации.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С23. `docs/.pm.json` поглощает `<tasks>/.tasks.json`.** Меняется цепочка
|
||||||
|
разрешения в `tasks.py` — сегодня он ищет `.tasks.json` вверх от текущего
|
||||||
|
каталога. Нужен переходный период либо чтение обоих.
|
||||||
|
|
||||||
|
**С24. `tasks.py adopt` становится шагом внутри `canon adopt`**, а не отдельной
|
||||||
|
пользовательской операцией: `docs/tasks/` — часть той же раскладки.
|
||||||
|
|
||||||
|
**С25. Версия канона — целое число**, не semver: у канона нет обратной
|
||||||
|
совместимости, есть только «приведён» и «не приведён».
|
||||||
|
|
||||||
|
**С26. Журнал изменений канона** живёт в плагине —
|
||||||
|
`av-dev-pm/skills/canon/references/changelog.md`, запись на версию: что
|
||||||
|
добавилось, что переехало, что удалено, что сделать проекту.
|
||||||
|
|
||||||
|
**С27. Открыто до [темы 6](06-docs-upkeep.md):** звать ли `docs.py check` из
|
||||||
|
гейта проекта. У healthlog `task gate` уже сверяет миграции с документацией, так
|
||||||
|
что место есть; но гейт принадлежит проекту, и плагин может только рекомендовать
|
||||||
|
строкой в отчёте.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# 6. Поддержание документов по ходу разработки (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Гейт healthlog **уже изобрёл нужный механизм** для одного документа —
|
||||||
|
`scripts/gate.py:177-181`: миграция изменена, а `docs/database.md` нет → `FAIL`.
|
||||||
|
Документ канона сверяется с кодом красным гейтом, а не напоминанием.
|
||||||
|
|
||||||
|
Против этого — прямое доказательство, что́ не работает: у `adr/` был список
|
||||||
|
триггеров прозой («выбор технологии, структурные решения, дорогой откат,
|
||||||
|
намеренный отказ»), и он дал **6 записей на 43 изменения**. Прозаический триггер,
|
||||||
|
который некому проверить, не срабатывает.
|
||||||
|
|
||||||
|
Механизируемы три документа из десяти: `database.md` (миграция), `architecture.md`
|
||||||
|
(capability в `openspec/specs/` без упоминания в обзоре), `tasks/` (`tasks.py
|
||||||
|
check`). Плюс `openspec/specs/` вливает `opsx:archive`.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р21. Принуждённое отрицание в докладе шага синка.** Шаг обязан назвать
|
||||||
|
**каждый** документ канона: обновлён — чем, либо «не требуется, потому что…».
|
||||||
|
Нетронутые группируются одной строкой с общей причиной.
|
||||||
|
|
||||||
|
*Причина:* это тот же приём, что «границы покрытия» в отчёте ревью и «пустой
|
||||||
|
пункт называется пустым» в каноне — и единственный, о котором в этом репозитории
|
||||||
|
есть данные, что он работает. Отличить «не написал» от «написал, что не
|
||||||
|
требуется» можно только тогда, когда отрицание обязательно.
|
||||||
|
|
||||||
|
**Триггер ADR, окончательная формулировка.** Запись заводится, когда верно одно
|
||||||
|
из трёх: **дорогой откат** (переделка стоит дороже переписывания одного файла);
|
||||||
|
**намеренный отказ** от очевидного подхода; **пересмотр прежнего решения** — тогда
|
||||||
|
у старой записи обязателен статус «заменено на». Не заводится для рутины и для
|
||||||
|
того, что видно из кода и `git log`. Источник — архивный `design.md`: ADR его
|
||||||
|
цитирует и на него ссылается, а не пересказывает.
|
||||||
|
|
||||||
|
**Р22. `docs.py check` обязателен в гейте проекта.** Скилл `canon` при адаптации
|
||||||
|
добавляет шаг и печатает это в отчёте.
|
||||||
|
|
||||||
|
*Причина:* гейт — единственный общий станок, который нельзя пропустить. Проверка,
|
||||||
|
которую зовёт агент, может быть не позвана; прецедент в самом healthlog уже есть.
|
||||||
|
|
||||||
|
Сверка «миграция изменена — `database.md` нет» **обобщается**: путь миграций
|
||||||
|
проекта записывается в `docs/.pm.json` рядом с версией канона, и `docs.py` делает
|
||||||
|
эту проверку сам, а не каждый проект заново.
|
||||||
|
|
||||||
|
**Р23. Остаток чистки помечается маркером и считается числом.** Неразобранный
|
||||||
|
раздел получает `<!-- канон: поведение → openspec/specs/<capability> -->`,
|
||||||
|
`docs.py` считает маркеры и печатает остаток. Закрывается порциями, как
|
||||||
|
переоценка задач.
|
||||||
|
|
||||||
|
**Маркеры гейт не красят.** Это долг, а не отказ: покрасневший гейт на первом
|
||||||
|
маркере сделал бы постепенный переезд невозможным, а разовый — обязательным.
|
||||||
|
Число печатается и убывает на глазах.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С28. Шаг 9 `task-pipeline` переписывается** из четырёх пунктов прозой в
|
||||||
|
построчный доклад по документам канона.
|
||||||
|
|
||||||
|
**С29. `promote.md`, шаг 3, переписывается:** «вычеркнуть пункт из брифа,
|
||||||
|
правило переезжает в перечень механизированного в разделе `## Карта`» → перечень
|
||||||
|
механизированного живёт в `conventions/README.md`. Брифа нет.
|
||||||
|
|
||||||
|
**С30. `docs/.pm.json` держит не только версию канона**, но и пути, нужные
|
||||||
|
проверкам: каталог миграций — как минимум.
|
||||||
|
|
||||||
|
**С31. `docs.py check` получает две сверки с кодом**, а не только раскладку:
|
||||||
|
миграции ↔ `database.md`, capability ↔ упоминание в `architecture.md`.
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# 7. Раскладка скиллов и доставка скриптов (2026-08-03)
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р24. Пять скиллов в `av-dev-pm`.**
|
||||||
|
|
||||||
|
```
|
||||||
|
av-dev-pm/skills/
|
||||||
|
init/ интервью по брифу → канон нового проекта
|
||||||
|
canon/ раскладка: check / adopt / upgrade
|
||||||
|
docs/ содержимое канона: ADR из архивного design.md, промоут конвенций,
|
||||||
|
запись в research/ и review.md, чистка architecture.md
|
||||||
|
tasks/ формат и содержимое задач
|
||||||
|
session/ ритуал спринта
|
||||||
|
```
|
||||||
|
|
||||||
|
*Причина отдельного `docs`:* правила ведения содержимого канона обязаны жить у
|
||||||
|
владельца канона, а не в шаге синка чужого плагина — иначе проект без пайплайна
|
||||||
|
документацию вести не может. Это работает потому, что **вызов скилла через
|
||||||
|
пространство имён между плагинами возможен**, в отличие от
|
||||||
|
`$CLAUDE_PLUGIN_ROOT`: `task-pipeline` уже зовёт `opsx:propose` и
|
||||||
|
`av-dev-pipeline:review-pipeline`. Шаг синка зовёт `av-dev-pm:docs`, а в чужом
|
||||||
|
проекте деградирует до прозаического списка.
|
||||||
|
|
||||||
|
Симметрия, по которой резалось: **раскладка и содержимое разделены и для
|
||||||
|
документов, и для задач** — `canon` / `docs`, `tasks` / `session`.
|
||||||
|
|
||||||
|
**Р25. Скрипты не копируются — живут вместе со скиллами.** Три вызывающих, три
|
||||||
|
способа дотянуться:
|
||||||
|
|
||||||
|
| Кто зовёт | Как |
|
||||||
|
| --- | --- |
|
||||||
|
| скиллы `tasks`, `canon`, `docs` | `$CLAUDE_PLUGIN_ROOT` — свой плагин, работает всегда |
|
||||||
|
| `task-pipeline`, `task-batch` | **вызов скилла** `av-dev-pm:tasks`, а не путь |
|
||||||
|
| гейт проекта | путь переменной с умолчанием на канонический путь маркетплейса; пишет `canon adopt`, внятный красный отказ, если не найден |
|
||||||
|
|
||||||
|
**Слот «Команда учёта задач» всё равно исчезает** — но снимает его не копия, а
|
||||||
|
**вызов скилла через пространство имён**. Тот же приём, которым шаг синка зовёт
|
||||||
|
`av-dev-pm:docs` (решение Р24): чужой плагин зовёт скилл, скилл разрешает свой
|
||||||
|
`$CLAUDE_PLUGIN_ROOT` сам. Путь наружу не выносится вовсе.
|
||||||
|
|
||||||
|
*Первоначально здесь было решено вендорить `scripts/tasks.py` и
|
||||||
|
`scripts/docs.py` в проект. Отменено после проверки фактов:*
|
||||||
|
|
||||||
|
- **CI нет ни в одном проекте** (ни `.github`, ни woodpecker, ни drone).
|
||||||
|
Pre-commit есть только у jellybit — `lefthook` с gofmt/vet/lint/test/gitleaks —
|
||||||
|
и гоняется на той же машине, где установлен плагин. Довод «не работает в CI и
|
||||||
|
у человека без Claude Code» оказался гипотетическим.
|
||||||
|
- **Пара «источник — копия» существует и без вендоринга.** Установленный
|
||||||
|
маркетплейс — git-клон; на момент разбора он стоял на `092d07c`, на четыре
|
||||||
|
коммита позади `master`, и `av-dev-tasks` с `av-dev-pipeline` в нём
|
||||||
|
отсутствовали вовсе. Довод «вендоринг создаёт вторую копию» был слабее, чем
|
||||||
|
подан.
|
||||||
|
- **Обновление маркетплейса — одна точка на все проекты.** При вендоринге каждый
|
||||||
|
проект повышается отдельно, и проекты расходятся друг с другом — ровно та
|
||||||
|
разнородность, против которой принято решение [Р6](02-project-doc-canon.md).
|
||||||
|
|
||||||
|
**Р26. Имени у процесса нет — процесс это `av-dev`.** Маркетплейс уже
|
||||||
|
`av-dev-skills`, плагины `av-dev-*`; в `CLAUDE.md` проекта пишется «процесс
|
||||||
|
av-dev, канон версии N». Имя, которое нигде не работает, — украшение.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С32. Решение P уточняется:** оркестратор закрывает задачи **вызовом скилла**
|
||||||
|
`av-dev-pm:tasks`, а не запуском скрипта по пути. Плагина в проекте нет — вызов
|
||||||
|
не разрешается, и пайплайн, как прежде, только докладывает исход.
|
||||||
|
|
||||||
|
**С33. Слот исчезает из двух скиллов** — `tasks` (слот 6) и `session` (слот 7),
|
||||||
|
— и из текстов `task-pipeline` и `task-batch`, которые на него ссылаются.
|
||||||
|
|
||||||
|
**С34. `canon upgrade` отвечает за раскладку и версию в `docs/.pm.json`.**
|
||||||
|
Скрипты обновляются обновлением маркетплейса, а не проектом.
|
||||||
|
|
||||||
|
**С35. Скрипты живут в `av-dev-pm/skills/{tasks,canon}/scripts/`.** `docs.py` —
|
||||||
|
в `canon`, потому что раскладку проверяет он.
|
||||||
|
|
||||||
|
**С36. `canon check` сверяет версию канона проекта с версией установленного
|
||||||
|
плагина** и говорит, кто отстал. Это нужно и без вендоринга: маркетплейс —
|
||||||
|
git-клон, обновляется явно, и на момент разбора отставал на четыре коммита.
|
||||||
|
|
||||||
|
**С37. Установленный маркетплейс требует обновления перед любой работой** —
|
||||||
|
сейчас в нём нет ни `av-dev-tasks`, ни `av-dev-pipeline`. Это первый шаг выката
|
||||||
|
([тема 8](08-rollout-order.md)), иначе проверять будет нечего.
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# 8. Порядок выката (2026-08-03)
|
||||||
|
|
||||||
|
## Объём
|
||||||
|
|
||||||
|
Ссылок на бриф — **168 строк в 19 файлах** `av-dev-pipeline`, из них ~48 уходят
|
||||||
|
вместе с удаляемыми `project-brief/SKILL.md`, `references/project-brief.md` и
|
||||||
|
`references/brief-template.md`. Остальное переписывается на пути канона.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р27. Инструмент строится целиком, потом проверяется.** Не пилот руками.
|
||||||
|
|
||||||
|
*Риск принят сознательно:* если замер покажет деградацию severity, чинить
|
||||||
|
придётся канон, зашитый к тому моменту в три скилла, скрипт и мигрированные файлы
|
||||||
|
healthlog.
|
||||||
|
|
||||||
|
*Удешевление, которое обязано быть заложено сразу:* **определение канона живёт в
|
||||||
|
единственном reference-файле**, который читают `init`, `canon` и `docs`, а не
|
||||||
|
повторяется в каждом. Правка канона — одно место плюс запись в журнал версий.
|
||||||
|
|
||||||
|
*Страховка порядка:* **замер ставится перед переездом jellybit**, а не после
|
||||||
|
всего, — он всё ещё блокирует то, что дороже всего откатывать.
|
||||||
|
|
||||||
|
**Р28. Работа ведётся в `docs/tasks/` самого `dev-skills`.** Скилл `tasks` не
|
||||||
|
требует ни OpenSpec, ни языка — задачи для него просто каталог markdown. Цели —
|
||||||
|
крупные куски, задачи — следствия. Заодно первая боевая обкатка собственного
|
||||||
|
инструмента.
|
||||||
|
|
||||||
|
**Р29. `AGENTIC-TASKS.md` сжимается до истории решений и переезжает в
|
||||||
|
`dev-skills`** отдельным `HISTORY.md`: почему не Scrum, числа первого замера,
|
||||||
|
что отвергнуто и почему. Он описывает процесс, а процесс живёт здесь, не в
|
||||||
|
healthlog. Остальное содержимое уже в плагинах, и второй дом для тех же правил —
|
||||||
|
ровно то, против чего документ сам и написан.
|
||||||
|
|
||||||
|
## Порядок
|
||||||
|
|
||||||
|
```
|
||||||
|
0. обновить установленный маркетплейс предусловие всего
|
||||||
|
0.5 завести docs/tasks в dev-skills, разложить 37 следствий по целям
|
||||||
|
|
||||||
|
1. РЕПОЗИТОРИЙ ПЛАГИНОВ
|
||||||
|
1.1 av-dev-tasks → av-dev-pm, пространство имён
|
||||||
|
1.2 канон одним reference-файлом — единственный дом определения
|
||||||
|
1.3 правки tasks и session: слоты, «Стимулы», .pm.json
|
||||||
|
1.4 новые init, canon, docs + docs.py
|
||||||
|
1.5 av-dev-pipeline: удалить project-brief, снять ветки деградации OpenSpec,
|
||||||
|
переписать шаг 9, девять charter'ов, promote.md, убрать слот
|
||||||
|
1.6 av-dev-backlog устаревшим; README; журнал канона v1; HISTORY.md
|
||||||
|
1.7 REMAINING.md пересобрать — часть его вопросов закрыта этим разбором
|
||||||
|
|
||||||
|
2. HEALTHLOG — первая боевая проверка инструмента
|
||||||
|
canon adopt, заполнение канона, security.md, review.md, ADR,
|
||||||
|
маркеры в architecture.md, docs.py check в гейте
|
||||||
|
|
||||||
|
3. КАЛИБРОВКА на четырёх находках healthlog БЛОКИРУЕТ шаг 5
|
||||||
|
|
||||||
|
4. один-два спринта healthlog на новом процессе
|
||||||
|
|
||||||
|
5. JELLYBIT — переезд, удаление дублей specs, растворение drafts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С38. `REMAINING.md` частично устарел:** пункт 2 «Завести брифы» отменён темой
|
||||||
|
3; закрыты открытые вопросы про `## Триггеры`, `av-dev-backlog`, имя процесса и
|
||||||
|
`AGENTIC-TASKS.md`. Пункт 1 (калибровка) стал обязательным, а не желательным.
|
||||||
|
Пересобрать на шаге 1.7.
|
||||||
|
|
||||||
|
**С39. Замер — единственный шаг, который нельзя переставить.** Всё остальное в
|
||||||
|
порядке 1–5 можно тасовать; шаг 3 стоит перед шагом 5 жёстко.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# 9. Линтеры скриптов (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Три скрипта на python, 3600 строк, ни одной проверки. `tasks.py` — 2450 строк,
|
||||||
|
которые ходят по файловой системе, переименовывают и удаляют файлы задач.
|
||||||
|
Требование к самим скриптам прежнее и не обсуждается: **голый `python3` 3.12,
|
||||||
|
ноль внешних зависимостей** — они лежат рядом со скиллами и запускаются в
|
||||||
|
чужом проекте, где ничего ставить нельзя.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р30. `pyproject.toml` в корне `dev-skills`, зависимости через `uv`.** Файл
|
||||||
|
живёт только здесь и не уезжает никуда: он держит **линтеры**, а не зависимости
|
||||||
|
скриптов. Скрипты остаются запускаемыми любым `python3` — это проверено прогоном
|
||||||
|
всех операций через `/usr/bin/python3`, а не через `.venv`.
|
||||||
|
|
||||||
|
**Р31. Ноль зависимостей охраняется двумя способами, и главный — второй.**
|
||||||
|
`banned-api` у ruff ловит частые соблазны по имени (`requests`, `yaml`,
|
||||||
|
`pydantic`, `click`, `rich`) — список заведомо неполный. Настоящий страж —
|
||||||
|
pyrefly: в окружении нет ничего, кроме линтеров, поэтому **любой** сторонний
|
||||||
|
импорт у него не разрешается. Первый способ даёт понятное сообщение, второй —
|
||||||
|
полноту.
|
||||||
|
|
||||||
|
**Р32. Версии линтеров прибиты точно** (`ruff==0.16.1`, `pyrefly==1.2.0`) плюс
|
||||||
|
`uv.lock` в git. Обновление линтера меняет набор находок, а находки правятся
|
||||||
|
руками в скриптах, которые уезжают в чужие проекты. Обновление обязано быть
|
||||||
|
отдельной осознанной правкой, а не побочным эффектом `uv sync`.
|
||||||
|
|
||||||
|
**Р33. `RUF001`–`RUF003` выключены.** Весь текст скриптов русский: сообщения,
|
||||||
|
докстроки, комментарии. «Похожая на латиницу кириллица» здесь норма, а не
|
||||||
|
опечатка, и три этих правила давали **311 срабатываний из 338** — шум, в котором
|
||||||
|
тонут остальные 27.
|
||||||
|
|
||||||
|
**Р34. `av-dev-backlog` исключён из проверки.** *(исчерпано [темой
|
||||||
|
30](30-av-dev-backlog-removed.md): плагин удалён, исключение снято из
|
||||||
|
`pyproject.toml` и `copies.py`.)* Плагин помечен устаревшим и живёт до перевода
|
||||||
|
последнего проекта, после чего удаляется целиком. Шесть его находок
|
||||||
|
косметические (`os.replace`, `l` как имя), а правка замороженного кода без
|
||||||
|
тестов — риск без выгоды. Исключение уходит вместе с плагином.
|
||||||
|
|
||||||
|
**Р35. Голый `except Exception` разрешён только помеченный.** Правило `BLE`
|
||||||
|
включено, а два места последнего рубежа (`main` обоих скриптов, код выхода 4 по
|
||||||
|
словарю) несут `# noqa: BLE001` с причиной. Так третий такой except не
|
||||||
|
появляется молча.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С40. Найдено и починено 27 находок ruff и 14 pyrefly.** Содержательных две:
|
||||||
|
мёртвая переменная `ques` в `check` (вычислялась и не использовалась — вопросы
|
||||||
|
проверяет `questions_open`) и два места в `check --fix`, где `find_entry_index`
|
||||||
|
может вернуть `None`, а результат идёт прямо в `list.pop` и в `range`. Оба
|
||||||
|
сегодня недостижимы, и недостижимость держалась на рассуждении о вызывающем
|
||||||
|
коде, а не на проверке. *Поправлено по ревью:* там стоит `raise`, а не
|
||||||
|
`continue`. Тихий пропуск превратил бы сломанный инвариант в отчёт «индексы
|
||||||
|
согласованы» — то есть в враньё; громкий отказ кодом 4 честнее.
|
||||||
|
|
||||||
|
**С41. `os` из `tasks.py` ушёл целиком.** `os.replace` → `Path.replace`,
|
||||||
|
`os.path.basename` → `Path.name`; импорт стал не нужен.
|
||||||
|
|
||||||
|
**С42. `fail()` в `docs.py` объявлен `NoReturn`.** Без этого `read_config`
|
||||||
|
выглядел как возвращающий неинициализированное значение — и это ровно то, что
|
||||||
|
читатель кода тоже не мог знать наверняка.
|
||||||
|
|
||||||
|
**С43. Проверка не входит ни в один гейт.** CI у репозитория нет, хука нет;
|
||||||
|
запускается руками командой из README. Заводить хук ради двух скриптов, которые
|
||||||
|
правятся раз в месяц, — плата ритуалом без выгоды.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# 10. Ревью готовых плагинов двумя проходами (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Два независимых сабагента `fable` — по одному на `av-dev-pm` и `av-dev-pipeline`.
|
||||||
|
**20 находок, из них две найдены обоими независимо.** Прошлые три круга ревью
|
||||||
|
шли по одному проходу на всё; два прохода с разными предметами дали и больший
|
||||||
|
урожай, и перекрёстное подтверждение самого дорогого дефекта.
|
||||||
|
|
||||||
|
## Что оказалось сломано по существу
|
||||||
|
|
||||||
|
**Р36. Перестановка закрытия за коммит (решение из [темы
|
||||||
|
8](08-rollout-order.md)) сломала `reopen` и батч — и это нашли оба прохода.**
|
||||||
|
`close --implemented` печатает «дорога назад: файл восстанавливается из git», а
|
||||||
|
`reopen` искал **коммит удаления**, которого в новом порядке ещё нет: шаг 11
|
||||||
|
идёт последним, и учёт остаётся незакоммиченным. Проверено прогоном: `reopen`
|
||||||
|
отказывал кодом 2 на свежезакрытой задаче — то есть в самом вероятном своём
|
||||||
|
применении. Тем же грязным деревом ломался `task-batch`: `git rebase` и `git
|
||||||
|
worktree remove` отказывают, и **каждая успешно закрывшая задачу ветка** уезжала
|
||||||
|
бы в провалившиеся.
|
||||||
|
|
||||||
|
Починено с обеих сторон: `reopen` берёт текст из `HEAD`, если коммита удаления
|
||||||
|
нет, а шаг 11 обязан **коммитить учёт вторым коммитом** — иначе закрытие не
|
||||||
|
доезжает до основной ветки и опора «`SPRINT.md` под git» остаётся словами.
|
||||||
|
|
||||||
|
**Р37. Канонический пример `docs/.pm.json` убивал `tasks.py`.** `canon.md`,
|
||||||
|
`skeletons.md`, `tasks/SKILL.md` и `adopt.md` показывали ключ `tasks.sections`,
|
||||||
|
которого скрипт не знает: `_validate_config` отвергает неизвестные ключи кодом 3
|
||||||
|
на **любой** команде. Проект, заведённый по канону дословно, остался бы без
|
||||||
|
работы с задачами целиком — а `docs.py check` при этом печатал «канон соблюдён»,
|
||||||
|
потому что чужой код 3 уходит в «не проверялось». Секции живут в заголовках `##`
|
||||||
|
индекса и второго дома не получают.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С44. Класс находок тот же, что и в прошлые три круга: стыки.** Не новый код, а
|
||||||
|
место, где один файл ссылается на другой. `sprint.md` в пункте «Сделана» всё ещё
|
||||||
|
отсылал к порядку, который сам же тремя экранами ниже отменил; три остатка «шаг
|
||||||
|
9а» несли **предкоммитную** позицию закрытия; путь отчёта триажа не переживал
|
||||||
|
`opsx:archive`, хотя по нему сверяют полноту ревью четверо.
|
||||||
|
|
||||||
|
**С45. Инструкция, которую нельзя выполнить, выглядит как выполненная.** Ответ
|
||||||
|
на вопрос по документированной процедуре (снять тег) оставлял задачу
|
||||||
|
незабираемой, потому что судит **раздел**, а не тег; `canon adopt` требовал
|
||||||
|
гнать `docs.py check` «до отсутствия дрейфа», недостижимого без нарушения
|
||||||
|
запрета сочинять цели; урожай спринта, заведённый после `sprint close`, терял
|
||||||
|
автотег молча.
|
||||||
|
|
||||||
|
**С46. Два прохода по разным предметам дороже одного, но не вдвое.** Перекрытие
|
||||||
|
оказалось ровно в одной находке из двадцати — той самой, что подтвердилась
|
||||||
|
дважды. Практика остаётся: ревью на плагин, а не одно на репозиторий.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# 11. Зависимости между плагинами (2026-08-03)
|
||||||
|
|
||||||
|
## Целевая картина, которую проверяли
|
||||||
|
|
||||||
|
`av-dev-git` ни от чего не зависит. `av-dev-pipeline` сам по себе: задача
|
||||||
|
приходит **и обычным текстом**, и из `tasks`. `av-dev-pm` оперирует абстрактным
|
||||||
|
«сделать задачу» и не знает, чем она выполняется.
|
||||||
|
|
||||||
|
## Что показала проверка
|
||||||
|
|
||||||
|
**Р38. Первые две цели выполняются, третья в исходной формулировке недостижима —
|
||||||
|
и формулировку надо поправить, а не картину.** `av-dev-pm` **владеет
|
||||||
|
конфигурационным файлом конвейера**: `docs/review.md` держит «Вопросы к
|
||||||
|
проходам» и «Триггеры профиля», то есть перечисляет проходы поимённо, а скелет
|
||||||
|
`review.md` несёт форму журнала дефектов. Кто-то этим словарём владеть обязан —
|
||||||
|
канон и есть схема данных, которую конвейер читает. Честная формулировка цели:
|
||||||
|
**`av-dev-pm` не зовёт пайплайн и не требует его наличия**. Она выполняется.
|
||||||
|
|
||||||
|
**Р39. Настоящая протечка была одна — необъявленная деградация опор приёмки.**
|
||||||
|
«Стимулы» в `session` и приёмка в `sprint.md` держались на «сохранённом отчёте
|
||||||
|
триажа» по конкретному OpenSpec-пути. В проекте без конвейера ревью защита от
|
||||||
|
занижения урожая исчезала **молча**: сверять не с чем, а текст об этом не
|
||||||
|
говорил. Теперь опора названа абстрактно («независимый отчёт ревью»), путь
|
||||||
|
`av-dev-pipeline` дан как частный случай, а отсутствие конвейера обязано
|
||||||
|
попадать строкой в доклад спринта.
|
||||||
|
|
||||||
|
**Р40. Ветка деградации шага 9 была неисполнима — ровно в том случае, ради
|
||||||
|
которого написана.** «Плагина нет — открой
|
||||||
|
`av-dev-pm/skills/canon/references/canon.md`»: путь в дерево маркетплейса, из
|
||||||
|
проекта без установленного плагина не разрешается ниоткуда. Кросс-плагинные пути
|
||||||
|
в дерево маркетплейса теперь не используются вообще: пайплайн ходит в **свой**
|
||||||
|
`references/project-facts.md`, а ссылки в чужой плагин даются через `Skill
|
||||||
|
<плагин>:<скилл>`.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С47. Знаниевый цикл есть и он законен, но каждый его контракт обязан иметь
|
||||||
|
единственный дом.** Пайплайн описывает раскладку `pm`, `pm` описывает артефакты
|
||||||
|
пайплайна — пять симметричных контрактов, из них два уже разошлись: форма
|
||||||
|
журнала дефектов (шесть полей против пяти, «Причина» потеряна) и список
|
||||||
|
читателей `docs/research/` (`specs` выпал). Дома назначены: форма журнала — у
|
||||||
|
конвейера, список читателей — у канона; в обеих копиях стоит явное указание на
|
||||||
|
дом.
|
||||||
|
|
||||||
|
**С48. Пайплайн больше не называет внутренние имена файлов `pm`.**
|
||||||
|
`items/<slug>.md` и `SPRINT.md` в его тексте были вторым домом для раскладки,
|
||||||
|
которую проект вправе переименовать через `docs/.pm.json`.
|
||||||
|
|
||||||
|
**С49. Описания плагинов в манифестах врали умолчанием.** Ни `marketplace.json`,
|
||||||
|
ни `plugin.json` не говорили, что `av-dev-pm` для конвейера **опционален**, а
|
||||||
|
задача принимается текстом. Теперь говорят — это первое, что читает человек,
|
||||||
|
выбирая, что подключать.
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# 12. Механическая проверка копий (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Разделение плагинов оставлено ([тема 11](11-plugin-dependencies.md)), но цена
|
||||||
|
его названа: пять симметричных контрактов в двух домах, два уже разошлись —
|
||||||
|
форма журнала дефектов потеряла в копии поле «Причина», список читателей
|
||||||
|
`docs/research/` потерял `specs`. Оба раза копия выглядела актуальной, и оба
|
||||||
|
раза расхождение прошло мимо трёх ревью подряд.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р41. Копия допустима, но обязана быть дословной и помеченной.** Разметка —
|
||||||
|
HTML-комментарии, невидимые в отрендеренном markdown: `<!-- дом: <id> -->` …
|
||||||
|
`<!-- /дом: <id> -->` и `<!-- копия: <id> из <путь> -->` … `<!-- /копия: <id>
|
||||||
|
-->`. `scripts/copies.py` требует побайтового совпадения текста между маркерами.
|
||||||
|
|
||||||
|
*Почему комментарии, а не манифест копий отдельным файлом:* маркер уезжает в
|
||||||
|
репозиторий проекта вместе со скелетом, и там он **полезен** — говорит читателю,
|
||||||
|
что у текста есть дом и правится он там. Манифест остался бы в маркетплейсе и
|
||||||
|
проекту ничего не сказал.
|
||||||
|
|
||||||
|
**Р42. Идентификатор строгий — буквы, цифры, дефис — и повторяется в закрывающем
|
||||||
|
маркере.** Иначе документация о самом механизме объявляет дом и роняет проверку:
|
||||||
|
это случилось на первом же прогоне, `README.md` объявил дом примером. Теперь
|
||||||
|
пример пишется `<id>`, угловые скобки под шаблон не подходят.
|
||||||
|
|
||||||
|
**Р43. Ограда блока кода в сверку не входит.** В доме текст обрамлён своей ```,
|
||||||
|
а в скелете тот же текст лежит внутри чужой, объемлющей ограды. Сверяется
|
||||||
|
содержимое, а не разметка вокруг него.
|
||||||
|
|
||||||
|
**Р44. Коды выхода — общий словарь** (0 сошлось, 1 расхождение, 2 разметка, 3 не
|
||||||
|
тот каталог, 4 сбой). Третий скрипт репозитория, и третий по тем же кодам.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С50. Помечены два контракта:** форма записи журнала дефектов (дом — конвейер
|
||||||
|
ревью, копия — скелет канона; это кросс-плагинная пара) и «когда заводить ADR»
|
||||||
|
(дом — канон, копия — его же скелет). Второй пришлось сперва **сделать**
|
||||||
|
дословным: копия говорила «обязателен статус», дом — «обязателен статус
|
||||||
|
„заменено на"», и это ровно тот класс, который и ищется.
|
||||||
|
|
||||||
|
**С51. Чего проверка не ловит — копию, которую забыли пометить.** Помечать
|
||||||
|
остаётся решением человека, и это названо в `README.md` вслух: иначе зелёный
|
||||||
|
прогон читался бы как «копий больше нет».
|
||||||
|
|
||||||
|
**С52. Дом без копий — расхождение, а не замечание.** Маркер, обещающий
|
||||||
|
дисциплину, за которой не за чем следить, — такая же ложная запись, как
|
||||||
|
разошедшаяся копия.
|
||||||
|
|
||||||
|
**С53. Запись в журнал версий канона проверка не заменяет.** Она видит, что
|
||||||
|
копия отстала, но не видит, что проект уже унёс старую версию к себе. Это
|
||||||
|
остаётся на человеке и сказано в обоих домах.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# 13. Секции `PLAN.md` переименованы (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Секции назывались **«линия»** и **«кусты»** — метафора, требующая расшифровки
|
||||||
|
при каждом употреблении. В текстах она и расшифровывалась: «звено упорядоченной
|
||||||
|
линии продукта», «тематический куст — цель, в последовательность не встающая».
|
||||||
|
Если название приходится объяснять рядом с каждым употреблением, объясняет не
|
||||||
|
название.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р45. «порядок» и «темы».** Заголовок называет ровно то свойство, которым
|
||||||
|
секции различаются: в первой очередь значима и обоснована прозой, во второй
|
||||||
|
порядка нет вовсе. Расшифровывать нечего — правило написано в самом имени.
|
||||||
|
|
||||||
|
**Р46. Записи в журнал версий канона не требуется — канон этих имён не знает.**
|
||||||
|
`canon.md` называет файл `docs/tasks/PLAN.md` и ничего не говорит о его секциях:
|
||||||
|
их дом — заголовки `##` индекса, а умолчание живёт в `tasks.py`. Версия канона
|
||||||
|
поэтому не меняется, и проект вправе называть секции по-своему. Причина названа
|
||||||
|
вслух, потому что соблазн повысить версию «на всякий случай» здесь сильный, а
|
||||||
|
повышение обязало бы каждый проект что-то делать — при том что делать нечего.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С54. Умолчание одно и живёт в `DEFAULT_PLAN_SECTIONS`.** Имена секций
|
||||||
|
по-прежнему настраиваются `--plan-sections`, а домом остаются заголовки `##`
|
||||||
|
индекса — переименование не трогает механику, только умолчание и тексты.
|
||||||
|
|
||||||
|
**С55. Метафора — плохое имя для секции индекса.** Секция читается человеком без
|
||||||
|
контекста, часто из вывода `list`, и второго шанса объяснить себя у неё нет.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# 14. Умолчания режимов прогона перевёрнуты (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Оба скилла держали одно и то же умолчание — «по очереди», — хотя цена очереди у
|
||||||
|
них разная. `review-pipeline` гнал проходы последовательно и требовал для
|
||||||
|
параллельности **двух** условий (явная просьба **и** поимённо названный набор).
|
||||||
|
`task-batch`, наоборот, планировал волны параллельных задач с потолком 2–3 и
|
||||||
|
считал параллельность нормой прогона.
|
||||||
|
|
||||||
|
Перепутаны оказались уровни. Проход ревью — чтение и рассуждение: он ничего не
|
||||||
|
поднимает, ни за что не дерётся и по построению не видит выводов соседа. Задача
|
||||||
|
батча — полный цикл пайплайна: гейт, поднятие сервиса вживую, вложенное ревью,
|
||||||
|
общие порты и рабочие каталоги. Дешёвое стояло в очереди, дорогое гонялось разом.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р47. В ревью умолчание — параллельно.** Стадии по-прежнему идут по порядку,
|
||||||
|
параллельность касается только проходов внутри стадии. Последовательно гоняем по
|
||||||
|
трём особым причинам, и каждая называется в отчёте: сказал оператор; проходы
|
||||||
|
меряют; машина занята — причём занятость видит вызывающий, а не конвейер.
|
||||||
|
Просьба «гони последовательно» **набора не требует**: очередь ничего не портит,
|
||||||
|
она только дольше, и домысливать тут нечего — в отличие от прежнего правила, где
|
||||||
|
неназванный набор блокировал отступление.
|
||||||
|
|
||||||
|
**Р48. Меряющая пара — правило стадии, а не решение прогона.** `adversary` и
|
||||||
|
`ops` идут по очереди всегда: оба доказывают находки числами и оба меряют одно
|
||||||
|
железо, а испорченный оракул хуже отсутствующего. Общее «гони параллельно» этого
|
||||||
|
не отменяет; отменяет только прямое слово оператора **про эту пару**, и тогда в
|
||||||
|
границы покрытия идёт строка про замеры под соседней нагрузкой.
|
||||||
|
|
||||||
|
**Р49. В батче умолчание — по одной задаче, параллельность — по графу
|
||||||
|
зависимостей.** План собирается как граф (рёбра — жёсткие зависимости и
|
||||||
|
сериализуемые пересечения) и в умолчании линеаризуется в один порядок. Просьба
|
||||||
|
«гони параллельно» разрешает использовать **ширину графа**, а не гнать всё
|
||||||
|
разом: потолок 2–3, замеряющая задача — волной по одной. Прежние правила волн
|
||||||
|
сохранены целиком, они просто перестали быть умолчанием.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С56. Режим батча задаёт режим ревью внутри задачи, и его называет charter.**
|
||||||
|
Батч идёт по одной — машина свободна, сабагент гонит проходы параллельно; батч
|
||||||
|
идёт волнами — сабагенту предписан последовательный режим с этой самой причиной.
|
||||||
|
Сабагент своего соседа не видит, поэтому решать это ему нельзя.
|
||||||
|
|
||||||
|
**С57. Ранний выход из ревью переехал на границу стадии.** Стадии идут по
|
||||||
|
порядку в любом режиме, так что остановиться между ними можно всегда; остановка
|
||||||
|
**внутри** стадии осталась побочной выгодой последовательного режима — но не
|
||||||
|
поводом его выбирать.
|
||||||
|
|
||||||
|
**С58. Цена параллельного батча проверяется до первой волны.** Тесты, делящие
|
||||||
|
фиксированный порт или файл БД, и проект, умеющий поднимать один экземпляр, —
|
||||||
|
основание гнать по одной даже после просьбы, сказанное строкой: просьба была про
|
||||||
|
параллельность, а не про сломанные тесты.
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# 15. Порядок проходов ревью — граф зависимостей (2026-08-03)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Решение 14 перевернуло умолчание, но оставило порядок в прежней форме: «стадии
|
||||||
|
идут по порядку номеров, параллельность — только внутри стадии». Номер стадии при
|
||||||
|
этом ничего не означает: между стадиями 1–4 ни один проход не читает вывод
|
||||||
|
другого, так что очередь между ними была платой ни за что. А правило про замеры
|
||||||
|
держалось на **двух именах** — `adversary` и `ops`, — и рассыпалось бы в тот
|
||||||
|
день, когда мерить начнёт третий проход или проект добавит свой.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р50. Порядок задаёт граф; стадии остаются единицей состава.** Профиль
|
||||||
|
по-прежнему набирается стадиями, но запускается всё, у чего закрыты входящие
|
||||||
|
рёбра. Рёбер три вида, и смешивать их нельзя: **зависимость** (гейт → все
|
||||||
|
проходы с мнением, все проходы → триаж), **конфликт за ресурс** (ненаправленный,
|
||||||
|
между теми, кто держит машину), **барьер стоимости** (только `deep`).
|
||||||
|
|
||||||
|
**Р51. Сериализует ресурс, а не имена.** Пометка «держит машину» — таблицей в
|
||||||
|
скилле: `gate`, `adversary`, `ops`, `triage`; читают и рассуждают — `specs`,
|
||||||
|
`code`, `reimpl`, `architecture`, `rubric`. Проект вправе пометить свой проход в
|
||||||
|
`docs/review.md`; снимать пометку с перечисленных нельзя. Правило теперь
|
||||||
|
самораспространяется: начнёт проход мерить — попадёт в цепочку по факту, а не по
|
||||||
|
поправке.
|
||||||
|
|
||||||
|
**Р52. Ранний выход заменён барьером стоимости.** Он стоит там, где ранний выход
|
||||||
|
зарабатывал: перед `reimpl` (пишет реализацию целиком) и `architecture`. В
|
||||||
|
`quick`/`standard` барьера нет — стадий 3–4 там не бывает; в `design` нет по
|
||||||
|
другой причине — предметом там и является форма, защищать нечего.
|
||||||
|
|
||||||
|
**Р53. Ребро — это порядок, никогда не данные.** В обычном графе задач ребро
|
||||||
|
тянет за собой вывод предшественника; здесь это запрещено: проход, увидевший
|
||||||
|
чужие находки, соглашается с ними, и разведённость — вся ценность конвейера —
|
||||||
|
обнуляется. Сказано в самом правиле, потому что графовый словарь провоцирует
|
||||||
|
ровно эту ошибку. Исключение одно и оно же сток: триаж.
|
||||||
|
|
||||||
|
**Р54. Диаграммы в скиллах — `mermaid`.** Граф, описанный прозой, читается как
|
||||||
|
инструкция и теряет форму; диаграмма показывает её целиком. В конвейере четыре:
|
||||||
|
общий граф прогона, граф профиля `design`, пример графа задач батча, веер
|
||||||
|
финальной сверки.
|
||||||
|
|
||||||
|
**Критерий, где диаграмма уместна: структура — граф или автомат, и проза
|
||||||
|
вынуждена его пересказывать.** По этому критерию диаграммы заведены ещё в шести
|
||||||
|
местах: жизненный цикл записи по индексам (`tasks`), четыре шага сессии с
|
||||||
|
причинами на рёбрах (`session`), исходы задачи в спринте (`sprint.md`), одиннадцать
|
||||||
|
шагов пайплайна с развилкой «тривиальная» (`task-pipeline`), храповик промоута с
|
||||||
|
обратным ребром (`promote.md`), счётчик `retune` до `drop` (`calibration.md`) и
|
||||||
|
граф вызовов между плагинами (`README.md`). Где структура — таблица соответствий
|
||||||
|
(чек-лист синка в `docs`, профили ревью, коды выхода), диаграмма не заводится:
|
||||||
|
она бы дублировала таблицу и разошлась с ней. Все диаграммы прогоняются через
|
||||||
|
`mermaid-cli` перед коммитом — синтаксическая ошибка в блоке не видна при чтении
|
||||||
|
и молча ломает рендер.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С59. Триаж — сток по определению, а не «стадия 5».** Отсюда без отдельного
|
||||||
|
обоснования следует правило, которое раньше приходилось защищать: на неполном
|
||||||
|
графе триаж не запускается, потому что агрегировал бы половину и выглядел бы
|
||||||
|
полным.
|
||||||
|
|
||||||
|
**С60. Словарь рёбер общий у ревью и батча.** «Жёсткая зависимость» и
|
||||||
|
«сериализуемое пересечение» в `task-batch` — те же два вида рёбер; формулировки
|
||||||
|
сведены, и в обоих скиллах стоит ссылка на другой.
|
||||||
|
|
||||||
|
**С61. Значения режима стали `по графу` и `линейно`.** Прежние «параллельно» и
|
||||||
|
«последовательно» описывали способ запуска, а не структуру; линеаризация
|
||||||
|
осталась отступлением с тремя причинами (оператор, занятая машина, разбор самого
|
||||||
|
конвейера).
|
||||||
|
|
||||||
|
**С62. Проход, держащий машину, знает об этом из своего charter'а.** `adversary`
|
||||||
|
и `ops` получили по абзацу: цепочка гарантирует им чистое железо, значит их
|
||||||
|
число — оракул, и шум в нём объясняется замером, а не соседом.
|
||||||
|
|
||||||
|
**С63. У каждой диаграммы объявлено старшинство — это цена второго дома.** Схема
|
||||||
|
и проза вокруг неё описывают один факт, и разойтись они могут молча: то самое,
|
||||||
|
против чего написан `copies.py`. Механической сверки здесь нет — дословного
|
||||||
|
соответствия между текстом и графом не существует, — поэтому работает
|
||||||
|
объявление: **в `review-pipeline` старший граф** (он и есть алгоритм
|
||||||
|
планировщика, проза объясняет рёбра), **в остальных местах старшая проза**
|
||||||
|
(диаграмма там сводка). Для агента это не философия: без объявления он идёт за
|
||||||
|
тем, что конкретнее, то есть чаще за схемой.
|
||||||
|
|
||||||
|
**С64. Рендер диаграмм проверяется скриптом, а не памятью автора.**
|
||||||
|
`scripts/diagrams.py` вынимает все блоки `mermaid` и гонит их через `mmdc` или
|
||||||
|
`npx @mermaid-js/mermaid-cli`; коды выхода — общий словарь, нет рендерера — код
|
||||||
|
3, а не молчаливый успех. Причина та же, что у остальных проверок репозитория:
|
||||||
|
**ошибка в блоке не видна при чтении** — текст правдоподобен, дифф разумен,
|
||||||
|
падает только рендер. Расхождение с прозой скрипт не ловит и не притворяется,
|
||||||
|
что ловит: это работа правила 63.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 16. Каталог вместо файла в `docs/` — отложено до переезда healthlog (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Вопрос: разрешить документам в корне `docs/` быть не только файлом, но и
|
||||||
|
каталогом — когда документ описывает несколько принципиальных решений или
|
||||||
|
перерастает 400–500 строк. Паспорт остаётся файлом в любом случае: компактность
|
||||||
|
и есть его функция.
|
||||||
|
|
||||||
|
Механизм в каноне уже работает — `conventions/`, `research/`, `adr/` каталоги с
|
||||||
|
обязательным `README.md`-индексом, — так что вопрос не «можно ли», а «от чего
|
||||||
|
лечим».
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р55. Порог в строках триггером не становится.** Замер по проектам: у порога
|
||||||
|
ровно один документ — `healthlog/docs/architecture.md`, 1662 строки. В нём
|
||||||
|
десять маркеров долга, а разделы — «Слои гранулярности» (211 строк), «Тренировки
|
||||||
|
и прочие секции» (222), «Условный запрос», «Свёртка и размер ответа», «Форма
|
||||||
|
ответа». Это **поведение**, чей нормативный дом `openspec/specs/`, где у проекта
|
||||||
|
уже лежат пять capability. Остальные документы 108–438 строк, `jellybit` — 169.
|
||||||
|
Порог сработал бы ровно там, где надо не разносить, а доводить переезд, и дал бы
|
||||||
|
долгу постоянное жильё: разложить 1662 строки по файлам дешевле, чем вынести их
|
||||||
|
в спеки, а после раскладки давление исчезнет и второй дом поведения останется
|
||||||
|
навсегда.
|
||||||
|
|
||||||
|
**Р56. Шов выноса — другой читатель или другой срок жизни, а не размер.** По
|
||||||
|
этому критерию кандидатов два. `review.md` — сильнее прочих: у него уже записаны
|
||||||
|
два раздела с разными сроками жизни, настройка конвейера стабильна и читается
|
||||||
|
проходами, а журнал дефектов растёт неограниченно. `architecture.md` — по шву
|
||||||
|
«окружение, деплой, наблюдатель», у которого отдельный читатель `ops`. А вот
|
||||||
|
расщепление архитектуры **по принципиальным решениям отвергнуто**: у факта
|
||||||
|
«почему решено так» дом `adr/`, и вынесенные разделы немедленно станут его
|
||||||
|
вторым домом.
|
||||||
|
|
||||||
|
**Р57. `security.md` и `passport.md` каталогом не становятся.** У `security.md`
|
||||||
|
ценность именно в цельности: периметр первой строкой и «что вне модели» читаются
|
||||||
|
враждебным проходом за один раз, а разнесённые — расходятся первыми. У
|
||||||
|
`database.md` механизм заводить не под что: 241 и 211 строк.
|
||||||
|
|
||||||
|
**Р58. Если вводить — точка входа остаётся одна.** `docs/architecture.md`
|
||||||
|
упомянут в репозитории 66 раз: девять charter'ов, карта `project-facts.md`,
|
||||||
|
`docs.py`, скелеты. Развилка «файл или каталог» размножится на девять «прочитай
|
||||||
|
либо обойди». Поэтому форма жёсткая: каталог легален только при
|
||||||
|
`<имя>/README.md`, и он **и есть** прежний документ — обзор целиком со ссылками
|
||||||
|
на вынесенное, а не оглавление к нему. Каждый файл каталога обязан быть достижим
|
||||||
|
ссылкой из `README.md`; это проверяется сегодняшним механизмом ссылок `docs.py`
|
||||||
|
и ловит файл-сироту. Вынеся раздел, `README.md` на него **ссылается, а не
|
||||||
|
пересказывает** — тот же приём, которым в архитектуре уже описаны компоненты со
|
||||||
|
ссылкой на capability.
|
||||||
|
|
||||||
|
**Р59. Решение отложено до конца переезда `healthlog` (шаг 2 TODO).** Порядок:
|
||||||
|
довести поведение в спеки, замерить остаток. Жмёт после этого — вводить каноном
|
||||||
|
версии 3, и сразу для `review.md` и `architecture.md`, а не для всех документов
|
||||||
|
корня скопом.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С65. Цена изменения — версия канона, а не правка одного файла.** Обратной
|
||||||
|
совместимости у канона нет, поэтому в счёт входят: `docs.py` (`check_stray` с
|
||||||
|
его `ALLOWED_FILES`/`ALLOWED_DIRS`, `check_required` — обязательный путь
|
||||||
|
становится развилкой, `check_capabilities` — сегодня читает ровно один файл),
|
||||||
|
`skeletons.md`, `project-facts.md`, девять charter'ов, запись в `changelog.md`
|
||||||
|
канона и ветка `upgrade` в скилле `canon`.
|
||||||
|
|
||||||
|
**С66. Раздутый документ канона — сначала подозреваемый, потом кандидат на
|
||||||
|
вынос.** Диагностика перед раскладкой — счёт маркеров долга (`grep -c "<!--
|
||||||
|
канон:"`) и вопрос, не поведение ли это. Разложить дрейф по файлам значит
|
||||||
|
перестать его видеть.
|
||||||
|
|
||||||
|
**С67. Материал для решения даёт `healthlog`, а не `jellybit`.** У второго 169
|
||||||
|
строк архитектуры — там вопрос не стоит вовсе, и принимать по нему решение
|
||||||
|
значит принимать его без предмета.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# 17. Разбор заметок: ступень ревью, род работы, роадмап (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Семь заметок из `NOTES.md`, накопленных по ходу работы: переименование
|
||||||
|
`PLAN.md`, тип у каждой задачи, цвета сабагентов по модели, кавычки во
|
||||||
|
фронтматтерах, уровни ревью для проекта, задачи в терминах функций и границ,
|
||||||
|
язык задач без англицизмов. Разного размера и из разных мест, но три из них
|
||||||
|
оказались об одном — **о том, можно ли оценить задачу, не открывая код**.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р60. Цвет charter'а кодирует модель, а не роль прохода.** Раскладка `sonnet` →
|
||||||
|
green, `opus` → yellow, `fable` → red. Роль прохода видна из имени, а стоимость
|
||||||
|
прогона — ниоткуда; цвет, розданный по ролям, не отвечает ни на один вопрос,
|
||||||
|
который задают во время прогона. Дом раскладки — таблица «Модель по проходу» в
|
||||||
|
`review-pipeline/SKILL.md`.
|
||||||
|
|
||||||
|
**Р61. Фронтматтеры проверяются машиной, а не вниманием.** Три описания из
|
||||||
|
четырнадцати содержали `: ` в незакавыченном значении — для YAML это вложенное
|
||||||
|
отображение, то есть синтаксическая ошибка, которую **нельзя увидеть чтением**:
|
||||||
|
текст читается правильно. Тот же класс, что у mermaid-диаграмм, и лечится тем же
|
||||||
|
способом — `scripts/frontmatter.py`. Он же держит раскладку цветов (HHH) и
|
||||||
|
сверку `name` с именем каталога.
|
||||||
|
|
||||||
|
**Р62. Между `standard` и `deep` заведена ступень `wide`.** *(содержание
|
||||||
|
триггеров пересмотрено темой 18, [Р72](18-tier-raises-pass-not-risk.md):
|
||||||
|
миграция схемы и публичный контракт ступень не поднимают.)* Прыжок стоил самого
|
||||||
|
дорогого прохода конвейера, а платить приходилось за одну архитектурную находку:
|
||||||
|
изменений, которые трогают публичный контракт, но не вводят нового правила
|
||||||
|
слияния, — большинство. `wide` — это `standard` плюс `architecture` (вход шире
|
||||||
|
диффа, отсюда имя), семь проходов против восьми у `deep`.
|
||||||
|
|
||||||
|
**Р63. Триггер независимой реализации стал триггером профиля.** Раньше условие
|
||||||
|
«изменение вводит новое правило идентичности, слияния или разбора» стояло
|
||||||
|
**внутри** `deep`, и профиль означал то семь проходов, то восемь. Реестр
|
||||||
|
состава, который «сверяется взглядом до коммита», проверять было нечем: у
|
||||||
|
профиля не было одного правильного ответа. Теперь условие выбирает профиль, а
|
||||||
|
`reimpl` в `deep` безусловен — и он единственное, чем `deep` отличается от
|
||||||
|
`wide`.
|
||||||
|
|
||||||
|
**Р64. Барьер стоимости остался только в `deep`.** В `wide` за ним стоял бы один
|
||||||
|
дешёвый проход с потолком в 3 находки, а барьер не бесплатен — он сериализует
|
||||||
|
то, что могло идти разом. Вторая причина помельче: барьер спрашивает «выживает
|
||||||
|
ли форма изменения», а `architecture` — как раз тот, кто на этот вопрос
|
||||||
|
отвечает.
|
||||||
|
|
||||||
|
**Р65. Род работы — вторая ось типа, и живёт тегом.** Тип записи
|
||||||
|
(`goal`/`idea`/`epic`/`task`) отвечает «что это за запись», род
|
||||||
|
(`feature`/`fix`/`chore`/`research`) — «какого рода работа». В один префикс их
|
||||||
|
не свести: идея бывает *про* функцию, эпик функцией *и является*. Дом — тег
|
||||||
|
`kind:<род>`, потому что теги здесь и есть единственный механизм разметки, а
|
||||||
|
`list --kind` работает даром. Принятая цена: в строку индекса род не попадает
|
||||||
|
(индексы производны), и состав набора по роду виден командой, а не глазами.
|
||||||
|
Словарь **закрыт** — открытый разъехался бы на синонимах `bug`/`bugfix`/`fix`.
|
||||||
|
|
||||||
|
**Р66. У `chore` тест готовности ослаблен честно.** Вопрос «что станет
|
||||||
|
наблюдаемо иначе» для обслуживания отвечается разработчику, а не пользователю.
|
||||||
|
Пока рода не было, такие задачи либо не заводились, либо придумывали себе
|
||||||
|
пользовательскую пользу — и это второе хуже: оно проходит проверку.
|
||||||
|
|
||||||
|
**Р67. Задача называет границы, а не намерения.** Раздел «Затрагивает» —
|
||||||
|
эндпоинт, таблица и миграция, формат на диске, публичный тип пакета. Без него
|
||||||
|
задача оценивается по объёму текста, а не по объёму поверхности, и оценка
|
||||||
|
систематически занижена ровно там, где текст короткий, а границ много. Механизм
|
||||||
|
проверяет **наличие** непустого раздела: полноту перечня машина не видит, и
|
||||||
|
делать вид, что видит, хуже, чем не проверять.
|
||||||
|
|
||||||
|
**Р68. Род и границы требуются к взятию в спринт, а не к заведению.** Тот же
|
||||||
|
приём, что уже работает для критериев приёмки, и по той же причине: беклог
|
||||||
|
пополняется чаще, чем разбирается, а требование на входе выгоняет в заметки то,
|
||||||
|
что должно лежать задачей. `check` о пропаже напоминает замечанием — иначе два
|
||||||
|
живых проекта покраснели бы на 98 задачах, заведённых до этого решения.
|
||||||
|
|
||||||
|
**Р69. `PLAN.md` → `ROADMAP.md`, вместе с ключом конфига и токенами команд.**
|
||||||
|
Слово «план» в репозитории значит три разных вещи — оглавление целей, план
|
||||||
|
реализации внутри задачи и `PLAN.json` разовой адаптации. Переименовано всё:
|
||||||
|
`tasks.plan` → `tasks.roadmap`, `--index plan` → `--index roadmap`,
|
||||||
|
`--plan-sections` → `--roadmap-sections`. Старый ключ в `docs/.pm.json` не
|
||||||
|
игнорируется молча — скрипт останавливается и называет переименование.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С68. Версия канона 3 занята этим изменением.** Отложенное решение [темы
|
||||||
|
16](16-directory-instead-of-file.md) (каталог вместо файла в `docs/`) вводится
|
||||||
|
теперь версией **4**, а не 3.
|
||||||
|
|
||||||
|
**С69. Род работы ничего не предписывает конвейеру.** Профиль ревью выбирается
|
||||||
|
по факту изменения: `chore` бывает миграцией схемы, `fix` — правкой публичного
|
||||||
|
контракта. Правило «предписание процесса в теле задачи снимается» родом не
|
||||||
|
отменяется, а подтверждается.
|
||||||
|
|
||||||
|
**С70. Проверка фронтматтеров — третья проверка репозитория того же класса.**
|
||||||
|
Копии, диаграммы, фронтматтеры: всё это ошибки, невидимые при чтении. Класс
|
||||||
|
опознаётся по признаку «диff выглядит разумно, а результат ломается», и каждый
|
||||||
|
его представитель получает скрипт, а не пункт чек-листа.
|
||||||
|
|
||||||
|
**С71. Ступеней профиля четыре, и правило выбора читается сверху вниз.** Первое
|
||||||
|
сработавшее условие и есть ответ: правило слияния → `deep`, контракт или схема →
|
||||||
|
`wide`, видимое снаружи поведение → `standard`, иначе `quick`.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# 18. Ступень поднимает проход, а не риск (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Наблюдение с живых проектов: полный набор проходов гоняется чаще, чем оправдано —
|
||||||
|
архитектура и независимая реализация нужны заметно реже, чем запускаются. Развилка
|
||||||
|
названа сразу: крупные задачи с частым полным ревью либо мелкие и средние задачи
|
||||||
|
со средним ревью. Выбран второй путь.
|
||||||
|
|
||||||
|
Разбор показал, что размер задач — только половина причины, и не главная.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р70. Профиль — максимум по поверхности, а не средневзвешенное.** Условия
|
||||||
|
читаются сверху вниз, первое подошедшее отвечает за весь дифф. Значит цена ревью
|
||||||
|
растёт быстрее размера задачи: на крупной задаче верхний профиль оплачивается в
|
||||||
|
том числе за ту её часть, которая сама по себе была бы `quick`. Это и есть
|
||||||
|
механизм, ради которого выбран путь мелких задач.
|
||||||
|
|
||||||
|
**Р71. Ступень поднимает то, что даёт работу новому проходу, а не то, что
|
||||||
|
кажется рискованным.** Правило вывода, по которому спорные случаи решаются без
|
||||||
|
нового списка. Проверка нынешних триггеров этим правилом:
|
||||||
|
|
||||||
|
| Триггер | Кто закрывает | Где этот проход |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| миграция схемы | `gate` (шаг миграций), `ops` (миграция под потоком, откат при двух версиях) | уже в `standard` |
|
||||||
|
| публичный контракт | `specs`, направление `code → spec` | во всех профилях |
|
||||||
|
| инвариант проекта | основание для `critical` у любого прохода | во всех |
|
||||||
|
| новый пакет, новое понятие | `architecture` | только `wide` |
|
||||||
|
| новое правило слияния | `reimpl` | только `deep` |
|
||||||
|
|
||||||
|
Три верхних триггера не добавляли ни одного прохода — они поднимали ступень «на
|
||||||
|
всякий случай». На проекте с базой и эндпоинтами это делало верхнюю ступень
|
||||||
|
умолчанием, то есть правило объявляло исключением то, что происходит всегда.
|
||||||
|
|
||||||
|
**Р72. Миграция схемы, публичный контракт и инвариант уехали в `standard`.**
|
||||||
|
`wide` теперь означает ровно одно: изменение вводит **новое понятие или
|
||||||
|
структурную единицу** — новый пакет или слой, новая точка входа, второй способ
|
||||||
|
делать то, что уже делается, перенос ответственности между узлами. Добавленное
|
||||||
|
поле в существующем ответе концептом не является. Это **отменяет часть JJJ темы
|
||||||
|
17**: ступень `wide` остаётся, её содержание меняется. Проект, где изменение
|
||||||
|
контракта и правда архитектурное (публичный SDK, чужие потребители), поднимает
|
||||||
|
его сам в `docs/review.md` — уточнением, а не возвратом прежнего умолчания.
|
||||||
|
|
||||||
|
**Р73. Чекпоинт `design` получил то же условие.** `review-specs` в режиме
|
||||||
|
«дизайн ДО кода» идёт всегда — это самый дешёвый чекпоинт конвейера.
|
||||||
|
`review-rubric` и `review-architecture` — только при новом понятии. Причина
|
||||||
|
арифметическая: чекпоинт стоит на **каждой** задаче, поэтому при мелкой нарезке
|
||||||
|
три прохода умножаются на число задач и становятся самой большой статьёй.
|
||||||
|
Причина по существу та же, что в SSS: рубрика на узел без нового понятия
|
||||||
|
порождает свойства уже существующего рода, записанные конвенциями и спеками.
|
||||||
|
|
||||||
|
**Р74. Шов нарезки — граница, за которой падает ступень.** Тест декомпозиции
|
||||||
|
отвечает, **допустим** ли разрез; шов отвечает, **где** его провести. Раздел
|
||||||
|
«Затрагивает» перечисляет границы; строка, поднимающая ступень выше остальных, и
|
||||||
|
есть кандидат на отдельную задачу.
|
||||||
|
|
||||||
|
**Р75. Костяк из четырёх проходов платится за каждую задачу.** Гейт, спеки, код,
|
||||||
|
триаж несокращаемы, поэтому разрез, после которого обе половины остаются в одной
|
||||||
|
ступени, делает ревью **дороже**: тот же объём тем же составом, но костяк
|
||||||
|
оплачен дважды. Резать — когда разрез снимает дорогой проход с большей части
|
||||||
|
диффа.
|
||||||
|
|
||||||
|
**Р76. Верхняя ступень задана тестом, а не списком.** «Идентичность, слияние,
|
||||||
|
разбор» — формулировка, пришедшая из одного проекта, и в общем виде она не
|
||||||
|
читалась: вопрос «как это применить к моему проекту» не имел ответа в тексте.
|
||||||
|
Теперь класс задан тремя условиями, независимыми от домена и языка: вариантов
|
||||||
|
несколько и оба защитимы; спека между ними не выбирает; неверный выбор не
|
||||||
|
падает, а молча меняет смысл данных. Отрицательный тест сильнее положительных —
|
||||||
|
то, что красит гейт или роняет запрос, в класс не входит. Три слова остались как
|
||||||
|
**три места**, где такие правила водятся (граница входа данных и место их
|
||||||
|
встречи), а проект перечисляет свои места в `docs/review.md` — перечень
|
||||||
|
производен от теста и не расширяет класс.
|
||||||
|
|
||||||
|
Оговорка, без которой правило вырождается: триггер — **новое или изменённое по
|
||||||
|
существу правило**, а не код рядом с ним. Проект, чей домен и состоит из таких
|
||||||
|
правил, иначе оказывался бы в `deep` всегда — та же болезнь, от которой лечилась
|
||||||
|
ступень `wide`.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С72. Порога в числе границ не заводится.** Тот же принцип, что в теме 16
|
||||||
|
([Р55](16-directory-instead-of-file.md)): размер не триггер. Шов проходит по
|
||||||
|
скачку ступени, а не по длине перечня.
|
||||||
|
|
||||||
|
**С73. Ступень — признак для планирования, но не запись в задаче.** Строка
|
||||||
|
«делать профилем standard» в теле — тот самый второй дом правила выбора, который
|
||||||
|
снимает гигиена полей. Профиль выбирает тот, кто видит изменение.
|
||||||
|
|
||||||
|
**С74. Дешёвое место заметить разнородную задачу — показ набора спринта.** Там
|
||||||
|
«Затрагивает» уже написан, а предложение об изменении ещё не заведено: разрез
|
||||||
|
стоит одного `edit` вместо выброшенного предложения.
|
||||||
|
|
||||||
|
**С75. Замер остаётся за обкаткой.** Правило выведено из состава проходов, а не
|
||||||
|
из статистики прогонов: считать, какая доля задач попадает в каждую ступень,
|
||||||
|
можно только на спринтах нового процесса (TODO шаг 4).
|
||||||
|
|
||||||
|
**С76. Отсутствие верхней ступени — законное состояние проекта.** Бывают
|
||||||
|
проекты, где данные приходят нормализованными, ничего ни с чем не сливается, а
|
||||||
|
внешних форматов нет: `deep` там не срабатывает никогда, и придумывать ему повод
|
||||||
|
не надо. Раньше это читалось как недонастройка.
|
||||||
|
|
||||||
|
**С77. Ступень определяет класс правила, а не вид работы.** Миграция схемы —
|
||||||
|
`standard`, но миграция, переносящая данные по правилу («сложить дубли»,
|
||||||
|
«привести к одному виду перед сравнением»), несёт правило идентичности и потому
|
||||||
|
`deep`. Одно слово в описании задачи попадает в разные ступени — это не
|
||||||
|
противоречие, смотрят не на слово.
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# 19. Роадмап — состояние проекта, а не очередь работ (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Основной инструмент владельца — роадмап и набор целей: «на каком этапе проект,
|
||||||
|
что сделали и что осталось». Оценка идёт по **поведению**, а не по внутреннему
|
||||||
|
устройству: что приложение уже может делать и чего ещё не может. Отсюда
|
||||||
|
требование к формулировкам: цель отвечает на «что приложение будет делать»,
|
||||||
|
задача — на «что для этого нужно сделать».
|
||||||
|
|
||||||
|
Разбор показал, что инструмент отвечал ровно на половину этого вопроса.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р77. Достигнутая цель из роадмапа не исчезает.** `close --implemented` удалял
|
||||||
|
у цели и файл, и строку — роадмап по построению показывал только «что осталось».
|
||||||
|
Свидетельство нашлось в самом роадмапе healthlog: там руками заведена секция
|
||||||
|
«Что уже пройдено» на двадцать строк прозы, и заканчивается она фразой «Эти
|
||||||
|
звенья целями не заведены: закрытая цель записи не оставляет, ей хватает коммита
|
||||||
|
и спеки». Обходной путь и его причина записаны рукой владельца. Теперь строка с
|
||||||
|
датой переезжает в секцию достигнутого; файл удаляется по-прежнему.
|
||||||
|
|
||||||
|
Вторым домом поведения это не делает: нормативное поведение живёт в
|
||||||
|
`openspec/specs/`, роадмап отвечает **когда и в каком порядке** оно появилось —
|
||||||
|
другой вопрос. Ссылки на файл в строке нет намеренно: файла больше нет, а битая
|
||||||
|
ссылка это законная ошибка `check`. Форма строки — как в `REJECTED.md`, и по той
|
||||||
|
же причине.
|
||||||
|
|
||||||
|
**Р78. Цель — возможность приложения, задача — шаг к ней.** Заголовок цели
|
||||||
|
отвечает на «что приложение будет уметь»: не «Работа со слиянием», а «Исход
|
||||||
|
слияния не зависит от порядка доставки». **Свойство поведения — тоже
|
||||||
|
возможность**: «сообщает о своём состоянии», «исход не зависит от порядка» —
|
||||||
|
законные цели, переформулировки в функцию не требуют. Единственный настоящий
|
||||||
|
чужак — работа над инструментом и процессом: на вопрос «что приложение будет
|
||||||
|
уметь» она не отвечает и живёт в отдельной секции роадмапа.
|
||||||
|
|
||||||
|
**Р79. Тест готовности задачи сменил защиту.** Требование «что станет наблюдаемо
|
||||||
|
иначе снаружи» переехало к цели. У задачи вместо него — **какую строку
|
||||||
|
«Завершения» своей цели она двигает**. «Отрефакторить X» проваливает тест не
|
||||||
|
потому, что невидим снаружи, а потому, что не находит строки, к которой
|
||||||
|
относится. Побочная выгода: видно и обратное — строка «Завершения», к которой не
|
||||||
|
относится ни одна задача, это незакрытая часть возможности. Отсюда требование к
|
||||||
|
«Завершению» быть **списком**, а не абзацем: на абзац не сошлёшься.
|
||||||
|
|
||||||
|
**Р80. Цель обязательна не у всякой задачи.** Прежнее правило — «у каждой задачи
|
||||||
|
должен быть `goal:`, иначе она не попадёт ни в один спринт» — было угрозой, а не
|
||||||
|
аргументом, и заставляло операционную работу выдумывать себе направление.
|
||||||
|
Граница проходит по роду работы: `feature` без цели не бывает (новая возможность
|
||||||
|
и есть содержание цели), `fix`, `chore` и `research` живут без цели законно и
|
||||||
|
входят в набор спринта помимо его цели. Это второй раз, когда род работы
|
||||||
|
окупается, — и первый, когда он что-то определяет за пределами отбора.
|
||||||
|
|
||||||
|
**Р81. Тип `[epic]` упразднён.** Зонтик между целью и задачами не нужен:
|
||||||
|
зонтиком стала цель, а слишком крупный шаг дробится на шаги помельче под ней.
|
||||||
|
Замер: ноль употреблений на 97 записей двух живых проектов, при том что тип
|
||||||
|
занимал место в словаре, тесте готовности, автомате переходов, `split.md` и трёх
|
||||||
|
местах `tasks.py`.
|
||||||
|
|
||||||
|
**Р82. Имена секций роадмапа — `Готово` / `Запланировано` / `Направления` /
|
||||||
|
`Разработка`.** Первый набор (`умеет` / `строим` / `станок`) прожил один заход и
|
||||||
|
был признан неудачным. Из четырёх предложенных имён отвергнуто одно, и по
|
||||||
|
проверяемой причине: **`окружение` уже занято** — в `architecture.md` это боевое
|
||||||
|
окружение приложения, «где работает, что рядом, кто перезапускает», и одно слово
|
||||||
|
в двух смыслах развело бы документы канона. Взято `Разработка`.
|
||||||
|
|
||||||
|
Принятый компромисс назван вслух: `Готово` слегка тянет обратно в трекерную рамку
|
||||||
|
«состояние работы», тогда как секция про **возможность**. Перевесила читаемость с
|
||||||
|
первого взгляда, а смысл несут заголовки целей внутри секции. Так же принято, что
|
||||||
|
цель в `Запланировано` может быть уже наполовину построена: это очередь, а не
|
||||||
|
«не начато», а «в работе» живёт в `SPRINT.md`.
|
||||||
|
|
||||||
|
**Р83. Секции роадмапа канонические, секции беклога — нет.** Разница выведена, а
|
||||||
|
не назначена: у секций роадмапа есть **семантика** (достигнутое, очередь,
|
||||||
|
долгое, не про продукт), в первую пишет сам `close`, и роадмап, названный
|
||||||
|
по-своему, читался бы только своим автором. Секции беклога (`Ядро`, `Инфра`)
|
||||||
|
семантики не несут — это полки. Поэтому `check` проверяет у роадмапа три вещи:
|
||||||
|
состав закреплён (чужая секция — ошибка), все четыре обязаны быть, язык один на
|
||||||
|
весь индекс; `--roadmap-sections` у `init` упразднён. Английский набор — `Done`
|
||||||
|
| `Planned` | `Directions` | `Tooling`.
|
||||||
|
|
||||||
|
Проверено на том самом случае, ради которого правило и заводилось: секция «Что
|
||||||
|
уже пройдено», которую healthlog вёл руками, теперь называется ошибкой поимённо.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С78. Ключа `tasks.achieved_section` не появилось.** Секция достигнутого
|
||||||
|
опознаётся по каноническому имени в любом из двух языков, и лишний knob не
|
||||||
|
заводится: канонический состав отвечает на тот же вопрос надёжнее конфига.
|
||||||
|
|
||||||
|
**С79. `reopen` цели снимает строку достигнутого.** Иначе роадмап продолжает
|
||||||
|
утверждать, что приложение умеет то, что вернулось в работу.
|
||||||
|
|
||||||
|
**С80. Прозаический раздел в индексе — дрейф.** Любой `##` проверка считает
|
||||||
|
секцией, поэтому «Что уже пройдено» и «Почему в таком порядке» в healthlog
|
||||||
|
формально были двумя лишними секциями, куда могла уехать задача. При повышении
|
||||||
|
они разбираются: звенья — строками в `Готово`, обоснование очереди — прозой
|
||||||
|
внутри `Запланировано`.
|
||||||
|
|
||||||
|
**С81. Правил стало пять, и нулевое — про смысл, а не про механику.** «Цель —
|
||||||
|
возможность, задача — шаг к ней» стоит перед правилами о гниении беклога и
|
||||||
|
производности индексов, потому что из него следует, зачем эти механики нужны.
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# 20. Форма записи: заголовок, секции, вычитка (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Обкатка обновлённого скилла на выдуманном проекте — консольные крестики-нолики
|
||||||
|
на JavaScript. Каталог задач заведён с нуля тем же скриптом: шесть целей, девять
|
||||||
|
задач, отказ, достижение цели, спринт. Смотрели три вещи: тексты, разделы, состав
|
||||||
|
задач.
|
||||||
|
|
||||||
|
Форма вылезла раньше содержания. Индексы вышли с секциями со строчной буквы и без
|
||||||
|
отбивки после заголовка — читается как список списков, а не как документ. А все
|
||||||
|
заголовки задач оказались **описательными**: «Лишние символы в ходе молча
|
||||||
|
отбрасываются», «Поле печатается одним куском кода», «Линтер и тесты гоняются
|
||||||
|
одной командой». Правило «задача отвечает на «что для этого нужно сделать»» в
|
||||||
|
скилле стояло с самого начала — но относилось к содержанию задачи, а не к её
|
||||||
|
заголовку, и потому не применялось там, где заголовок и есть всё, что видно в
|
||||||
|
списке.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р84. Заголовок отвечает на вопрос своего типа, и форм три.** Цель —
|
||||||
|
утверждение о возможности («Соперником может быть компьютер»); задача — глагол в
|
||||||
|
неопределённой форме, допускается «не» перед ним («Не отбрасывать молча лишние
|
||||||
|
символы в ходе»); идея — назывное, без обещания. Причина не стилистическая:
|
||||||
|
описательный заголовок называет **состояние**, а из состояния не видно, чего от
|
||||||
|
работы ждут — «Ничья объявляется, пока клетки есть» одинаково читается как
|
||||||
|
жалоба и как задание. В списке, где решают «брать или не брать», это разные
|
||||||
|
вещи.
|
||||||
|
|
||||||
|
Отсюда же разница индексов: роадмап — список возможностей, беклог — список работ.
|
||||||
|
Перепутанные формы заголовков делают каждый из них похожим на другой.
|
||||||
|
|
||||||
|
**Р85. Механизировано ровно то, что механизируется, — счётчиком, а не
|
||||||
|
замечанием.** `check` считает заголовки, у которых первое слово не оканчивается
|
||||||
|
на `-ть`/`-ти`/`-чь` (перед ним допускается «не»), и печатает **число** в блоке
|
||||||
|
здоровья. Замечанием на файл этого делать нельзя: проверка эвристическая, а
|
||||||
|
беклог, заведённый до правила, переоформляют не «заодно» — десятки одинаковых
|
||||||
|
строк научили бы пропускать весь блок.
|
||||||
|
|
||||||
|
**Р86. Годность формулировки судит отдельный агент `doc-wording`, а не чек-лист
|
||||||
|
в скилле.** Самопроверка текста слабее всего там, где формулировка казалась
|
||||||
|
удачной при написании, — а пишет и проверяет иначе один и тот же агент в одном
|
||||||
|
контексте. Агент читает пачку записей и возвращает **готовые формулировки на
|
||||||
|
замену**, ничего не правя сам; заголовок и «зачем» подставляются командой и
|
||||||
|
показываются человеку, потому что именно по ним задачу выбирают. Он намеренно не
|
||||||
|
проверяет ничего из того, что ловит `tasks.py check`: повторить машинную
|
||||||
|
проверку словами значит завести правилу второй дом.
|
||||||
|
|
||||||
|
**Р87. Заголовок секции — с прописной, после него пустая строка.** Во всех
|
||||||
|
индексах, включая секции беклога, имена которых выбирает проект: правило про
|
||||||
|
**оформление**, а не про имя. Канонические имена стали писаться с прописной
|
||||||
|
(`Готово` | `Запланировано` | `Направления` | `Разработка`, англ. `Done` |
|
||||||
|
`Planned` | `Directions` | `Tooling`), сверка везде идёт по нижнему регистру,
|
||||||
|
так что старые индексы читаются по-прежнему и поднимаются `check --fix`.
|
||||||
|
|
||||||
|
**Р88. Имя секции принадлежит заголовку индекса, файл на неё только ссылается.**
|
||||||
|
Это разрешает единственную неоднозначность починки: расхождение файла и
|
||||||
|
заголовка **в одном регистре** правится в пользу заголовка. Без этого шага
|
||||||
|
переезд на канон оставил бы `Готово` в роадмапе и `готово` в каждом файле цели —
|
||||||
|
расхождение безвредное, но вечное, потому что свести его было бы некому.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С82. Отбивка живёт на записи, а не на вставке.** `spaced_sections` вызывается
|
||||||
|
в `Plan.index`, через который проходит **каждая** запись индекса. Чинить отбивку
|
||||||
|
в каждом месте вставки значило бы полагаться на то, что ни одного не забыли, — а
|
||||||
|
мест вставки три (`--first`, `--after`, в конец).
|
||||||
|
|
||||||
|
**С83. Обкатка нашла два дефекта, которых не нашли ни линтеры, ни свои
|
||||||
|
проверки.** Вставка в пустую секцию съедала отбивку перед следующим заголовком;
|
||||||
|
мета, разорванная пустой строкой, теряла поля молча, а `check` видел только
|
||||||
|
следствие («без рода работы») и советовал `edit --kind`, который дописывал
|
||||||
|
**второе** такое же поле. Оба класса теперь названы: пропуск пустых строк идёт
|
||||||
|
только до первой непустой, а поле меты в теле — ошибка с названной причиной,
|
||||||
|
которую `--fix` намеренно не чинит.
|
||||||
|
|
||||||
|
**С84. Пустой проект показывает форму хуже живого.** Чтобы увидеть достигнутую
|
||||||
|
цель, отказ, спринт и все четыре рода работы, проект пришлось поставить на
|
||||||
|
середину пути. Это довод в пользу того, чтобы обкатку вести на *состоянии*, а не
|
||||||
|
на *старте*: у старта половина формы не наблюдаема.
|
||||||
|
|
||||||
|
**С85. Мелкая цель даёт две задачи, и это не повод её укрупнять.** У цели
|
||||||
|
«Соперником может быть компьютер» третья задача напрашивалась (выбор уровня
|
||||||
|
соперника), но не мерджится порознь: без сильного соперника выбирать не из чего.
|
||||||
|
Тест декомпозиции её отбил — частичный ответ на вопрос «не выродились ли цели в
|
||||||
|
ярлыки тем».
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# 21. Язык проектных текстов — информационный стиль (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Языковые правила лежали внутри скилла `tasks`, в разделе «Как написана задача»:
|
||||||
|
англицизмы, неизвестные термины, «сложность формулировки — не признак сложности
|
||||||
|
работы». Три пункта, выведенные из практики, без общей опоры и без ответа на
|
||||||
|
вопрос «а что ещё сюда относится».
|
||||||
|
|
||||||
|
Дал ссылку на чужой скилл `prepare-jira-text` — там раздел «Язык» с
|
||||||
|
информационным стилем, таблицей англицизмов-калек и таблицей жаргона. Заодно
|
||||||
|
попросил найти справку об информационном стиле Максима Ильяхова и адаптировать
|
||||||
|
его.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р89. У языка появился один дом — `canon/references/language.md`.** Не в
|
||||||
|
`tasks`, хотя пришёл он оттуда: правила относятся к документам канона, решениям
|
||||||
|
ADR, запискам разведки и сообщениям коммитов в той же мере, что к задачам, а
|
||||||
|
каталог задач и сам часть `docs/`. Раскладка отвечает, **где** текст лежит; этот
|
||||||
|
файл — **каким он должен быть**. `tasks/SKILL.md` оставил у себя четыре правила,
|
||||||
|
которые нарушаются чаще прочих, и ссылку.
|
||||||
|
|
||||||
|
**Р90. Инфостиль взят не целиком, и отброшенное названо вслух.** Он написан для
|
||||||
|
рекламы, статей и писем — текстов, где читателя надо удержать; проектный текст
|
||||||
|
читают потому, что надо. Взято: полезное действие, глагол вместо отглагольного
|
||||||
|
существительного, активный залог, факт вместо оценки, стоп-слова, «одна мысль —
|
||||||
|
одно предложение», параллельность, работающий заголовок. Отброшено:
|
||||||
|
**парцелляция** (рубленые фразы ломают причинную связь, а в решении ценность
|
||||||
|
именно в ней), **запрет вводных целиком** («если», «иначе», «в отличие от» — это
|
||||||
|
условия, то есть сведения), **запрет скобок и точки с запятой** (в технической
|
||||||
|
записи скобки несут уточнение — имя команды, единицы, слаг). Многоточие
|
||||||
|
запрещено: в проектном тексте оно значит «дописать позже».
|
||||||
|
|
||||||
|
Раздел «Что отброшено намеренно» написан не для полноты. Без него правило
|
||||||
|
читается как «пиши короче», и первый же агент начинает резать «поэтому» и
|
||||||
|
«иначе» — то есть ровно то, ради чего текст и писался.
|
||||||
|
|
||||||
|
**Р91. «Снять корону» переведено на здешнего читателя.** У Ильяхова это «надеть
|
||||||
|
корону на клиента». Здесь клиент — **ты сам через квартал** и тот, кто возьмёт
|
||||||
|
задачу. Отсюда конкретное требование: называть состояние и остаток, а не
|
||||||
|
пересказывать, как было интересно разбираться.
|
||||||
|
|
||||||
|
**Р92. Таблицы англицизмов и жаргона уехали в агента помеченной копией.** Устав
|
||||||
|
агента обязан быть самодостаточным — он не разрешает пути плагина и не ходит по
|
||||||
|
ссылкам, — а два дома у одного правила уже трижды расходились. Механизм для
|
||||||
|
этого в репозитории есть (`scripts/copies.py`), и это ровно его случай: копия
|
||||||
|
дословная и помеченная, проверка ловит расхождение.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С86. У агента вычитки правил стало двенадцать, и они разделены на две
|
||||||
|
группы.** «Форма записи» верна только для каталога задач, «язык» — для любого
|
||||||
|
проектного текста. Разделение не косметическое: находки докладываются группами и
|
||||||
|
в этом порядке, потому что форма меняет решение «брать или не брать», а язык —
|
||||||
|
только цену чтения.
|
||||||
|
|
||||||
|
**С87. Порог правки записан дважды и одинаково** — в `language.md` и в уставе
|
||||||
|
агента: правка без нарушенного правила не делается. Это единственная защита от
|
||||||
|
списка, в котором половина замечаний вкусовые: такой список перестают читать
|
||||||
|
целиком, и настоящие находки пропадают вместе с ним.
|
||||||
|
|
||||||
|
**С88. Переезд на канон 3 языком ничего не требует.** Шаг в changelog так и
|
||||||
|
записан: прочитать и ничего не переписывать задним числом. Сплошная вычитка
|
||||||
|
старых документов стоит дороже, чем даёт, а правила применяются к тому, что
|
||||||
|
правится сейчас.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# 22. Обкатка агента вычитки: имя, охват и «так везде» (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Агента вычитки прогнали по тестовому набору — 13 записей выдуманного проекта.
|
||||||
|
Устав он читал сам, как обычный подрядчик.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р93. Агент называется `doc-wording`, а не `task-wording`.** Имя пришло из
|
||||||
|
задач, но правила языка относятся ко всем проектным текстам: документам канона,
|
||||||
|
решениям ADR, запискам разведки. Форма записи — вторая половина устава — верна
|
||||||
|
только для файлов `docs/tasks/items/`, и теперь это сказано заголовком раздела,
|
||||||
|
а не подразумевается. Вход агента расширен: список файлов или каталог,
|
||||||
|
вперемешку тоже.
|
||||||
|
|
||||||
|
**Р94. «Так сделано везде» — не оправдание, а признак.** Агент нашёл, что раздел
|
||||||
|
«Затрагивает» в нескольких записях называет не только границу, но и её будущее
|
||||||
|
состояние («источник хода становится двумя»), — и **промолчал**, объяснив это
|
||||||
|
принятым стилем каталога. Записи писал один агент за один заход: систематичность
|
||||||
|
здесь значит ровно обратное — правило не применялось вовсе.
|
||||||
|
|
||||||
|
В устав добавлено: одна и та же ошибка в пяти файлах даёт **одну находку на весь
|
||||||
|
набор** с перечнем, но не даёт права промолчать. Принятым стилем считается
|
||||||
|
только то, что назвал зовущий или что записано в конвенциях проекта.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С89. Находка агента попала в слово из собственного скилла.** «Цель про станок,
|
||||||
|
а не про игру» — метафора, которую я перенёс в тестовую запись из
|
||||||
|
`tasks/SKILL.md`. Проверка показала худшее: `станок` в каноне уже занят — «общий
|
||||||
|
станок» это красная проверка, врывающаяся в замороженный спринт (`canon.md`,
|
||||||
|
`session/SKILL.md`). Одно слово в двух смыслах, тот же класс, что и `окружение`
|
||||||
|
в [теме 19](19-roadmap-is-state-not-queue.md). В `tasks/SKILL.md` заменено на
|
||||||
|
«работа над инструментом и процессом» — как названа и секция роадмапа.
|
||||||
|
|
||||||
|
**С90. Одна находка на 13 записей — не провал вычитки.** Тексты писались сразу
|
||||||
|
по правилам, и находить в них было почти нечего. Показательно другое: агент
|
||||||
|
удержал порог (вкусовых правок не предложил) и явно сказал, по чему проверял
|
||||||
|
термины, — то есть отработали обе защиты, а не только та, что ищет.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# 23. Вычитка разделена на два прохода (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
В уставе агента вычитки стоял заголовок «Форма записи — только для
|
||||||
|
`docs/tasks/items/`». Условная половина устава: на документе канона она молчит,
|
||||||
|
на задаче включается.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р95. Проходов два: `task-form` и `doc-wording`.** Разделены не по охвату — по
|
||||||
|
**глубине**. Язык проверяется по словам и фразам, поштучно, и это подметание:
|
||||||
|
залог, оценки, стоп-слова, англицизмы, жаргон. Форма записи требует понять, что
|
||||||
|
задача делает, и **открыть файл цели**, на которую она ссылается, чтобы сверить,
|
||||||
|
какую строку «Завершения» задача двигает. Слитый проход одну половину делает
|
||||||
|
дорогой, а вторую — поверхностной.
|
||||||
|
|
||||||
|
Отсюда и разные модели: `doc-wording` — sonnet, `task-form` — opus. Первый
|
||||||
|
подметает, второй судит смысл, и ровно на суждении обкатка показала провал —
|
||||||
|
агент сам себе объяснил находку «принятым стилем каталога» ([тема
|
||||||
|
22](22-wording-agent-trial.md)).
|
||||||
|
|
||||||
|
**Р96. Условная половина устава — плохая конструкция сама по себе.** Правило,
|
||||||
|
которое «применяется только если», агент применяет по своему усмотрению, а
|
||||||
|
усмотрение и есть то, чего от него не ждут. Два коротких устава без условий
|
||||||
|
надёжнее одного длинного с ними — и это довод, годный за пределами этого случая.
|
||||||
|
|
||||||
|
**Р97. Каждый устав отказывается от чужой половины прямо.** «Увидел не по своей
|
||||||
|
части — скажи строкой в границах покрытия, не находкой». Без такого отказа две
|
||||||
|
проверки одного места расходятся и начинают спорить, а разнимать их потом
|
||||||
|
дороже, чем не сводить. Исключение ровно одно и названо: неудачное слово **в
|
||||||
|
заголовке** судит `task-form`, потому что заголовок целиком его.
|
||||||
|
|
||||||
|
**Р98. Порог правки переехал в дом и копируется в оба устава.** Он теперь в
|
||||||
|
`language.md` помеченным домом `порог-правки`: правка без нарушенного правила не
|
||||||
|
делается, систематичность нарушения — не довод в его пользу. Дублировать его
|
||||||
|
руками в двух уставах значило бы получить два разных порога через месяц.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С91. Шестое правило `task-form` — единственное, что читает больше одного
|
||||||
|
файла.** Оно же единственное, что смотрит **набор**, а не запись: строка
|
||||||
|
«Завершения», к которой не относится ни одна поданная задача, докладывается
|
||||||
|
отдельным блоком. Это граница между вычиткой и разбором, и она проведена внутри
|
||||||
|
правила, а не между агентами.
|
||||||
|
|
||||||
|
**С92. Порядок вызова — сперва `task-form`.** Его находки меняют решение «брать
|
||||||
|
или не брать», а язык — только цену чтения; и переписанный заголовок
|
||||||
|
бессмысленно вычитывать до того, как он переписан.
|
||||||
|
|
||||||
|
**С93. Помеченных копий стало шесть при пяти домах.** Механизм
|
||||||
|
`scripts/copies.py` впервые используется не для скелетов канона, а чтобы
|
||||||
|
удержать одно правило в двух уставах подрядчиков. Случай тот же: текст обязан
|
||||||
|
быть на месте, потому что подрядчик по ссылкам не ходит.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# 24. Обкатка двух проходов: два дефекта в собственных правилах (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Оба прохода запущены на тестовом наборе из 13 записей. `task-form` дал три
|
||||||
|
находки и блок «строки Завершения», `doc-wording` — пять находок. Разделение
|
||||||
|
окупилось сразу: `task-form` поймал ровно тот класс, на котором слитый агент
|
||||||
|
промолчал (границы, названные будущим состоянием, — тема 22,
|
||||||
|
[Р94](22-wording-agent-trial.md)).
|
||||||
|
|
||||||
|
Но два его правила разошлись с остальным каноном.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р99. «Одна мысль — одно предложение» не распространяется на поля меты.**
|
||||||
|
`doc-wording` предложил разбить «зачем» надвое — а `task-format.md` требует от
|
||||||
|
«зачем» **одного предложения**: оно повторяется строкой индекса, и второму там
|
||||||
|
не поместиться. Агент честно выполнил тот документ, который читал; виноват не
|
||||||
|
он, а правило без оговорки. Оговорка записана и в доме (`language.md`), и в
|
||||||
|
уставе: тесно — сокращай, но не дели.
|
||||||
|
|
||||||
|
**Р100. «Не своё» бывает двух родов, и поступают с ними по-разному.** Чужому
|
||||||
|
подрядчику — строкой в границах покрытия, чтобы находка не пропала. **Машинной
|
||||||
|
проверке — вообще ничего, даже строкой**: это не потерянная находка, а уже
|
||||||
|
проверенное. `doc-wording` отправил в «замечено не по моей части» открытый
|
||||||
|
вопрос в задаче — а его ловит `tasks.py check`, и строка получилась шумом,
|
||||||
|
который выглядит как работа.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С94. Шестое правило нашло то, чего не искали.** Три строки «Завершения»
|
||||||
|
оказались **закрыты критериями задач, но не заявлены** самими задачами, а одна
|
||||||
|
строка цели (`checks-one-command`, «названа в README и в описании работы над
|
||||||
|
проектом») — закрыта наполовину. Агент назвал оба толкования и выбирать не стал,
|
||||||
|
как и велено. Выбрано сужение цели: описания работы над проектом у выдуманной
|
||||||
|
игры нет вовсе, и строка обещала то, чего негде исполнить.
|
||||||
|
|
||||||
|
**С95. Спорные находки полезны тем, что показывают спор правил, а не вкуса.** Из
|
||||||
|
пяти языковых находок три приняты сразу, две отклонены — и обе отклонённые
|
||||||
|
указывали на одно и то же место канона (правило 4 без оговорки). Вкусовых
|
||||||
|
находок не было ни одной: порог держится.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user