Compare commits
10
Commits
7333953b1e
...
6251157d8d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6251157d8d
|
||
|
|
ed83ec7dc0
|
||
|
|
8d8c1656e5
|
||
|
|
3849f084be
|
||
|
|
0627199a1a
|
||
|
|
dff05ad097
|
||
|
|
3529cd8425
|
||
|
|
eae734f5cc
|
||
|
|
bf6a173115
|
||
|
|
b411d4edb8
|
@@ -8,7 +8,7 @@
|
|||||||
{
|
{
|
||||||
"name": "av-dev",
|
"name": "av-dev",
|
||||||
"source": "./av-dev",
|
"source": "./av-dev",
|
||||||
"description": "Личный процесс разработки одним плагином: документы проекта, учёт работ и работа по задачам. Документы — канон раскладки (CLAUDE.md плюс docs/: паспорт, архитектура, схема БД, безопасность, конвенции, разведка, ADR, журнал ревью), три операции одной машиной сравнения в doc-canon (check, adopt, upgrade) со скриптом docs.py, заведение нового проекта интервью в doc-init, ведение содержимого по ходу разработки в doc-sync, суд о смысловом здоровье в doc-healthcheck двумя агентами разом. Учёт — задачи и цели каталогом markdown-файлов в task-track, у записи тип (goal, feature, fix, chore, research), и тип решает её схему; приоритет расставляет интерактивный task-groom порядком строк. Работа — одна задача от постановки до закрытия скиллом code-resolve: точка входа одна, сценария три (решение циклом SDD с чекпоинтом, разведка без кода, обслуживание без change), и выбирает сценарий сам скилл, прочитав постановку; плюс конвейер ревью code-review с детерминированным гейтом, сверкой со спеками и обязательным триажем, плюс code-openspec, который заводит и проверяет openspec/. Версия раскладки и настройки живут в .av-dev.toml в корне репозитория. Части включаются следом в проекте, а не установкой: нет docs/ — документы не ведутся, нет каталога задач — учёт остаётся владельцу, нет openspec/ — его заводит сценарий решения. Коммиты — отдельный плагин av-dev-git."
|
"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-openspec, который заводит и проверяет openspec/. Версия раскладки и настройки живут в .av-dev.toml в корне репозитория. Части включаются следом в проекте, а не установкой: нет docs/ — документы не ведутся, нет каталога задач — учёт остаётся владельцу, нет openspec/ — его заводит сценарий решения. Коммиты — отдельный плагин av-dev-git."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "av-dev-git",
|
"name": "av-dev-git",
|
||||||
|
|||||||
-3985
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,45 +3,52 @@
|
|||||||
Личный маркетплейс плагинов для разработки. Процесс имени не имеет — он и есть
|
Личный маркетплейс плагинов для разработки. Процесс имени не имеет — он и есть
|
||||||
`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** — весь процесс, и **av-dev-git** — сообщения коммитов.
|
Плагина два: **av-dev** — весь процесс, и **av-dev-git** — сообщения коммитов.
|
||||||
Второй работает в любом репозитории, в том числе не приведённом к канону. До 13 августа 2026 процесс жил тремя плагинами
|
Второй работает в любом репозитории, в том числе не приведённом к канону. До 13 августа 2026 процесс жил тремя плагинами
|
||||||
(`av-dev-docs`, `av-dev-tasks`, `av-dev-code`); раскол делался под раздельную
|
(`av-dev-docs`, `av-dev-tasks`, `av-dev-code`); раскол делался под раздельную
|
||||||
установку, она не понадобилась ни разу, и плагины слились — тема 64
|
установку, она не понадобилась ни разу, и плагины слились —
|
||||||
[DECISIONS.md](DECISIONS.md).
|
[тема 64](decisions/64-three-plugins-merged.md) журнала решений.
|
||||||
|
|
||||||
Имя **скилла** несёт префикс прежнего плагина: `doc-`, `task-`, `code-`. Вызов
|
Имя **скилла** несёт префикс материала, с которым он работает: `doc-`, `task-`,
|
||||||
выходит вида `/av-dev:<скилл>`.
|
`code-`. Вызов выходит вида `/av-dev:<скилл>`. Префикса нет ровно у одного —
|
||||||
|
`canon`: он работает не с материалом, а с **формой**, общей у всех частей
|
||||||
|
проекта.
|
||||||
|
|
||||||
### av-dev — документы, учёт, работа
|
### av-dev — форма, документы, учёт, работа
|
||||||
|
|
||||||
**Документы проекта.** Владеют `docs/` и `CLAUDE.md`.
|
**Форма раскладки.** Одна на весь проект, и держит её один скилл.
|
||||||
|
|
||||||
|
- `canon` — раскладка проекта и её обновление: `check` / `adopt` / `upgrade`,
|
||||||
|
плюс скрипт `docs.py`. `check` сверяет раскладку документов, `adopt` заводит
|
||||||
|
все части сразу и зовёт владельцев каталога задач и `openspec/`, `upgrade`
|
||||||
|
повышает **всю** раскладку по журналу версий — общему, и на документы, и на
|
||||||
|
каталог задач. Содержимого он не ведёт: это соседние скиллы.
|
||||||
|
|
||||||
|
**Документы проекта.** Владеют **содержимым** `docs/` и `CLAUDE.md`; раскладка —
|
||||||
|
у `canon`.
|
||||||
|
|
||||||
- `doc-init` — новый проект: интервью по свободному описанию замысла →
|
- `doc-init` — новый проект: интервью по свободному описанию замысла →
|
||||||
первичная документация;
|
первичная документация;
|
||||||
- `doc-canon` — привести проект к канону документов: `check` / `adopt` /
|
|
||||||
`upgrade`, плюс скрипт `docs.py`. Он же ведёт журнал версий раскладки —
|
|
||||||
общий, и на документы, и на каталог задач;
|
|
||||||
- `doc-healthcheck` — здоровье документации **судом, а не машиной**: не
|
- `doc-healthcheck` — здоровье документации **судом, а не машиной**: не
|
||||||
разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон
|
разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон
|
||||||
разом — `doc-consistency` (документы между собой и с openspec) и
|
разом — `doc-consistency` (документы между собой и с openspec) и
|
||||||
`doc-code-drift` (факты против кода) — и разбирает урожай порциями. Дорого,
|
`doc-code-drift` (факты против кода) — и разбирает урожай порциями. Дорого,
|
||||||
поэтому не на каждой задаче. Язык документов вычитывает отдельный агент
|
поэтому не на каждой задаче. Язык документов вычитывает отдельный агент
|
||||||
`doc-wording`, и зовут его не отсюда, а те, кто только что писал текст:
|
`doc-wording`, и зовут его не отсюда, а те, кто только что писал текст:
|
||||||
`doc-sync`, `doc-init` и `doc-canon`;
|
`doc-sync`, `doc-init` и `canon`;
|
||||||
- `doc-sync` — содержимое канона по ходу разработки: ADR из архивного
|
- `doc-sync` — содержимое канона по ходу разработки: ADR из архивного
|
||||||
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
||||||
архитектуры.
|
архитектуры.
|
||||||
|
|
||||||
**Учёт работ.** Владеет каталогом задач.
|
**Учёт работ.** Владеет каталогом задач.
|
||||||
|
|
||||||
- `task-track` — задачи и цели каталогом markdown-файлов, у каждой записи тип
|
- `task-track` — задачи каталогом markdown-файлов: у каждой тип (`feature`,
|
||||||
(`goal`, `feature`, `fix`, `chore`, `research`), и тип задаёт её схему;
|
`fix`, `chore`, `research`), задающий её схему, а у проекта — стадия (`build`
|
||||||
|
или `support`), решающая, что значит порядок строк беклога;
|
||||||
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
||||||
`task-wording` (язык записей);
|
`task-wording` (язык записей);
|
||||||
- `task-groom` — груминг беклога: что сейчас самое важное и что перестало быть
|
- `task-groom` — груминг беклога: что сейчас самое важное и что перестало быть
|
||||||
@@ -114,10 +121,10 @@ flowchart TB
|
|||||||
tp["code-resolve<br/>3 сценария: разведка,<br/>решение, обслуживание"] --> rp["code-review<br/>10 агентов-проходов"]
|
tp["code-resolve<br/>3 сценария: разведка,<br/>решение, обслуживание"] --> rp["code-review<br/>10 агентов-проходов"]
|
||||||
osp["code-openspec<br/>заводит и проверяет openspec/"]
|
osp["code-openspec<br/>заводит и проверяет openspec/"]
|
||||||
end
|
end
|
||||||
subgraph docsp["документы, владеют docs/"]
|
canon["canon<br/>форма раскладки всего проекта"]
|
||||||
|
subgraph docsp["документы, владеют содержимым docs/"]
|
||||||
direction LR
|
direction LR
|
||||||
init["doc-init"]
|
init["doc-init"]
|
||||||
canon["doc-canon"]
|
|
||||||
docs["doc-sync"]
|
docs["doc-sync"]
|
||||||
hc["doc-healthcheck"]
|
hc["doc-healthcheck"]
|
||||||
end
|
end
|
||||||
@@ -152,18 +159,20 @@ flowchart TB
|
|||||||
`docs/`, каталог задач, `openspec/`. Тогда вызывающий называет строкой, чего теперь не делает
|
`docs/`, каталог задач, `openspec/`. Тогда вызывающий называет строкой, чего теперь не делает
|
||||||
никто, и работу не останавливает. Правило целиком —
|
никто, и работу не останавливает. Правило целиком —
|
||||||
[shared/absence.md](av-dev/shared/absence.md): оно нужно почти каждому скиллу, и
|
[shared/absence.md](av-dev/shared/absence.md): оно нужно почти каждому скиллу, и
|
||||||
ни один им не владеет. Там же, в `shared/`, живут язык проектных текстов и
|
ни один им не владеет. Там же, в `shared/`, живут язык проектных текстов,
|
||||||
словарь сопровождения; скиллы читают их по ссылке, а дословной копией они
|
словарь сопровождения и **перечень осей процесса**
|
||||||
уезжают только в уставы вычитки — туда, где текст обязан лежать внутри промпта.
|
[axes.md](av-dev/shared/axes.md) — какие закрытые словари правят ходом работы,
|
||||||
|
где дом каждого и чего он **не** решает. Скиллы читают эти дома по ссылке, а
|
||||||
|
дословной копией оттуда уезжает лишь то, что обязано лежать внутри промпта.
|
||||||
|
|
||||||
## Канон документов проекта
|
## Канон раскладки проекта
|
||||||
|
|
||||||
Все проекты приводятся к одной раскладке — так проще ориентироваться, когда
|
Все проекты приводятся к одной раскладке — так проще ориентироваться, когда
|
||||||
проектов много, и рядом OpenSpec тоже держит строгую структуру. `CLAUDE.md` плюс
|
проектов много, и рядом OpenSpec тоже держит строгую структуру. `CLAUDE.md` плюс
|
||||||
`docs/` (паспорт, архитектура, база, безопасность, конвенции, разведка, ADR,
|
`docs/` (паспорт, архитектура, база, безопасность, конвенции, разведка, ADR,
|
||||||
ревью, задачи) и `openspec/` — **раскладка целиком, роли документов и правило
|
ревью, задачи) и `openspec/` — **раскладка целиком, роли документов и правило
|
||||||
единственного дома живут одним домом**:
|
единственного дома живут одним домом**:
|
||||||
[canon.md](av-dev/skills/doc-canon/references/canon.md). Здесь она не
|
[canon.md](av-dev/skills/canon/references/canon.md). Здесь она не
|
||||||
пересказывается: копия перечня путей уже расходилась с домом, и как раз в
|
пересказывается: копия перечня путей уже расходилась с домом, и как раз в
|
||||||
обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает
|
обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает
|
||||||
нарушением.
|
нарушением.
|
||||||
@@ -181,9 +190,9 @@ flowchart TB
|
|||||||
«тема → её дом → что оттуда берётся» —
|
«тема → её дом → что оттуда берётся» —
|
||||||
[project-facts.md](av-dev/skills/code-review/references/project-facts.md).
|
[project-facts.md](av-dev/skills/code-review/references/project-facts.md).
|
||||||
|
|
||||||
Прийти в старый проект и перевести его на канон — `/av-dev:doc-canon`.
|
Прийти в старый проект и перевести его на канон — `/av-dev:canon`.
|
||||||
Раскладка версионируется, и проекты повышаются по [журналу
|
Раскладка версионируется, и проекты повышаются по [журналу
|
||||||
версий](av-dev/skills/doc-canon/references/changelog.md).
|
версий](av-dev/skills/canon/references/changelog.md).
|
||||||
|
|
||||||
**Версия одна, и живёт она в `.av-dev.toml` в корне репозитория** — вместе с
|
**Версия одна, и живёт она в `.av-dev.toml` в корне репозитория** — вместе с
|
||||||
настройками: `[docs] migrations` и секция `[tasks]`, которая говорит, где лежит
|
настройками: `[docs] migrations` и секция `[tasks]`, которая говорит, где лежит
|
||||||
@@ -232,11 +241,22 @@ claude plugin install av-dev-git@av-dev-skills --scope project
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**При установке в проект, где лежали проектные копии** скиллов и агентов
|
**При установке в проект, где лежали проектные копии** скиллов и агентов —
|
||||||
(`.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`.
|
||||||
|
|
||||||
|
Две копии одного скилла расходятся, и побеждает та, что **короче названа**:
|
||||||
|
короткое имя разрешится в устаревшую проектную копию молча и без признаков
|
||||||
|
подмены.
|
||||||
|
|
||||||
|
<!-- /дом: проектные-копии -->
|
||||||
|
|
||||||
## Обновление
|
## Обновление
|
||||||
|
|
||||||
@@ -369,7 +389,7 @@ python3 scripts/frontmatter.py # 0 в порядке, 1 расхождени
|
|||||||
простого скаляра начинает вложенное отображение, так что «конвейер ревью: гейт,
|
простого скаляра начинает вложенное отображение, так что «конвейер ревью: гейт,
|
||||||
сверка…» — это не текст с двоеточием, а синтаксическая ошибка. Так было
|
сверка…» — это не текст с двоеточием, а синтаксическая ошибка. Так было
|
||||||
написано часть описаний плагинов, и читались они правильно — замер и разбор
|
написано часть описаний плагинов, и читались они правильно — замер и разбор
|
||||||
в [DECISIONS.md](DECISIONS.md), решение III;
|
в [решении Р61](decisions/17-notes-tier-work-kind-roadmap.md) журнала;
|
||||||
- **`name`, разошедшееся с именем каталога скилла или файла charter'а.** Вызов
|
- **`name`, разошедшееся с именем каталога скилла или файла charter'а.** Вызов
|
||||||
разрешается по каталогу, а сообщение о промахе говорит «нет такого скилла»,
|
разрешается по каталогу, а сообщение о промахе говорит «нет такого скилла»,
|
||||||
а не «имя не то»;
|
а не «имя не то»;
|
||||||
@@ -426,7 +446,7 @@ python3 scripts/copies.py # 0 сошлось, 1 расхождение, 2 р
|
|||||||
когда он решает; и в скелетах, уезжающих в репозиторий проекта.
|
когда он решает; и в скелетах, уезжающих в репозиторий проекта.
|
||||||
|
|
||||||
**Дом ставится в `shared/` только тогда, когда владельца нет.** У адресов
|
**Дом ставится в `shared/` только тогда, когда владельца нет.** У адресов
|
||||||
владелец есть: раскладку `docs/` держит `doc-canon`, каталог задач —
|
владелец есть: раскладку `docs/` держит `canon`, каталог задач —
|
||||||
`task-track`, и переносить их наружу значило бы отобрать у владельца его же
|
`task-track`, и переносить их наружу значило бы отобрать у владельца его же
|
||||||
предмет. Общее без владельца живёт в `shared/`; чужое с владельцем остаётся
|
предмет. Общее без владельца живёт в `shared/`; чужое с владельцем остаётся
|
||||||
дома, а потребитель на него ссылается.
|
дома, а потребитель на него ссылается.
|
||||||
@@ -464,7 +484,7 @@ python3 scripts/resync.py # переписать тела всех разо
|
|||||||
## Проверка адресов документов
|
## Проверка адресов документов
|
||||||
|
|
||||||
Адрес документа принадлежит одному плагину, а называют его все: `docs/*` стоит
|
Адрес документа принадлежит одному плагину, а называют его все: `docs/*` стоит
|
||||||
примерно в сорока местах конвейера, `tasks/ROADMAP.md` — в четырёх местах канона.
|
примерно в сорока местах конвейера, `tasks/BACKLOG.md` — в нескольких местах канона.
|
||||||
Переименование в каноне до этих мест само не доходит.
|
Переименование в каноне до этих мест само не доходит.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -524,7 +544,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), ставится один раз на клон:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -537,6 +557,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-файлы | доли секунды |
|
||||||
@@ -552,7 +573,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`) — девять мест, и проверить её исполнение некому:
|
|
||||||
приёмщик и исполнитель одно лицо (`task-groom/SKILL.md`, «Стимулы»). Выродившаяся
|
|
||||||
строка **хуже отсутствия**: доклад выглядит проверенным.
|
|
||||||
|
|
||||||
Приём не правится: это гипотеза об износе, а не находка, и менять работающее по
|
|
||||||
догадке дороже. **Наблюдение к первой обкатке на живом проекте:** если в трёх
|
|
||||||
докладах подряд границы покрытия совпали дословно или называют не то, чего
|
|
||||||
проверка действительно не касалась, — приём выродился, и вот тогда решать.
|
|
||||||
|
|
||||||
**Форма ADR при пересмотре решения.** Парный статус («старая запись получает
|
|
||||||
`заменено на`») судит агент `doc-consistency` — правило 6 его устава. Охват был
|
|
||||||
открытым вопросом, пока агент звался пачкой, отобранной работой; переезд вызова
|
|
||||||
в `av-dev:doc-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,133 +0,0 @@
|
|||||||
# Что осталось сделать
|
|
||||||
|
|
||||||
**Здесь только работы и их порядок.** Чего здесь нет намеренно:
|
|
||||||
|
|
||||||
- **риски, открытые вопросы и принятые пределы** — [REMAINING.md](REMAINING.md);
|
|
||||||
- **почему решено так** — [DECISIONS.md](DECISIONS.md), записи датированы;
|
|
||||||
- **шаги повышения проекта с версии канона на версию** — журнал версий
|
|
||||||
([changelog.md](av-dev/skills/doc-canon/references/changelog.md)). Пересказ их
|
|
||||||
сюда был бы вторым домом, и прежний план на этом уже разъезжался: он повторял
|
|
||||||
записи версий 3, 4 и 5 построчно, и половина повторов протухла молча.
|
|
||||||
|
|
||||||
Сделанное отсюда **удаляется, а не помечается галочкой**. След остаётся в
|
|
||||||
коммитах и в `DECISIONS.md`; список из двух сотен `[x]` перестают читать целиком,
|
|
||||||
и живые пункты в нём теряются — прежний план умер именно так.
|
|
||||||
|
|
||||||
## Где мы сейчас
|
|
||||||
|
|
||||||
Плагина два: `av-dev` — весь процесс девятью скиллами (`doc-*` — документы,
|
|
||||||
`task-*` — учёт работ, `code-*` — работа по задачам), и `av-dev-git` —
|
|
||||||
сообщения коммитов. Прежние три (`av-dev-docs`, `av-dev-tasks`, `av-dev-code`)
|
|
||||||
слились 13 августа 2026, тема 64 DECISIONS. Общее, что нужно нескольким скиллам,
|
|
||||||
живёт домом в `av-dev/shared/`.
|
|
||||||
|
|
||||||
Раскладка — **версия 1**, одна на документы и на каталог задач, в
|
|
||||||
`.av-dev.toml` в корне проекта. Живые проекты стоят на каноне 2–3 и на плагине
|
|
||||||
`av-dev-pm`, которого больше нет: им идти сперва по закрытому журналу канона до
|
|
||||||
14, потом по записи 1 действующего.
|
|
||||||
|
|
||||||
Бумажная часть закрыта аудитом четырёх плагинов и четырьмя пропусками правок
|
|
||||||
(DECISIONS, запись 59). Всё, что ниже, проверяется **только на живом коде**.
|
|
||||||
|
|
||||||
## 1. Живые проекты — вернуть в рабочее состояние
|
|
||||||
|
|
||||||
Блокирует всё остальное: под текущим каноном не стоит ни один проект, и ни один
|
|
||||||
скилл, кроме `docs.py check`, не исполнялся на живом коде ни разу
|
|
||||||
(см. REMAINING, «Что ещё не сделано»).
|
|
||||||
|
|
||||||
### healthlog — первым
|
|
||||||
|
|
||||||
- [ ] переустановить плагины: снять `av-dev-pm` и `av-dev-pipeline`, поставить
|
|
||||||
`av-dev` и `av-dev-git`. Прежние имена мертвы, и `plugin update` их не
|
|
||||||
переименует — только снять и поставить. `marketplace update`, затем `plugin update` — одного шага мало
|
|
||||||
(README, «Обновление»)
|
|
||||||
- [ ] удалить проектные копии: `.claude/skills/healthlog-{task,review}-pipeline`
|
|
||||||
и девять `.claude/agents/healthlog-review-*.md`. Они прошлого поколения и
|
|
||||||
после переезда указывают на документы, которых уже не будет
|
|
||||||
- [ ] `av-dev:doc-canon` в режиме `adopt` — он приведёт проект к раскладке 1
|
|
||||||
сразу, картой и с подтверждением. Файл-в-файл здесь не расписан: раскладку
|
|
||||||
знает скилл, и второй перечень разошёлся бы с ним
|
|
||||||
- [ ] каталог задач — в `tasks/` корня (канон 11), не в `docs/tasks/`, без
|
|
||||||
`SPRINT.md` (канон 12); версия и настройки — в `.av-dev.toml` корня, там
|
|
||||||
же секция `[tasks]`. Скилл задач зовётся из `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` удалён. Осталось то, что на бумаге не проверяется:
|
|
||||||
|
|
||||||
- [ ] прогнать сценарий разведки на живой задаче: он написан целиком на бумаге и
|
|
||||||
не исполнялся ни разу. Самое неизвестное — выбор сценария на входе (не
|
|
||||||
уедет ли всё в решение, потому что «способ вроде понятен») и объём того,
|
|
||||||
что разведка пишет в документы
|
|
||||||
- [ ] прогнать сценарий обслуживания на живой задаче `chore`. Неизвестных три:
|
|
||||||
**держится ли связка признаков** (не уедет ли в обслуживание то, что меняет
|
|
||||||
поведение, и наоборот — не заведут ли пустой change по привычке); **работает
|
|
||||||
ли ревью без change** — конвейер написан вокруг него, и прогон с
|
|
||||||
фиксированным планом не запускался ни разу; **есть ли чем сверить состав
|
|
||||||
гейта** — на живых проектах семантика гейта в `CLAUDE.md` может не называть
|
|
||||||
шагов поимённо, и тогда сверка вырождается в цвет
|
|
||||||
- [ ] перемерить скилл `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,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "av-dev",
|
"name": "av-dev",
|
||||||
"description": "Личный процесс разработки одним плагином: документы проекта, учёт работ и работа по задачам. Документы — канон раскладки (CLAUDE.md плюс docs/: паспорт, архитектура, схема БД, безопасность, конвенции, разведка, ADR, журнал ревью), три операции одной машиной сравнения в doc-canon (check, adopt, upgrade) со скриптом docs.py, заведение нового проекта интервью в doc-init, ведение содержимого по ходу разработки в doc-sync, суд о смысловом здоровье в doc-healthcheck двумя агентами разом. Учёт — задачи и цели каталогом markdown-файлов в task-track, у записи тип (goal, feature, fix, chore, research), и тип решает её схему; приоритет расставляет интерактивный task-groom порядком строк. Работа — одна задача от постановки до закрытия скиллом code-resolve: точка входа одна, сценария три (решение циклом SDD с чекпоинтом, разведка без кода, обслуживание без change), и выбирает сценарий сам скилл, прочитав постановку; плюс конвейер ревью code-review с детерминированным гейтом, сверкой со спеками и обязательным триажем, плюс code-openspec, который заводит и проверяет openspec/. Версия раскладки и настройки живут в .av-dev.toml в корне репозитория. Части включаются следом в проекте, а не установкой: нет docs/ — документы не ведутся, нет каталога задач — учёт остаётся владельцу, нет openspec/ — его заводит сценарий решения. Коммиты — отдельный плагин av-dev-git.",
|
"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-openspec, который заводит и проверяет openspec/. Версия раскладки и настройки живут в .av-dev.toml в корне репозитория. Части включаются следом в проекте, а не установкой: нет docs/ — документы не ведутся, нет каталога задач — учёт остаётся владельцу, нет openspec/ — его заводит сценарий решения. Коммиты — отдельный плагин av-dev-git.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Anton Vakhrushev",
|
"name": "Anton Vakhrushev",
|
||||||
"email": "anwinged@gmail.com"
|
"email": "anwinged@gmail.com"
|
||||||
|
|||||||
@@ -15,19 +15,19 @@ color: yellow
|
|||||||
машина, а что человек», и её правая колонка — твой устав дословно.
|
машина, а что человек», и её правая колонка — твой устав дословно.
|
||||||
|
|
||||||
Карта домов, по которой ты судишь о правиле 1, — дословная копия канона; дом её
|
Карта домов, по которой ты судишь о правиле 1, — дословная копия канона; дом её
|
||||||
`av-dev/skills/doc-canon/references/canon.md`, раздел «Правило единственного
|
`av-dev/skills/canon/references/canon.md`, раздел «Правило единственного
|
||||||
дома», и правится она там. Здесь она стоит потому, что устав — это твой промпт
|
дома», и правится она там. Здесь она стоит потому, что устав — это твой промпт
|
||||||
целиком: за ссылкой ты пошёл бы отдельным чтением, а карта нужна тебе в тот
|
целиком: за ссылкой ты пошёл бы отдельным чтением, а карта нужна тебе в тот
|
||||||
момент, когда ты судишь.
|
момент, когда ты судишь.
|
||||||
|
|
||||||
<!-- копия: карта-домов из av-dev/skills/doc-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` |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: doc-wording
|
name: doc-wording
|
||||||
description: "Вычитка языка документов проекта по информационному стилю — паспорт, архитектура, конвенции, безопасность, решения ADR, записки разведки, CLAUDE.md. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в имени файла. Отдаёт готовые формулировки на замену и ничего не правит сам. Записи каталога задач вычитывает отдельный агент task-wording, их форму — task-form. Зовётся по названной пачке правленных документов, а не на весь канон: последним шагом синка документации (av-dev:doc-sync), шагом заведения проекта (av-dev:doc-init), шагами adopt и upgrade скилла av-dev:doc-canon. Скилл healthcheck его не зовёт — там сверка утверждений, а не языка. Только чтение."
|
description: "Вычитка языка документов проекта по информационному стилю — паспорт, архитектура, конвенции, безопасность, решения ADR, записки разведки, CLAUDE.md. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в имени файла. Отдаёт готовые формулировки на замену и ничего не правит сам. Записи каталога задач вычитывает отдельный агент 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
|
||||||
|
|||||||
@@ -213,11 +213,15 @@ color: green
|
|||||||
- **незнакомое** — форму предстоит нащупать по ходу. Признак один и
|
- **незнакомое** — форму предстоит нащупать по ходу. Признак один и
|
||||||
проверяемый: **перед работой нельзя назвать, какие узлы будут тронуты**.
|
проверяемый: **перед работой нельзя назвать, какие узлы будут тронуты**.
|
||||||
|
|
||||||
| | знакомое | незнакомое |
|
<!-- копия: матрица-метки из av-dev/skills/code-review/references/review-levels.md -->
|
||||||
|
|
||||||
|
| | **знакомое** — форму решения можно назвать до начала | **незнакомое** — форму предстоит нащупать по ходу |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **малое** | `small` | `large` |
|
| **малое** — один узел | `small` | `large` |
|
||||||
| **среднее** | `medium` | `large` |
|
| **среднее** — несколько узлов одного слоя | `medium` | `large` |
|
||||||
| **крупное** | `large` | `large` |
|
| **крупное** — несколько слоёв, перенос ответственности, большой рефакторинг | `large` | `large` |
|
||||||
|
|
||||||
|
<!-- /копия: матрица-метки -->
|
||||||
|
|
||||||
**Метка — не синоним размера, и это главная ловушка таблицы.** Размер `малое` и
|
**Метка — не синоним размера, и это главная ловушка таблицы.** Размер `малое` и
|
||||||
метка `small` совпадают только в левом верхнем углу: малое **незнакомое**
|
метка `small` совпадают только в левом верхнем углу: малое **незнакомое**
|
||||||
@@ -272,6 +276,8 @@ color: green
|
|||||||
**Ревью кода — раздача тем.** Кто закрывает тему, зависит от метки. Раскладка
|
**Ревью кода — раздача тем.** Кто закрывает тему, зависит от метки. Раскладка
|
||||||
жёсткая, выдумывать её не надо:
|
жёсткая, выдумывать её не надо:
|
||||||
|
|
||||||
|
<!-- копия: тема-метка-глубина из av-dev/skills/code-review/SKILL.md -->
|
||||||
|
|
||||||
| Тема | `small` | `medium` | `large` |
|
| Тема | `small` | `medium` | `large` |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор |
|
| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор |
|
||||||
@@ -282,6 +288,8 @@ color: green
|
|||||||
| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство |
|
| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство |
|
||||||
| тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор |
|
| тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор |
|
||||||
|
|
||||||
|
<!-- /копия: тема-метка-глубина -->
|
||||||
|
|
||||||
Две глубины, которые ты назначаешь:
|
Две глубины, которые ты назначаешь:
|
||||||
|
|
||||||
- **сверка** — открыть дом, открыть дифф, сравнить. Один-два вопроса на тему,
|
- **сверка** — открыть дом, открыть дифф, сравнить. Один-два вопроса на тему,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-triage
|
name: review-triage
|
||||||
description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Сверяет план разметки задачи с пришедшими отчётами: тема, размеченная и оставшаяся без отчёта, — находка о самом прогоне. Формирует итоговый отчёт с планом, перечнем проходов и обязательной секцией границ покрытия."
|
description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Сверяет план с пришедшими отчётами: тема, стоявшая в плане и оставшаяся без отчёта, — находка о самом прогоне; на прогоне без метки план даёт сценарий обслуживания, а не разметчик. Формирует итоговый отчёт с планом, перечнем проходов и обязательной секцией границ покрытия."
|
||||||
tools: Read, Grep, Glob, Bash, Write
|
tools: Read, Grep, Glob, Bash, Write
|
||||||
model: opus
|
model: opus
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -21,18 +21,26 @@ color: yellow
|
|||||||
|
|
||||||
## Вход
|
## Вход
|
||||||
|
|
||||||
Сырые выводы всех запущенных проходов, `git diff <база>..HEAD`, **план разметки
|
Сырые выводы всех запущенных проходов, `git diff <база>..HEAD`, **план прогона**
|
||||||
задачи** (агент `review-scope`, один запуск после `propose`) и режим прогона.
|
и режим. Дельта-спеки — по мере надобности.
|
||||||
Дельта-спеки — по мере надобности.
|
|
||||||
|
|
||||||
План — это таблица «тема → дом → глубина → кто закрывает» плюс размер, сложность
|
План — таблица «тема → дом → глубина → кто закрывает». Он твой главный инструмент
|
||||||
и метка с обоснованием. Он твой главный инструмент сверки: ты единственный, кто
|
сверки: ты единственный, кто видит и то, что заявлено, и то, что пришло.
|
||||||
видит и то, что размечено, и то, что пришло.
|
|
||||||
|
|
||||||
**Плана нет — ты не запускаешься, и исключений нет.** Сверка размеченного с
|
**Откуда план приходит, зависит от режима, и режимов два.**
|
||||||
пришедшим — твоя единственная защита от молчащего пропуска, и без плана она не
|
|
||||||
выполняется вовсе. Отчёт, собранный без неё, выглядит полным ровно настолько же,
|
- **С меткой** — план собрал `review-scope` (один запуск после `propose`), и к
|
||||||
насколько и неполный.
|
таблице прилагаются размер, сложность и метка с обоснованием.
|
||||||
|
- **Без метки** — так идёт прогон сценария обслуживания: изменение не меняет
|
||||||
|
поведения, размечать нечего, и разметчик не запускается вовсе. План
|
||||||
|
**фиксирован сценарием** (`av-dev:code-resolve`, `references/maintain.md`), а
|
||||||
|
размера, сложности и метки не существует. Не ищи их и не подставляй: в отчёте
|
||||||
|
на их месте — строка «прогон без метки, план сценария».
|
||||||
|
|
||||||
|
**Плана нет ни от разметчика, ни от сценария — ты не запускаешься, и исключений
|
||||||
|
нет.** Сверка заявленного с пришедшим — твоя единственная защита от молчащего
|
||||||
|
пропуска, и без плана она не выполняется вовсе. Отчёт, собранный без неё,
|
||||||
|
выглядит полным ровно настолько же, насколько и неполный.
|
||||||
|
|
||||||
Из документов проекта тебе нужны:
|
Из документов проекта тебе нужны:
|
||||||
|
|
||||||
@@ -174,7 +182,9 @@ severity:
|
|||||||
|
|
||||||
**Сигнала нет — тоже скажи строкой.** «Корректор метки отработал, возражений
|
**Сигнала нет — тоже скажи строкой.** «Корректор метки отработал, возражений
|
||||||
нет» и «корректор не запускался» — разные вещи, и отличить их по молчанию
|
нет» и «корректор не запускался» — разные вещи, и отличить их по молчанию
|
||||||
нельзя.
|
нельзя. **На прогоне без метки корректору нечего поднимать**, и это третье
|
||||||
|
состояние: пиши «метки нет, корректор неприменим», а не «не запускался» —
|
||||||
|
последнее читается как пропуск.
|
||||||
|
|
||||||
## Границы покрытия — не сокращаются
|
## Границы покрытия — не сокращаются
|
||||||
|
|
||||||
@@ -238,9 +248,12 @@ severity:
|
|||||||
Строго секциями из контракта: `Блокирует мердж` (≤3) / `Стоит исправить сейчас`
|
Строго секциями из контракта: `Блокирует мердж` (≤3) / `Стоит исправить сейчас`
|
||||||
(≤4) / `Гипотезы без доказательства` / `Promote candidates` / `Границы покрытия`.
|
(≤4) / `Гипотезы без доказательства` / `Promote candidates` / `Границы покрытия`.
|
||||||
|
|
||||||
Перед секциями — сводка: размер, сложность и метка с обоснованием разметки и режим прогона,
|
Перед секциями — сводка: режим прогона, состояние гейта, **план с исходом по
|
||||||
состояние гейта, **план с исходом по каждой теме**, сколько находок пришло на
|
каждой теме**, сколько находок пришло на вход и сколько осталось. На прогоне
|
||||||
вход и сколько осталось.
|
**с меткой** к этому добавляются размер, сложность и метка с обоснованием
|
||||||
|
разметки; на прогоне **без метки** их место занимает строка «прогон без метки,
|
||||||
|
план сценария обслуживания» — выдумывать метку задним числом нельзя, её никто
|
||||||
|
не снимал.
|
||||||
|
|
||||||
## Ограничения
|
## Ограничения
|
||||||
|
|
||||||
|
|||||||
+19
-44
@@ -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
|
|||||||
открывая код.
|
открывая код.
|
||||||
|
|
||||||
Оптика — смысл записи в её собственных рамках. Ты **не** судишь, нужна ли
|
Оптика — смысл записи в её собственных рамках. Ты **не** судишь, нужна ли
|
||||||
задача, верно ли выбрана цель и не крупна ли она: это разбор, и его ведёт
|
задача и не крупна ли она: это разбор, и его ведёт человек со скиллом
|
||||||
человек со скиллом `task-track`.
|
`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`, у которого «Вопрос» — это тема, а не вопрос.** «Разобраться с
|
||||||
выводом в терминалах» вопросом не является: на него нельзя ответить. Пока
|
выводом в терминалах» вопросом не является: на него нельзя ответить. Пока
|
||||||
вопроса нет, запись остаётся сырьём — и это законное состояние, но назови
|
вопроса нет, запись остаётся сырьём — и это законное состояние, но назови
|
||||||
@@ -105,34 +98,19 @@ color: green
|
|||||||
постановке. Он же путь понизить требования решением, принятым до
|
постановке. Он же путь понизить требования решением, принятым до
|
||||||
проектирования.
|
проектирования.
|
||||||
|
|
||||||
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,9 +120,9 @@ color: green
|
|||||||
твоя — раздел, который **есть и лжёт**: границы вместо замысла, критерий с
|
твоя — раздел, который **есть и лжёт**: границы вместо замысла, критерий с
|
||||||
оракулом только на словах.
|
оракулом только на словах.
|
||||||
|
|
||||||
**Содержание работы**: нужна ли задача, верно ли выбрана цель, не крупна ли она,
|
**Содержание работы**: нужна ли задача, не крупна ли она, достаточна ли
|
||||||
достаточна ли декомпозиция. Седьмое правило подходит к этому близко и
|
декомпозиция. **И место в списке**: порядок строк значит зависимость на стройке и
|
||||||
останавливается там, где кончается сверка с текстом цели. Об этом молчи.
|
важность на доработке, а ты записи видишь поштучно, вне списка. Об этом молчи.
|
||||||
|
|
||||||
## Порог вмешательства
|
## Порог вмешательства
|
||||||
|
|
||||||
@@ -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`, где та же запись представлена строкой. **Строка индекса вычитывается наравне с файлом**: по ней запись
|
||||||
строкой. **Строка индекса вычитывается наравне с файлом**: по ней запись
|
|
||||||
выбирают, не открывая тела, и «зачем» в ней повторяется дословно.
|
выбирают, не открывая тела, и «зачем» в ней повторяется дословно.
|
||||||
|
|
||||||
Плюс, если зовущий их назвал, документы проекта — паспорт, архитектура,
|
Плюс, если зовущий их назвал, документы проекта — паспорт, архитектура,
|
||||||
@@ -200,8 +199,8 @@ color: green
|
|||||||
критериев `check` только считает — поимённо их судит `tasks.py ready`, и это
|
критериев `check` только считает — поимённо их судит `tasks.py ready`, и это
|
||||||
тоже не твоя находка: твоя — язык того, что уже написано.
|
тоже не твоя находка: твоя — язык того, что уже написано.
|
||||||
|
|
||||||
**Содержание работы**: нужна ли задача, верно ли выбрана цель, не крупна ли она,
|
**Содержание работы**: нужна ли задача, не крупна ли она, достаточна ли
|
||||||
достаточна ли декомпозиция. Это разбор, и его ведёт человек со скиллом `task-track`.
|
декомпозиция. Это разбор, и его ведёт человек со скиллом `task-track`.
|
||||||
|
|
||||||
**Полезное действие, параллельность и работающий заголовок** — тоже не твои.
|
**Полезное действие, параллельность и работающий заголовок** — тоже не твои.
|
||||||
Они в доктрине языка, судит их человек: находка по ним требует увидеть текст
|
Они в доктрине языка, судит их человек: находка по ним требует увидеть текст
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
поведении.
|
поведении.
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# Оси процесса
|
||||||
|
|
||||||
|
**Это дом перечня, а не значений.** Что означает каждое значение и как оно
|
||||||
|
работает, знает владелец оси — здесь только сама ось, её дом и **чего она не
|
||||||
|
решает**. Второй пересказ механики разошёлся бы с первым; перечень же нужен
|
||||||
|
целиком и в одном месте, потому что вопрос «а не задаёт ли это метку» задают из
|
||||||
|
скилла, который метку не ведёт.
|
||||||
|
|
||||||
|
**Ось — это закрытый перечень значений, по которому что-то ветвится.** Признак
|
||||||
|
проверяемый, и он отсекает похожее: темы ревью и документы проекта — списки
|
||||||
|
**открытые**, их пополняет проект, и перечень в плагине протух бы на первом же
|
||||||
|
своём документе. Модель прохода — не ось, а цена прогона; её дом — «Модель по
|
||||||
|
проходу» в `code-review`, механизация — `frontmatter.py`.
|
||||||
|
|
||||||
|
## Перечень
|
||||||
|
|
||||||
|
| Ось | Значения | Дом |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| стадия проекта | `build` `support` | `task-track/SKILL.md`, «Две стадии» |
|
||||||
|
| тип записи | `feature` `fix` `chore` `research` | `task-track/SKILL.md`, «Тип записи» |
|
||||||
|
| сценарий | решение · обслуживание · разведка | `code-resolve/SKILL.md`, «Развилка» |
|
||||||
|
| метка | `small` `medium` `large` | `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` |
|
||||||
|
| тип записи | сценарий — **предлагает**, подтверждает предмет работы | `task-track/SKILL.md`, «Тип записи» |
|
||||||
|
| тип записи | метку и глубину — **не влияет, и это записано явно** | там же |
|
||||||
|
| сценарий | режим прогона: обслуживание идёт без метки | `code-resolve/references/maintain.md` |
|
||||||
|
| метка | состав проходов обеих стадий | `code-review/SKILL.md`, «Метки» |
|
||||||
|
| метка | глубину темы: против чего смотрят и как | там же |
|
||||||
|
| режим прогона | состав проходов и саму возможность запуска прохода | `code-review/SKILL.md`, «Прогон без change» |
|
||||||
|
| категория документа | заводит ли документ направление проверки | `canon.md`, «Три категории» |
|
||||||
|
| severity | что с находкой делают дальше | `code-review/SKILL.md`, «Что происходит с находками» |
|
||||||
|
|
||||||
|
**Пять клеток пусты, и это сказано намеренно, а не забыто.**
|
||||||
|
|
||||||
|
**Категория документа × режим прогона.** На прогоне **с меткой** своя тема
|
||||||
|
проекта закрыта при любом значении: `review-basics` — приёмник проектных тем и
|
||||||
|
при `small`, и при `large`, и при `medium`. На прогоне **без метки** план
|
||||||
|
фиксирован сценарием — `autotests`, `operations`, `conventions`, — и своих тем
|
||||||
|
проекта в нём нет. Значит, документ, заведённый проектом как тема, на
|
||||||
|
обслуживании не смотрит никто, и строкой это нигде не называется.
|
||||||
|
|
||||||
|
**Стадия проекта × метка.** Изменение на стройке ничем не проще того же
|
||||||
|
изменения на доработке: метку назначает разметка по факту изменения, и стадия в
|
||||||
|
неё не входит. Заманчивая мысль «на стройке всё `small`, потому что приложения
|
||||||
|
ещё нет» разбивается о первый же шаг, кладущий схему хранилища.
|
||||||
|
|
||||||
|
**Стадия проекта × режим прогона и × стадия ревью.** Не влияет ни на одну:
|
||||||
|
режим выбирает сценарий, стадию ревью — наличие дизайна. Прогон обслуживания на
|
||||||
|
стройке — обычное дело (первые шаги плана заводят гейт и сборку), и идёт он там
|
||||||
|
так же, как на доработке.
|
||||||
|
|
||||||
|
**Стадия проекта × категория документа и × коды выхода.** Не влияет: категория —
|
||||||
|
свойство документа, коды — общий словарь скриптов. Названо потому, что перечень
|
||||||
|
объявлен полным, и клетка без ответа читается как забытая.
|
||||||
|
|
||||||
|
**Режим прогона × severity.** Триаж обязателен всегда, в том числе без метки. Но
|
||||||
|
часть оснований `critical` — построенный путь к отказу, замер — добывается
|
||||||
|
проходами, которые без метки не запускаются. Значит ли это, что `critical` на
|
||||||
|
прогоне обслуживания не бывает, или что его основания там другие, не сказано.
|
||||||
|
|
||||||
|
## Режим прогона
|
||||||
|
|
||||||
|
<!-- дом: режим-прогона -->
|
||||||
|
|
||||||
|
**Прогон ревью идёт в одном из двух режимов, и режим — не глубина.**
|
||||||
|
|
||||||
|
- **С меткой** — обычный прогон по change: разметку сделал `review-scope`, состав
|
||||||
|
обеих стадий выведен из метки.
|
||||||
|
- **Без метки** — прогон сценария обслуживания: change нет, размечать нечего,
|
||||||
|
план фиксирован и назван сценарием. Разметчик не запускается вовсе.
|
||||||
|
|
||||||
|
**Без метки — не то же самое, что `small`.** `small` — это суждение о размере и
|
||||||
|
сложности, снятое с изменения; отсутствие метки — утверждение, что снимать её
|
||||||
|
не с чего. Проход, подставивший себе `small` там, где метки нет, вывел бы
|
||||||
|
глубину из ничего.
|
||||||
|
|
||||||
|
**Режим правит не только состав, но и саму возможность запуска.** Проход, у
|
||||||
|
которого запуск задан меткой, без метки не имеет ответа на вопрос «запускаться
|
||||||
|
ли» — и ответ ему даёт план сценария, а не умолчание.
|
||||||
|
|
||||||
|
<!-- /дом: режим-прогона -->
|
||||||
|
|
||||||
|
## Коды выхода
|
||||||
|
|
||||||
|
<!-- дом: коды-выхода -->
|
||||||
|
|
||||||
|
**Коды выхода — общий словарь всех скриптов `av-dev`. Ветвись на коде, а не на
|
||||||
|
тексте вывода.**
|
||||||
|
|
||||||
|
| Код | Что случилось |
|
||||||
|
| --- | --- |
|
||||||
|
| 0 | сошлось |
|
||||||
|
| 1 | дрейф: рабочая ситуация, чинится |
|
||||||
|
| 2 | ошибка употребления: аргументы или нарушенное правило |
|
||||||
|
| 3 | окружение: не тот каталог, битый конфиг, нет инструмента |
|
||||||
|
| 4 | внутренний сбой — дефект скрипта, доложить |
|
||||||
|
|
||||||
|
**Различать 1 и 3 обязательно.** «Дрейф» — рабочая ситуация, и чинится она
|
||||||
|
правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет.
|
||||||
|
Одинаковая реакция на них неверна в обоих случаях.
|
||||||
|
|
||||||
|
<!-- /дом: коды-выхода -->
|
||||||
|
|
||||||
|
Словарь был объявлен «общим» в одиннадцати местах, и каждое объявление
|
||||||
|
перечисляло **свой** набор соседей: «тот же, что у `tasks.py`», «тот же, что у
|
||||||
|
`tasks.py`, `docs.py` и `copies.py`», «общий словарь скриптов av-dev». Ни одно из
|
||||||
|
них не было домом, все — списки по памяти. Отсюда дом здесь: у словаря восемь
|
||||||
|
скриптов-потребителей и ни одного владельца.
|
||||||
+36
-4
@@ -26,9 +26,9 @@
|
|||||||
|
|
||||||
[tasks]
|
[tasks]
|
||||||
dir = "tasks" # каталог задач от корня репозитория
|
dir = "tasks" # каталог задач от корня репозитория
|
||||||
|
stage = "build" # стадия проекта: build | support
|
||||||
items = "items" # имена частей каталога — необязательны
|
items = "items" # имена частей каталога — необязательны
|
||||||
backlog = "BACKLOG.md"
|
backlog = "BACKLOG.md"
|
||||||
roadmap = "ROADMAP.md"
|
|
||||||
|
|
||||||
Коды выхода зовущих скриптов — общий словарь av-dev; отсюда возвращается
|
Коды выхода зовущих скриптов — общий словарь av-dev; отсюда возвращается
|
||||||
исключение `ConfigError`, а решает по нему вызывающий.
|
исключение `ConfigError`, а решает по нему вызывающий.
|
||||||
@@ -55,8 +55,8 @@ LEGACY = ("docs/.docs.json", "docs/.pm.json")
|
|||||||
LEGACY_TASKS = ".tasks.json"
|
LEGACY_TASKS = ".tasks.json"
|
||||||
|
|
||||||
# Версия раскладки — одна на плагин. Журнал версий — references/changelog.md
|
# Версия раскладки — одна на плагин. Журнал версий — references/changelog.md
|
||||||
# скилла `doc-canon`, повышает его операция `upgrade`.
|
# скилла `canon`, повышает его операция `upgrade`.
|
||||||
VERSION = 1
|
VERSION = 3
|
||||||
|
|
||||||
VERSION_KEY = "version"
|
VERSION_KEY = "version"
|
||||||
|
|
||||||
@@ -280,6 +280,33 @@ def merge_section(root: Path, name: str, values: dict) -> list[str]:
|
|||||||
return added
|
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:
|
def missing_keys(root: Path, name: str, values: dict) -> dict:
|
||||||
"""Ключи секции, которые уже есть и разошлись с тем, что мы собирались дать.
|
"""Ключи секции, которые уже есть и разошлись с тем, что мы собирались дать.
|
||||||
|
|
||||||
@@ -317,7 +344,12 @@ def skeleton(number: int, docs: dict | None = None, tasks: dict | None = None) -
|
|||||||
out += ["", "[tasks]",
|
out += ["", "[tasks]",
|
||||||
"# каталог задач от корня репозитория; имена частей — умолчания скрипта",
|
"# каталог задач от корня репозитория; имена частей — умолчания скрипта",
|
||||||
f"dir = {quote(tasks.get('dir', 'tasks'))}"]
|
f"dir = {quote(tasks.get('dir', 'tasks'))}"]
|
||||||
for key in ("items", "backlog", "roadmap"):
|
if tasks.get("stage"):
|
||||||
|
out += ["# стадия проекта: build — беклог это план стройки, порядок строк"
|
||||||
|
" значит зависимость;",
|
||||||
|
"# support — беклог это очередь правок, порядок значит важность",
|
||||||
|
f"stage = {quote(tasks['stage'])}"]
|
||||||
|
for key in ("items", "backlog", "rejected"):
|
||||||
if tasks.get(key):
|
if tasks.get(key):
|
||||||
out.append(f"{key} = {quote(tasks[key])}")
|
out.append(f"{key} = {quote(tasks[key])}")
|
||||||
return "\n".join(out) + "\n"
|
return "\n".join(out) + "\n"
|
||||||
|
|||||||
@@ -21,9 +21,18 @@
|
|||||||
проверяет вовсе, — а вложенных блоков разметка не знает, и внутри `язык-правила`
|
проверяет вовсе, — а вложенных блоков разметка не знает, и внутри `язык-правила`
|
||||||
его было бы не забрать отдельно.
|
его было бы не забрать отдельно.
|
||||||
|
|
||||||
Правила — для всего, что пишется словами: задачи и цели, документы канона,
|
Правила — для всего, что пишется словами **в этом плагине**: задачи, документы
|
||||||
решения ADR, записки разведки, сообщения коммитов. Не для кода и не для
|
канона, решения ADR, записки разведки. Не для кода и не для сообщений программы
|
||||||
сообщений программы пользователю — там свои конвенции проекта.
|
пользователю — там свои конвенции проекта.
|
||||||
|
|
||||||
|
**Сообщения коммитов сюда не входят, и это названо намеренно.** Их форму держит
|
||||||
|
отдельный плагин `av-dev-git`, скилл `commit`, и она с этими правилами
|
||||||
|
расходится по существу: там предписан результат страдательным залогом
|
||||||
|
(«добавлены», «обновлено»), здесь — действие активным. Расхождение осознанное:
|
||||||
|
строка коммита отвечает на «что стало», а не на «что я сделал», и читают её в
|
||||||
|
`git log` подряд сотнями. Объявлять юрисдикцию над чужим плагином, до которого
|
||||||
|
отсюда нет и ссылки, значило бы завести правило, нарушение которого никто не
|
||||||
|
увидит.
|
||||||
|
|
||||||
Основа — **информационный стиль** Максима Ильяхова ([учебник
|
Основа — **информационный стиль** Максима Ильяхова ([учебник
|
||||||
бюро](https://bureau.ru/projects/book-text/), книга «Пиши, сокращай»). Он
|
бюро](https://bureau.ru/projects/book-text/), книга «Пиши, сокращай»). Он
|
||||||
@@ -228,8 +237,8 @@
|
|||||||
## Доклад вычитки
|
## Доклад вычитки
|
||||||
|
|
||||||
Не правило языка, а **контракт прохода**: форма, в которой находка приходит к
|
Не правило языка, а **контракт прохода**: форма, в которой находка приходит к
|
||||||
человеку. Живёт здесь потому, что проходов вычитки несколько — по одному на
|
человеку. Живёт здесь потому, что проходов вычитки два — `doc-wording` по документам и
|
||||||
плагин, — и разойтись формой они не должны.
|
`task-wording` по записям задач, — и разойтись формой они не должны.
|
||||||
|
|
||||||
<!-- дом: вычитка-доклад -->
|
<!-- дом: вычитка-доклад -->
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Сопровождение и эксплуатация
|
# Сопровождение и эксплуатация
|
||||||
|
|
||||||
**Это дом.** Словарь «чем держат проект» назван в трёх местах трёх разных
|
**Это дом.** Словарь «чем держат проект» назван в трёх местах трёх разных
|
||||||
скиллов: секция `Сопровождение` в роадмапе (`task-track`), раздел «Эксплуатация»
|
скиллов: задачи типа `chore` (`task-track`), раздел «Эксплуатация»
|
||||||
в `architecture.md` (`doc-canon`) и тема ревью `operations` (`code-review`). Ни
|
в `architecture.md` (`canon`) и тема ревью `operations` (`code-review`). Ни
|
||||||
один из трёх им не владеет, поэтому дом стоит в `shared/`.
|
один из трёх им не владеет, поэтому дом стоит в `shared/`.
|
||||||
|
|
||||||
Три перечня «чем держат проект» уже разъезжались — на «метриках и логах» против
|
Три перечня «чем держат проект» уже разъезжались — на «метриках и логах» против
|
||||||
@@ -18,16 +18,18 @@
|
|||||||
|
|
||||||
| Место | Уровень | Что там |
|
| Место | Уровень | Что там |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `ROADMAP.md`, секция `Сопровождение` | план | **работы**, которые собираемся делать: цели и их задачи |
|
| `BACKLOG.md`, задачи `chore` | план | **работы**, которые собираемся делать |
|
||||||
| `architecture.md`, раздел «Эксплуатация» | состояние | **как устроено сейчас**: где работает, что рядом, кто перезапускает |
|
| `architecture.md`, раздел «Эксплуатация» | состояние | **как устроено сейчас**: где работает, что рядом, кто перезапускает |
|
||||||
| тема ревью `operations` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
| тема ревью `operations` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
||||||
|
|
||||||
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
||||||
пользователю, а это другая работа.
|
пользователю, а это другая работа. По той же причине им не названа и **стадия
|
||||||
|
проекта**: у неё имя «доработка» (`support`), словарь — `task-track`, «Две
|
||||||
|
стадии».
|
||||||
|
|
||||||
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
||||||
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
||||||
целей: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
задач: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
||||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в задачи
|
||||||
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
разных типов, и это верно — типы отвечают на разные вопросы.
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: doc-canon
|
name: canon
|
||||||
description: Привести проект к канону документов av-dev и держать его в соответствии — три операции одной машиной сравнения. check — что разошлось с текущей версией канона; adopt — перевод проекта из любой прежней раскладки (docs/specs, drafts, backlog, BRIEF.md, review-brief) в канон с переносом файлов; upgrade — повышение проекта с версии канона N до текущей по журналу версий. Использовать, когда просят проверить документацию проекта, перевести проект на канон, обновить его под новую версию канона или когда пришли в старый проект и надо понять, что в нём не так. Заведение нового проекта с нуля — скилл av-dev:doc-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).** Здесь оно не
|
||||||
пересказывается: два описания одной раскладки разъедутся, и работать будет то,
|
пересказывается: два описания одной раскладки разъедутся, и работать будет то,
|
||||||
которое прочитали последним. Прочитай его **до** первой правки.
|
которое прочитали последним. Прочитай его **до** первой правки.
|
||||||
@@ -45,7 +53,7 @@ description: Привести проект к канону документов
|
|||||||
## Инструмент
|
## Инструмент
|
||||||
|
|
||||||
```
|
```
|
||||||
ds="$CLAUDE_PLUGIN_ROOT/skills/doc-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 <корень> # версия раскладки: скрипта и проекта
|
||||||
@@ -58,11 +66,30 @@ python3 $ds bump --dir <корень> # поднять вер
|
|||||||
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
|
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
|
||||||
верна.
|
верна.
|
||||||
|
|
||||||
**Коды выхода — тот же словарь, что у `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 обязательно.** «Дрейф» — рабочая ситуация, и чинится она
|
||||||
|
правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет.
|
||||||
|
Одинаковая реакция на них неверна в обоих случаях.
|
||||||
|
|
||||||
|
<!-- /копия: коды-выхода -->
|
||||||
|
|
||||||
|
Здесь это значит: «дрейф раскладки» — рабочая ситуация, «это не корень проекта» —
|
||||||
|
нерабочая.
|
||||||
|
|
||||||
### Граница механизируемого — объявляется вслух
|
### Граница механизируемого — объявляется вслух
|
||||||
|
|
||||||
@@ -109,7 +136,7 @@ capability: незаполненный канон это переходное с
|
|||||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
поведении.
|
поведении.
|
||||||
@@ -228,10 +255,11 @@ capability), `openspec/config.yaml`.
|
|||||||
|
|
||||||
**Задачи `docs.py` не проверяет** — их ведёт другой скилл, и согласованность
|
**Задачи `docs.py` не проверяет** — их ведёт другой скилл, и согласованность
|
||||||
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
|
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
|
||||||
его отчёт идёт в доклад отдельной строкой, и пункт «задачи без цели» в нём
|
его отчёт идёт в доклад отдельной строкой, и зелёным он сразу не станет:
|
||||||
зелёным не станет: цели не сочиняются адаптацией (запрет записан у того, кто
|
у перенесённых записей нет критериев приёмки, а `check` без объявленной
|
||||||
ведёт задачи), их проставляет человек порциями переоценки на первом груминге —
|
**стадии** отказывает вовсе. Стадию называет человек (`tasks.py stage
|
||||||
скилл `av-dev:task-groom`.
|
build|support`) — машина её не выводит: список пунктов одинаково выглядит и
|
||||||
|
планом стройки, и очередью правок.
|
||||||
|
|
||||||
### 5. Объяви переходное состояние
|
### 5. Объяви переходное состояние
|
||||||
|
|
||||||
+31
-28
@@ -6,7 +6,7 @@
|
|||||||
[журнала](changelog.md). Литерал в шапке был третьим и отстал на первом же
|
[журнала](changelog.md). Литерал в шапке был третьим и отстал на первом же
|
||||||
повышении — версию 13 он пережил, объявляя канон двенадцатым.
|
повышении — версию 13 он пережил, объявляя канон двенадцатым.
|
||||||
|
|
||||||
Это **единственный дом определения канона**. Скиллы `doc-init`, `doc-canon` и `doc-sync`
|
Это **единственный дом определения канона**. Скиллы `doc-init`, `canon` и `doc-sync`
|
||||||
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
||||||
работать будет то, которое прочитали последним. Меняется канон — меняется этот
|
работать будет то, которое прочитали последним. Меняется канон — меняется этот
|
||||||
файл и появляется запись в [changelog.md](changelog.md).
|
файл и появляется запись в [changelog.md](changelog.md).
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
Рядом лежит OpenSpec, у которого структура тоже строгая.
|
Рядом лежит OpenSpec, у которого структура тоже строгая.
|
||||||
|
|
||||||
Цена принята сознательно: плагин не переносится на чужой репозиторий как есть —
|
Цена принята сознательно: плагин не переносится на чужой репозиторий как есть —
|
||||||
чужой репозиторий **приводится** к канону скиллом `doc-canon`.
|
чужой репозиторий **приводится** к канону скиллом `canon`.
|
||||||
|
|
||||||
Раскладка отвечает, **где** текст лежит и на какой вопрос отвечает. Каким он
|
Раскладка отвечает, **где** текст лежит и на какой вопрос отвечает. Каким он
|
||||||
должен быть **словами** — общий для всех документов канона файл
|
должен быть **словами** — общий для всех документов канона файл
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
## Сопровождение и эксплуатация — целое и часть
|
## Сопровождение и эксплуатация — целое и часть
|
||||||
|
|
||||||
Словарь этой темы — [shared/operations.md](../../../shared/operations.md):
|
Словарь этой темы — [shared/operations.md](../../../shared/operations.md):
|
||||||
целое и часть, три места одной темы (секция роадмапа, раздел архитектуры, тема
|
целое и часть, три места одной темы (задачи `chore`, раздел архитектуры, тема
|
||||||
ревью `operations`) и граница с возможностями проекта. Здесь он не
|
ревью `operations`) и граница с возможностями проекта. Здесь он не
|
||||||
пересказывается: копия жила рядом с домом в одном дереве и была ровно тем
|
пересказывается: копия жила рядом с домом в одном дереве и была ровно тем
|
||||||
вторым домом, против которого правило и написано.
|
вторым домом, против которого правило и написано.
|
||||||
@@ -71,6 +71,9 @@ openspec/
|
|||||||
|
|
||||||
## Три категории документов
|
## Три категории документов
|
||||||
|
|
||||||
|
Категория документа — ось процесса; перечень осей и того, чего каждая **не**
|
||||||
|
решает, — [shared/axes.md](../../../shared/axes.md).
|
||||||
|
|
||||||
Раньше здесь стояло плоское правило «каждый документ `docs/` — тема ревью». Оно
|
Раньше здесь стояло плоское правило «каждый документ `docs/` — тема ревью». Оно
|
||||||
неверно ровно наполовину: паспорт и схема хранилища ревью нужны, но темами не
|
неверно ровно наполовину: паспорт и схема хранилища ревью нужны, но темами не
|
||||||
являются, а журнал решений и журнал наблюдений ревью изменения не нужны вовсе.
|
являются, а журнал решений и журнал наблюдений ревью изменения не нужны вовсе.
|
||||||
@@ -348,42 +351,42 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
скриптом и своими проверками; где каталог лежит и как названы его части, говорит
|
скриптом и своими проверками; где каталог лежит и как названы его части, говорит
|
||||||
секция `[tasks]` в `.av-dev.toml`. Версия там одна на всю раскладку: канон и
|
секция `[tasks]` в `.av-dev.toml`. Версия там одна на всю раскладку: канон и
|
||||||
каталог задач двигаются вместе, потому что ведёт их один плагин.
|
каталог задач двигаются вместе, потому что ведёт их один плагин.
|
||||||
Канон **резервирует место** в `docs/` и внутрь не смотрит:
|
Каталог лежит **в корне репозитория, вне `docs/`** — места в `docs/` канон ему
|
||||||
`docs.py` каталог не открывает, его отсутствия не считает дрейфом и согласованность
|
больше не резервирует (переезд — запись 11 закрытого журнала), а внутрь не
|
||||||
задач не проверяет. Проект, не заведший каталог задач, их не ведёт
|
смотрит и подавно: `docs.py` каталог не открывает, его отсутствия не считает
|
||||||
|
дрейфом и согласованность задач не проверяет. Проект, не заведший каталог задач, их не ведёт
|
||||||
вовсе, и отказом это быть не может.
|
вовсе, и отказом это быть не может.
|
||||||
|
|
||||||
Раскладку, форму записи и команды держит скилл `av-dev:task-track`. Ниже — то,
|
Раскладку, форму записи и команды держит скилл `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:task-track`, раздел «Тип
|
`av-dev:task-track`, раздел «Тип записи», подробно — по файлу на тип в его
|
||||||
записи», подробно — по файлу на тип в его `references/task-<тип>.md`. Канон
|
`references/task-<тип>.md`. Канон фиксирует **словарь**, потому что от него
|
||||||
фиксирует **словарь**, потому что
|
зависит, читается ли проект как продукт; схема — механика ведения задач, и второй
|
||||||
от него зависит, читается ли проект как продукт; схема — механика ведения задач,
|
её экземпляр разошёлся бы с первым (он и разошёлся: канон успел объявить цель у
|
||||||
и второй её экземпляр разошёлся бы с первым (он и разошёлся: канон успел
|
`fix` запрещённой, хотя она была необязательна, — и целей теперь нет вовсе).
|
||||||
объявить цель у `fix` запрещённой, хотя она там необязательна).
|
|
||||||
|
|
||||||
Схема требуется **к взятию в работу**, а не к заведению: беклог пополняется чаще,
|
Схема требуется **к взятию в работу**, а не к заведению: беклог пополняется чаще,
|
||||||
чем разбирается, и требование на входе выгоняло бы в заметки то, что должно
|
чем разбирается, и требование на входе выгоняло бы в заметки то, что должно
|
||||||
@@ -451,7 +454,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` |
|
||||||
@@ -485,8 +488,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` |
|
||||||
@@ -524,7 +527,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
|||||||
разрез, что между `task-form` и `task-wording`.
|
разрез, что между `task-form` и `task-wording`.
|
||||||
|
|
||||||
**Зовутся оба одинаково и одним скиллом — `av-dev:doc-healthcheck`, на весь
|
**Зовутся оба одинаково и одним скиллом — `av-dev:doc-healthcheck`, на весь
|
||||||
канон разом; шагом `adopt` и шагом `upgrade` его зовёт `doc-canon`.** Не на синке
|
канон разом; шагом `adopt` и шагом `upgrade` его зовёт `canon`.** Не на синке
|
||||||
документации: `doc-consistency` на
|
документации: `doc-consistency` на
|
||||||
`opus` по каждой сделанной задаче не окупается, а расхождение между двумя документами по
|
`opus` по каждой сделанной задаче не окупается, а расхождение между двумя документами по
|
||||||
определению требует двух, и на большинстве задач синк правит один. Пачка,
|
определению требует двух, и на большинстве задач синк правит один. Пачка,
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
# Журнал версий раскладки
|
||||||
|
|
||||||
|
Одна запись на версию. Проект знает свою версию из ключа `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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Версия 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 <каталог задач>` — до отсутствия
|
||||||
|
дрейфа.
|
||||||
|
|
||||||
|
**Чего делать не надо.** Переписывать прошлые записи журналов под новые имена.
|
||||||
|
Они описывают состояния, которые были, и адрес, верный на день записи, остаётся
|
||||||
|
верным как свидетельство.
|
||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
# Скелеты документов канона
|
# Скелеты документов канона
|
||||||
|
|
||||||
Что кладут `init` и `doc-canon adopt` в незаполненный слот. Правило одно:
|
Что кладут `init` и `canon adopt` в незаполненный слот. Правило одно:
|
||||||
**честная информативная строка вместо заглушки**. Проход читает строку как факт;
|
**честная информативная строка вместо заглушки**. Проход читает строку как факт;
|
||||||
`<!-- заполнить: … -->` он читает как пробел, и `docs.py check` о таком
|
`<!-- заполнить: … -->` он читает как пробел, и `docs.py check` о таком
|
||||||
плейсхолдере напоминает.
|
плейсхолдере напоминает.
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
# Паспорт проекта
|
# Паспорт проекта
|
||||||
|
|
||||||
Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
|
Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
|
||||||
устроено», [tasks/ROADMAP.md](../tasks/ROADMAP.md) — «в каком порядке», паспорт —
|
устроено», [tasks/BACKLOG.md](../tasks/BACKLOG.md) — «что осталось», паспорт —
|
||||||
«зачем и для кого».
|
«зачем и для кого».
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
|
|
||||||
Верно одно из трёх:
|
Верно одно из трёх:
|
||||||
|
|
||||||
<!-- копия: adr-когда-заводить из av-dev/skills/doc-canon/references/canon.md -->
|
<!-- копия: adr-когда-заводить из av-dev/skills/canon/references/canon.md -->
|
||||||
- **дорогой откат** — переделка стоит дороже переписывания одного файла;
|
- **дорогой откат** — переделка стоит дороже переписывания одного файла;
|
||||||
- **намеренный отказ** от очевидного подхода;
|
- **намеренный отказ** от очевидного подхода;
|
||||||
- **пересмотр прежнего решения** — тогда у старой записи обязателен статус
|
- **пересмотр прежнего решения** — тогда у старой записи обязателен статус
|
||||||
@@ -261,7 +261,7 @@
|
|||||||
## Последствия
|
## Последствия
|
||||||
|
|
||||||
- `+` что стало лучше.
|
- `+` что стало лучше.
|
||||||
- `−` чем платим: ограничения, риски, нагрузка на поддержку.
|
- `−` чем платим: ограничения, риски, нагрузка на сопровождение.
|
||||||
```
|
```
|
||||||
|
|
||||||
## `docs/review.md`
|
## `docs/review.md`
|
||||||
@@ -6,12 +6,8 @@
|
|||||||
маркеры долга и две сверки с кодом. Смысловые дубли и оставшееся в архитектуре
|
маркеры долга и две сверки с кодом. Смысловые дубли и оставшееся в архитектуре
|
||||||
поведение судит агент — скрипт об этом говорит вслух в конце отчёта.
|
поведение судит агент — скрипт об этом говорит вслух в конце отчёта.
|
||||||
|
|
||||||
Коды выхода — тот же словарь, что у tasks.py:
|
Коды выхода — общий словарь скриптов av-dev; дом словаря и разбор «дрейф
|
||||||
0 сошлось
|
против окружения» — av-dev/shared/axes.md. Значения — в константах ниже.
|
||||||
1 дрейф раскладки (рабочая ситуация, чинится)
|
|
||||||
2 ошибка употребления
|
|
||||||
3 окружение: не тот каталог, битый конфиг
|
|
||||||
4 внутренний сбой
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -131,10 +127,10 @@ 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 (ведёт скилл task-track)",
|
"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/ в корне репозитория (ведёт скилл task-track)",
|
"backlog": "→ tasks/ в корне репозитория (ведёт скилл task-track)",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +316,7 @@ def check_version(root: Path, cfg: dict, rep: Report) -> None:
|
|||||||
if got < LAYOUT_VERSION:
|
if got < LAYOUT_VERSION:
|
||||||
rep.error(
|
rep.error(
|
||||||
f"проект приведён к раскладке версии {got}, текущая — {LAYOUT_VERSION}:"
|
f"проект приведён к раскладке версии {got}, текущая — {LAYOUT_VERSION}:"
|
||||||
f" нужно повышение (скилл av-dev:doc-canon, операция upgrade)"
|
f" нужно повышение (скилл av-dev:canon, операция upgrade)"
|
||||||
)
|
)
|
||||||
elif got > LAYOUT_VERSION:
|
elif got > LAYOUT_VERSION:
|
||||||
rep.error(
|
rep.error(
|
||||||
@@ -377,7 +373,7 @@ def check_legacy(root: Path, rep: Report) -> None:
|
|||||||
f"нет {CONFIG}, а настройки лежат по прежней раскладке"
|
f"нет {CONFIG}, а настройки лежат по прежней раскладке"
|
||||||
f" ({', '.join(legacy)}) — она осталась от трёх плагинов, которые"
|
f" ({', '.join(legacy)}) — она осталась от трёх плагинов, которые"
|
||||||
f" слились в один: перенеси значения и удали старые файлы операцией"
|
f" слились в один: перенеси значения и удали старые файлы операцией"
|
||||||
f" upgrade скилла av-dev:doc-canon (журнал, версия 1). Прежние имена не"
|
f" upgrade скилла av-dev:canon (журнал, версия 1). Прежние имена не"
|
||||||
f" читаются, поэтому в этом прогоне всё остальное проверено так, будто"
|
f" читаются, поэтому в этом прогоне всё остальное проверено так, будто"
|
||||||
f" настроек нет вовсе"
|
f" настроек нет вовсе"
|
||||||
)
|
)
|
||||||
@@ -706,9 +702,22 @@ def cmd_bump(args: argparse.Namespace) -> int:
|
|||||||
if was is not None and was > LAYOUT_VERSION:
|
if was is not None and was > LAYOUT_VERSION:
|
||||||
fail(ENV, f"проект на версии {was}, скрипт знает {LAYOUT_VERSION}:"
|
fail(ENV, f"проект на версии {was}, скрипт знает {LAYOUT_VERSION}:"
|
||||||
f" устарел плагин, обнови маркетплейс")
|
f" устарел плагин, обнови маркетплейс")
|
||||||
conf.set_version(root, LAYOUT_VERSION)
|
# Двигается **одна** запись за раз, а не сразу до текущей: число объявляет
|
||||||
|
# пройденными шаги журнала, и прыжок через запись объявил бы пройденным то,
|
||||||
|
# чего никто не делал. Отставшему на три записи проекту `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 'не была объявлена'}"
|
print(f"версия раскладки: {was if was is not None else 'не была объявлена'}"
|
||||||
f" → {LAYOUT_VERSION} в {CONFIG}")
|
f" → {target} в {CONFIG}")
|
||||||
|
if target < LAYOUT_VERSION:
|
||||||
|
print(f" до текущей ({LAYOUT_VERSION}) осталось записей журнала:"
|
||||||
|
f" {LAYOUT_VERSION - target}. Пройди шаги следующей и позови bump"
|
||||||
|
f" снова — по разу на запись")
|
||||||
return OK
|
return OK
|
||||||
|
|
||||||
|
|
||||||
@@ -74,10 +74,30 @@ 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 не читает и об этом
|
||||||
@@ -118,7 +138,7 @@ python3 $os form # слепок формы против жив
|
|||||||
## Кто зовёт этот скилл
|
## Кто зовёт этот скилл
|
||||||
|
|
||||||
- `av-dev:doc-init` — шагом заведения нового проекта, до первого документа;
|
- `av-dev:doc-init` — шагом заведения нового проекта, до первого документа;
|
||||||
- `av-dev:doc-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 у обоих жёсткая предпосылка, и на проекте без каталога оба посылают
|
||||||
@@ -140,7 +160,7 @@ python3 $os form # слепок формы против жив
|
|||||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
поведении.
|
поведении.
|
||||||
@@ -168,7 +188,7 @@ python3 $os form # слепок формы против жив
|
|||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
- **Не пишет спеки и предложения.** Это `opsx:propose` и конвейер задачи.
|
- **Не пишет спеки и предложения.** Это `opsx:propose` и конвейер задачи.
|
||||||
- **Не ведёт документы канона** — их дом скилл `av-dev:doc-canon`, и адреса в
|
- **Не ведёт документы канона** — их дом скилл `av-dev:canon`, и адреса в
|
||||||
`context` только на них ссылаются.
|
`context` только на них ссылаются.
|
||||||
- **Не чинит расхождение формы с версией OpenSpec в проекте.** Оно чинится в
|
- **Не чинит расхождение формы с версией OpenSpec в проекте.** Оно чинится в
|
||||||
плагине: константы скрипта, образец здесь, запись в журнал версий канона.
|
плагине: константы скрипта, образец здесь, запись в журнал версий канона.
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -220,7 +216,7 @@ def check_form(root: Path, rep: Report) -> None:
|
|||||||
rep.skip(
|
rep.skip(
|
||||||
f"{where} в проекте нет — ссылка на него в context не "
|
f"{where} в проекте нет — ссылка на него в context не "
|
||||||
f"требуется. Документы канона проект не завёл, и без них "
|
f"требуется. Документы канона проект не завёл, и без них "
|
||||||
f"конвейер работает вслепую: заводит их av-dev:doc-canon"
|
f"конвейер работает вслепую: заводит их av-dev:canon"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
if pointer not in live:
|
if pointer not in live:
|
||||||
|
|||||||
@@ -41,12 +41,20 @@ description: "Взять одну задачу и довести её до за
|
|||||||
делает скилл `av-dev:code-openspec`. **Сценариям разведки и обслуживания
|
делает скилл `av-dev:code-openspec`. **Сценариям разведки и обслуживания
|
||||||
OpenSpec не нужен** — они не заводят change; `opsx:explore` берётся разведкой,
|
OpenSpec не нужен** — они не заводят change; `opsx:explore` берётся разведкой,
|
||||||
если плагин есть.
|
если плагин есть.
|
||||||
- **Проектные копии этих скиллов и агентов удаляются при установке плагина**
|
- **Проектные копии этих скиллов и агентов удаляются при установке плагина.**
|
||||||
(`.claude/skills/` — голые имена `resolve`, `review`, а у проектов прошлого
|
|
||||||
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`, и с префиксом
|
<!-- копия: проектные-копии из README.md -->
|
||||||
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`;
|
|
||||||
`.claude/agents/<проект>-review-*.md`). Две копии одного скилла расходятся, и
|
Голые имена в `.claude/skills/`: `resolve`, `review`, а у проектов прошлого
|
||||||
побеждает та, что короче названа.
|
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`. С префиксом
|
||||||
|
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`. Агенты:
|
||||||
|
`.claude/agents/<проект>-review-*.md`.
|
||||||
|
|
||||||
|
Две копии одного скилла расходятся, и побеждает та, что **короче названа**:
|
||||||
|
короткое имя разрешится в устаревшую проектную копию молча и без признаков
|
||||||
|
подмены.
|
||||||
|
|
||||||
|
<!-- /копия: проектные-копии -->
|
||||||
|
|
||||||
### Чего может не быть
|
### Чего может не быть
|
||||||
|
|
||||||
@@ -66,7 +74,7 @@ description: "Взять одну задачу и довести её до за
|
|||||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
поведении.
|
поведении.
|
||||||
@@ -97,7 +105,7 @@ description: "Взять одну задачу и довести её до за
|
|||||||
карта «что где» — `references/project-facts.md` конвейера ревью.
|
карта «что где» — `references/project-facts.md` конвейера ревью.
|
||||||
|
|
||||||
**Документов канона нет — проект к нему не приведён.** Скажи это строкой и
|
**Документов канона нет — проект к нему не приведён.** Скажи это строкой и
|
||||||
предложи скилл `av-dev:doc-canon`: одна операция на проект против поразрядной
|
предложи скилл `av-dev:canon`: одна операция на проект против поразрядной
|
||||||
деградации на каждой задаче. Работу при этом не останавливай.
|
деградации на каждой задаче. Работу при этом не останавливай.
|
||||||
|
|
||||||
## Вход
|
## Вход
|
||||||
@@ -108,8 +116,7 @@ description: "Взять одну задачу и довести её до за
|
|||||||
|
|
||||||
**Запись из каталога сперва проверяется на готовность, и проверяет её машина.**
|
**Запись из каталога сперва проверяется на готовность, и проверяет её машина.**
|
||||||
Вызови Skill `av-dev:task-track` и попроси прогнать `ready <слаг>`: он смотрит
|
Вызови Skill `av-dev:task-track` и попроси прогнать `ready <слаг>`: он смотрит
|
||||||
тип, цель у `feature`, пустой ли раздел вопросов и собраны ли разделы схемы
|
тип, пустой ли раздел вопросов и собраны ли разделы схемы типа. Судить это глазами нельзя — ровно тот случай, где машина дешевле и точнее,
|
||||||
типа. Судить это глазами нельзя — ровно тот случай, где машина дешевле и точнее,
|
|
||||||
а цена ошибки отложенная: недостающие критерии приёмки обнаружатся на приёмке,
|
а цена ошибки отложенная: недостающие критерии приёмки обнаружатся на приёмке,
|
||||||
когда сверять уже не с чем.
|
когда сверять уже не с чем.
|
||||||
|
|
||||||
@@ -126,6 +133,9 @@ description: "Взять одну задачу и довести её до за
|
|||||||
|
|
||||||
## Развилка: какой сценарий
|
## Развилка: какой сценарий
|
||||||
|
|
||||||
|
Сценарий — ось процесса; перечень осей и их границ —
|
||||||
|
[shared/axes.md](../../shared/axes.md).
|
||||||
|
|
||||||
Она в два вопроса, и оба стоят до всякой работы.
|
Она в два вопроса, и оба стоят до всякой работы.
|
||||||
|
|
||||||
**Первый: есть ли у задачи один очевидный способ решения?**
|
**Первый: есть ли у задачи один очевидный способ решения?**
|
||||||
@@ -298,13 +308,14 @@ flowchart TD
|
|||||||
|
|
||||||
## Границы: чем этот скилл не владеет
|
## Границы: чем этот скилл не владеет
|
||||||
|
|
||||||
- **Беклогом, целями и приоритетами.** Задача приходит извне. Скилл её не
|
- **Беклогом и порядком работ.** Задача приходит извне. Скилл её не выбирает,
|
||||||
выбирает, не приоритизирует, не заводит и не переоценивает.
|
не переставляет, не заводит и не переоценивает.
|
||||||
- **Форматом задач и документов.** Индексы и документы канона руками не правятся,
|
- **Форматом задач и документов.** Индексы и документы канона руками не правятся,
|
||||||
путь к чужому скрипту не выдумывается: этим владеют `av-dev:task-track` и
|
путь к чужому скрипту не выдумывается: этим владеют `av-dev:task-track` и
|
||||||
`av-dev:doc-sync`. Закрытие — работа этого скилла, и это осознанное решение с
|
`av-dev:doc-sync`. Закрытие — работа этого скилла, и это осознанное решение с
|
||||||
названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек на
|
названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек
|
||||||
груминге (`av-dev:task-groom`) возвращает задачу `reopen` с причиной, а доклад
|
возвращает задачу `reopen` с причиной (на доработке это делают грумингом,
|
||||||
|
`av-dev:task-groom`, на стройке — сразу, как заметили), а доклад
|
||||||
по критериям приёмки становится единственным, по чему приёмка вообще возможна.
|
по критериям приёмки становится единственным, по чему приёмка вообще возможна.
|
||||||
- **Определением ценности.** «Нужна ли эта функциональность» — не вопрос этого
|
- **Определением ценности.** «Нужна ли эта функциональность» — не вопрос этого
|
||||||
скилла ни на одном шаге и ни в одном сценарии. Чекпоинт решения спрашивает «так
|
скилла ни на одном шаге и ни в одном сценарии. Чекпоинт решения спрашивает «так
|
||||||
|
|||||||
@@ -81,10 +81,15 @@
|
|||||||
- **чем задача становится** — `fix` или `feature`, с причиной из разреза выше;
|
- **чем задача становится** — `fix` или `feature`, с причиной из разреза выше;
|
||||||
- **что уже сделано** и что из этого лежит в рабочем дереве.
|
- **что уже сделано** и что из этого лежит в рабочем дереве.
|
||||||
|
|
||||||
Проверка на простой язык та же, что у чекпоинтов двух других сценариев: **в
|
Проверка на простой язык — общая у трёх сценариев:
|
||||||
тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
|
||||||
|
<!-- копия: чекпоинт-простой-язык из av-dev/skills/code-resolve/references/solve.md -->
|
||||||
|
|
||||||
|
**в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||||
по-русски, и нет слов, которых нет в паспорте проекта.**
|
по-русски, и нет слов, которых нет в паспорте проекта.**
|
||||||
|
|
||||||
|
<!-- /копия: чекпоинт-простой-язык -->
|
||||||
|
|
||||||
**3. Дай два решения и жди ответа.** Их ровно два, и оба законны:
|
**3. Дай два решения и жди ответа.** Их ровно два, и оба законны:
|
||||||
|
|
||||||
- **переформулировать запись** — тип меняется на названный, и дальше задача идёт
|
- **переформулировать запись** — тип меняется на названный, и дальше задача идёт
|
||||||
@@ -172,9 +177,12 @@ flowchart TD
|
|||||||
остаётся в рабочем дереве незакоммиченным. Доклад называет **оба**: какой тип
|
остаётся в рабочем дереве незакоммиченным. Доклад называет **оба**: какой тип
|
||||||
предложен и что человек выбрал;
|
предложен и что человек выбрал;
|
||||||
- **нужна разведка** — форма правки неизвестна (мажорное обновление, смена
|
- **нужна разведка** — форма правки неизвестна (мажорное обновление, смена
|
||||||
сборщика, переезд гейта на другой инструмент) **или сработал триггер ADR**:
|
сборщика, переезд гейта на другой инструмент) **или сработал триггер ADR**.
|
||||||
дорогой откат, намеренный отказ от очевидного подхода, пересмотр прежнего
|
Перечень триггеров не пересказывается: он живёт в
|
||||||
решения. Стоп с названной причиной, разведка идёт следующим прогоном.
|
[canon.md](../../canon/references/canon.md#adr), и здесь он работает
|
||||||
|
стоп-признаком — то есть от его точности зависит выбор сценария, а пересказ
|
||||||
|
расходится с домом молча. Стоп с названной причиной, разведка идёт следующим
|
||||||
|
прогоном.
|
||||||
|
|
||||||
**Последний исход — не редкость, и его стоит ждать.** Незнакомое обслуживание —
|
**Последний исход — не редкость, и его стоит ждать.** Незнакомое обслуживание —
|
||||||
это выбор подхода с ценой и с тем, что становится невозможным, — предмет чекпоинта
|
это выбор подхода с ценой и с тем, что становится невозможным, — предмет чекпоинта
|
||||||
@@ -223,6 +231,14 @@ ADR: список источников канон закрыл двумя — а
|
|||||||
Код и конфиги — по конвенциям проекта. Правка по размеру задачи: чинится названное в записи, соседнее не улучшается
|
Код и конфиги — по конвенциям проекта. Правка по размеру задачи: чинится названное в записи, соседнее не улучшается
|
||||||
заодно.
|
заодно.
|
||||||
|
|
||||||
|
**Гейта ещё нет — сказать это, а не изображать сверку.** Первые шаги плана
|
||||||
|
стройки заводят гейт, сборку и хуки: у них нет ни «до», ни «прежнего», и
|
||||||
|
определение сделанного через зелёный гейт на них не выполнимо буквально. Такая
|
||||||
|
задача сделана, когда **заведённое работает на чистом клоне** и это показано в
|
||||||
|
докладе; пункты 1 и 3 определения ниже закрываются строкой «заводится впервые,
|
||||||
|
сверять не с чем». Изображать сверку с несуществующим прежним состоянием нельзя —
|
||||||
|
это ровно то враньё, против которого весь абзац ниже и написан.
|
||||||
|
|
||||||
**Гейт правится — сверь состав, а не цвет.** Красный, ставший зелёным, виден
|
**Гейт правится — сверь состав, а не цвет.** Красный, ставший зелёным, виден
|
||||||
сразу; убыли проверок гейт не покажет — он зелёный и до, и после. Это самая
|
сразу; убыли проверок гейт не покажет — он зелёный и до, и после. Это самая
|
||||||
дорогая из возможных правок оснастки: молча выключено то, чем проверяется всё
|
дорогая из возможных правок оснастки: молча выключено то, чем проверяется всё
|
||||||
@@ -242,7 +258,9 @@ ADR: список источников канон закрыл двумя — а
|
|||||||
|
|
||||||
**Поведенческая верификация здесь другая, чем в решении.** Проверяется не новое
|
**Поведенческая верификация здесь другая, чем в решении.** Проверяется не новое
|
||||||
поведение, а то, что прежнее не поехало: команда из `CLAUDE.md` поднимается, шаг
|
поведение, а то, что прежнее не поехало: команда из `CLAUDE.md` поднимается, шаг
|
||||||
сборки отрабатывает, хук ставится на чистом клоне. Правка, которую нельзя
|
сборки отрабатывает, хук ставится на чистом клоне. **У оснастки, заводимой
|
||||||
|
впервые, прежнего нет** — тогда проверяется, что заведённое работает на чистом
|
||||||
|
клоне, и в докладе это называется своим именем, а не «регрессий не найдено». Правка, которую нельзя
|
||||||
проверить ничем, кроме «у меня локально работает», называется в докладе строкой.
|
проверить ничем, кроме «у меня локально работает», называется в докладе строкой.
|
||||||
|
|
||||||
### 4. Ревью — план фиксирован сценарием
|
### 4. Ревью — план фиксирован сценарием
|
||||||
@@ -259,12 +277,16 @@ Change ты не передаёшь — его нет.
|
|||||||
Поэтому план у сценария **свой и постоянный**, и глубину он называет сам —
|
Поэтому план у сценария **свой и постоянный**, и глубину он называет сам —
|
||||||
проходы берут её из метки, а метки здесь нет:
|
проходы берут её из метки, а метки здесь нет:
|
||||||
|
|
||||||
|
<!-- дом: план-без-метки -->
|
||||||
|
|
||||||
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
|
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда |
|
| `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда |
|
||||||
| `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда |
|
| `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда |
|
||||||
| `conventions` + технический разбор | `conventions.*` | `review-code` | вход `small`: только индекс конвенций; потолки 3 технических и 2 конвенционных; **третья половина включена** — сверка с инвариантами `CLAUDE.md`, потолок 1 | дифф трогает код, а не только оснастку |
|
| `conventions` + технический разбор | `conventions.*` | `review-code` | вход `small`: только индекс конвенций; потолки 3 технических и 2 конвенционных; **третья половина включена** — сверка с инвариантами `CLAUDE.md`, потолок 1 | дифф трогает код, а не только оснастку |
|
||||||
|
|
||||||
|
<!-- /дом: план-без-метки -->
|
||||||
|
|
||||||
**Глубина названа в плане потому, что иначе её неоткуда взять.** Вход и потолки
|
**Глубина названа в плане потому, что иначе её неоткуда взять.** Вход и потолки
|
||||||
`review-code` заданы меткой, у `review-basics` меткой задана и сама возможность
|
`review-code` заданы меткой, у `review-basics` меткой задана и сама возможность
|
||||||
запуска; на прогоне без метки оба взяли бы их наугад — то есть по-разному от
|
запуска; на прогоне без метки оба взяли бы их наугад — то есть по-разному от
|
||||||
@@ -324,16 +346,16 @@ Change ты не передаёшь — его нет.
|
|||||||
**`adr/` этот сценарий не пополняет, и это не пропуск.** У ADR закрытый список
|
**`adr/` этот сценарий не пополняет, и это не пропуск.** У ADR закрытый список
|
||||||
источников — архивный `design.md` либо записка разведки, — и ни того ни другого
|
источников — архивный `design.md` либо записка разведки, — и ни того ни другого
|
||||||
обслуживание не производит. Поэтому триггеры ADR здесь работают **стоп-признаком,
|
обслуживание не производит. Поэтому триггеры ADR здесь работают **стоп-признаком,
|
||||||
а не поводом завести запись**: сработал дорогой откат, намеренный отказ от
|
а не поводом завести запись**: сработал любой из них — сценарий выбран неверно,
|
||||||
очевидного подхода или пересмотр прежнего решения — сценарий выбран неверно,
|
объявляй исход **нужна разведка** и останавливайся. Перечень триггеров — в
|
||||||
объявляй исход **нужна разведка** и останавливайся. Решение с ценой обязано
|
[canon.md](../../canon/references/canon.md#adr) и здесь не пересказывается. Решение с ценой обязано
|
||||||
пройти чекпоинт вариантов, а не появиться в коммите обслуживания, чей смысл —
|
пройти чекпоинт вариантов, а не появиться в коммите обслуживания, чей смысл —
|
||||||
«ничего не решали, поменяли оснастку».
|
«ничего не решали, поменяли оснастку».
|
||||||
|
|
||||||
Список документов и их триггеров здесь не дублируется — он в чек-листе скилла
|
Список документов и их триггеров здесь не дублируется — он в чек-листе скилла
|
||||||
`av-dev:doc-sync`; копия уже однажды разошлась с оригиналом. Документов канона в
|
`av-dev:doc-sync`; копия уже однажды разошлась с оригиналом. Документов канона в
|
||||||
проекте нет — синка нет вовсе: скажи это исходом и предложи завести канон
|
проекте нет — синка нет вовсе: скажи это исходом и предложи завести канон
|
||||||
скиллом `av-dev:doc-canon`.
|
скиллом `av-dev:canon`.
|
||||||
|
|
||||||
### 6. Коммит
|
### 6. Коммит
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
**Отсутствие канона бьёт по разведке сильнее, чем по решению**, и сказать об этом
|
**Отсутствие канона бьёт по разведке сильнее, чем по решению**, и сказать об этом
|
||||||
строкой мало: без документов у ответа нет дома, и знание осядет в переписке.
|
строкой мало: без документов у ответа нет дома, и знание осядет в переписке.
|
||||||
Назови исход и предложи `av-dev:doc-canon`; работу не останавливай, но адрес
|
Назови исход и предложи `av-dev:canon`; работу не останавливай, но адрес
|
||||||
ответа тогда выбираешь сам и говоришь об этом вслух.
|
ответа тогда выбираешь сам и говоришь об этом вслух.
|
||||||
|
|
||||||
## Что этот сценарий требует от входа
|
## Что этот сценарий требует от входа
|
||||||
@@ -103,10 +103,10 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
ходу. Исключение ровно одно и оно не про изменение системы: одноразовый
|
ходу. Исключение ровно одно и оно не про изменение системы: одноразовый
|
||||||
**читающий** прогон (запрос, замер, скрипт в песочнице), чей результат уезжает
|
**читающий** прогон (запрос, замер, скрипт в песочнице), чей результат уезжает
|
||||||
в ответ с провенансом и который ничего не оставляет в репозитории.
|
в ответ с провенансом и который ничего не оставляет в репозитории.
|
||||||
- **Приоритетом.** Заведённая задача встаёт в конец своей секции; где ей стоять
|
- **Местом в списке.** Заведённая задача встаёт в конец своей секции; куда её
|
||||||
в очереди, решает человек на груминге (`av-dev:task-groom`). Разведка, сама
|
поставить, решает человек — на доработке грумингом (`av-dev:task-groom`), на
|
||||||
ставящая свой исход первым в беклоге, назначает приоритет тому, что только что
|
стройке сразу же, по зависимости. Разведка, сама ставящая свой исход первым,
|
||||||
придумала.
|
назначает место тому, что только что придумала.
|
||||||
- **Форматом задач и документов.** Индексы и документы руками не правятся: их
|
- **Форматом задач и документов.** Индексы и документы руками не правятся: их
|
||||||
ведут `av-dev:task-track` и `av-dev:doc-sync`. Твоё — содержание ответа, их —
|
ведут `av-dev:task-track` и `av-dev:doc-sync`. Твоё — содержание ответа, их —
|
||||||
форма и дом.
|
форма и дом.
|
||||||
@@ -227,9 +227,14 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
отвергнутое (отвергнутый вариант с названной причиной — половина будущего ADR);
|
отвергнутое (отвергнутый вариант с названной причиной — половина будущего ADR);
|
||||||
приносить один вариант и называть это выбором.
|
приносить один вариант и называть это выбором.
|
||||||
|
|
||||||
Проверка на простой язык та же, что у чекпоинта решения: **в тексте нет `SHALL`,
|
Проверка на простой язык — общая у трёх сценариев:
|
||||||
нет имён файлов и функций там, где вещь называется по-русски, и нет слов, которых
|
|
||||||
нет в паспорте проекта.**
|
<!-- копия: чекпоинт-простой-язык из av-dev/skills/code-resolve/references/solve.md -->
|
||||||
|
|
||||||
|
**в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||||
|
по-русски, и нет слов, которых нет в паспорте проекта.**
|
||||||
|
|
||||||
|
<!-- /копия: чекпоинт-простой-язык -->
|
||||||
|
|
||||||
Исходы чекпоинта:
|
Исходы чекпоинта:
|
||||||
|
|
||||||
@@ -255,8 +260,8 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
- **ответ на вопрос** — по адресу из шага 1;
|
- **ответ на вопрос** — по адресу из шага 1;
|
||||||
- **отвергнутые варианты с причинами.** Это половина будущего ADR и единственная
|
- **отвергнутые варианты с причинами.** Это половина будущего ADR и единственная
|
||||||
защита от повторной разведки того же самого;
|
защита от повторной разведки того же самого;
|
||||||
- **решение с ценой — в ADR**, если оно проходит триггер канона (дорогой откат,
|
- **решение с ценой — в ADR**, если оно проходит [триггер
|
||||||
намеренный отказ, пересмотр прежнего решения). У разведки, кончившейся без
|
канона](../../canon/references/canon.md#adr). У разведки, кончившейся без
|
||||||
кода, `design.md` не будет никогда, поэтому здесь ADR цитирует **записку
|
кода, `design.md` не будет никогда, поэтому здесь ADR цитирует **записку
|
||||||
разведки**, а не архивный change; канон это допускает прямо, и в записи
|
разведки**, а не архивный change; канон это допускает прямо, и в записи
|
||||||
источник называется.
|
источник называется.
|
||||||
@@ -267,7 +272,7 @@ git и читается диффом, а второй стоп на каждой
|
|||||||
перечня адресов неотличим от доклада о ненаписанном.
|
перечня адресов неотличим от доклада о ненаписанном.
|
||||||
|
|
||||||
**Документов канона в проекте нет** — писать ответ некуда: назови это исходом,
|
**Документов канона в проекте нет** — писать ответ некуда: назови это исходом,
|
||||||
предложи завести канон скиллом `av-dev:doc-canon` и оставь ответ в докладе
|
предложи завести канон скиллом `av-dev:canon` и оставь ответ в докладе
|
||||||
целиком, чтобы работа не пропала. Заводить `docs/` мимо канона нельзя.
|
целиком, чтобы работа не пропала. Заводить `docs/` мимо канона нельзя.
|
||||||
|
|
||||||
### 5. Задачи: завести и уточнить
|
### 5. Задачи: завести и уточнить
|
||||||
|
|||||||
@@ -195,10 +195,17 @@ flowchart TD
|
|||||||
накопленные до этого места, и находки ревью с пометкой `развилка`;
|
накопленные до этого места, и находки ревью с пометкой `развилка`;
|
||||||
- **что дальше**, если возражений нет.
|
- **что дальше**, если возражений нет.
|
||||||
|
|
||||||
Проверка на «простой язык» одна и механическая: **в тексте нет `SHALL`, нет имён
|
Проверка на «простой язык» одна и механическая, и она общая у чекпоинтов всех
|
||||||
файлов и функций там, где вещь называется по-русски, и нет слов, которых нет в
|
трёх сценариев — поэтому её дом здесь, а у соседей помеченные копии:
|
||||||
паспорте проекта.** Не проходит — переписывай, а не объясняй, почему иначе
|
|
||||||
нельзя.
|
<!-- дом: чекпоинт-простой-язык -->
|
||||||
|
|
||||||
|
**в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||||
|
по-русски, и нет слов, которых нет в паспорте проекта.**
|
||||||
|
|
||||||
|
<!-- /дом: чекпоинт-простой-язык -->
|
||||||
|
|
||||||
|
Не проходит — переписывай, а не объясняй, почему иначе нельзя.
|
||||||
|
|
||||||
Длина — экран. Длинное объяснение не читают, а пролистывают, и чекпоинт
|
Длина — экран. Длинное объяснение не читают, а пролистывают, и чекпоинт
|
||||||
превращается в ритуал одобрения.
|
превращается в ритуал одобрения.
|
||||||
@@ -333,7 +340,7 @@ flowchart TD
|
|||||||
триггера.
|
триггера.
|
||||||
|
|
||||||
**Документов канона в проекте нет** — синка нет вовсе: назови это исходом и
|
**Документов канона в проекте нет** — синка нет вовсе: назови это исходом и
|
||||||
предложи завести канон скиллом `av-dev:doc-canon`. Придумывать раскладку под
|
предложи завести канон скиллом `av-dev:canon`. Придумывать раскладку под
|
||||||
задачу нельзя: документ, заведённый мимо канона, окажется вторым домом ровно
|
задачу нельзя: документ, заведённый мимо канона, окажется вторым домом ровно
|
||||||
тому, что канон потом заведёт своим.
|
тому, что канон потом заведёт своим.
|
||||||
|
|
||||||
|
|||||||
@@ -54,17 +54,25 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
непроверенная ветка деградации хуже честного отказа. Заводить руками не надо:
|
непроверенная ветка деградации хуже честного отказа. Заводить руками не надо:
|
||||||
этим владеет скилл `av-dev:code-openspec` — он заводит каталог и заменяет
|
этим владеет скилл `av-dev:code-openspec` — он заводит каталог и заменяет
|
||||||
пример в `config.yaml` настройкой. Его же зовут `av-dev:doc-init` на новом
|
пример в `config.yaml` настройкой. Его же зовут `av-dev:doc-init` на новом
|
||||||
проекте и `av-dev:doc-canon` в режиме `adopt` — на переводимом.
|
проекте и `av-dev:canon` в режиме `adopt` — на переводимом.
|
||||||
**Предпосылка эта — про изменение поведения, а не про всякий прогон:**
|
**Предпосылка эта — про изменение поведения, а не про всякий прогон:**
|
||||||
сценарий обслуживания зовёт конвейер без change и без дельта-спек, и ни один
|
сценарий обслуживания зовёт конвейер без change и без дельта-спек, и ни один
|
||||||
проход его плана на них не завязан. См. «Прогон без change».
|
проход его плана на них не завязан. См. «Прогон без change».
|
||||||
- **Документы канона** — см. следующий раздел.
|
- **Документы канона** — см. следующий раздел.
|
||||||
- **Проектные копии этих скиллов и агентов удаляются при установке.** Если в
|
- **Проектные копии этих скиллов и агентов удаляются при установке.**
|
||||||
проекте уже лежат свои `.claude/skills/review`,
|
|
||||||
`.claude/skills/review-pipeline`, `.claude/skills/task-pipeline`,
|
<!-- копия: проектные-копии из README.md -->
|
||||||
`.claude/skills/task-batch`, `.claude/skills/resolve` или
|
|
||||||
`.claude/agents/<проект>-review-*.md` — снеси их. Иначе короткое имя разрешится
|
Голые имена в `.claude/skills/`: `resolve`, `review`, а у проектов прошлого
|
||||||
в устаревшую проектную копию, молча и без признаков подмены.
|
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`. С префиксом
|
||||||
|
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`. Агенты:
|
||||||
|
`.claude/agents/<проект>-review-*.md`.
|
||||||
|
|
||||||
|
Две копии одного скилла расходятся, и побеждает та, что **короче названа**:
|
||||||
|
короткое имя разрешится в устаревшую проектную копию молча и без признаков
|
||||||
|
подмены.
|
||||||
|
|
||||||
|
<!-- /копия: проектные-копии -->
|
||||||
|
|
||||||
### Чего может не быть
|
### Чего может не быть
|
||||||
|
|
||||||
@@ -83,7 +91,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
поведении.
|
поведении.
|
||||||
@@ -132,7 +140,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не
|
критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не
|
||||||
открывает никто.
|
открывает никто.
|
||||||
|
|
||||||
Дом канона этой раскладки — скилл `av-dev:doc-canon`, раздел «Три категории
|
Дом канона этой раскладки — скилл `av-dev:canon`, раздел «Три категории
|
||||||
документов». Конвейер её **читатель**: категории и имена тем он берёт
|
документов». Конвейер её **читатель**: категории и имена тем он берёт
|
||||||
оттуда и своих не заводит.
|
оттуда и своих не заводит.
|
||||||
|
|
||||||
@@ -191,7 +199,7 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
дом для тех же фактов разошёлся бы и выглядел актуальным.
|
дом для тех же фактов разошёлся бы и выглядел актуальным.
|
||||||
|
|
||||||
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
|
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
|
||||||
и предложи скилл `av-dev:doc-canon`: одна операция на проект против деградации на
|
и предложи скилл `av-dev:canon`: одна операция на проект против деградации на
|
||||||
каждой задаче. Прогон при этом не останавливается.
|
каждой задаче. Прогон при этом не останавливается.
|
||||||
|
|
||||||
## Что получает каждый проход
|
## Что получает каждый проход
|
||||||
@@ -313,6 +321,11 @@ charter'а, а модель потом двигает калибровка, и
|
|||||||
|
|
||||||
## Метки
|
## Метки
|
||||||
|
|
||||||
|
**«Стадия» и «ступень» — разные членения, и путать их нельзя.** Стадий ревью
|
||||||
|
две — дизайна и кода, — и они видны снаружи: их зовёт `av-dev:code-resolve` в
|
||||||
|
разных точках цикла. Ступеней внутри прогона кода пять, они нумерованы и наружу
|
||||||
|
не выходят. Перечень осей процесса целиком — [shared/axes.md](../../shared/axes.md).
|
||||||
|
|
||||||
**Классификация задачи выдаёт ровно одно значение — метку**: `small`, `medium`
|
**Классификация задачи выдаёт ровно одно значение — метку**: `small`, `medium`
|
||||||
или `large`. Это **единственный вход, по которому конвейер выбирает
|
или `large`. Это **единственный вход, по которому конвейер выбирает
|
||||||
исполнителей**: и на дизайне, и на коде состав читается из неё, а не из класса
|
исполнителей**: и на дизайне, и на коде состав читается из неё, а не из класса
|
||||||
@@ -330,6 +343,8 @@ charter'а, а модель потом двигает калибровка, и
|
|||||||
|
|
||||||
Ревью кода:
|
Ревью кода:
|
||||||
|
|
||||||
|
<!-- дом: тема-метка-глубина -->
|
||||||
|
|
||||||
| Тема | `small` | `medium` | `large` |
|
| Тема | `small` | `medium` | `large` |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор |
|
| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор |
|
||||||
@@ -340,6 +355,8 @@ charter'а, а модель потом двигает калибровка, и
|
|||||||
| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство |
|
| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство |
|
||||||
| тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор |
|
| тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор |
|
||||||
|
|
||||||
|
<!-- /дом: тема-метка-глубина -->
|
||||||
|
|
||||||
Весь процесс с выбором исполнителей на каждом этапе — одной схемой. **Метка
|
Весь процесс с выбором исполнителей на каждом этапе — одной схемой. **Метка
|
||||||
считается один раз, в узле разметки, и дальше только читается:**
|
считается один раз, в узле разметки, и дальше только читается:**
|
||||||
|
|
||||||
@@ -402,7 +419,7 @@ flowchart TD
|
|||||||
|
|
||||||
Отсюда состав обеих стадий:
|
Отсюда состав обеих стадий:
|
||||||
|
|
||||||
| Метка | Когда | Ревью дизайна | Ревью кода: стадии | Проходов всего | Доля задач |
|
| Метка | Когда | Ревью дизайна | Ревью кода: ступени | Проходов всего | Доля задач |
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| `small` | малое **и** знакомое: багфикс, локальная правка, доки | `specs` | 1, 2, 5 (+3 при своих темах) | **5–6** | **до трети, и меньше, чем `medium`** |
|
| `small` | малое **и** знакомое: багфикс, локальная правка, доки | `specs` | 1, 2, 5 (+3 при своих темах) | **5–6** | **до трети, и меньше, чем `medium`** |
|
||||||
| `medium` | **рабочее умолчание**: среднее и знакомое | `specs`, `rubric` | 1, 2, 3, 5 | **7** | **большинство** |
|
| `medium` | **рабочее умолчание**: среднее и знакомое | `specs`, `rubric` | 1, 2, 3, 5 | **7** | **большинство** |
|
||||||
@@ -447,8 +464,8 @@ flowchart TD
|
|||||||
— и очередь между ними была бы платой ни за что.
|
— и очередь между ними была бы платой ни за что.
|
||||||
|
|
||||||
Рёбер два вида, и они разной природы. Путать их нельзя: первое про
|
Рёбер два вида, и они разной природы. Путать их нельзя: первое про
|
||||||
**осмысленность** (без плана задание не определено, на красном гейте проход с мнением
|
**осмысленность** (без плана задание не определено, а на красном гейте проходу с
|
||||||
проход не о чем), второе про **железо**.
|
мнением не о чем судить), второе про **железо**.
|
||||||
|
|
||||||
| Ребро | Смысл | Между кем |
|
| Ребро | Смысл | Между кем |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -463,7 +480,7 @@ flowchart TD
|
|||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
plan[/"план разметки задачи<br/>(готов до ревью кода)"/]
|
plan[/"план разметки задачи<br/>(готов до ревью кода)"/]
|
||||||
autotests["autotests<br/>(стадия 1, держит машину)"]
|
autotests["autotests<br/>(ступень 1, держит машину)"]
|
||||||
specs["specs"]
|
specs["specs"]
|
||||||
code["code"]
|
code["code"]
|
||||||
basics["basics<br/>(medium: темы ядра и свои;<br/>small, large: только свои темы проекта)"]
|
basics["basics<br/>(medium: темы ядра и свои;<br/>small, large: только свои темы проекта)"]
|
||||||
@@ -513,7 +530,7 @@ flowchart TD
|
|||||||
|
|
||||||
Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск.
|
Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск.
|
||||||
Проходы, заявившие его, сериализуются между собой при любой метке и на любой
|
Проходы, заявившие его, сериализуются между собой при любой метке и на любой
|
||||||
стадии; порядок внутри цепочки произволен.
|
ступени; порядок внутри цепочки произволен.
|
||||||
|
|
||||||
| Проход | Держит машину | Почему |
|
| Проход | Держит машину | Почему |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -653,6 +670,30 @@ flowchart TD
|
|||||||
(тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит **без
|
(тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит **без
|
||||||
change**: у работы, не меняющей поведения, дельта-спек нет по построению.
|
change**: у работы, не меняющей поведения, дельта-спек нет по построению.
|
||||||
|
|
||||||
|
**Копия.** Дом оси — `shared/axes.md` в репозитории плагина: режим делят конвейер,
|
||||||
|
сценарий обслуживания и два устава, и ни один из них им не владеет. Правится дом,
|
||||||
|
а не этот файл.
|
||||||
|
|
||||||
|
<!-- копия: режим-прогона из av-dev/shared/axes.md -->
|
||||||
|
|
||||||
|
**Прогон ревью идёт в одном из двух режимов, и режим — не глубина.**
|
||||||
|
|
||||||
|
- **С меткой** — обычный прогон по change: разметку сделал `review-scope`, состав
|
||||||
|
обеих стадий выведен из метки.
|
||||||
|
- **Без метки** — прогон сценария обслуживания: change нет, размечать нечего,
|
||||||
|
план фиксирован и назван сценарием. Разметчик не запускается вовсе.
|
||||||
|
|
||||||
|
**Без метки — не то же самое, что `small`.** `small` — это суждение о размере и
|
||||||
|
сложности, снятое с изменения; отсутствие метки — утверждение, что снимать её
|
||||||
|
не с чего. Проход, подставивший себе `small` там, где метки нет, вывел бы
|
||||||
|
глубину из ничего.
|
||||||
|
|
||||||
|
**Режим правит не только состав, но и саму возможность запуска.** Проход, у
|
||||||
|
которого запуск задан меткой, без метки не имеет ответа на вопрос «запускаться
|
||||||
|
ли» — и ответ ему даёт план сценария, а не умолчание.
|
||||||
|
|
||||||
|
<!-- /копия: режим-прогона -->
|
||||||
|
|
||||||
**Метка на таком прогоне не назначается, и разметчик не зовётся.** Обе его оси
|
**Метка на таком прогоне не назначается, и разметчик не зовётся.** Обе его оси
|
||||||
здесь не определены: размер он выводит из `proposal.md`, `design.md`, `tasks.md`
|
здесь не определены: размер он выводит из `proposal.md`, `design.md`, `tasks.md`
|
||||||
и дельта-спек, а сложность — из формы решения, которая у обслуживания либо
|
и дельта-спек, а сложность — из формы решения, которая у обслуживания либо
|
||||||
@@ -664,11 +705,15 @@ change**: у работы, не меняющей поведения, дельт
|
|||||||
называет глубину и вход каждого прохода** — их обычный источник метка, и без неё
|
называет глубину и вход каждого прохода** — их обычный источник метка, и без неё
|
||||||
проходы взяли бы их наугад:
|
проходы взяли бы их наугад:
|
||||||
|
|
||||||
| Тема | Кто закрывает | Глубина и вход | Когда |
|
<!-- копия: план-без-метки из av-dev/skills/code-resolve/references/maintain.md -->
|
||||||
|---|---|---|---|
|
|
||||||
| `autotests` | `review-autotests` | как обычно | всегда |
|
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
|
||||||
| `operations` | `review-basics` | сверка, потолок 2 | всегда |
|
| --- | --- | --- | --- | --- |
|
||||||
| `conventions` + технический разбор | `review-code` | вход `small` (индекс конвенций), потолки 3 и 2, третья половина включена — потолок 1 | дифф трогает код |
|
| `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда |
|
||||||
|
| `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда |
|
||||||
|
| `conventions` + технический разбор | `conventions.*` | `review-code` | вход `small`: только индекс конвенций; потолки 3 технических и 2 конвенционных; **третья половина включена** — сверка с инвариантами `CLAUDE.md`, потолок 1 | дифф трогает код, а не только оснастку |
|
||||||
|
|
||||||
|
<!-- /копия: план-без-метки -->
|
||||||
|
|
||||||
Триаж обязателен и здесь — он единственный сток и единственный, кто сверяет план
|
Триаж обязателен и здесь — он единственный сток и единственный, кто сверяет план
|
||||||
с исходом; на его вход подаётся этот план вместо плана разметки. Тема
|
с исходом; на его вход подаётся этот план вместо плана разметки. Тема
|
||||||
@@ -687,7 +732,7 @@ change**: у работы, не меняющей поведения, дельт
|
|||||||
проект семантикой гейта в `CLAUDE.md`; не объявил — это строка границ покрытия, а
|
проект семантикой гейта в `CLAUDE.md`; не объявил — это строка границ покрытия, а
|
||||||
не догадка прохода.
|
не догадка прохода.
|
||||||
|
|
||||||
## Стадия 1 — Автотесты (обязательна при любой метке)
|
## Ступень 1 — Автотесты (обязательна при любой метке)
|
||||||
|
|
||||||
Агент `review-autotests`, тема `autotests`. Запускает команду гейта из семантики
|
Агент `review-autotests`, тема `autotests`. Запускает команду гейта из семантики
|
||||||
гейта в `CLAUDE.md` и интерпретирует вывод.
|
гейта в `CLAUDE.md` и интерпретирует вывод.
|
||||||
@@ -714,11 +759,11 @@ change**: у работы, не меняющей поведения, дельт
|
|||||||
Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу
|
Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу
|
||||||
запрещено списывать такой отказ в мелочь.
|
запрещено списывать такой отказ в мелочь.
|
||||||
|
|
||||||
## Стадия 2 — Сверка (обязательна при любой метке)
|
## Ступень 2 — Сверка (обязательна при любой метке)
|
||||||
|
|
||||||
Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра
|
Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра
|
||||||
между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со
|
между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со
|
||||||
стадией 3 или 4 — той, что в метки.
|
стадией 3 или 4 — той, которую назначила метка.
|
||||||
|
|
||||||
- `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек
|
- `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек
|
||||||
предлагаемого изменения**, а не из proposal, сообщения коммита или описания
|
предлагаемого изменения**, а не из proposal, сообщения коммита или описания
|
||||||
@@ -728,7 +773,7 @@ change**: у работы, не меняющей поведения, дельт
|
|||||||
обычном входе: необработанная ветка отказа, пустое значение, граница диапазона,
|
обычном входе: необработанная ветка отказа, пустое значение, граница диапазона,
|
||||||
перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс
|
перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс
|
||||||
библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть,
|
библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть,
|
||||||
которая **не выражается правилом**: механизируемое уже проверила стадия 1.
|
которая **не выражается правилом**: механизируемое уже проверила ступень 1.
|
||||||
**На `small` у него есть третья, узкая обязанность** — сверить дифф с
|
**На `small` у него есть третья, узкая обязанность** — сверить дифф с
|
||||||
записанными инвариантами `CLAUDE.md` по темам `security`, `operations` и
|
записанными инвариантами `CLAUDE.md` по темам `security`, `operations` и
|
||||||
`architecture`, потому что с этой меткой `basics` не идёт. Потолок 1 находка
|
`architecture`, потому что с этой меткой `basics` не идёт. Потолок 1 находка
|
||||||
@@ -756,7 +801,7 @@ change**: у работы, не меняющей поведения, дельт
|
|||||||
недосмотренной темы.
|
недосмотренной темы.
|
||||||
|
|
||||||
Recall темы `conventions` равен длине конвенций проекта — это предел любой
|
Recall темы `conventions` равен длине конвенций проекта — это предел любой
|
||||||
сверки, и ровно ради него существуют стадии 3 и 4.
|
сверки, и ровно ради него существуют ступени 3 и 4.
|
||||||
|
|
||||||
**Оба прохода на верхней модели, и по одной причине — цене пропуска.** У `specs`
|
**Оба прохода на верхней модели, и по одной причине — цене пропуска.** У `specs`
|
||||||
это направление `code → spec`: надо заметить **отсутствие** — тихий фолбэк,
|
это направление `code → spec`: надо заметить **отсутствие** — тихий фолбэк,
|
||||||
@@ -765,7 +810,7 @@ Recall темы `conventions` равен длине конвенций прое
|
|||||||
границах покрытия; прочие проходы с мнением держат `opus` из-за цены **ложных**
|
границах покрытия; прочие проходы с мнением держат `opus` из-за цены **ложных**
|
||||||
находок, эти двое — из-за цены пропущенных.
|
находок, эти двое — из-за цены пропущенных.
|
||||||
|
|
||||||
## Стадия 3 — Темы (`medium` целиком; `small` и `large` — только свои темы проекта)
|
## Ступень 3 — Темы (`medium` целиком; `small` и `large` — только свои темы проекта)
|
||||||
|
|
||||||
Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не
|
Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не
|
||||||
меряет — уходит одним сообщением вместе со стадией 2, сразу после зелёного гейта.
|
меряет — уходит одним сообщением вместе со стадией 2, сразу после зелёного гейта.
|
||||||
@@ -802,7 +847,7 @@ Recall темы `conventions` равен длине конвенций прое
|
|||||||
взгляда на ось времени — значит изменение, которое не откатывается обратной
|
взгляда на ось времени — значит изменение, которое не откатывается обратной
|
||||||
правкой, на `small` не идёт вовсе, каким бы малым оно ни было.
|
правкой, на `small` не идёт вовсе, каким бы малым оно ни было.
|
||||||
|
|
||||||
## Стадия 4 — Доказательство (только `large`)
|
## Ступень 4 — Доказательство (только `large`)
|
||||||
|
|
||||||
Три прохода, и все три уходят сразу после зелёного гейта, в одном ряду со
|
Три прохода, и все три уходят сразу после зелёного гейта, в одном ряду со
|
||||||
стадией 2. Каждый берёт свою тему и доводит её до **доказательства**:
|
стадией 2. Каждый берёт свою тему и доводит её до **доказательства**:
|
||||||
@@ -831,7 +876,7 @@ Recall темы `conventions` равен длине конвенций прое
|
|||||||
ось времени и эксплуатации. Ровно поэтому они и стоят денег: оракул добывается
|
ось времени и эксплуатации. Ровно поэтому они и стоят денег: оракул добывается
|
||||||
запуском, а запуск — это машина, цепочка и часы.
|
запуском, а запуск — это машина, цепочка и часы.
|
||||||
|
|
||||||
Раньше эта пара стояла в `medium`, то есть на большинстве задач. Стадия
|
Раньше эта пара стояла в `medium`, то есть на большинстве задач. Ступень
|
||||||
переехала в `large` **сознательно и по цене, а не потому, что перестала находить**:
|
переехала в `large` **сознательно и по цене, а не потому, что перестала находить**:
|
||||||
она осталась самой ценной, но её ценность оплачивается на каждой задаче, а
|
она осталась самой ценной, но её ценность оплачивается на каждой задаче, а
|
||||||
получается — на немногих. Что из-за этого перестало проверяться на младших метках, названо в «Честном пределе» и обязано идти строкой в границы покрытия
|
получается — на немногих. Что из-за этого перестало проверяться на младших метках, названо в «Честном пределе» и обязано идти строкой в границы покрытия
|
||||||
@@ -849,7 +894,7 @@ Recall темы `conventions` равен длине конвенций прое
|
|||||||
записки; для архитектурного — что граница домена берётся из `passport.*`, а не из
|
записки; для архитектурного — что граница домена берётся из `passport.*`, а не из
|
||||||
истории решений. Обе потери названы в «Честном пределе».
|
истории решений. Обе потери названы в «Честном пределе».
|
||||||
|
|
||||||
**Условие стадии и есть условие метки `large`:** изменение крупное **или**
|
**Условие ступени и есть условие метки `large`:** изменение крупное **или**
|
||||||
незнакомое — любая из двух осей. Разведены они не для красоты: у архитектурного
|
незнакомое — любая из двух осей. Разведены они не для красоты: у архитектурного
|
||||||
прохода работа появляется от **размера** (трогается несколько слоёв разом или в
|
прохода работа появляется от **размера** (трогается несколько слоёв разом или в
|
||||||
проекте становится больше сущностей, чем было), у меряющей пары — от
|
проекте становится больше сущностей, чем было), у меряющей пары — от
|
||||||
@@ -870,7 +915,7 @@ Recall темы `conventions` равен длине конвенций прое
|
|||||||
конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс
|
конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс
|
||||||
секция «дешевле переделать до мерджа».
|
секция «дешевле переделать до мерджа».
|
||||||
|
|
||||||
## Стадия 5 — Triage (обязательна)
|
## Ступень 5 — Triage (обязательна)
|
||||||
|
|
||||||
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
|
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
|
||||||
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
|
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
|
||||||
@@ -1113,7 +1158,7 @@ flowchart TD
|
|||||||
и где это лежит в документах проекта; таблица поразрядной деградации.
|
и где это лежит в документах проекта; таблица поразрядной деградации.
|
||||||
- [references/review-levels.md](references/review-levels.md) — дом правила выбора
|
- [references/review-levels.md](references/review-levels.md) — дом правила выбора
|
||||||
метки: две оси, спорное вниз, чем `small` дешевле, доли как проверка правила.
|
метки: две оси, спорное вниз, чем `small` дешевле, доли как проверка правила.
|
||||||
- Skill `av-dev:doc-canon` — приведение проекта к канону документов.
|
- Skill `av-dev:canon` — приведение проекта к канону документов.
|
||||||
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
||||||
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
|
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
|
||||||
- [references/calibration.md](references/calibration.md) — калибровка инъекцией, вердикты keep/retune/drop.
|
- [references/calibration.md](references/calibration.md) — калибровка инъекцией, вердикты keep/retune/drop.
|
||||||
|
|||||||
@@ -43,6 +43,8 @@
|
|||||||
|
|
||||||
## Шкала severity
|
## Шкала severity
|
||||||
|
|
||||||
|
Severity — ось процесса; перечень осей — [shared/axes.md](../../../shared/axes.md).
|
||||||
|
|
||||||
| Severity | Что это | Пример |
|
| Severity | Что это | Пример |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `critical` | нарушение инварианта проекта, потеря или порча данных, утечка секрета, построенный путь к отказу | запись потеряна при слиянии; тело пользовательской выгрузки в поле лога |
|
| `critical` | нарушение инварианта проекта, потеря или порча данных, утечка секрета, построенный путь к отказу | запись потеряна при слиянии; тело пользовательской выгрузки в поле лога |
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
и проход читает их напрямую: пути жёсткие, посредник не нужен, а
|
и проход читает их напрямую: пути жёсткие, посредник не нужен, а
|
||||||
второй дом для тех же фактов разошёлся бы и выглядел актуальным.
|
второй дом для тех же фактов разошёлся бы и выглядел актуальным.
|
||||||
|
|
||||||
Определение канона держит скилл `av-dev:doc-canon`. Здесь только карта «тема →
|
Определение канона держит скилл `av-dev:canon`. Здесь только карта «тема →
|
||||||
её дом → что оттуда берётся».
|
её дом → что оттуда берётся».
|
||||||
|
|
||||||
## Карта тем
|
## Карта тем
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
строка неотличима от «мы просто не стали» и перестаёт читаться на третьей задаче.
|
строка неотличима от «мы просто не стали» и перестаёт читаться на третьей задаче.
|
||||||
|
|
||||||
**Документов канона нет вовсе** — проект не приведён к канону. Это не повод
|
**Документов канона нет вовсе** — проект не приведён к канону. Это не повод
|
||||||
работать вслепую: скажи об этом строкой и предложи `av-dev:doc-canon`. Одна
|
работать вслепую: скажи об этом строкой и предложи `av-dev:canon`. Одна
|
||||||
операция на проект против деградации на каждой задаче.
|
операция на проект против деградации на каждой задаче.
|
||||||
|
|
||||||
## Правило чтения
|
## Правило чтения
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
## Форма записи
|
## Форма записи
|
||||||
|
|
||||||
**Это дом формы, и у него есть копия.** Скелет `docs/review.md`, который кладёт
|
**Это дом формы, и у него есть копия.** Скелет `docs/review.md`, который кладёт
|
||||||
в проект `av-dev:doc-canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы
|
в проект `av-dev:canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы
|
||||||
здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе
|
здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе
|
||||||
проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет.
|
проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет.
|
||||||
Дословность сверяет `scripts/copies.py` маркетплейса по маркерам ниже — но
|
Дословность сверяет `scripts/copies.py` маркетплейса по маркерам ниже — но
|
||||||
|
|||||||
@@ -5,20 +5,26 @@
|
|||||||
его достаточно. Здесь то, что читают, когда метку **выбирают, оспаривают или
|
его достаточно. Здесь то, что читают, когда метку **выбирают, оспаривают или
|
||||||
калибруют**.
|
калибруют**.
|
||||||
|
|
||||||
Применяет правило `review-scope` при разметке задачи — не автор изменения. Его
|
Применяет правило `review-scope` при разметке задачи — не автор изменения. Сама
|
||||||
рабочая выжимка лежит в уставе агента; расходиться она с этим файлом не вправе, а
|
матрица уехала в его устав **помеченной копией**, и дословность её держит
|
||||||
при расхождении прав этот.
|
`copies.py`, а не обещание: прежде здесь стояло «расходиться не вправе», и
|
||||||
|
подкреплено это было ничем. Проза вокруг матрицы — отрицательный тест `small`,
|
||||||
|
доли, цена — принадлежит месту и живёт только здесь.
|
||||||
|
|
||||||
## Правило выбора — две оси, а не один вопрос
|
## Правило выбора — две оси, а не один вопрос
|
||||||
|
|
||||||
**Оси две, они измеряют разное, и метка есть максимум по ним.**
|
**Оси две, они измеряют разное, и метка есть максимум по ним.**
|
||||||
|
|
||||||
|
<!-- дом: матрица-метки -->
|
||||||
|
|
||||||
| | **знакомое** — форму решения можно назвать до начала | **незнакомое** — форму предстоит нащупать по ходу |
|
| | **знакомое** — форму решения можно назвать до начала | **незнакомое** — форму предстоит нащупать по ходу |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **малое** — один узел | `small` | `large` |
|
| **малое** — один узел | `small` | `large` |
|
||||||
| **среднее** — несколько узлов одного слоя | `medium` | `large` |
|
| **среднее** — несколько узлов одного слоя | `medium` | `large` |
|
||||||
| **крупное** — несколько слоёв, перенос ответственности, большой рефакторинг | `large` | `large` |
|
| **крупное** — несколько слоёв, перенос ответственности, большой рефакторинг | `large` | `large` |
|
||||||
|
|
||||||
|
<!-- /дом: матрица-метки -->
|
||||||
|
|
||||||
**Метка — не синоним размера.** Совпадают они только в левом верхнем углу: малое
|
**Метка — не синоним размера.** Совпадают они только в левом верхнем углу: малое
|
||||||
**незнакомое** изменение получает `large`, трогая один узел. Поэтому в плане
|
**незнакомое** изменение получает `large`, трогая один узел. Поэтому в плане
|
||||||
стоят три строки, а не одна: размер, сложность и метка — каждая со своим
|
стоят три строки, а не одна: размер, сложность и метка — каждая со своим
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
# Журнал версий раскладки
|
|
||||||
|
|
||||||
Одна запись на версию. Проект знает свою версию из ключа `version` в
|
|
||||||
`.av-dev.toml`; операция `upgrade` скилла `av-dev:doc-canon` идёт по записям
|
|
||||||
снизу вверх от версии проекта до текущей и делает то, что в них названо.
|
|
||||||
|
|
||||||
Правило записи: **что добавилось, что переехало, что удалено, что сделать
|
|
||||||
проекту**. Без последнего пункта запись бесполезна — по ней и работает
|
|
||||||
`upgrade`.
|
|
||||||
|
|
||||||
Версия — целое число. Обратной совместимости нет: есть «приведён» и «не
|
|
||||||
приведён». Версия **одна на всю раскладку** — и на документы канона, и на
|
|
||||||
каталог задач: ведёт их один плагин, и второе число означало бы только вопрос,
|
|
||||||
по какому журналу повышать.
|
|
||||||
|
|
||||||
**До слияния журналов было два**, и нумерация в них своя:
|
|
||||||
[changelog-before-merge.md](changelog-before-merge.md) — канон документов,
|
|
||||||
версии 1–14; [changelog-tasks-before-merge.md](changelog-tasks-before-merge.md) —
|
|
||||||
формат задач, версия 1. Оба **закрыты и не переписаны**: адрес, верный на день
|
|
||||||
записи, там и остался. Проект, отставший от канона 14, идёт сперва по ним, а
|
|
||||||
потом по этому журналу — порядок назван в записи 1.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Версия 1 — 2026-08-13
|
|
||||||
|
|
||||||
Три плагина — `av-dev-docs`, `av-dev-tasks` и `av-dev-code` — слились в один,
|
|
||||||
`av-dev`. Раскол делался под раздельную установку: проект мог взять учёт работ
|
|
||||||
без документов канона или конвейер без обоих. Практика посылку не подтвердила —
|
|
||||||
подмножество не понадобилось ни разу, — а платился раскол помеченными копиями
|
|
||||||
общих правил и веткой «плагина нет» на каждый вызов соседа.
|
|
||||||
|
|
||||||
**Что переехало в проекте.** Служебных файла было два, стал один:
|
|
||||||
|
|
||||||
| Было | Стало |
|
|
||||||
| --- | --- |
|
|
||||||
| `docs/.docs.json`, ключ `canon` | `.av-dev.toml` в корне, ключ `version` |
|
|
||||||
| `docs/.docs.json`, ключ `migrations` | `.av-dev.toml`, секция `[docs]` |
|
|
||||||
| `<каталог задач>/.tasks.json`, ключ `tasks` | тот же `version`: версия теперь одна |
|
|
||||||
| `<каталог задач>/.tasks.json`, имена частей | `.av-dev.toml`, секция `[tasks]` |
|
|
||||||
|
|
||||||
Корень выбран потому, что он есть у обоих: и у проекта без `docs/`, и у проекта
|
|
||||||
без каталога задач. Формат TOML — ради комментариев: файл лежит в репозитории
|
|
||||||
проекта, и назначение числа читают из него самого, а не из документации плагина.
|
|
||||||
|
|
||||||
**Что переехало в вызовах.** Имена скиллов сменили пространство имён и получили
|
|
||||||
префикс по прежнему плагину: `av-dev-docs:canon` → `av-dev:doc-canon`,
|
|
||||||
`av-dev-docs:init` → `av-dev:doc-init`, `av-dev-docs:docs` → `av-dev:doc-sync`,
|
|
||||||
`av-dev-docs:healthcheck` → `av-dev:doc-healthcheck`, `av-dev-tasks:tasks` →
|
|
||||||
`av-dev:task-track`, `av-dev-tasks:groom` → `av-dev:task-groom`,
|
|
||||||
`av-dev-code:openspec` → `av-dev:code-openspec`, `av-dev-code:resolve` →
|
|
||||||
`av-dev:code-resolve`, `av-dev-code:review` → `av-dev:code-review`.
|
|
||||||
|
|
||||||
**Что сделать проекту.**
|
|
||||||
|
|
||||||
1. **Отставшим сперва прежние журналы.** Версия канона в `docs/.docs.json`
|
|
||||||
меньше 14 — пройди записи до 14 по
|
|
||||||
[changelog-before-merge.md](changelog-before-merge.md), и только потом эту.
|
|
||||||
Иначе повышение объявит приведённым то, чего никто не делал.
|
|
||||||
2. **Завести `.av-dev.toml`** в корне репозитория: `version = 1`, секция
|
|
||||||
`[docs]` с `migrations`, если ключ был, секция `[tasks]` с `dir` и теми
|
|
||||||
именами частей, которые в `.tasks.json` отличались от умолчаний. Комментарии
|
|
||||||
пиши свои — файл читает человек.
|
|
||||||
3. **Удалить `docs/.docs.json` и `<каталог задач>/.tasks.json`.** Прежние имена
|
|
||||||
не читаются: два дома для одной версии расходятся молча. Пока старые файлы на
|
|
||||||
месте, `docs.py check` и `tasks.py check` называют это прежней раскладкой.
|
|
||||||
4. **Переставить плагины.** `av-dev-docs`, `av-dev-tasks` и `av-dev-code`
|
|
||||||
удалить, `av-dev` поставить — команды в README репозитория плагинов.
|
|
||||||
5. **Поправить гейт проекта.** Пути к `docs.py`, `tasks.py` и `openspec.py`
|
|
||||||
сменились вместе с именами каталогов скиллов: `skills/canon/` →
|
|
||||||
`skills/doc-canon/`, `skills/tasks/` → `skills/task-track/`,
|
|
||||||
`skills/openspec/` → `skills/code-openspec/`. Шаг, который не нашёл скрипт,
|
|
||||||
обязан краснеть, а не пропускаться, — проверь, что он краснеет.
|
|
||||||
6. **Поправить свои вызовы скиллов** — в `CLAUDE.md`, в `Taskfile`, в записях
|
|
||||||
задач: короткое имя разрешится в проектную копию, а прежнее полное не
|
|
||||||
разрешится вовсе.
|
|
||||||
7. **Найти, где проект читает служебный файл сам.** Шаг гейта, скрипт, шаблон —
|
|
||||||
что угодно, что брало значение из `docs/.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 <каталог задач>` — до отсутствия
|
|
||||||
дрейфа.
|
|
||||||
|
|
||||||
**Чего делать не надо.** Переписывать прошлые записи журналов под новые имена.
|
|
||||||
Они описывают состояния, которые были, и адрес, верный на день записи, остаётся
|
|
||||||
верным как свидетельство.
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: doc-healthcheck
|
name: doc-healthcheck
|
||||||
description: "Проверка здоровья документации проекта судом, а не машиной: не разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом — doc-consistency (один факт в двух домах, прямое противоречие, поведение в architecture.md вместо спек, ADR без парного статуса, число без провенанса) и doc-code-drift (протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки, capability). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию раскладки проверяет скилл av-dev:doc-canon, язык документов — агент doc-wording."
|
description: "Проверка здоровья документации проекта судом, а не машиной: не разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом — doc-consistency (один факт в двух домах, прямое противоречие, поведение в architecture.md вместо спек, ADR без парного статуса, число без провенанса) и doc-code-drift (протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки, capability). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию раскладки проверяет скилл av-dev:canon, язык документов — агент doc-wording."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Здоровье документации
|
# Здоровье документации
|
||||||
@@ -23,7 +23,7 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
`architecture.md` и уже живущий в `CLAUDE.md`;
|
`architecture.md` и уже живущий в `CLAUDE.md`;
|
||||||
- **вернулись к проекту после перерыва** — прежде чем опираться на написанное;
|
- **вернулись к проекту после перерыва** — прежде чем опираться на написанное;
|
||||||
- **перед тем как опереться на документ в решении**, если оно дорогое;
|
- **перед тем как опереться на документ в решении**, если оно дорогое;
|
||||||
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `doc-canon` сам.
|
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `canon` сам.
|
||||||
|
|
||||||
**Не на каждой задаче и не на каждом синке документации.** Цена реальная:
|
**Не на каждой задаче и не на каждом синке документации.** Цена реальная:
|
||||||
`doc-consistency` идёт на `opus`, потому что сличение утверждений — суждение;
|
`doc-consistency` идёт на `opus`, потому что сличение утверждений — суждение;
|
||||||
@@ -52,7 +52,7 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
поведении.
|
поведении.
|
||||||
@@ -132,15 +132,15 @@ check` и его скрипт; здесь начинается там, где к
|
|||||||
идёт из его собственного отчёта — перечень фактов у него закрытый, и он
|
идёт из его собственного отчёта — перечень фактов у него закрытый, и он
|
||||||
называет, какие из них проверить было нечем.
|
называет, какие из них проверить было нечем.
|
||||||
- Канона в проекте нет вовсе — это исход, а не пустой прогон: скажи строкой и
|
- Канона в проекте нет вовсе — это исход, а не пустой прогон: скажи строкой и
|
||||||
предложи `av-dev:doc-canon`.
|
предложи `av-dev:canon`.
|
||||||
|
|
||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
- **Не проверяет раскладку, версию и ссылки** — это `doc-canon check`, там машина.
|
- **Не проверяет раскладку, версию и ссылки** — это `canon check`, там машина.
|
||||||
- **Не судит язык** документов: залог, англицизмы, жаргон, термин без дома — это
|
- **Не судит язык** документов: залог, англицизмы, жаргон, термин без дома — это
|
||||||
агент `doc-wording`, и зовут его отдельно, по пачке правленных документов.
|
агент `doc-wording`, и зовут его отдельно, по пачке правленных документов.
|
||||||
Звонящие у него названные — последний шаг синка в `av-dev:doc-sync`, шаг 9
|
Звонящие у него названные — последний шаг синка в `av-dev:doc-sync`, шаг 9
|
||||||
`av-dev:doc-init` и шаг вычитки в обоих режимах `doc-canon`, — просто ни один из
|
`av-dev:doc-init` и шаг вычитки в обоих режимах `canon`, — просто ни один из
|
||||||
них не здесь. У него другой ритм: он нужен там, где текст только что писали, а
|
них не здесь. У него другой ритм: он нужен там, где текст только что писали, а
|
||||||
не там, где он год лежал. Оркестровать его нечем — он один и работает по
|
не там, где он год лежал. Оркестровать его нечем — он один и работает по
|
||||||
названному списку.
|
названному списку.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: doc-init
|
name: doc-init
|
||||||
description: "Завести новый проект — сессия вопросов и ответов по свободному описанию замысла, из которой рождается первичная документация по канону av-dev: паспорт, CLAUDE.md с инвариантами и командами, модель угроз с периметром и скелет остальных документов; первые цели собирает интервью, а записывает их вызовом скилла av-dev:task-track — роадмап принадлежит учёту работ. OpenSpec заводит не сам, а вызовом скилла av-dev:code-openspec — каталог принадлежит конвейеру. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл doc-canon."
|
description: "Завести новый проект — сессия вопросов и ответов по свободному описанию замысла, из которой рождается первичная документация по канону av-dev: паспорт, CLAUDE.md с инвариантами и командами, модель угроз с периметром и скелет остальных документов; первый план работ собирает интервью, а записывает его вызовом скилла av-dev:task-track — беклог принадлежит учёту работ. OpenSpec заводит не сам, а вызовом скилла av-dev:code-openspec — каталог принадлежит конвейеру. Использовать, когда начинают новый проект с нуля, когда есть только текст «что мне нужно и почему» и надо превратить его в рабочую документацию, когда просят провести стартовое интервью по брифу. Проект, где документация уже как-то ведётся, переводит скилл canon."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Заведение нового проекта
|
# Заведение нового проекта
|
||||||
@@ -8,9 +8,9 @@ description: "Завести новый проект — сессия вопро
|
|||||||
Вход — свободный текст «что мне нужно и почему». Выход — канон документов, с
|
Вход — свободный текст «что мне нужно и почему». Выход — канон документов, с
|
||||||
которого дальше работают все остальные скиллы.
|
которого дальше работают все остальные скиллы.
|
||||||
|
|
||||||
**Определение канона — [канон](../doc-canon/references/canon.md).** Прочитай его до
|
**Определение канона — [канон](../canon/references/canon.md).** Прочитай его до
|
||||||
первого вопроса: интервью идёт по слотам канона, а не по вкусу. Что класть в
|
первого вопроса: интервью идёт по слотам канона, а не по вкусу. Что класть в
|
||||||
каждый файл — [скелеты](../doc-canon/references/skeletons.md); не выдумывай заглушки
|
каждый файл — [скелеты](../canon/references/skeletons.md); не выдумывай заглушки
|
||||||
своей формы, `docs.py` узнаёт только плейсхолдер оттуда.
|
своей формы, `docs.py` узнаёт только плейсхолдер оттуда.
|
||||||
|
|
||||||
## Что `init` физически не может произвести
|
## Что `init` физически не может произвести
|
||||||
@@ -32,10 +32,10 @@ description: "Завести новый проект — сессия вопро
|
|||||||
Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет,
|
Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет,
|
||||||
заводится первой задачей». Проход читает её как факт.
|
заводится первой задачей». Проход читает её как факт.
|
||||||
|
|
||||||
**`tasks/ROADMAP.md` в таблице нет намеренно.** Первые цели `init` собирает
|
**`tasks/BACKLOG.md` в таблице нет намеренно.** Первый план работ `init`
|
||||||
интервью (блок 6), но записывает их не он: каталогом задач и формой целей владеет
|
собирает интервью (блок 6), но записывает его не он: каталогом задач владеет
|
||||||
`av-dev:task-track`, и это шаг 7. Человек от каталога задач отказался — цели
|
`av-dev:task-track`, и это шаг 7. Человек от каталога задач отказался — план
|
||||||
остаются списком в докладе, роадмапа в проекте не появляется, и это говорится
|
остаётся списком в докладе, беклога в проекте не появляется, и это говорится
|
||||||
строкой.
|
строкой.
|
||||||
|
|
||||||
## Порядок интервью — зависимость, а не удобство
|
## Порядок интервью — зависимость, а не удобство
|
||||||
@@ -54,9 +54,11 @@ description: "Завести новый проект — сессия вопро
|
|||||||
проекте нельзя откатить — деплой, выкладка наружу, перезапись данных.
|
проекте нельзя откатить — деплой, выкладка наружу, перезапись данных.
|
||||||
5. **Чем краснеет гейт.** Какие проверки обязательны; что красит безусловно;
|
5. **Чем краснеет гейт.** Какие проверки обязательны; что красит безусловно;
|
||||||
чего в гейте намеренно не будет и кто тогда это гоняет.
|
чего в гейте намеренно не будет и кто тогда это гоняет.
|
||||||
6. **Первые цели.** Возможности приложения, а не задачи: три-пять целей в
|
6. **Первые шаги стройки.** Новый проект по определению начинается со стадии
|
||||||
`Запланировано`, каждая — ответ на «что приложение будет уметь», с
|
`build`: приложения ещё нет. Собери **список от базы к деталям** — что нужно
|
||||||
обоснованием очереди прозой.
|
сделать, чтобы приложение заработало, — и обоснуй **порядок**: он значит
|
||||||
|
зависимость, а не важность. Пять-десять шагов достаточно: план дописывается
|
||||||
|
по ходу стройки, и это законно.
|
||||||
|
|
||||||
### Как вести
|
### Как вести
|
||||||
|
|
||||||
@@ -90,7 +92,7 @@ description: "Завести новый проект — сессия вопро
|
|||||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
поведении.
|
поведении.
|
||||||
@@ -133,12 +135,12 @@ description: "Завести новый проект — сессия вопро
|
|||||||
5. Напиши заполняемые документы. **Бриф переезжает в `passport.md`** и
|
5. Напиши заполняемые документы. **Бриф переезжает в `passport.md`** и
|
||||||
отдельным файлом не остаётся: два дома для одного замысла разойдутся на
|
отдельным файлом не остаётся: два дома для одного замысла разойдутся на
|
||||||
первом же уточнении.
|
первом же уточнении.
|
||||||
6. Заведи скелет остальных по [скелетам](../doc-canon/references/skeletons.md) —
|
6. Заведи скелет остальных по [скелетам](../canon/references/skeletons.md) —
|
||||||
каждый с честной строкой.
|
каждый с честной строкой.
|
||||||
7. Каталог задач и первые цели — **вызови скилл `av-dev:task-track`**: он владеет
|
7. Каталог задач и первые шаги — **вызови скилл `av-dev:task-track`**: он владеет
|
||||||
форматом целей и задач. Не разрешился — учёт задач остаётся владельцу, и это
|
форматом задач и стадией (`init --stage build`). Не разрешился — учёт задач
|
||||||
тоже строка доклада.
|
остаётся владельцу, и это тоже строка доклада.
|
||||||
8. `docs.py check` из скилла `doc-canon` — до отсутствия дрейфа. Замечания о
|
8. `docs.py check` из скилла `canon` — до отсутствия дрейфа. Замечания о
|
||||||
незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа.
|
незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа.
|
||||||
9. **Вычитай написанное — агент `doc-wording`**, по пачке заполненных документов
|
9. **Вычитай написанное — агент `doc-wording`**, по пачке заполненных документов
|
||||||
(`passport.md`, `CLAUDE.md`, `security.md`). Здесь он нужен сильнее, чем где
|
(`passport.md`, `CLAUDE.md`, `security.md`). Здесь он нужен сильнее, чем где
|
||||||
@@ -152,7 +154,7 @@ description: "Завести новый проект — сессия вопро
|
|||||||
## Что дальше
|
## Что дальше
|
||||||
|
|
||||||
- Содержимое канона по ходу разработки ведёт скилл `doc-sync`.
|
- Содержимое канона по ходу разработки ведёт скилл `doc-sync`.
|
||||||
- Раскладку проверяет `doc-canon check`.
|
- Раскладку проверяет `canon check`.
|
||||||
- Первую задачу берёт конвейер проекта; `architecture.md` и `conventions/`
|
- Первую задачу берёт конвейер проекта; `architecture.md` и `conventions/`
|
||||||
наполняются его шагом синка, а не заранее.
|
наполняются его шагом синка, а не заранее.
|
||||||
|
|
||||||
@@ -160,6 +162,6 @@ description: "Завести новый проект — сессия вопро
|
|||||||
|
|
||||||
- **Не проектирует систему.** Архитектура выводится из кода, а не наоборот.
|
- **Не проектирует систему.** Архитектура выводится из кода, а не наоборот.
|
||||||
- **Не пишет код** и не заводит сборку.
|
- **Не пишет код** и не заводит сборку.
|
||||||
- **Не переводит существующий проект** — это `doc-canon adopt`. Признак: в
|
- **Не переводит существующий проект** — это `canon adopt`. Признак: в
|
||||||
репозитории уже есть документация или беклог в какой-то раскладке.
|
репозитории уже есть документация или беклог в какой-то раскладке.
|
||||||
- **Не решает за человека**, что важно: цель, границы и периметр — его ответы.
|
- **Не решает за человека**, что важно: цель, границы и периметр — его ответы.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
name: doc-sync
|
name: doc-sync
|
||||||
description: Вести содержимое документов канона по ходу разработки — синк после сделанной задачи с построчным отчётом по каждому документу, заведение ADR промоутом из архивного design.md или из записки разведки, запись наблюдения в research, запись дефекта и настройки конвейера в review.md, чистка architecture.md от поведения с маркерами долга. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл av-dev:doc-canon.
|
description: Вести содержимое документов канона по ходу разработки — синк после сделанной задачи с построчным отчётом по каждому документу, заведение ADR промоутом из архивного design.md или из записки разведки, запись наблюдения в research, запись дефекта и настройки конвейера в review.md, чистка architecture.md от поведения с маркерами долга. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл av-dev:canon.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Ведение содержимого канона
|
# Ведение содержимого канона
|
||||||
|
|
||||||
Скилл владеет **содержимым** документов канона; раскладкой владеет `doc-canon`.
|
Скилл владеет **содержимым** документов канона; раскладкой владеет `canon`.
|
||||||
Определение канона и роли документов — [канон](../doc-canon/references/canon.md),
|
Определение канона и роли документов — [канон](../canon/references/canon.md),
|
||||||
здесь не пересказывается.
|
здесь не пересказывается.
|
||||||
|
|
||||||
Главный вызывающий — **шаг синка документации в конвейере задачи**: скилл
|
Главный вызывающий — **шаг синка документации в конвейере задачи**: скилл
|
||||||
@@ -36,7 +36,7 @@ description: Вести содержимое документов канона
|
|||||||
| `openspec/specs/` | всегда при изменении поведения | вливает `opsx:archive` |
|
| `openspec/specs/` | всегда при изменении поведения | вливает `opsx:archive` |
|
||||||
| `database.md` | тронуты миграции | `docs.py check --base` |
|
| `database.md` | тронуты миграции | `docs.py check --base` |
|
||||||
| `architecture.md` | новый компонент, граница, внешняя зависимость, изменилось окружение | `docs.py`: capability без упоминания |
|
| `architecture.md` | новый компонент, граница, внешняя зависимость, изменилось окружение | `docs.py`: capability без упоминания |
|
||||||
| `adr/` | дорогой откат, намеренный отказ, пересмотр прежнего | нет — только этот чек-лист |
|
| `adr/` | сработал триггер канона (перечень — [canon.md](../canon/references/canon.md#adr)) | нет — только этот чек-лист |
|
||||||
| `research/` | узнали новое о внешнем формате или данных | нет |
|
| `research/` | узнали новое о внешнем формате или данных | нет |
|
||||||
| `security.md` | новый недоверенный вход, токен, путь наружу, сдвиг периметра | нет |
|
| `security.md` | новый недоверенный вход, токен, путь наружу, сдвиг периметра | нет |
|
||||||
| `conventions/` | находка принята и не специфична для одного места | промоут |
|
| `conventions/` | находка принята и не специфична для одного места | промоут |
|
||||||
@@ -106,11 +106,11 @@ description: Вести содержимое документов канона
|
|||||||
`av-dev:code-resolve`, сценарий разведки: решение, принятое разведкой (намеренный отказ, выбор
|
`av-dev:code-resolve`, сценарий разведки: решение, принятое разведкой (намеренный отказ, выбор
|
||||||
подхода, «проверили и не делаем»), `design.md` не имеет по построению — change по
|
подхода, «проверили и не делаем»), `design.md` не имеет по построению — change по
|
||||||
нему не будет никогда. Промоут при этом тот же: цитата и ссылка, но на записку.
|
нему не будет никогда. Промоут при этом тот же: цитата и ссылка, но на записку.
|
||||||
Перечень источников закрыт и живёт в [каноне](../doc-canon/references/canon.md),
|
Перечень источников закрыт и живёт в [каноне](../canon/references/canon.md),
|
||||||
раздел `adr/`.
|
раздел `adr/`.
|
||||||
|
|
||||||
**Триггер заведения, форма имени и правило замены — в
|
**Триггер заведения, форма имени и правило замены — в
|
||||||
[каноне](../doc-canon/references/canon.md), раздел `adr/`.** Здесь они не
|
[каноне](../canon/references/canon.md), раздел `adr/`.** Здесь они не
|
||||||
повторяются: копия правила расходится с оригиналом на первой же смене версии
|
повторяются: копия правила расходится с оригиналом на первой же смене версии
|
||||||
канона, а расходится незаметно.
|
канона, а расходится незаметно.
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ description: Вести содержимое документов канона
|
|||||||
|
|
||||||
Обзор не держит поведение — его нормативный дом `openspec/specs/`. **Форма
|
Обзор не держит поведение — его нормативный дом `openspec/specs/`. **Форма
|
||||||
маркера долга и правило «гейт от них не краснеет» — в
|
маркера долга и правило «гейт от них не краснеет» — в
|
||||||
[каноне](../doc-canon/references/canon.md), раздел `architecture.md`.**
|
[каноне](../canon/references/canon.md), раздел `architecture.md`.**
|
||||||
|
|
||||||
Разбирается порциями: раздел вычищает та задача, которая его касается.
|
Разбирается порциями: раздел вычищает та задача, которая его касается.
|
||||||
Содержимое не выбрасывается, а переезжает — требования в дельта-спеку change,
|
Содержимое не выбрасывается, а переезжает — требования в дельта-спеку change,
|
||||||
@@ -136,7 +136,7 @@ description: Вести содержимое документов канона
|
|||||||
|
|
||||||
Наблюдение о внешнем мире: что реально шлёт источник, чем документация формата
|
Наблюдение о внешнем мире: что реально шлёт источник, чем документация формата
|
||||||
расходится с практикой. **Требование провенанса и правило про расходящееся
|
расходится с практикой. **Требование провенанса и правило про расходящееся
|
||||||
число — в [каноне](../doc-canon/references/canon.md), раздел `research/`.**
|
число — в [каноне](../canon/references/canon.md), раздел `research/`.**
|
||||||
|
|
||||||
Твоя часть — заметить, что по ходу задачи узналось новое о внешних данных, и не
|
Твоя часть — заметить, что по ходу задачи узналось новое о внешних данных, и не
|
||||||
дать этому остаться в контексте. Признак: ты правил разбор, опираясь на то, чего
|
дать этому остаться в контексте. Признак: ты правил разбор, опираясь на то, чего
|
||||||
@@ -163,7 +163,7 @@ description: Вести содержимое документов канона
|
|||||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||||
|
|
||||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||||
поведении.
|
поведении.
|
||||||
@@ -191,7 +191,7 @@ description: Вести содержимое документов канона
|
|||||||
|
|
||||||
Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку
|
Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку
|
||||||
конвейера. **Что в каком и в какой форме — в
|
конвейера. **Что в каком и в какой форме — в
|
||||||
[каноне](../doc-canon/references/canon.md), раздел `review.md`**; подробности формы
|
[каноне](../canon/references/canon.md), раздел `review.md`**; подробности формы
|
||||||
записи и выбор адреса, куда она ведёт, — у конвейера ревью: `Skill
|
записи и выбор адреса, куда она ведёт, — у конвейера ревью: `Skill
|
||||||
av-dev:code-review`, его `references/review-journal.md`.
|
av-dev:code-review`, его `references/review-journal.md`.
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ av-dev:code-review`, его `references/review-journal.md`.
|
|||||||
Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком
|
Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком
|
||||||
принадлежит конвейеру ревью — его `references/promote.md`, читается через
|
принадлежит конвейеру ревью — его `references/promote.md`, читается через
|
||||||
`Skill av-dev:code-review`; роль каталога конвенций — в
|
`Skill av-dev:code-review`; роль каталога конвенций — в
|
||||||
[каноне](../doc-canon/references/canon.md). **Прогон идёт вне конвейера**
|
[каноне](../canon/references/canon.md). **Прогон идёт вне конвейера**
|
||||||
(находку принесли руками) — три шага всё равно твои, просто без его процедуры:
|
(находку принесли руками) — три шага всё равно твои, просто без его процедуры:
|
||||||
сформулируй правило,
|
сформулируй правило,
|
||||||
поищи, чем оно механизируется, и вычеркни прозу, если механизировалось.
|
поищи, чем оно механизируется, и вычеркни прозу, если механизировалось.
|
||||||
@@ -217,8 +217,8 @@ av-dev:code-review`, его `references/review-journal.md`.
|
|||||||
|
|
||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
|
|
||||||
- **Не проверяет раскладку** — это `doc-canon`.
|
- **Не проверяет раскладку** — это `canon`.
|
||||||
- **Не заводит недостающие документы** — их скелет кладёт `doc-canon adopt` или
|
- **Не заводит недостающие документы** — их скелет кладёт `canon adopt` или
|
||||||
`doc-init`.
|
`doc-init`.
|
||||||
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
|
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
|
||||||
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.
|
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ description: "Груминг беклога — интерактивный ра
|
|||||||
## Три правила, из которых всё следует
|
## Три правила, из которых всё следует
|
||||||
|
|
||||||
1. **Порядок назначает человек, машина его не выводит.** Ни давность, ни тип, ни
|
1. **Порядок назначает человек, машина его не выводит.** Ни давность, ни тип, ни
|
||||||
число задач под целью приоритетом не являются. Единственное место в очереди,
|
размер секции приоритетом не являются. Единственное место в очереди,
|
||||||
назначенное не человеком, — конец секции у сырья, и оно из очереди изъято
|
назначенное не человеком, — конец секции у сырья, и оно из очереди изъято
|
||||||
(`task-track`, правило 4).
|
(`task-track`, правило 4).
|
||||||
2. **Порция важнее охвата.** Тридцать задач за заход — это усталость и
|
2. **Порция важнее охвата.** Тридцать задач за заход — это усталость и
|
||||||
@@ -37,6 +37,36 @@ 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`. Вторая привязана к грумингу только по привычке — заметил,
|
||||||
|
что закрытая задача сделана не тем, чем обещала, возвращай сразу, на любой
|
||||||
|
стадии и в любой момент.
|
||||||
|
|
||||||
## Когда груминг созрел
|
## Когда груминг созрел
|
||||||
|
|
||||||
**Зовёт человек.** Скилл сам себя не назначает, но обязан **напоминать**, и
|
**Зовёт человек.** Скилл сам себя не назначает, но обязан **напоминать**, и
|
||||||
@@ -47,6 +77,8 @@ description: "Груминг беклога — интерактивный ра
|
|||||||
- на верхних строках очереди есть задача с открытым вопросом — очередь
|
- на верхних строках очереди есть задача с открытым вопросом — очередь
|
||||||
показывает то, что взять нельзя;
|
показывает то, что взять нельзя;
|
||||||
- `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,15 +178,15 @@ flowchart TD
|
|||||||
кодом стоит меньше, чем та же работа через квартал;
|
кодом стоит меньше, чем та же работа через квартал;
|
||||||
- **что дорожает от ожидания** — данные копятся, миграция усложняется, внешний
|
- **что дорожает от ожидания** — данные копятся, миграция усложняется, внешний
|
||||||
срок приближается;
|
срок приближается;
|
||||||
- **цель, которую человек назвал следующей.**
|
- **то, что человек назвал следующим.**
|
||||||
|
|
||||||
Довод **записывается причиной** (`move --after <слаг> --reason …`). Порядок без
|
Довод **записывается причиной** (`move --after <слаг> --reason …`). Порядок без
|
||||||
причины — это порядок, который на следующем груминге назначат заново с нуля.
|
причины — это порядок, который на следующем груминге назначат заново с нуля.
|
||||||
|
|
||||||
**Цель и приоритет — независимые оси.** Очередь может идти поперёк целей, и это
|
**Тема и приоритет — независимые оси.** Очередь может идти поперёк полок, и это
|
||||||
законно: задачи одной цели не обязаны стоять подряд. Но если под целью годами
|
законно: задачи одной темы не обязаны стоять подряд. Но если из одной полки
|
||||||
ничего не поднимается наверх — это разговор про цель, а не про очередь, и он
|
годами ничего не поднимается наверх — это разговор про саму работу, а не про
|
||||||
идёт на шаге 3.
|
очередь, и он идёт на шаге 3.
|
||||||
|
|
||||||
## Документы устаревают тем же ходом работы
|
## Документы устаревают тем же ходом работы
|
||||||
|
|
||||||
@@ -193,9 +225,9 @@ flowchart TD
|
|||||||
- **независимый отчёт ревью** — артефакт, написанный не исполнителем: отчёт
|
- **независимый отчёт ревью** — артефакт, написанный не исполнителем: отчёт
|
||||||
триажа в
|
триажа в
|
||||||
`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` по беклогу показывает,
|
||||||
что и когда закрыто, потому что закрытие коммитится отдельным коммитом учёта.
|
что и когда закрыто, потому что закрытие коммитится отдельным коммитом учёта.
|
||||||
|
|
||||||
Известные обходы:
|
Известные обходы:
|
||||||
@@ -231,11 +263,11 @@ flowchart TD
|
|||||||
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
|
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
|
||||||
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
|
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
|
||||||
- **Что перестало быть важным**: удалено как реализованное (со ссылками), ушло
|
- **Что перестало быть важным**: удалено как реализованное (со ссылками), ушло
|
||||||
без реализации (с причинами), понижено до сырья, слито, сменило тип или цель.
|
без реализации (с причинами), понижено до сырья, слито, сменило тип.
|
||||||
- **Что важно сейчас**: верх очереди по каждой секции — слаги в порядке, и по
|
- **Что важно сейчас**: верх очереди по каждой секции — слаги в порядке, и по
|
||||||
каждому движению довод одной строкой.
|
каждому движению довод одной строкой.
|
||||||
- **Границы покрытия**: сколько задач не трогали и какие именно секции, теги или
|
- **Границы покрытия**: сколько задач не трогали и какие именно секции или теги
|
||||||
цели остались — иначе доклад читается как «беклог разобран».
|
остались — иначе доклад читается как «беклог разобран».
|
||||||
- `tasks.py check` после правок — результат строкой.
|
- `tasks.py check` после правок — результат строкой.
|
||||||
|
|
||||||
## Чего этот скилл не делает
|
## Чего этот скилл не делает
|
||||||
@@ -244,4 +276,4 @@ flowchart TD
|
|||||||
себе — формат и содержимое ведёт `task-track` (груминг зовёт его операции). Не решает
|
себе — формат и содержимое ведёт `task-track` (груминг зовёт его операции). Не решает
|
||||||
за человека, что важно: он готовит развилки и рекомендует. Не принимает
|
за человека, что важно: он готовит развилки и рекомендует. Не принимает
|
||||||
закрытые задачи отдельным ритуалом — `reopen` есть, момента у него нет. Не судит
|
закрытые задачи отдельным ритуалом — `reopen` есть, момента у него нет. Не судит
|
||||||
документы проекта — это скиллы `av-dev:doc-canon` и `av-dev:doc-healthcheck`.
|
документы проекта — это скиллы `av-dev:canon` и `av-dev:doc-healthcheck`.
|
||||||
|
|||||||
@@ -41,8 +41,8 @@
|
|||||||
проверки на нужность. Свежесть меряется git'ом, как и залежалость, — датой
|
проверки на нужность. Свежесть меряется git'ом, как и залежалость, — датой
|
||||||
появления файла в истории;
|
появления файла в истории;
|
||||||
2. дальше **по залежалости** — `list --stale`;
|
2. дальше **по залежалости** — `list --stale`;
|
||||||
3. по потребности — одна секция целиком, один тег (партия ревью), одна цель
|
3. по потребности — одна секция целиком, один тег (партия ревью), список от
|
||||||
(`--goal`), список от человека.
|
человека.
|
||||||
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
||||||
Между порциями — промежуточный доклад.
|
Между порциями — промежуточный доклад.
|
||||||
|
|
||||||
@@ -84,20 +84,14 @@
|
|||||||
|
|
||||||
6. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
|
6. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
|
||||||
сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
|
сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
|
||||||
7. **Та ли цель — и нужна ли она вообще.** `feature`, которой не находится цель,
|
7. **Тот ли тип.** Заводилась починкой, а после разбора оказалось, что
|
||||||
— кандидат на выход: новая возможность вне цели это возможность, которой никто
|
поведение никогда и не было заявлено, — это `feature`. Тип, оставшийся от
|
||||||
не заказывал. Операционной задаче (`fix`, `chore`, `research`) цель не нужна,
|
прошлой формулировки, врёт ровно там, где по нему отбирают, и требует не тех
|
||||||
и выдумывать её здесь не надо.
|
разделов.
|
||||||
|
|
||||||
**Отменяется и сама цель** — когда замысел оказался неверен, а не когда
|
|
||||||
задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую
|
|
||||||
либо закрыть своей причиной, либо перевесить на другую цель, и только потом
|
|
||||||
закрыть цель. Порядок и почему он такой —
|
|
||||||
[task-goal.md](../../task-track/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`, — это очередь, которая врёт:
|
||||||
взять её нельзя. Либо дописывается здесь же, либо уступает место.
|
взять её нельзя. Либо дописывается здесь же, либо уступает место.
|
||||||
|
|||||||
+254
-258
@@ -1,13 +1,13 @@
|
|||||||
---
|
---
|
||||||
name: task-track
|
name: task-track
|
||||||
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Каталог отстал от версии раскладки — это скажет tasks.py check, а повышает проект скилл av-dev:doc-canon по общему журналу версий. Расстановка приоритетов и разбор накопившегося — скилл av-dev:task-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`. Скилл владеет **форматом и содержимым**: заводит,
|
||||||
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
|
редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
|
||||||
|
|
||||||
Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть
|
Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть
|
||||||
важным, решает скилл `task-groom`, а этот скилл лишь даёт ему операции; и выполнением
|
важным, решает скилл `task-groom`, а этот скилл лишь даёт ему операции; и выполнением
|
||||||
@@ -17,57 +17,53 @@ description: Ведение задач и целей как каталога mar
|
|||||||
|
|
||||||
Ситуация не покрыта инструкцией — решай по ним.
|
Ситуация не покрыта инструкцией — решай по ним.
|
||||||
|
|
||||||
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. **Тип решает, что с задачей можно делать.** Тип — вторая ось и первое
|
||||||
поле меты: от него зависит, какие разделы обязательны в теле, нужна ли цель,
|
поле меты: от него зависит, какие разделы обязательны в теле и берётся ли
|
||||||
берётся ли запись в работу и в каком индексе живёт её строка. Словарь закрыт;
|
запись в работу. Словарь закрыт; ни один тип не подошёл — значит, в записи их
|
||||||
ни один тип не подошёл — значит, в записи их два, и её надо разделить.
|
два, и её надо разделить.
|
||||||
|
|
||||||
## Раскладка
|
## Раскладка
|
||||||
|
|
||||||
@@ -79,55 +75,23 @@ description: Ведение задач и целей как каталога mar
|
|||||||
|
|
||||||
```
|
```
|
||||||
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,85 +137,92 @@ stateDiagram-v2
|
|||||||
Схема — **сводка**: условия и оговорки живут в тексте разделов, и при
|
Схема — **сводка**: условия и оговорки живут в тексте разделов, и при
|
||||||
расхождении прав текст.
|
расхождении прав текст.
|
||||||
|
|
||||||
## Цели
|
## Две стадии
|
||||||
|
|
||||||
**Цель — возможность приложения.** Такой же файл в `items/`, тип `goal` (🎯),
|
**Стадия проекта — ось, и решает она, что значит порядок строк беклога.**
|
||||||
перечисленный в `ROADMAP.md`. Формулируется ответом на вопрос **«что приложение
|
Значения два, дом — ключ `[tasks] stage` в `.av-dev.toml`.
|
||||||
будет уметь»**, а не названием области работ: не «Работа с чтением», а «Чтение
|
|
||||||
данных клиентами»; не «Рефакторинг слияния», а «Исход слияния не зависит от
|
|
||||||
порядка доставки».
|
|
||||||
|
|
||||||
**Свойство поведения — тоже возможность.** «Наблюдаемость» это «приложение
|
| | `build` — стройка | `support` — доработка |
|
||||||
сообщает о своём состоянии»; «прочность слияния» это «исход не зависит от
|
| --- | --- | --- |
|
||||||
порядка». Такие цели законны и переформулировки в функцию не требуют — требуют
|
| Порядок строк | зависимость: раньше **нельзя** | важность: раньше **лучше** |
|
||||||
только, чтобы формулировка отвечала на «что приложение делает», а не на «какую
|
| Секции | ровно одна: список от базы к деталям | полки домена, сколько нужно |
|
||||||
часть кода мы трогаем».
|
| Заведение | список пишется вперёд целиком | по одной, по мере появления |
|
||||||
|
| Пустой беклог | план исчерпан, стройка окончена | нормальное состояние |
|
||||||
|
| Груминг | не применяется; замысел сменился — план пересматривается целиком | основная гигиена, порциями по 5–8 |
|
||||||
|
| Залежалость | не считается: шаг ждёт своей очереди законно | считается, `list --stale` |
|
||||||
|
|
||||||
**Целью не становится работа, которой держат проект.** Состав перечислен
|
**Стадия называется явно, и молчание ответом не считается.** Без неё порядок
|
||||||
[в словаре сопровождения](../../shared/operations.md);
|
строк нечем прочитать: переставить строку значит на стройке сломать план, а на
|
||||||
на вопрос «что приложение будет уметь» ничто из него не отвечает. Им отведена отдельная секция роадмапа,
|
доработке — принять решение о важности, и это разные действия. `init --stage`
|
||||||
чтобы они были видны в том же экране и при этом не читались как возможности
|
обязателен, `check` без ключа отказывает, `check --fix` его не подставляет:
|
||||||
продукта.
|
какая стадия у проекта, знает человек, а подставленное умолчание соврало бы ровно
|
||||||
|
там, где по нему принимают решение.
|
||||||
|
|
||||||
**Граница проходит по тому, кто наблюдает, а не по теме.** «Приложение сообщает
|
**Секций на стройке одна, и это не педантизм.** Порядок там — зависимость, и
|
||||||
о своём состоянии» — возможность: наблюдает пользователь сервиса, и цели место
|
разложенный по полкам список перестаёт быть планом: два шага из разных секций
|
||||||
среди прочих. «Дежурный видит состояние на одном экране» — сопровождение:
|
уже не сравнить. На доработке полки законны — правки независимы, и очередь
|
||||||
наблюдаем мы. Одна и та же наблюдаемость попадает в разные секции, и это верно —
|
внутри полки самостоятельна.
|
||||||
секции отвечают на разные вопросы.
|
|
||||||
|
|
||||||
**Сопровождение и эксплуатация — целое и часть**, а не синонимы, и та же тема
|
**Переход — событие, а не настройка.** `tasks.py stage support` переносит остаток
|
||||||
живёт ещё в двух местах: разделе «Эксплуатация» в `architecture.md` и теме ревью
|
беклога в первую новую секцию, правит «Категорию» в файлах и говорит, что порядок
|
||||||
`operations`. Словарь у всех трёх общий, и дом у него один:
|
с этого момента значит другое. Датой ему служит коммит: отдельного журнала ради
|
||||||
[shared/operations.md](../../shared/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` и
|
||||||
@@ -299,7 +248,8 @@ stateDiagram-v2
|
|||||||
изменения, а не по типу задачи: `chore` бывает миграцией схемы, `fix` — правкой
|
изменения, а не по типу задачи: `chore` бывает миграцией схемы, `fix` — правкой
|
||||||
публичного контракта. Правило «предписание процесса в теле задачи снимается»
|
публичного контракта. Правило «предписание процесса в теле задачи снимается»
|
||||||
типом не отменяется, а подтверждается: он описывает работу, а не то, как её
|
типом не отменяется, а подтверждается: он описывает работу, а не то, как её
|
||||||
проверять.
|
проверять. **Стадия проекта их тоже не выбирает**: изменение на стройке ничем не
|
||||||
|
проще того же изменения на доработке, и метку ему по-прежнему назначает разметка.
|
||||||
|
|
||||||
**Одно исполнителю тип всё же говорит — каким сценарием работу вести, и то не
|
**Одно исполнителю тип всё же говорит — каким сценарием работу вести, и то не
|
||||||
один.** Скилл `av-dev:code-resolve` выбирает сценарий связкой из двух
|
один.** Скилл `av-dev:code-resolve` выбирает сценарий связкой из двух
|
||||||
@@ -314,11 +264,10 @@ stateDiagram-v2
|
|||||||
Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы
|
Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы
|
||||||
задачу можно было **оценить, не открывая код**.
|
задачу можно было **оценить, не открывая код**.
|
||||||
|
|
||||||
**Заголовок отвечает на вопрос своего типа.** Вопросов три, поэтому и форм три:
|
**Заголовок отвечает на вопрос своего типа.** Вопросов два, поэтому и форм две:
|
||||||
|
|
||||||
| Тип | Отвечает на | Пример |
|
| Тип | Отвечает на | Пример |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 🎯 `goal` | что приложение будет уметь | Соперником может быть компьютер |
|
|
||||||
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | Печатать поле одним куском кода |
|
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | Печатать поле одним куском кода |
|
||||||
| 🔬 `research` | о чём разведка | Подсказка следующего хода |
|
| 🔬 `research` | о чём разведка | Подсказка следующего хода |
|
||||||
|
|
||||||
@@ -331,10 +280,6 @@ stateDiagram-v2
|
|||||||
исход знание, а что делать — ещё неизвестно, и заголовок-действие обещал бы
|
исход знание, а что делать — ещё неизвестно, и заголовок-действие обещал бы
|
||||||
решённость, которой нет.
|
решённость, которой нет.
|
||||||
|
|
||||||
Из этого же правила растёт разница индексов: роадмап — список возможностей,
|
|
||||||
беклог — список работ, и если заголовки перепутать формами, каждый из них
|
|
||||||
начинает читаться как другой.
|
|
||||||
|
|
||||||
`check` считает заголовки не в форме действия и печатает **число** в блоке
|
`check` считает заголовки не в форме действия и печатает **число** в блоке
|
||||||
здоровья, не замечанием на файл: проверка эвристическая (первое слово на
|
здоровья, не замечанием на файл: проверка эвристическая (первое слово на
|
||||||
`-ть`/`-ти`/`-чь`), а беклог, заведённый до правила, не переоформляют «заодно».
|
`-ть`/`-ти`/`-чь`), а беклог, заведённый до правила, не переоформляют «заодно».
|
||||||
@@ -384,58 +329,70 @@ stateDiagram-v2
|
|||||||
подкаталога — обычное дело.
|
подкаталога — обычное дело.
|
||||||
|
|
||||||
```
|
```
|
||||||
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 | окружение: каталог не найден, конфиг битый или мимо диска | чинится путём или `.av-dev.toml` в корне, повтор не поможет |
|
|
||||||
| 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` кладёт заголовок, мета-блок и шаблон с подсказками, тело дописываешь
|
||||||
@@ -446,18 +403,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` машина не отличает, и подставленное наугад значение врало бы ровно там,
|
||||||
@@ -468,7 +430,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
работу — там, где по ней принимают решение; `check` поимённо о ней не говорит, а
|
работу — там, где по ней принимают решение; `check` поимённо о ней не говорит, а
|
||||||
считает: строка здоровья **«схема типа не выполнена: N из M»** называет число и
|
считает: строка здоровья **«схема типа не выполнена: N из M»** называет число и
|
||||||
первые слаги, строка **«готово к взятию»** — сколько задач беклога пройдут
|
первые слаги, строка **«готово к взятию»** — сколько задач беклога пройдут
|
||||||
`ready` целиком (схема плюс цель плюс отсутствие открытого вопроса). Это две
|
`ready` целиком (схема плюс отсутствие открытого вопроса). Это две
|
||||||
разные строки, и совпадение их чисел — совпадение. У каждой части своя глубина:
|
разные строки, и совпадение их чисел — совпадение. У каждой части своя глубина:
|
||||||
|
|
||||||
- **тип** — жёстко: назван и из закрытого словаря;
|
- **тип** — жёстко: назван и из закрытого словаря;
|
||||||
@@ -476,7 +438,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
(меньше двух отказ, больше пяти замечание), наличие оракула **эвристикой** по
|
(меньше двух отказ, больше пяти замечание), наличие оракула **эвристикой** по
|
||||||
слову «оракул» в пункте;
|
слову «оракул» в пункте;
|
||||||
- **прочие разделы схемы** (`Затрагивает`, `Воспроизведение`, `Вопрос`,
|
- **прочие разделы схемы** (`Затрагивает`, `Воспроизведение`, `Вопрос`,
|
||||||
`Куда ляжет ответ`, `Завершение`) — только **наличие непустого**. Содержимое
|
`Куда ляжет ответ`) — только **наличие непустого**. Содержимое
|
||||||
машине не видно: границу, которую забыли назвать, она от отсутствующей не
|
машине не видно: границу, которую забыли назвать, она от отсутствующей не
|
||||||
отличает, а шаги, по которым ничего не воспроизводится, — от годных.
|
отличает, а шаги, по которым ничего не воспроизводится, — от годных.
|
||||||
|
|
||||||
@@ -485,10 +447,10 @@ 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).
|
||||||
|
|
||||||
@@ -496,7 +458,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
Формат каталога задач меняется, и проект должен знать, к какой версии он
|
Формат каталога задач меняется, и проект должен знать, к какой версии он
|
||||||
приведён. Число живёт ключом `version` в `.av-dev.toml` в корне репозитория,
|
приведён. Число живёт ключом `version` в `.av-dev.toml` в корне репозитория,
|
||||||
журнал версий — [журнал скилла `doc-canon`](../doc-canon/references/changelog.md),
|
журнал версий — [журнал скилла `canon`](../canon/references/changelog.md),
|
||||||
сверяет их `tasks.py check`: отстало — строка расхождения, ушло вперёд — устарел
|
сверяет их `tasks.py check`: отстало — строка расхождения, ушло вперёд — устарел
|
||||||
плагин. Обратной совместимости нет: есть «приведён» и «не приведён».
|
плагин. Обратной совместимости нет: есть «приведён» и «не приведён».
|
||||||
|
|
||||||
@@ -506,7 +468,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
половины проектов нет. Плагин один — довод ушёл, а два числа оставляли бы
|
половины проектов нет. Плагин один — довод ушёл, а два числа оставляли бы
|
||||||
вопрос, по какому журналу повышать.
|
вопрос, по какому журналу повышать.
|
||||||
|
|
||||||
**Повышает проект скилл `av-dev:doc-canon`, операция `upgrade`** — он идёт по
|
**Повышает проект скилл `av-dev:canon`, операция `upgrade`** — он идёт по
|
||||||
журналу, двигает число и зовёт этот скилл там, где запись касается задач. Здесь
|
журналу, двигает число и зовёт этот скилл там, где запись касается задач. Здесь
|
||||||
повышения нет намеренно: две операции, двигающие одно число, разъезжаются на
|
повышения нет намеренно: две операции, двигающие одно число, разъезжаются на
|
||||||
первом же проекте, где прошла только одна из них.
|
первом же проекте, где прошла только одна из них.
|
||||||
@@ -515,9 +477,13 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
### Завести запись из диалога
|
### Завести запись из диалога
|
||||||
|
|
||||||
1. **Фильтр.** Делаем прямо сейчас — не заводим. Не пожалеем о потере — не
|
0. **Посмотри стадию** — `stage`. От неё зависят шаг 1 и место новой строки: на
|
||||||
заводим. Родилось три кандидата — покажи их и спроси, какие заводить: молча
|
доработке беклог пополняют по одной и с фильтром, на стройке пишут планом.
|
||||||
заведённая пачка и есть тот самый отказ из правила 1.
|
1. **Фильтр — на доработке.** Делаем прямо сейчас — не заводим. Не пожалеем о
|
||||||
|
потере — не заводим. Родилось три кандидата — покажи их и спроси, какие
|
||||||
|
заводить: молча заведённая пачка и есть тот самый отказ из правила 1.
|
||||||
|
**На стройке фильтра нет**: план пишется вперёд целиком, и «этого мы сейчас
|
||||||
|
не делаем» — не довод против шага, а описание всякого шага, кроме первого.
|
||||||
2. **Дедуп.** `list` плюс поиск по слагам, полю «зачем» и телам (`grep -ril`),
|
2. **Дедуп.** `list` плюс поиск по слагам, полю «зачем» и телам (`grep -ril`),
|
||||||
**включая `REJECTED.md`**. Нашлось среди живых — **дописываем в существующий
|
**включая `REJECTED.md`**. Нашлось среди живых — **дописываем в существующий
|
||||||
файл**, а не заводим соседний. Нашлось в `REJECTED.md` — покажи пользователю
|
файл**, а не заводим соседний. Нашлось в `REJECTED.md` — покажи пользователю
|
||||||
@@ -526,7 +492,6 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
переоценки.
|
переоценки.
|
||||||
3. **Тип** — `--type` обязателен, и он же первое содержательное решение:
|
3. **Тип** — `--type` обязателен, и он же первое содержательное решение:
|
||||||
|
|
||||||
- возможность приложения, а не шаг к ней → `goal`;
|
|
||||||
- снаружи появляется то, чего не было → `feature`;
|
- снаружи появляется то, чего не было → `feature`;
|
||||||
- поведение расходится с заявленным и **воспроизводится** → `fix`
|
- поведение расходится с заявленным и **воспроизводится** → `fix`
|
||||||
(не воспроизводится → `research`);
|
(не воспроизводится → `research`);
|
||||||
@@ -535,12 +500,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 …`, затем допиши тело редактором **по схеме своего типа** — шаблон её
|
||||||
уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем
|
уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем
|
||||||
нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый
|
нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый
|
||||||
@@ -554,18 +519,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:doc-canon`, и он зовёт этот сценарий сам на своём шаге.
|
`av-dev:canon`, и он зовёт этот сценарий сам на своём шаге.
|
||||||
|
|
||||||
|
### Пересмотр плана стройки
|
||||||
|
|
||||||
|
Операция стадии `build`, и на доработке её нет: там переоценка идёт порциями и
|
||||||
|
называется грумингом. **Повод один — сменился замысел**, а не «давно не
|
||||||
|
смотрели»: план стройки протухает не по частям, а целиком, потому что порядок в
|
||||||
|
нём — зависимость, и одна изменившаяся посылка переставляет всё, что ниже.
|
||||||
|
|
||||||
|
Порционный разбор здесь вреден, и это не вкус: вынуть пять шагов из середины
|
||||||
|
списка, порядок которого и есть его содержание, — значит получить план, про
|
||||||
|
который никто уже не скажет, почему он такой.
|
||||||
|
|
||||||
|
1. **Назови, что изменилось в замысле.** Одной фразой, и она уедет причиной в
|
||||||
|
каждое движение. Не находится — значит повода нет, и пересмотр не нужен.
|
||||||
|
2. **Прочитай список целиком**, сверху вниз, и по каждой строке ответь одно из
|
||||||
|
трёх: остаётся как есть, переезжает (`move --after` с причиной), уходит
|
||||||
|
(`close --reason`). Дописанное новое встаёт туда, куда велит зависимость, а
|
||||||
|
не в конец.
|
||||||
|
3. **Покажи человеку весь новый список**, а не отдельные решения: план читается
|
||||||
|
только целиком. `AskUserQuestion` с готовым порядком и доводом на каждое
|
||||||
|
движение.
|
||||||
|
4. `check` и доклад: сколько строк тронуто из скольких, что ушло и почему.
|
||||||
|
|
||||||
|
**Границу с грумингом держи твёрдо.** Если хочется пересмотреть план «потому что
|
||||||
|
накопилось» — это не пересмотр, а признак того, что стройка кончилась: беклог
|
||||||
|
перестал быть планом и стал очередью. Проверь `stage`.
|
||||||
|
|
||||||
### Декомпозиция и штурм сырья
|
### Декомпозиция и штурм сырья
|
||||||
|
|
||||||
@@ -586,16 +579,15 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
| Проход | Что смотрит | Над чем работает |
|
| Проход | Что смотрит | Над чем работает |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `task-form` | заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса, связь со строкой «Завершения» цели | только `items/`, **открывает файл цели** |
|
| `task-form` | заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса | только `items/` |
|
||||||
| `task-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индексов; документы проекта — только как словарь |
|
| `task-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индекса; документы проекта — только как словарь |
|
||||||
|
|
||||||
Разделены они не по охвату, а **по глубине**. Язык проверяется по словам и
|
Разделены они не по охвату, а **по глубине**. Язык проверяется по словам и
|
||||||
фразам, поштучно; форма записи требует понять, что задача делает, и открыть
|
фразам, поштучно; форма записи требует понять, что задача делает. Слитый проход
|
||||||
цель, на которую она ссылается. Слитый проход одну половину делает дорогой, а
|
одну половину делает дорогой, а вторую — поверхностной.
|
||||||
вторую — поверхностной.
|
|
||||||
|
|
||||||
Модель у обоих одна, `sonnet`, и это не отменяет разреза. Оба судят по
|
Модель у обоих одна, `sonnet`, и это не отменяет разреза. Оба судят по
|
||||||
**записанному правилу** — семь пунктов формы против правил языка, — а их находка
|
**записанному правилу** — шесть пунктов формы против правил языка, — а их находка
|
||||||
приходит готовой формулировкой, которую читает и отклоняет человек, а не молча
|
приходит готовой формулировкой, которую читает и отклоняет человек, а не молча
|
||||||
реализует оркестратор. Ошибка здесь стоит строки чтения, и платить за неё верхней
|
реализует оркестратор. Ошибка здесь стоит строки чтения, и платить за неё верхней
|
||||||
моделью не за что.
|
моделью не за что.
|
||||||
@@ -670,23 +662,25 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
- **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда:
|
- **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда:
|
||||||
раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога
|
раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога
|
||||||
нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект
|
нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект
|
||||||
действительно новый, а перевод чужой раскладки делает `av-dev:doc-canon`.
|
действительно новый, а перевод чужой раскладки делает `av-dev:canon`.
|
||||||
У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и
|
У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и
|
||||||
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
|
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
|
||||||
- **Версия и настройки живут в `.av-dev.toml` в корне репозитория** — версия
|
- **Версия и настройки живут в `.av-dev.toml` в корне репозитория** — версия
|
||||||
ключом `version`, настройки каталога секцией `[tasks]`: `dir` — где каталог
|
ключом `version`, настройки каталога секцией `[tasks]`: `dir` — где каталог
|
||||||
лежит, плюс **имена** файлов и заголовков, и последние только если отличаются
|
лежит, `stage` — стадия проекта, плюс **имена** файлов и заголовков, и
|
||||||
от умолчания. Неизвестный ключ в секции — код 3 на любой команде, так что
|
последние только если отличаются от умолчания. Неизвестный ключ в секции — код
|
||||||
лишнее слово останавливает работу с задачами целиком.
|
3 на любой команде, так что лишнее слово останавливает работу с задачами
|
||||||
|
целиком.
|
||||||
|
|
||||||
Дом в корне, а не внутри каталога задач, по двум причинам: настройка, лежащая
|
Дом в корне, а не внутри каталога задач, по двум причинам: настройка, лежащая
|
||||||
внутри настраиваемого каталога, не смогла бы сказать, **где он**; и версия
|
внутри настраиваемого каталога, не смогла бы сказать, **где он**; и версия
|
||||||
одна на весь плагин, а корень есть и у проекта без `docs/`. Прежние
|
одна на весь плагин, а корень есть и у проекта без `docs/`. Прежние
|
||||||
`<каталог задач>/.tasks.json` и `docs/.docs.json` не читаются — увидев их,
|
`<каталог задач>/.tasks.json` и `docs/.docs.json` не читаются — увидев их,
|
||||||
скрипт говорит «прежняя раскладка» и зовёт `upgrade`.
|
скрипт говорит «прежняя раскладка» и зовёт `upgrade`.
|
||||||
- **Секции беклога** берутся из заголовков `##` индекса как есть; их количество
|
- **Секции беклога** берутся из заголовков `##` индекса как есть; их названия —
|
||||||
и названия — дело проекта (умолчание `Ядро` / `Инфра`). **В конфиге их нет** —
|
дело проекта (умолчание `План` на стройке, `Ядро` / `Инфра` на доработке), а
|
||||||
второй список разошёлся бы с заголовками молча.
|
количество ограничено стадией: на стройке секция одна. **В конфиге секций
|
||||||
|
нет** — второй список разошёлся бы с заголовками молча.
|
||||||
|
|
||||||
### Вызов из другого плагина
|
### Вызов из другого плагина
|
||||||
|
|
||||||
@@ -723,8 +717,10 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
|||||||
|
|
||||||
- **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным
|
- **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным
|
||||||
предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть,
|
предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть,
|
||||||
под какую цель отнести, какая рамка разведки верна — решение пользователя. Слаг,
|
какая рамка разведки верна, пора ли менять стадию — решение пользователя. Слаг
|
||||||
формулировка, порядок строк в индексе — механика, делаем сами.
|
и формулировка — механика, делаем сами. **Порядок строк механикой не
|
||||||
|
считается** ни на одной стадии: на стройке он зависимость, на доработке
|
||||||
|
приоритет, и оба называет человек.
|
||||||
- **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа;
|
- **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа;
|
||||||
решений больше — веди **несколько итераций** диалога по ≤3, а не один
|
решений больше — веди **несколько итераций** диалога по ≤3, а не один
|
||||||
перегруженный запрос. Между итерациями применяй уже решённое.
|
перегруженный запрос. Между итерациями применяй уже решённое.
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
# Адаптация каталога задач
|
# Адаптация каталога задач
|
||||||
|
|
||||||
Проект, где задачи уже как-то ведутся, и из имеющегося материала **выводится**
|
Проект, где задачи уже как-то ведутся, и из имеющегося материала **выводится**
|
||||||
заполненный каталог задач: цели, задачи, кладбище, индексы. Операция разовая —
|
заполненный каталог задач: задачи, кладбище, индекс. Операция разовая —
|
||||||
после неё проект живёт скиллами `task-track` и `task-groom`.
|
после неё проект живёт скиллами `task-track` и `task-groom`.
|
||||||
|
|
||||||
**Это часть приведения проекта к канону.** Раскладку `docs/` целиком ведёт скилл
|
**Это часть приведения проекта к канону.** Раскладку `docs/` целиком ведёт скилл
|
||||||
`av-dev:doc-canon`; он же зовёт этот сценарий на шаге «каталог задач», потому что
|
`av-dev:canon`; он же зовёт этот сценарий на шаге «каталог задач», потому что
|
||||||
форматом задач владеет `task-track`, а не `doc-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. **Сперва карта, потом файлы.** Человеку показывается, что найдено, в каком
|
||||||
разложилось по целям и **что не разложилось**, — и только после подтверждения
|
порядке разложилось и **что не разложилось**, — и только после подтверждения
|
||||||
пишется хоть один файл. Это то же правило, что у интейка находок ревью:
|
пишется хоть один файл. Это то же правило, что у интейка находок ревью:
|
||||||
массовое заведение записей без подтверждения — самый дорогой отказ, потому
|
массовое заведение записей без подтверждения — самый дорогой отказ, потому
|
||||||
что разгребает его потом переоценка.
|
что разгребает его потом переоценка.
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
tk="$CLAUDE_PLUGIN_ROOT/skills/task-track/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`) — на стройке ровно одна (умолчание `План`), на
|
||||||
здесь, а не подгоняется под умолчание, и становится **заголовками `##`
|
доработке сколько нужно (умолчание `Ядро,Инфра`); если у проекта деление
|
||||||
индекса** — их единственным домом. В `.av-dev.toml` секции не пишутся: там
|
другое по существу, оно называется здесь, а не подгоняется под умолчание, и
|
||||||
версия формата и имена частей, а второй список секций разошёлся бы с
|
становится **заголовками `##` индекса** — их единственным домом. В
|
||||||
заголовками молча.
|
`.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` — результат строкой.
|
||||||
|
|||||||
@@ -50,17 +50,12 @@
|
|||||||
заведённое и уже выкинутое. Нашлось среди живых — дописываем находку в
|
заведённое и уже выкинутое. Нашлось среди живых — дописываем находку в
|
||||||
существующий файл. Нашлось в `REJECTED.md` — это сигнал: причина отказа могла
|
существующий файл. Нашлось в `REJECTED.md` — это сигнал: причина отказа могла
|
||||||
устареть, выноси пользователю, а не заводи молча заново.
|
устареть, выноси пользователю, а не заводи молча заново.
|
||||||
4. **Разложи по целям — там, где цель нужна.** Большинство находок ревью это
|
4. **Проставь типы.** Большинство находок ревью это `fix` и `chore`. Находка,
|
||||||
`fix` и `chore`, и **цель им не требуется**: они служат работоспособности, а
|
оказавшаяся **новой возможностью** (`feature`), — отдельный случай: нашлось
|
||||||
не направлению. Придуманная им цель —
|
поведение, которого никто не заказывал, и решение тут не «завести задачу», а
|
||||||
ровно то враньё, от которого спасает тип.
|
«заказать или убрать». Выноси такую пользователю отдельно от прочих.
|
||||||
|
|
||||||
Цель обязательна у находки, которая оказалась **новой возможностью**
|
|
||||||
(`feature`): нашлось поведение, которого никто не заказывал, и его надо
|
|
||||||
либо заказать целью, либо убрать. Подходящей цели нет — заведи её
|
|
||||||
(`add --type goal --section Направления`) в том же проходе.
|
|
||||||
5. **Покажи карту до создания файлов.** Кластер → задача / сырьё / строка в
|
5. **Покажи карту до создания файлов.** Кластер → задача / сырьё / строка в
|
||||||
пакетный файл / уже заведено / отброшено, и под какую цель — пачкой через
|
пакетный файл / уже заведено / отброшено — пачкой через
|
||||||
`AskUserQuestion`. Это тот же барьер, что и «три кандидата» в интейке из
|
`AskUserQuestion`. Это тот же барьер, что и «три кандидата» в интейке из
|
||||||
диалога: массовое заведение файлов без подтверждения — ровно тот отказ, ради
|
диалога: массовое заведение файлов без подтверждения — ровно тот отказ, ради
|
||||||
которого интейк из ревью и выделен. Дешёвая мелочь по явному согласию может
|
которого интейк из ревью и выделен. Дешёвая мелочь по явному согласию может
|
||||||
@@ -88,8 +83,16 @@
|
|||||||
[скилле груминга](../../task-groom/SKILL.md#приоритет-как-его-расставляют), и
|
[скилле груминга](../../task-groom/SKILL.md#приоритет-как-его-расставляют), и
|
||||||
серьёзность попадает ровно в один из них.
|
серьёзность попадает ровно в один из них.
|
||||||
|
|
||||||
- **тяжёлая находка со свидетельством о сломанном сейчас** → задача под ту цель,
|
**Всё это — про доработку.** На стройке порядок строк значит зависимость, и
|
||||||
которой она угрожает, и **первой строкой секции**: `move <слаг> --first
|
`--first` там означает «ни от чего не зависит», а не «важнее всех»: находка,
|
||||||
|
поднятая наверх, встанет перед собственной зависимостью. Место находке на стройке
|
||||||
|
называет зависимость — `move --after <шаг, после которого её можно делать>`, — а
|
||||||
|
серьёзность идёт **причиной в мете** и разбирается ближайшим пересмотром плана
|
||||||
|
(`task-track`, «Пересмотр плана стройки»). Груминга там нет, и откладывать «до
|
||||||
|
него» некуда.
|
||||||
|
|
||||||
|
- **тяжёлая находка со свидетельством о сломанном сейчас** → задача
|
||||||
|
**первой строкой секции**: `move <слаг> --first
|
||||||
--reason «сломано сейчас: …»`. Это довод «что сломано сейчас» из перечня
|
--reason «сломано сейчас: …»`. Это довод «что сломано сейчас» из перечня
|
||||||
груминга — единственный, который не требует сравнения с соседями по очереди,
|
груминга — единственный, который не требует сравнения с соседями по очереди,
|
||||||
потому что сломанное дорожает само. Позицию всё равно назначает человек, и
|
потому что сломанное дорожает само. Позицию всё равно назначает человек, и
|
||||||
@@ -124,7 +127,7 @@
|
|||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
- Источник (какое ревью/аудит, сколько находок на входе).
|
- Источник (какое ревью/аудит, сколько находок на входе).
|
||||||
- Свёрнуто в задачи: N кластеров из M находок, со слагами, целями и тегом партии.
|
- Свёрнуто в задачи: N кластеров из M находок, со слагами и тегом партии.
|
||||||
- Что не заведено и почему: починено инлайн, уже заведено, стало сырьём, ушло в
|
- Что не заведено и почему: починено инлайн, уже заведено, стало сырьём, ушло в
|
||||||
`REJECTED.md`.
|
`REJECTED.md`.
|
||||||
- Поимённая сверка: находок на входе N, исход есть у N.
|
- Поимённая сверка: находок на входе N, исход есть у N.
|
||||||
|
|||||||
@@ -8,14 +8,19 @@
|
|||||||
|
|
||||||
Задачу можно дробить, только если части удовлетворяют **обоим** условиям:
|
Задачу можно дробить, только если части удовлетворяют **обоим** условиям:
|
||||||
|
|
||||||
1. **Мерджатся независимо.** Часть Б не требует, чтобы часть А была уже влита.
|
1. **Каждая мерджится сама по себе.** Часть, после которой дерево не собирается
|
||||||
Есть порядок «сперва А, потом Б, иначе не собрать» → это не декомпозиция, а
|
или поведение сломано до прихода соседней, — не часть, а половина.
|
||||||
план реализации: шаги остаются **внутри одного файла**.
|
|
||||||
2. **Каждая — самостоятельный шаг.** Часть, осмысленная только в комплекте с
|
2. **Каждая — самостоятельный шаг.** Часть, осмысленная только в комплекте с
|
||||||
другой, — не задача. Проверяй тестом «готова к взятию» (task-format): какую
|
другой, — не задача. Проверяй тестом «готова к взятию» (task-format): свои
|
||||||
строку «Завершения» цели двигает **именно эта часть** и какие у неё
|
критерии приёмки у неё есть или нет.
|
||||||
собственные критерии приёмки. У операционных частей (`fix`, `chore`,
|
|
||||||
`research`) цели может не быть — тогда достаточно собственных критериев.
|
**Порядок между частями законен на стройке и подозрителен на доработке**, и это
|
||||||
|
единственное, что стадия здесь меняет. Беклог стройки **весь** состоит из
|
||||||
|
упорядоченных зависимостью шагов: «сперва А, потом Б» — не повод не дробить, а
|
||||||
|
описание того, как этот список устроен, и части просто встают подряд. На
|
||||||
|
доработке правки независимы, и обнаруженный порядок «иначе не собрать» чаще
|
||||||
|
всего значит, что перед тобой не декомпозиция, а план реализации: шаги остаются
|
||||||
|
**внутри одного файла**.
|
||||||
|
|
||||||
Не проходит хотя бы одно — **не дроби**. Ложная декомпозиция плодит файлы,
|
Не проходит хотя бы одно — **не дроби**. Ложная декомпозиция плодит файлы,
|
||||||
которые нельзя взять поодиночке, и переоценка потом склеивает их обратно.
|
которые нельзя взять поодиночке, и переоценка потом склеивает их обратно.
|
||||||
@@ -45,37 +50,29 @@
|
|||||||
гигиена полей снимает. Шов пользуется меткой как **признаком**, что в задаче
|
гигиена полей снимает. Шов пользуется меткой как **признаком**, что в задаче
|
||||||
две разнородные работы; решение о метке остаётся за конвейером.
|
две разнородные работы; решение о метке остаётся за конвейером.
|
||||||
|
|
||||||
**Цель наследуется.** Все части несут `goal:` родителя: декомпозиция не меняет
|
|
||||||
того, чему работа служит. Если у части цель другая — это признак, что дробили не
|
|
||||||
по той границе, либо что часть вообще из другой работы.
|
|
||||||
|
|
||||||
## Что делать с родителем
|
## Что делать с родителем
|
||||||
|
|
||||||
После разделения родитель **не остаётся** третьей висящей строкой:
|
После разделения родитель **не остаётся** третьей висящей строкой:
|
||||||
|
|
||||||
- части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
|
части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
|
||||||
`REJECTED.md` здесь — не «выкинули», а именно тот след, что переживает запись:
|
`REJECTED.md` здесь — не «выкинули», а именно тот след, что переживает запись:
|
||||||
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
|
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
|
||||||
наследников, а не археологией git;
|
наследников, а не археологией git.
|
||||||
- родитель осмыслен как **возможность**, а не как шаг → это цель, и **строка
|
|
||||||
переезжает**: `edit <slug> --type goal --section <часть роадмапа>` снимает её с
|
|
||||||
`BACKLOG.md` и вставляет в `ROADMAP.md`. Файл в `items/` при этом не двигается —
|
|
||||||
он и есть запись. Части получают `--goal <слаг родителя>`, а закрывать родителя
|
|
||||||
нечем и незачем: он не выкинут, он стал целью.
|
|
||||||
|
|
||||||
**Промежуточного зонтика между целью и задачей нет.** Тип `epic` упразднён:
|
**Зонтика над частями нет никакого.** Тип `epic` упразднён, цель, игравшая его
|
||||||
роль зонтика играет цель, а слишком крупный шаг дробится на шаги помельче под
|
роль после него, — тоже. Если частям нужен общий заголовок, у них общее место в
|
||||||
той же целью. Если частям нужен общий заголовок — значит у них общая
|
списке: они встают подряд, и соседство и есть тот ответ, ради которого заводили
|
||||||
возможность, и её надо назвать целью, а не заводить временный тип.
|
зонтик.
|
||||||
|
|
||||||
## Когда декомпозиция случается посреди работы
|
## Когда декомпозиция случается посреди работы
|
||||||
|
|
||||||
Задача, которая **оказалась крупнее задачи**, распознаётся до того, как под неё
|
Задача, которая **оказалась крупнее задачи**, распознаётся до того, как под неё
|
||||||
заведено предложение об изменении: иначе его придётся выбрасывать. Она выходит
|
заведено предложение об изменении: иначе его придётся выбрасывать. Она выходит
|
||||||
из работы на декомпозицию, а её строка возвращается в беклог с причиной
|
из работы на декомпозицию, а её строка возвращается в беклог с причиной
|
||||||
(`move … --reason "крупнее задачи"`). Части заводятся сразу под той же целью, и
|
(`move … --reason "крупнее задачи"`). **Место в списке частям назначает
|
||||||
**место в очереди им назначает человек**: машина поставит их в конец секции, а
|
человек**: машина поставит их в конец секции, а на стройке место наследуется от
|
||||||
крупная задача редко распадается на что-то менее срочное, чем была сама.
|
родителя (`move --after`), да и на доработке крупная задача редко распадается на
|
||||||
|
что-то менее срочное, чем была сама.
|
||||||
|
|
||||||
## Мозговой штурм сырья
|
## Мозговой штурм сырья
|
||||||
|
|
||||||
@@ -96,9 +93,9 @@ Applicative-штурм («перечисли задачи, следующие и
|
|||||||
applicative.
|
applicative.
|
||||||
2. **Вынеси формы пользователю** через `AskUserQuestion` с компромиссами. Рамку
|
2. **Вынеси формы пользователю** через `AskUserQuestion` с компромиссами. Рамку
|
||||||
выбирает он: это продуктовое решение, не механика.
|
выбирает он: это продуктовое решение, не механика.
|
||||||
3. **Назови цель.** Выбранная форма служит цели — существующей или новой. Идея,
|
3. **Назови пользу.** Выбранная форма отвечает на «что станет наблюдаемо иначе».
|
||||||
для которой цель не находится, скорее всего уезжает в `REJECTED.md`, а не
|
Идея, для которой такого ответа не находится, скорее всего уезжает в
|
||||||
заводится задачей.
|
`REJECTED.md`, а не заводится задачей.
|
||||||
4. **Только выбранную форму** дроби по тесту декомпозиции выше и проставь
|
4. **Только выбранную форму** дроби по тесту декомпозиции выше и проставь
|
||||||
критерии приёмки: без них наследники останутся идеями под другим именем.
|
критерии приёмки: без них наследники останутся идеями под другим именем.
|
||||||
|
|
||||||
@@ -109,8 +106,8 @@ Applicative-штурм («перечисли задачи, следующие и
|
|||||||
## Доклад
|
## Доклад
|
||||||
|
|
||||||
- Идея/задача на входе, выбранная рамка (для штурма), задачи-наследники со
|
- Идея/задача на входе, выбранная рамка (для штурма), задачи-наследники со
|
||||||
слагами, целями и секциями.
|
слагами, секциями и местом в списке.
|
||||||
- Судьба родителя: удалён / стал целью / выкинут с причиной.
|
- Судьба родителя: удалён / выкинут с причиной.
|
||||||
- `tasks.py check` после правок.
|
- `tasks.py check` после правок.
|
||||||
- Границы покрытия: какие постановки рассмотрены и какие сознательно отброшены —
|
- Границы покрытия: какие постановки рассмотрены и какие сознательно отброшены —
|
||||||
чтобы штурм не пришлось повторять с нуля.
|
чтобы штурм не пришлось повторять с нуля.
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
|
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
|
||||||
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
||||||
| Поле места | **Категория** — полка домена беклога |
|
| Поле места | **Категория** — полка домена беклога |
|
||||||
| Цель (`goal:<слаг>`) | нет: цель — это возможность, а здесь её не появляется |
|
|
||||||
| Индекс | `BACKLOG.md` |
|
| Индекс | `BACKLOG.md` |
|
||||||
| Берётся в работу | да |
|
| Берётся в работу | да |
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@
|
|||||||
## Алгоритм
|
## Алгоритм
|
||||||
|
|
||||||
1. **Проверить, что поведение не меняется.** Меняется — это `feature` или `fix`,
|
1. **Проверить, что поведение не меняется.** Меняется — это `feature` или `fix`,
|
||||||
и у неё другие требования (цель, воспроизведение).
|
и у последнего другие требования (воспроизведение).
|
||||||
2. **Назвать, что перестанет мешать** — одной фразой, адресуясь разработчику.
|
2. **Назвать, что перестанет мешать** — одной фразой, адресуясь разработчику.
|
||||||
«Прибраться в модуле X» — не ответ: непонятно, что изменится.
|
«Прибраться в модуле X» — не ответ: непонятно, что изменится.
|
||||||
3. **Назвать границы** в `Затрагивает`. У обслуживания они часто не в коде:
|
3. **Назвать границы** в `Затрагивает`. У обслуживания они часто не в коде:
|
||||||
@@ -55,9 +54,6 @@
|
|||||||
5. **Проверить, что это не «заодно».** Обслуживание любит склеиваться в пачку
|
5. **Проверить, что это не «заодно».** Обслуживание любит склеиваться в пачку
|
||||||
(«обновить зависимости и переписать сборку и убрать мёртвый код»). Не
|
(«обновить зависимости и переписать сборку и убрать мёртвый код»). Не
|
||||||
мерджится порознь — это несколько задач ([split.md](split.md)).
|
мерджится порознь — это несколько задач ([split.md](split.md)).
|
||||||
6. **Цель не проставлять.** `chore` служит работоспособности, а не направлению.
|
|
||||||
Работа по сопровождению проекта при этом видна в роадмапе — секцией
|
|
||||||
`Сопровождение`, но целью не становится.
|
|
||||||
|
|
||||||
## Кто такую задачу решает
|
## Кто такую задачу решает
|
||||||
|
|
||||||
|
|||||||
@@ -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`, «Что механизировано, а что нет»).
|
||||||
|
|
||||||
|
|||||||
@@ -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` с пометкой «проскочил / пойман
|
|
||||||
ревью». Проскочившие — проверочный набор для калибровки конвейера; пойманные с
|
ревью». Проскочившие — проверочный набор для калибровки конвейера; пойманные с
|
||||||
оракулом — лучшая опора для прохода ревью: проектные, воспроизводимые,
|
оракулом — лучшая опора для прохода ревью: проектные, воспроизводимые,
|
||||||
однажды оказавшиеся правдой.
|
однажды оказавшиеся правдой.
|
||||||
|
|||||||
@@ -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]` упразднён, и цель, ставшая
|
||||||
сама цель.
|
зонтиком после него, упразднена тоже.
|
||||||
|
|
||||||
Тест применяется при заведении и при переоценке. К старым задачам, которых
|
Тест применяется при заведении и при переоценке. К старым задачам, которых
|
||||||
операция не касается, задним числом не применяется — беклог не переоформляют
|
операция не касается, задним числом не применяется — беклог не переоформляют
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
# 🎯 `goal` — возможность приложения
|
|
||||||
|
|
||||||
Цель отвечает на **«что приложение будет уметь»**. Не область работ и не имя
|
|
||||||
подсистемы: не «Работа со слиянием», а «Исход слияния не зависит от порядка
|
|
||||||
доставки». Свойство поведения — тоже возможность.
|
|
||||||
|
|
||||||
Общая форма записи (мета, слаг, строка индекса) — [task-format.md](task-format.md).
|
|
||||||
Здесь только то, что у этого типа своё.
|
|
||||||
|
|
||||||
## Схема
|
|
||||||
|
|
||||||
| | |
|
|
||||||
| --- | --- |
|
|
||||||
| Заголовок отвечает на | что приложение будет уметь |
|
|
||||||
| Обязательные разделы | `Завершение` |
|
|
||||||
| Допустимые сверх того | — |
|
|
||||||
| Поле места | **Секция** — часть роадмапа |
|
|
||||||
| Цель (`goal:<слаг>`) | запрещена: цель и есть цель |
|
|
||||||
| Индекс | `ROADMAP.md`, и никогда `BACKLOG.md` |
|
|
||||||
| Берётся в работу | нет — берутся её задачи |
|
|
||||||
|
|
||||||
Поле места у цели называется **«Секция»**, а не «Категория», и это не разнобой:
|
|
||||||
у задачи оно называет полку домена, на которой она лежит, а у цели
|
|
||||||
— часть роадмапа, то есть состояние очереди. Одно имя на два смысла их и
|
|
||||||
смешивало.
|
|
||||||
|
|
||||||
## «Завершение» — списком, а не абзацем
|
|
||||||
|
|
||||||
Это признаки того, что приложение **уже умеет**, и на строки этого раздела
|
|
||||||
ссылаются задачи цели: «двигает пункт 2 «Завершения» — накопительная метрика
|
|
||||||
перестаёт уменьшаться». Абзацем такая ссылка не берётся, поэтому список.
|
|
||||||
|
|
||||||
Отсюда же читается обратное и более полезное: **строка «Завершения», к которой
|
|
||||||
не относится ни одна задача, — незакрытая часть возможности**. Достаточность
|
|
||||||
набора задач видна из самой цели, а не из чьей-то памяти.
|
|
||||||
|
|
||||||
## Алгоритм
|
|
||||||
|
|
||||||
1. **Проверить, что это возможность, а не работа.** Работа, которой держат
|
|
||||||
проект, на вопрос «что приложение будет уметь» не отвечает; состав перечислен
|
|
||||||
[в словаре сопровождения](../../../shared/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 груминга
|
|
||||||
(скилл `task-groom`, «что перестало быть важным»). Отменять на ходу,
|
|
||||||
между делом, — верный способ закрыть скопом то, что стоило перевесить.
|
|
||||||
|
|
||||||
## Что видит машина, а что человек
|
|
||||||
|
|
||||||
`check` считает цели, различает разобранные и пустые, ставит `decomposed`,
|
|
||||||
запрещает закрыть цель с живыми задачами и держит `Секцию` в согласии с
|
|
||||||
заголовком роадмапа. **Годность формулировки — не машине**: «возможность это или
|
|
||||||
область работ» решает [агент вычитки](../SKILL.md#вычитка-два-прохода-а-не-один).
|
|
||||||
|
|
||||||
Достигнутая цель **не исчезает**: «что приложение умеет» — половина вопроса, ради
|
|
||||||
которого роадмап открывают. Вторым домом поведения роадмап при этом не
|
|
||||||
становится: нормативное поведение живёт в `openspec/specs/`, роадмап отвечает,
|
|
||||||
**когда и в каком порядке** оно появилось.
|
|
||||||
@@ -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 скилла).
|
||||||
Место сырья **из него изъято**: оно производно от типа и заполненности, а не от
|
Место сырья **из него изъято**: оно производно от типа и заполненности, а не от
|
||||||
чьего-то решения, и потому его проверяет и чинит машина. Приоритетом оно не
|
чьего-то решения, и потому его проверяет и чинит машина. Приоритетом оно не
|
||||||
становится: сырьё не берут вовсе, и место в конце говорит именно это.
|
становится: сырьё не берут вовсе, и место в конце говорит именно это.
|
||||||
|
|||||||
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 без оговорки). Вкусовых
|
||||||
|
находок не было ни одной: порог держится.
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# 25. Секция `Сопровождение` и общий словарь трёх мест (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
`Разработка` — имя, которое называло слишком много: роадмап **весь** про
|
||||||
|
разработку, и секция с таким именем не отличалась от остальных ничем. Предложено
|
||||||
|
`Сопровождение` (англ. `Operations`).
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р101. Секция называется `Сопровождение` / `Operations`, и её смысл расширен.**
|
||||||
|
Было «инструмент и процесс», стало «чем держат проект: инструмент, процесс,
|
||||||
|
эксплуатация». Расширение не косметическое: английское `Operations` при узком
|
||||||
|
смысле обещало бы эксплуатацию, а внутри лежал бы линтер. Либо слово, либо смысл
|
||||||
|
— сошлись на смысле, потому что метрики, логи, инфраструктура и выкладка в эту
|
||||||
|
секцию просятся и так.
|
||||||
|
|
||||||
|
**Р102. Версия канона не менялась, и это законно.** ~~Ни один проект на каноне 3
|
||||||
|
не стоит: healthlog и jellybit держат канон 2, повышение только предстоит.~~
|
||||||
|
|
||||||
|
**Отменено в тот же день ([тема
|
||||||
|
26](26-canon-4-retroactive-edit-cancelled.md)).** Посылка была ложной: healthlog
|
||||||
|
уже переехал на канон 3, и правка записи версии 3 задним числом переписывала то,
|
||||||
|
по чему он ехал. Правило осталось верным, применение — нет: черновиком запись
|
||||||
|
версии является ровно до того, как **первый** проект по ней поехал.
|
||||||
|
|
||||||
|
**Р103. Сопровождение и эксплуатация — целое и часть, и словарь у трёх мест
|
||||||
|
общий.** Тема живёт в трёх документах, и раньше каждое место говорило своим
|
||||||
|
словом. Теперь: сопровождение — всё, чем держат проект (инструмент, процесс,
|
||||||
|
выкладка, метрики, логи, инфраструктура, дежурство); эксплуатация — его часть,
|
||||||
|
работа системы на проде.
|
||||||
|
|
||||||
|
| Место | Уровень | Что там |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `ROADMAP.md`, секция `Сопровождение` | план | работы, которые собираемся делать |
|
||||||
|
| `architecture.md`, раздел «Эксплуатация» | состояние | как устроено сейчас |
|
||||||
|
| эксплуатационный проход ревью | оптика | «это упало через неделю на проде» |
|
||||||
|
|
||||||
|
**Сливать три места в одно слово было бы ошибкой**: они отвечают на разные
|
||||||
|
вопросы — план, состояние, проверка. Синхронизирован **словарь**, а не границы;
|
||||||
|
дом словаря — `canon.md`.
|
||||||
|
|
||||||
|
Слово **«поддержка» запрещено вовсе**: в нём слышится помощь пользователю, а это
|
||||||
|
третья работа, к этим двум не относящаяся.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С96. Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
||||||
|
сообщает о своём состоянии» — возможность (наблюдает пользователь сервиса);
|
||||||
|
«дежурный видит состояние на одном экране» — сопровождение (наблюдаем мы). Одни
|
||||||
|
и те же метрики попадают в разные секции роадмапа, и это верно.
|
||||||
|
|
||||||
|
**С97. `check --fix` чужую секцию не переименовывает — и правильно.** На
|
||||||
|
переименовании `Разработка` → `Сопровождение` проверка назвала секцию роадмапа
|
||||||
|
чужой и остановилась: регистр она правит сама, смысл — нет. Ровно то поведение,
|
||||||
|
которое нужно проекту при повышении канона.
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# 26. Канон 4: правка задним числом отменена (2026-08-04)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Секцию `Разработка` переименовали в `Сопровождение` без повышения версии канона
|
||||||
|
— на посылке «ни один проект на каноне 3 не стоит» (тема 25,
|
||||||
|
[Р102](25-maintenance-section-shared-vocab.md)). Посылка оказалась ложной:
|
||||||
|
healthlog уже переехал, `docs/.pm.json` держит `"canon": 3`, а роадмап — секцию
|
||||||
|
`Разработка` с прописной. Правка записи версии 3 переписывала то, по чему он
|
||||||
|
ехал.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р104. Запись версии — черновик ровно до первого переехавшего проекта.** После
|
||||||
|
этого она **история**, и любое изменение канона заводит новую версию, даже если
|
||||||
|
меняется одно слово. Проверять это дёшево: `grep '"canon"' */docs/.pm.json` по
|
||||||
|
живым проектам. Дорого — обратное: проект, повышенный по тексту, которого больше
|
||||||
|
не существует, невоспроизводим.
|
||||||
|
|
||||||
|
Запись версии 3 восстановлена дословно (`Разработка` | `Tooling`), переименование
|
||||||
|
уехало в версию 4. jellybit, стоящий на каноне 2, прочтёт обе записи подряд и
|
||||||
|
заведёт `Разработка`, чтобы через шаг переименовать; в шаг версии 3 добавлена
|
||||||
|
оговорка «едешь сразу на 4 — заводи `Готово` последней и не переставляй дважды».
|
||||||
|
Лишний шаг — плата за честную историю, и она мала.
|
||||||
|
|
||||||
|
**Р105. `Готово` переехало вниз, и порядок секций стал каноническим.**
|
||||||
|
Достигнутое **копится**: через год этой секции больше, чем всех остальных
|
||||||
|
вместе, — и стоя первой она отодвигает за экран ровно то, ради чего роадмап
|
||||||
|
открывают чаще всего. Порядок теперь проверяется (`roadmap_lint`) и правится
|
||||||
|
(`check --fix` переставляет секции вместе с содержимым): без проверки порядок
|
||||||
|
разъедется молча, а переставлять секцию с десятком строк руками — работа, на
|
||||||
|
которой ошибаются.
|
||||||
|
|
||||||
|
**Р106. Индексы позиций считаются из самого кортежа.** `ACHIEVED` был `0` и стал
|
||||||
|
`3`; хардкод индексов пережил бы перестановку молча и сломал бы `close`. Теперь
|
||||||
|
`PLANNED, DIRECTIONS, OPERATIONS, ACHIEVED = range(len(ROADMAP_SECTIONS))` —
|
||||||
|
переставили секцию, индексы переехали сами.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С98. Отбивка нужна и перед заголовком.** Перестановка блоков ставит два
|
||||||
|
заголовка вплотную — `spaced_sections` правил только строку после. Дефект
|
||||||
|
нашёлся сразу же, на первой перестановке демо-набора: класс правки, существующий
|
||||||
|
только потому, что появилась другая правка.
|
||||||
|
|
||||||
|
**С99. `check --fix` переставляет, но не переименовывает.** Чужую секцию он
|
||||||
|
оставляет ошибкой, и на переименовании `Разработка` → `Сопровождение`
|
||||||
|
останавливается: имя — решение человека, порядок — механика. Тот же разрез, что
|
||||||
|
между регистром (правит) и составом (не трогает).
|
||||||
|
|
||||||
|
**С100. Версия канона отделяет состояния проектов, а не редакции текста** — и
|
||||||
|
ровно поэтому её нельзя не поднять, когда состояние хоть одного проекта уже
|
||||||
|
зафиксировано.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# 27. Тип записи стал единственной осью и задаёт схему (2026-08-05)
|
||||||
|
|
||||||
|
Заметка просила «каждый тип задач сделать своей сущностью»: эмодзи на тип, тип
|
||||||
|
первым полем меты, категория вместо секции, описание типа с обязательными
|
||||||
|
разделами и алгоритмом, идеи в конец. Разбор показал, что первый шаг обязан быть
|
||||||
|
другим — не добавить типу свойств, а **сократить число осей**.
|
||||||
|
|
||||||
|
**Р107. Осей было две, и ортогональность была фальшивой.** Тип записи
|
||||||
|
(`goal`/`idea`/`task`) и род работы (`kind:<род>` тегом) давали двенадцать
|
||||||
|
клеток произведения, из которых законны шесть: у цели род запрещён, у задачи
|
||||||
|
обязателен, у идеи пуст и на практике не ставится. Плюс «алгоритм работы над
|
||||||
|
записью такого типа» крепится не к `task`, а к `fix` и `research` — то есть к
|
||||||
|
роду. Ось, к которой пишется алгоритм, и была настоящим типом. Оси схлопнуты в
|
||||||
|
одну из пяти значений: `goal` | `feature` | `fix` | `chore` | `research`.
|
||||||
|
|
||||||
|
**Р108. Тип `idea` упразднён: состояние не может быть типом.** Он значил не род
|
||||||
|
работы, а незаполненность — «первый, второй или третий вопрос теста готовности
|
||||||
|
не отвечается». Состояние меняется по мере того, как запись дописывают, а тип
|
||||||
|
меняют командой, и на этом расхождении `idea` и жила: её приходилось «понижать»
|
||||||
|
и «повышать» вручную. Теперь состояние выводится из заполненности — **`research`
|
||||||
|
без раздела «Вопрос» это сырьё**, — и различие держит та же машина, что и всё
|
||||||
|
остальное.
|
||||||
|
|
||||||
|
Цена решения названа сразу: `research` теперь вбирает и замер реальности, и
|
||||||
|
сырую функцию («Подсказка следующего хода»). Обосновано это тем, что у обоих
|
||||||
|
**один исход — записанный ответ, а не изменение системы**, и одна приёмка. Имя
|
||||||
|
`rnd` из заметки отклонено в пользу `research`: аббревиатура читается как
|
||||||
|
`random` и не расшифровывается тому, кто вернётся к беклогу через квартал, а
|
||||||
|
`research` уже стоял в файлах живых проектов — миграция тронула только бывшие
|
||||||
|
идеи.
|
||||||
|
|
||||||
|
**Р109. Дом типа — поле меты, эмодзи производна.** Прежнее правило «отдельного
|
||||||
|
поля типа нет: два места для одного факта разъезжаются» отменено не потому, что
|
||||||
|
разонравилось, а потому, что его аргумент был против **префикса плюс поля**. При
|
||||||
|
переносе дома в мету дом остаётся один; из индекса тип при этом пропадал бы —
|
||||||
|
там, где принимают решение «брать или не брать», — и это чинит эмодзи. Она стоит
|
||||||
|
в H1, а не в строке индекса, чтобы инвариант «заголовок в индексе дословно»
|
||||||
|
остался нетронутым: одна проверка вместо двух.
|
||||||
|
|
||||||
|
**Р110. Поле места назвали по типу, а не одним словом на всех.** «Категория»
|
||||||
|
вместо «Секции» — просьба заметки, но одинаковое переименование закрепило бы
|
||||||
|
смешение: у задачи поле называет полку домена, в которую она вернётся из
|
||||||
|
спринта, у цели — часть роадмапа, то есть состояние очереди. Разные имена
|
||||||
|
(`Категория` / `Секция`) выбраны именно потому, что **какое поле обязательно,
|
||||||
|
решает тип** — то самое, ради чего затевалась вся правка.
|
||||||
|
|
||||||
|
**Р111. Два новых обязательных раздела появились из уже записанных правил,
|
||||||
|
которые нечем было проверить.** «Не воспроизводится — это `research`, а не
|
||||||
|
`fix`» стояло в каноне и не проверялось: раздел `Воспроизведение` делает его
|
||||||
|
проверяемым. Приёмка разведки — «записанный ответ, а не изменённый код» — тоже
|
||||||
|
стояла, но `sprint take` требовал от `research` два-пять критериев с оракулами,
|
||||||
|
и они писались ради проверки; вместо них `Вопрос` и `Куда ляжет ответ`.
|
||||||
|
|
||||||
|
**Р112. Сортировка «по важности» отклонена, «сырьё в конец» взято.** Первая
|
||||||
|
требует, чтобы кто-то важность поддерживал, — это ровно тот приоритет, от
|
||||||
|
которого правило 4 отказалось сознательно. Вторая **выводится из типа и
|
||||||
|
заполненности**, а не назначается человеком, и потому проверяется машиной и
|
||||||
|
приоритетом не становится. Разрез прошёл по признаку «кто источник порядка», а
|
||||||
|
не по признаку «полезно ли».
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С101. Правило можно отменять его собственным аргументом.** «Отдельного поля
|
||||||
|
типа нет» держалось на «два места для одного факта»; перенос дома оставил одно
|
||||||
|
место, и правило перестало применяться. Проверять надо не запись правила, а то,
|
||||||
|
выполняется ли ещё его посылка.
|
||||||
|
|
||||||
|
**С102. Схема, шаблон и проверка растут из одной таблицы.** `TYPE_SCHEMA` кормит
|
||||||
|
и `body_template`, и `schema_verdict`: иначе `add` кладёт то, на чём `sprint
|
||||||
|
take` потом откажет. Тот же приём, что нормализатор `spaced_sections` для
|
||||||
|
оформления индексов.
|
||||||
|
|
||||||
|
**С103. `--fix` не угадывает того, чего нет.** Тип переносится из тега `kind:` и
|
||||||
|
префикса `[goal]`/`[idea]` детерминированно, но записи, заведённые до появления
|
||||||
|
рода работы, не несут ни того ни другого — `feature` от `chore` машина не
|
||||||
|
отличает. Они уходят в `НЕОДНОЗНАЧНО` поимённо, а не получают значение по
|
||||||
|
умолчанию, которое врало бы ровно там, где по нему принимают решение.
|
||||||
|
|
||||||
|
**С104. Мигрирующие шаги обязаны читать отложенный текст, а не диск.** Шагов,
|
||||||
|
правящих мету, стало пять, и второй, перечитавший файл, стёр бы правку первого.
|
||||||
|
Общий `stage()` поверх `files` снял целый класс отказов, который до этого
|
||||||
|
держался на том, что шагов было мало.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# 28. Слаг подкреплён проверкой, обещанный судья заведён (2026-08-05)
|
||||||
|
|
||||||
|
Два пункта заметок, оба про одно: правило было записано и никем не исполнялось.
|
||||||
|
|
||||||
|
**Р113. Правило про английские слаги существовало и не проверялось ничем.**
|
||||||
|
`canon.md` говорил «слаги файлов, capability и задач — английские, kebab-case»
|
||||||
|
одной строкой в хвосте раскладки; `docs.py` имён файлов не смотрел вовсе. Итог
|
||||||
|
предсказуем и нашёлся в самом плагине: единственный пример ADR в скилле `docs`
|
||||||
|
назывался `ADR-2026-08-03-ochered-tablicej`. Раскладка канона при этом
|
||||||
|
приглашала к нарушению — в схеме стояли плейсхолдеры `<тема>.md`, то есть слово
|
||||||
|
«тема» по-русски там, где надо было писать `<slug>`.
|
||||||
|
|
||||||
|
Разрез проверки — по тому, что машина знает точно: кириллица в имени и не-kebab-case
|
||||||
|
**жёстко**, форма `ADR-ГГГГ-ММ-ДД-slug.md` жёстко, транслит **эвристикой**, то есть
|
||||||
|
замечанием. Набор маркеров транслита подобран так, чтобы **ложных срабатываний не
|
||||||
|
было вовсе**: выброшены `ost` (ловит `post`, `cost`), `sch` (`schema`), `ya`
|
||||||
|
(`yaml`), `nost` (`nostalgia`), хвост `ii` (`radii`). Цена названа: `sostoyanie-partii`
|
||||||
|
проходит мимо. Правило, краснеющее на правде, приучает пролистывать весь блок —
|
||||||
|
это дороже пропуска.
|
||||||
|
|
||||||
|
**Р114. Канон три версии обещал судью, которого не было.** В `canon.md` есть
|
||||||
|
таблица «Что проверяет машина, а что человек», и её правая колонка — смысловой
|
||||||
|
дубль, поведение в `architecture.md`, протухший факт, достаточность честной
|
||||||
|
строки — описывала работу, которую никто не делал: скилл `canon` предлагал
|
||||||
|
агенту судить об этом самому, то есть проверять то, что он же и писал. Заведены
|
||||||
|
`doc-consistency` и `doc-code-drift`, а колонка получила третий столбец с именем
|
||||||
|
судьи: обещание без адресата и есть тот способ, которым правило перестаёт
|
||||||
|
исполняться.
|
||||||
|
|
||||||
|
**Р115. Агентов двое, разрез по глубине, а не по охвату.** Тот же довод, что
|
||||||
|
развёл `task-form` и `doc-wording`: сверка текста с текстом дёшева и зовётся на
|
||||||
|
каждом синке документации, сверка с кодом требует читать репозиторий и зовётся
|
||||||
|
раз в спринт. Слитый агент делает дешёвую половину редкой либо дорогую —
|
||||||
|
поверхностной.
|
||||||
|
|
||||||
|
**Р116. Перечень фактов, сверяемых с кодом, закрыт.** Имя основной ветки,
|
||||||
|
команды, пути, зависимости поимённо, настройки с числовым значением, единые
|
||||||
|
точки проекта, capability, проверяемые инварианты. «Сверить архитектуру с кодом»
|
||||||
|
— задача без дна, и агент, которому её поставили, выдаёт правдоподобную труху
|
||||||
|
вместо находок. Отсюда и форма доклада `doc-code-drift`: он начинается
|
||||||
|
**таблицей проверенного**, а не находками, — по ней видно, чего он не смотрел.
|
||||||
|
|
||||||
|
**Р117. Карта домов уехала в устав агента помеченной копией.** Устав ссылался на
|
||||||
|
файл плагина, а агент работает в репозитории проекта, где плагина может не быть.
|
||||||
|
Копия дословная, под маркерами `дом`/`копия`, и `copies.py` теперь её сторожит —
|
||||||
|
механизм для этого в репозитории уже был.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С105. Записанное правило без проверки не исполняется даже автором.** Слаг ADR
|
||||||
|
нарушен в единственном примере, который плагин показывает как образец. Тот же
|
||||||
|
класс, что «прозаический триггер ADR дал 6 записей на 43 изменения»: умолчание
|
||||||
|
становится отличимым только когда его проверяют.
|
||||||
|
|
||||||
|
**С106. Плейсхолдер — часть правила.** `<тема>.md` в схеме раскладки перевешивал
|
||||||
|
строку правила, стоявшую двумя абзацами ниже: образец читают вместо текста.
|
||||||
|
|
||||||
|
**С107. Эвристика настраивается по ложным срабатываниям, а не по полноте.** Ноль
|
||||||
|
ложных при одном пропуске лучше, чем наоборот: пропуск стоит одной ненайденной
|
||||||
|
находки, ложное срабатывание — доверия ко всему блоку.
|
||||||
|
|
||||||
|
**С108. Докстрока разошлась с кодом ровно там, где её читают.** `copies.py`
|
||||||
|
показывал закрывающие маркеры как `<!-- /дом -->`, а требовал `<!-- /дом: <id>
|
||||||
|
-->`; нашлось это первой же попыткой ими воспользоваться. Пример в докстроке —
|
||||||
|
тот же образец, что плейсхолдер в схеме.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# 29. Обкатка `doc-consistency` на самом dev-skills (2026-08-05)
|
||||||
|
|
||||||
|
Первый прогон агента — по репозиторию, который его же и содержит. Два прохода
|
||||||
|
(av-dev-pm; пайплайн плюс верхний уровень), 17 находок, все подтверждены по
|
||||||
|
файлам.
|
||||||
|
|
||||||
|
**Р118. Агент нашёл ровно тот класс, ради которого заводился, и в свежей
|
||||||
|
работе.** Пять находок — остатки прежней модели типов в файлах, которые я не
|
||||||
|
дошёл поправить двумя коммитами раньше: `adopt.md` держал имена секций **канона
|
||||||
|
2**, `from-review.md` и `TODO.md` — упразднённый `[idea]`, `task-batch` в другом
|
||||||
|
плагине — «задачи-идеи». Правку модели я вёл от документов, которые менял, а не
|
||||||
|
от документов, которые на них ссылаются, — и обратный обход не сделал ни разу.
|
||||||
|
|
||||||
|
**Р119. Самая дорогая находка была моей и свежей.** Таблица типов в `canon.md`
|
||||||
|
объявляла цель у `fix` запрещённой, а `tasks/SKILL.md` и `task-fix.md` —
|
||||||
|
необязательной; код на стороне вторых. Копия разошлась с домом **за один день**
|
||||||
|
— я написал обе половины в одном коммите. Это и есть цена второго дома в чистом
|
||||||
|
виде: не «когда-нибудь разойдётся», а «разошлось прежде, чем высохли чернила».
|
||||||
|
|
||||||
|
Исход не «поправить значение», а **убрать причину**: `canon.md` дважды объявлял,
|
||||||
|
что фиксирует только словарь типов, — значит колонкам «разделы» и «цель» в нём
|
||||||
|
не место. Осталась таблица из двух колонок и ссылка на дом схемы.
|
||||||
|
|
||||||
|
**Р120. Копии перечня «чем держат проект» разъехались втроём.** `canon.md`,
|
||||||
|
`tasks/SKILL.md` и `task-goal.md` пересказывали его своими словами: «метрики и
|
||||||
|
логи» против «мониторинга», «проверки» есть в двух из трёх. При этом
|
||||||
|
`tasks/SKILL.md` **ссылался на дом рядом с собственным пересказом** — ссылка не
|
||||||
|
мешает копии разойтись, если копия всё равно стоит.
|
||||||
|
|
||||||
|
**Р121. Находка про коммиты снята как неверная, и это дефект самого агента.** Он
|
||||||
|
прочитал `av-dev-git/skills/commit/SKILL.md` («без `Co-Authored-By`») как
|
||||||
|
описание практики этого репозитория и предъявил 38 коммитов с трейлером. Но
|
||||||
|
dev-skills — **маркетплейс плагинов**: скилл коммита здесь продукт, уезжающий в
|
||||||
|
чужие проекты, а не правило, которому подчиняется сам репозиторий. Устав агента
|
||||||
|
не различает «документ описывает этот репозиторий» и «документ описывает то, что
|
||||||
|
репозиторий производит».
|
||||||
|
|
||||||
|
**Р122. Счётчики в документах отменены как класс.** `REMAINING.md` держал «после
|
||||||
|
разбора двенадцати тем и 16 коммитов» (стало 28 и 52) и «три неизмеренных
|
||||||
|
изменения подряд» (стало больше). Оба числа обязан двигать человек, и оба
|
||||||
|
отстали молча. Заменены на формулировки, которые не надо поддерживать, и в шапку
|
||||||
|
записана причина.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С109. Правка модели идёт по обратным ссылкам, а не по изменённым файлам.**
|
||||||
|
Меняешь дом — обойди тех, кто на него ссылается: `grep` по упразднённому слову
|
||||||
|
дал бы все пять остатков за минуту. Это дешевле любого агента и должно идти до
|
||||||
|
него.
|
||||||
|
|
||||||
|
**С110. Ссылка на дом не отменяет копию, стоящую рядом.** Проверять надо не
|
||||||
|
«есть ли ссылка», а «есть ли пересказ»; `tasks/SKILL.md` имел и то и другое.
|
||||||
|
|
||||||
|
**С111. Копия расходится с домом в пределах одного коммита.** Прежняя оценка
|
||||||
|
(«разойдётся на первой правке») занижена: расхождение возникает при написании,
|
||||||
|
если оба места пишет один проход.
|
||||||
|
|
||||||
|
**С112. Агент, читающий репозиторий-продукт, обязан различать «про нас» и «про
|
||||||
|
то, что мы производим».** Иначе он предъявляет продукту практику его
|
||||||
|
потребителя. Устав `doc-consistency` этого различения не содержит — остаток
|
||||||
|
записан в REMAINING.
|
||||||
|
|
||||||
|
**С113. Число в документе — обязанность, которую никто не берёт.** Счётчик тем,
|
||||||
|
коммитов, правок протухает молча; формулировка без числа дешевле его
|
||||||
|
сопровождения.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# 30. `av-dev-backlog` удалён (2026-08-05)
|
||||||
|
|
||||||
|
Плагин был помечен устаревшим решением [Р17](04-plugin-boundaries.md) и жил до
|
||||||
|
перевода jellybit. Удалён раньше этого срока.
|
||||||
|
|
||||||
|
**Р123. Замороженный плагин стоит дороже, чем кажется.** Он не менялся, но
|
||||||
|
платил собой в каждой проверке репозитория: `exclude` в `pyproject.toml`,
|
||||||
|
`SKIP_DIRS` в `copies.py`, два абзаца README, оговорка в описании маркетплейса,
|
||||||
|
чтобы не ловить триггер «добавь задачу в беклог». Пять исключений ради кода,
|
||||||
|
который никто не читает, — и каждое надо было объяснять всякий раз, когда
|
||||||
|
кто-нибудь спрашивал, почему проверка обходит каталог.
|
||||||
|
|
||||||
|
**Р124. Понимание старой раскладки уехало из плагина раньше самого плагина.**
|
||||||
|
`docs/backlog/` читает не `backlog.py`, а `av-dev-pm:tasks` — `adopt.md` и
|
||||||
|
адаптер в `tasks.py` держат ту же раскладку как **вход миграции**. Плагин
|
||||||
|
перестал быть единственным, кто её знает, ещё когда писался `adopt`; условие
|
||||||
|
«живёт до перевода последнего проекта» с тех пор охраняло пустоту.
|
||||||
|
|
||||||
|
**Р125. Опасение про порядок снятия не подтвердилось.** Удаление опередило
|
||||||
|
снятие: на jellybit плагин оставался включённым, когда записи в маркетплейсе уже
|
||||||
|
не было, и ожидалась ручная чистка `enabledPlugins` и `installed_plugins.json`.
|
||||||
|
`claude plugin uninstall` отработал штатно — он идёт **по реестру, а не по
|
||||||
|
манифесту маркетплейса**, и отсутствие записи там ему безразлично.
|
||||||
|
Предупреждение из README снято, вместо него записан проверенный факт.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С114. Устаревшее удаляют, а не замораживают.** Заморозка выглядит бесплатной,
|
||||||
|
но растекается исключениями по конфигам и требует объяснения в каждом месте,
|
||||||
|
куда попала. Если удалять пока рано — назвать условие и срок; условие без срока
|
||||||
|
переживает свою причину.
|
||||||
|
|
||||||
|
**С115. Условие «живёт до X» проверяют на живость, а не на X.** Здесь X (перевод
|
||||||
|
jellybit) не наступил, но причина условия отпала раньше: знание раскладки
|
||||||
|
переехало в `adopt`. Перепроверять надо основание, иначе условие держит само
|
||||||
|
себя.
|
||||||
|
|
||||||
|
**С116. Порядок снятия и удаления из маркетплейса свободный.** `uninstall` живёт
|
||||||
|
реестром, манифест ему не нужен. Правило записано после проверки, а не из
|
||||||
|
осторожности, — и осторожность здесь стоила бы лишнего абзаца в README про
|
||||||
|
починку, которой не бывает.
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# 31. Ревизия покрытия `av-dev-pm` продакт-оптикой (2026-08-05)
|
||||||
|
|
||||||
|
Сабагент в роли продакт-менеджера оценил покрытие жизненного цикла личного
|
||||||
|
проекта (один человек, недели-месяцы) скиллами и агентами `av-dev-pm`. Скоуп
|
||||||
|
сужен по ходу разбора: деплой и разбор инцидентов на проде делаются вручную,
|
||||||
|
скиллов под них не заводим. Осталось планирование, разработка и доработка.
|
||||||
|
|
||||||
|
**Р126. Шаг 2 сессии требовал чисел, которых процесс отказался собирать
|
||||||
|
решением.** `cadence.md` делал обязанностью пересмотр «ориентира по размеру
|
||||||
|
спринта, прироста беклога на закрытую задачу, времени на задачу» и «сколько
|
||||||
|
заняли задачи **против ожидания**» — с обоснованием «иначе обязанность висит
|
||||||
|
ничья». Данных под это нет: у записи нет дат заведения, взятия и закрытия,
|
||||||
|
`close --implemented` удаляет файл, `sprint close` очищает `SPRINT.md`. Хуже
|
||||||
|
того, «против ожидания» и «время на задачу» требуют оценки и тайм-бокса, а
|
||||||
|
`session/SKILL.md` в «Почему не Scrum» их прямо не берёт: пункт противоречил
|
||||||
|
решению, стоящему через файл от него.
|
||||||
|
|
||||||
|
Исход — **выкинуть, а не подпереть данными**. На практике числа не
|
||||||
|
пересматривались ни разу, и заводить под них учёт дат значило бы обслуживать
|
||||||
|
обязанность, которой никто не брал. Осталось качественное: что сломалось в
|
||||||
|
процессе, что оказалось дороже, чем выглядело при заведении, какие правила не
|
||||||
|
сработали. Шаг 3 пункт 9 переименован из «переоценки по измеренному» в
|
||||||
|
«переоценку по пройденному», судит человек по памяти о спринте. Рядом записано,
|
||||||
|
что замеров нет **намеренно** — иначе следующий читатель заведёт их обратно как
|
||||||
|
недостающие.
|
||||||
|
|
||||||
|
**Р127. `doc-consistency` переехал с каждого синка на сессию, к
|
||||||
|
`doc-code-drift`.** Агент на `opus` зовётся шагом 9 пайплайна, то есть на каждой
|
||||||
|
задаче: 5–8 opus-проходов за спринт по документам, которые за спринт меняются на
|
||||||
|
несколько абзацев. Обоснование в каноне («сверка текста с текстом дёшева») верно
|
||||||
|
относительно второго агента, но не в абсолюте на одиночке.
|
||||||
|
|
||||||
|
Довод сильнее денег: **расхождение между двумя документами по определению
|
||||||
|
требует двух документов**, а на большинстве задач синк правит один. И пачка,
|
||||||
|
отбираемая работой, не видит того, чего работа не касалась, — а расхождение живёт
|
||||||
|
ровно там: правка отменяет решение в одном документе, парный статус нужен в
|
||||||
|
другом. Это был открытый вопрос `REMAINING` про охват ADR при пересмотре; переезд
|
||||||
|
его закрыл. Цена — потеря привязки находки к задаче, которая её породила: по теме
|
||||||
|
29 именно эта привязка дала пять самых точных находок. Принято сознательно.
|
||||||
|
|
||||||
|
**Р128. Отмена цели получила порядок, но не флаг.** `close` запрещал закрыть
|
||||||
|
цель с живыми задачами, а что делать с этими задачами, не говорил нигде: шаг 3
|
||||||
|
сессии знал только «та ли цель», `task-goal.md` описывал одно достижение, а
|
||||||
|
`session/SKILL.md` вдобавок утверждал «цель постоянна». Человек получал отказ с
|
||||||
|
перечнем и никакой подсказки.
|
||||||
|
|
||||||
|
Порядок записан: сперва задачи поштучно (`close --reason` своей причиной либо
|
||||||
|
`edit --goal` на другую цель), потом сама цель через `close --reason` в
|
||||||
|
`REJECTED.md`, а не в `Готово` — отменённая цель не умеет ничего. Флаг
|
||||||
|
`--cascade` отвергнут: отмена цели редка и дорога, и поштучный разбор здесь не
|
||||||
|
церемония, а единственный момент, когда видно, что из задач переживёт цель.
|
||||||
|
Каскад превратил бы его в один Enter. **Причина у каждой задачи своя**: «цель
|
||||||
|
отменена» это пересказ команды, в `REJECTED.md` от него нет пользы через квартал.
|
||||||
|
|
||||||
|
Место процедуры — переоценка на сессии, а не отдельный заход: отмена цели **и
|
||||||
|
есть** разбор всех её задач, а разбор задач — шаг 3.
|
||||||
|
|
||||||
|
**Р129. У брошенного спринта появился второй законный исход, без порога.**
|
||||||
|
`--dissolve` во всех текстах был привязан к блокеру, и скрипт отказывал словами
|
||||||
|
«роспуск объясняется блокером». Вернувшийся к набору, который стоял месяц, не
|
||||||
|
имел законного хода: двигать нельзя (заморозка), распускать не по чему. Теперь
|
||||||
|
роспуск объясняется блокером **или тем, что набор протух**.
|
||||||
|
|
||||||
|
Порога в неделях сознательно нет — это тот же класс, что выкинутые числа шага 2:
|
||||||
|
счётчик простоя пришлось бы вести руками, а решает всё равно человек. Признак не
|
||||||
|
срок, а **что набор перестал быть твоим**: перечитываешь, зачем эти задачи вместе
|
||||||
|
— он протух. Туда же добавлена точка входа «вернулся, а спринт открыт»: `check`,
|
||||||
|
`SPRINT.md`, развилка продолжать/распустить. Середины у развилки нет намеренно —
|
||||||
|
«доделаю пару штук и решу» это работа по набору, которого ты не понимаешь.
|
||||||
|
|
||||||
|
**Р130. Журнал канона прогоняется как есть, а проверка исхода поручена судьям.**
|
||||||
|
Схлопнуть записи 3 и 4 в один переход «с 2 на 4» отвергнуто: журнал описывает не
|
||||||
|
только *что сделать*, но и порядок, в котором это делалось, и слитая запись
|
||||||
|
экономит один проход ценой невоспроизводимости остальных. Оба живых проекта
|
||||||
|
пройдут 2→3→4 по записям.
|
||||||
|
|
||||||
|
Взамен появилась проверка исхода: **шагом 6 `adopt` и шагом 6 `upgrade` зовутся
|
||||||
|
оба судьи документов**. Это прямой ответ на открытый вопрос REMAINING «как
|
||||||
|
проверять, что канон не разошёлся с проектами после `upgrade`»: `check` сверяет
|
||||||
|
**число** в `.pm.json` с версией скрипта и про существо записи не знает ничего.
|
||||||
|
Проект несёт `"canon": 4` и может не иметь того, чего требовала любая из
|
||||||
|
пройденных версий — записи применяются руками, а ручной проход по трём записям
|
||||||
|
подряд ровно то место, где половина шага делается и забывается.
|
||||||
|
|
||||||
|
У `adopt` добавка другого рода: там судьи ловят не недоделанную миграцию, а
|
||||||
|
последствия переноса — факт, растащенный по двум домам, поведение, осевшее в
|
||||||
|
`architecture.md`, ADR, оторванный от своего `design.md`. Им передаётся
|
||||||
|
объявленное переходное состояние из шага 5, иначе честная строка в незаполненном
|
||||||
|
слоте вернётся находкой.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С117. Обязанность без источника данных отменяют, а не механизируют.** Первый
|
||||||
|
позыв — дать шагу данные (дописать даты, сводку спринта). Но обязанность, не
|
||||||
|
исполнявшуюся ни разу, дешевле снять: механизация под неё производит учёт,
|
||||||
|
который надо вести, ради разбора, который не делается.
|
||||||
|
|
||||||
|
**С118. Требование, противоречащее решению через файл от него, — не мелочь, а
|
||||||
|
признак копии.** «Против ожидания» пережило решение «не берём оценки», потому
|
||||||
|
что стояло в другом документе. Обратный обход по решению «что мы не берём» нашёл
|
||||||
|
бы это сразу — тот же приём, что и следствие
|
||||||
|
[С109](29-doc-consistency-trial.md).
|
||||||
|
|
||||||
|
**С119. Частота вызова агента выводится из того, что он ищет.** Судья
|
||||||
|
расхождений **между** документами бессмысленен там, где документ один; значит
|
||||||
|
его место не на задаче, а на наборе задач. Цена вызова подтвердила вывод, но не
|
||||||
|
она его дала.
|
||||||
|
|
||||||
|
**С120. Запрет обязан называть выход.** `close` верно не давал осиротить задачи,
|
||||||
|
но текст отказа перечислял препятствия и молчал о ходе. Проверка без названного
|
||||||
|
следующего шага — половина работы: она защищает данные и бросает человека.
|
||||||
|
|
||||||
|
**С121. Признак вместо порога там, где счётчик пришлось бы вести руками.**
|
||||||
|
«Набор перестал быть твоим» проверяется в момент вопроса и ничего не требует
|
||||||
|
хранить; «прошло N недель» требует учёта, который никто не ведёт, и всё равно
|
||||||
|
кончается решением человека.
|
||||||
|
|
||||||
|
**С122. Версионирование без единого переехавшего проекта — не журнал миграций, а
|
||||||
|
история правок.** Довод за схлопывание был верен по факту и отвергнут по
|
||||||
|
принципу: обкатка на живых проектах и проверяет, работает ли механизм. Схлопнуть
|
||||||
|
значило бы не прогнать его ни разу и оставить вопрос открытым.
|
||||||
|
|
||||||
|
**С123. Проверка версии не есть проверка миграции.** Число в `.pm.json` двигает
|
||||||
|
тот же проход, что делал шаги, — и двигает независимо от того, все ли сделаны.
|
||||||
|
Механической проверки существа нет; там, где её нет, ставится судья, а не
|
||||||
|
отметка.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# 32. Сквозной проход по словарю: пять слов сняты, девять закрыты списком (2026-08-05)
|
||||||
|
|
||||||
|
Проход упрощения ([тема 31](31-pm-coverage-product-review.md)) уткнулся в один и
|
||||||
|
тот же класс у всех пяти агентов: слово, живущее в трёх-шести файлах разом.
|
||||||
|
Правка в одном месте развела бы словарь, правка во всех — уже не упрощение
|
||||||
|
текста скилла. Каждый агент честно остановился и записал слово в свой отчёт, и
|
||||||
|
одни и те же слова всплыли в разных отчётах. Разобрано отдельным проходом.
|
||||||
|
|
||||||
|
**Р131. «Слово прижилось» не проверяется, поэтому заменено списком.** Оговорка в
|
||||||
|
`language.md` звучала так: не переводится «термин, у которого нет точного
|
||||||
|
русского эквивалента и который в команде уже прижился». Проверить это на глаз
|
||||||
|
нельзя — прижившимся выглядит любое слово, встреченное трижды, и ровно так пять
|
||||||
|
агентов подряд и рассудили. Оговорка заменена **закрытым списком из девяти
|
||||||
|
терминов** с колонкой «что называет»: интейк, триаж, провенанс, дедуп, чек-лист,
|
||||||
|
дифф, промпт, сущности OpenSpec, роды проходов ревью. Слово не из списка и не из
|
||||||
|
таблицы имён вещей — находка, а не принятый стиль.
|
||||||
|
|
||||||
|
Список заведён домом `язык-словарь` в `language.md` и копией в уставе
|
||||||
|
`doc-wording`. Копия обязательна: агент работает в репозитории проекта, где
|
||||||
|
плагина может не быть, и без списка предъявил бы «интейк» как англицизм.
|
||||||
|
|
||||||
|
**Р132. Пять слов сняты, и все пятеро выглядели словарём, не будучи им.**
|
||||||
|
`конфляция` → смешение (4 места), `декорреляция` → разведённость (6),
|
||||||
|
`непоймание` → почему не поймали (9), `эвал-сет` → проверочный набор (4), `гайд`
|
||||||
|
→ руководство (6). Латинизм или калька при живом русском слове в каждом случае.
|
||||||
|
|
||||||
|
Разбор `декорреляции` показателен: проект **уже владел** нужным словом — «агенты
|
||||||
|
разведены по глубине», «разведены по охвату» — и держал рядом латинский синоним
|
||||||
|
того же понятия. Это не англицизм, а второй дом для слова.
|
||||||
|
|
||||||
|
`непоймание` снято ещё и потому, что форма журнала дефектов, которую канон кладёт
|
||||||
|
в проекты, спрашивает «Почему не поймали» — а проза рядом называла это
|
||||||
|
«причиной непоймания». Скелет и проза о скелете говорили разными словами.
|
||||||
|
|
||||||
|
**Р133. Снятое записано вместе с оставленным, в одном списке.** Иначе снятое
|
||||||
|
возвращается: слово уходит из текстов, но ничто не мешает следующему проходу
|
||||||
|
завести его заново — оно ведь короткое и точное. Пять слов названы поимённо с
|
||||||
|
заменой каждого.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С124. Escape hatch без перечня — это разрешение, а не исключение.** «Термин,
|
||||||
|
который прижился» освобождает от правила любое слово: проверка «прижился ли»
|
||||||
|
возвращает «да» всякий раз, когда слово встретилось. Исключение из правила
|
||||||
|
обязано быть списком, иначе оно съедает правило.
|
||||||
|
|
||||||
|
**С125. Слово, от которого агент отказался править, — материал для отдельного
|
||||||
|
прохода, а не мусор отчёта.** Пять независимых агентов сошлись на одном наборе
|
||||||
|
слов, ни разу друг друга не видя. Список «что не тронул» оказался полезнее
|
||||||
|
списка правок именно этим.
|
||||||
|
|
||||||
|
**С126. Снятое слово называется вместе с заменой и остаётся записанным.** Убрать
|
||||||
|
из текстов недостаточно: без записи «это снято и вот чем заменено» слово
|
||||||
|
возвращается первым же, кто найдёт его удачным.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 33. Стоимость ревью: снят самый дорогой проход и самая дорогая модель (2026-08-06)
|
||||||
|
|
||||||
|
Прогоны стали долгими, а счёт в токенах — заметным. Разбор шёл не по находкам, а
|
||||||
|
по статьям расхода: что в конвейере стоит больше всего и что из этого окупается.
|
||||||
|
Две статьи названы прямо оператором.
|
||||||
|
|
||||||
|
**Р134. Проход независимой реализации снят целиком, и с ним профиль `deep`.**
|
||||||
|
`reimpl` писал свою реализацию узла, не открывая существующую, и диффил по
|
||||||
|
решениям. Его счёт определялся **объёмом вывода** — он один писал код, а не
|
||||||
|
читал его, — и на прогоне это была самая большая строка расхода. Снят по решению
|
||||||
|
о стоимости.
|
||||||
|
|
||||||
|
Профиль `deep` от этого не «похудел», а исчез: `reimpl` был **единственным**, чем
|
||||||
|
он отличался от `wide` (обоим оставалось бы 0, 1, 2, 4, 5). Держать два имени для
|
||||||
|
одного состава нельзя — ровно от этой болезни лечилась ступень `wide` (решение
|
||||||
|
JJJ): у профиля обязан быть один правильный ответ, иначе реестр состава нечем
|
||||||
|
проверять. Ступеней теперь три: `quick`, `standard`, `wide`.
|
||||||
|
|
||||||
|
Вместе с профилем ушло всё, что обслуживало только его:
|
||||||
|
|
||||||
|
- **барьер стоимости** — он существовал ровно затем, чтобы дорогой проход не
|
||||||
|
писал реализацию против кода, который через час перепишут. Дорогого прохода
|
||||||
|
нет, и граф стал плоским во всех профилях: от гейта до триажа. Рёбер осталось
|
||||||
|
два вида вместо трёх — зависимость и конфликт за ресурс;
|
||||||
|
- **тест «идентичность, слияние, разбор»** (решение из [темы
|
||||||
|
27](27-record-type-single-axis.md)) — он служил
|
||||||
|
единственной цели: выбрать `deep` не по ощущению. Выбирать больше нечего, и
|
||||||
|
полторы страницы теста сняты вместе с проектным перечнем мест в
|
||||||
|
`docs/review.md`;
|
||||||
|
- **стадии перенумерованы**: 0 гейт, 1 сверка, 2 враждебный и эксплуатационный,
|
||||||
|
3 архитектурный, 4 триаж. Дыра на месте третьей читалась бы как пропущенная
|
||||||
|
стадия.
|
||||||
|
|
||||||
|
**Р135. Снятие записано как сознательное сужение, а не как «класс оказался
|
||||||
|
пустым».** `calibration.md` требует замера на двух проектах перед удалением
|
||||||
|
прохода, и замера не было — было решение о цене. Значит и в «Честном пределе»
|
||||||
|
стоит честная строка: **«не знаю, чего не знаю» больше не достаёт никто.**
|
||||||
|
Остаток независимого взгляда дают профиль `design` (код пишется под его находки)
|
||||||
|
и `architecture` (второй способ, лишние слои), но альтернативной реализации, с
|
||||||
|
которой можно сдиффить решения, у конвейера нет. Класс уходит в границы покрытия
|
||||||
|
каждого прогона, а у проекта — в подраздел «перестали проверять сознательно».
|
||||||
|
|
||||||
|
Без этой записи снятие через месяц читается как «проверено и признано лишним»,
|
||||||
|
и вернуть проход было бы не на чем.
|
||||||
|
|
||||||
|
**Р136. Самая дорогая модель снята со всех проходов.** На ней сидели трое:
|
||||||
|
`review-triage`, `review-architecture` и `doc-code-drift` из `av-dev-pm`. Все
|
||||||
|
трое переведены на `opus`. Основание для верхней модели — «ошибка
|
||||||
|
распространяется дальше самой находки» — никуда не делось, но оно объясняет,
|
||||||
|
почему эти двое **не опускаются до `sonnet`**, а не почему им нужна ступень выше
|
||||||
|
`opus`: разницы в пользу более дорогой модели не показал ни один прогон, а время
|
||||||
|
и счёт она множила.
|
||||||
|
|
||||||
|
Палитра цветов схлопнулась до двух: `sonnet` → green, `opus` → yellow. Красного в
|
||||||
|
репозитории больше нет, и `frontmatter.py` теперь отвергнет модель вне этих двух —
|
||||||
|
раскладка проверяется механически, как и раньше.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С127. Профиль, у которого не осталось собственного прохода, — не профиль.**
|
||||||
|
Ступень стоимости определяется тем, что она **добавляет**; сняли добавку — сняли
|
||||||
|
ступень, а не оставили имя. Иначе два имени указывают на один прогон, и состав
|
||||||
|
снова нечем проверить.
|
||||||
|
|
||||||
|
**С128. Удаление по цене и удаление по замеру записываются по-разному.** Первое
|
||||||
|
обязано назвать класс, который перестал проверяться, и оставить его в границах
|
||||||
|
покрытия. Второе — сослаться на замер. Смешение их даёт самый дорогой вид
|
||||||
|
тишины: пробел, выглядящий как решённый вопрос.
|
||||||
|
|
||||||
|
**С129. Механика, обслуживающая один проход, снимается вместе с ним.** Барьер
|
||||||
|
стоимости, тест выбора верхней ступени и проектный перечень мест держались
|
||||||
|
только на `reimpl`. Оставшись, они выглядели бы работающими правилами и тратили
|
||||||
|
бы внимание на каждом прогоне.
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# 34. Пропускная способность против глубины: тяжёлые проходы уехали в верхнюю ступень (2026-08-06)
|
||||||
|
|
||||||
|
Тема 33 сняла самую большую разовую статью расхода, но не тронула главную —
|
||||||
|
**частоту**. Меряющая пара стояла в `standard`, то есть на большинстве задач, и
|
||||||
|
именно она делала прогон долгим: два прохода держат машину, идут цепочкой и
|
||||||
|
доказывают находки запуском. Разбор шёл от цели, названной прямо: **лучше
|
||||||
|
поправить в следующей задаче, чем держать одну два часа.**
|
||||||
|
|
||||||
|
**Р137. `adversary` и `ops` переехали в `wide`, и это решение по цене, а не по
|
||||||
|
ценности.** Стадия осталась самой урожайной за всю историю замеров — пять из
|
||||||
|
семи выживших находок дозапуска и единственная находка про молчаливый старт
|
||||||
|
отката. Но её ценность оплачивается на **каждой** задаче, а получается на
|
||||||
|
немногих: оракул добывается запуском, запуск — это машина, цепочка и часы.
|
||||||
|
Ступень, которая раньше была умолчанием, стала исключением на 5–10% задач.
|
||||||
|
|
||||||
|
**Р138. Заведён `review-basics` — мелкая осадка двух тяжёлых проходов, без
|
||||||
|
единого запуска.** Он стоит только в `standard` и берёт ту половину вопросов, на
|
||||||
|
которые отвечают **чтением**: таймаут и отказ соседа, идемпотентность и
|
||||||
|
одновременная запись, остановка на середине, частичный откат при двух версиях,
|
||||||
|
наблюдаемость и тишина, очевидный рост объёма — плюс два вопроса архитектурного:
|
||||||
|
второй способ мимо единой точки (грепом, не картой) и что отсюда удалить.
|
||||||
|
Потолок 4 находки, машину не держит, ничего не меряет.
|
||||||
|
|
||||||
|
Отдельная его обязанность — **вопрос 4, частичный откат**. Без него правило
|
||||||
|
«миграция схемы не поднимает ступень» рассыпалось бы: раньше миграцию разбирал
|
||||||
|
`ops`, а он теперь в `wide`. Проход заведён не «до кучи», а затем, чтобы у
|
||||||
|
`standard` остался хоть один взгляд на ось времени.
|
||||||
|
|
||||||
|
Модель у него верхняя, `opus`, и это не противоречит слову «средний»: усилие
|
||||||
|
режется **входом и потолком**, а не моделью. Дешёвая модель на проходе
|
||||||
|
с мнением платит триажем — это записанный замер, и отменять его без нового замера
|
||||||
|
нельзя.
|
||||||
|
|
||||||
|
**Р139. Объём и незнакомость изменения вошли в правило выбора ступени.** Раньше
|
||||||
|
ступень выбиралась только по классу («вводит ли новое понятие»), и правило прямо
|
||||||
|
запрещало смотреть на размер. Теперь вопросов два: крупное или незнакомое
|
||||||
|
(трогает несколько узлов, переносит ответственность, форму решения нащупывают по
|
||||||
|
ходу) → `wide`; мелкое (один узел, форма очевидна заранее, откат — обратная
|
||||||
|
правка) → `quick`; всё остальное → `standard`. Причина смены: цена
|
||||||
|
разбирательства растёт именно с объёмом и неизвестностью, а не с классом
|
||||||
|
правила.
|
||||||
|
|
||||||
|
Отрицательный тест `quick` сохранил прежнюю мудрость в новой рамке: **что после
|
||||||
|
мерджа не откатывается обратной правкой — не `quick`, каким бы маленьким ни был
|
||||||
|
дифф.** Три строки миграции идут в `standard`.
|
||||||
|
|
||||||
|
**Р140. Спорный случай решается вниз, и асимметрия объяснена ценой.** Между
|
||||||
|
`standard` и `wide` — в пользу `standard`: ошибка сюда стоит находки на
|
||||||
|
следующей задаче, ошибка обратно стоит трёх тяжёлых проходов на каждой задаче,
|
||||||
|
выбранной неверно. Между `quick` и `standard` — тоже в пользу `standard`, но по
|
||||||
|
другой причине: там разница в один дешёвый проход, зато единственный, кто на
|
||||||
|
нижних ступенях смотрит на отказы.
|
||||||
|
|
||||||
|
Доля `wide` 5–10% записана как **проверка правила, а не пожелание**: если ступень
|
||||||
|
уходит каждой третьей задаче, её выбирают по ощущению важности.
|
||||||
|
|
||||||
|
**Р141. Сделка записана вместе с механизмом обратной связи, иначе это тихая
|
||||||
|
потеря качества.** На `quick` и `standard` не проверяется ничего, что требует
|
||||||
|
запуска: построенный путь, эксперимент против драйвера, любое число. Это самая
|
||||||
|
крупная граница покрытия конвейера, и она обязана идти строкой в каждом таком
|
||||||
|
прогоне поимённо. Обратная связь — журнал дефектов `docs/review.md`: класс,
|
||||||
|
который ловят только меряющие проходы, начал всплывать после мерджа — значит
|
||||||
|
ступень выбирают слишком низко. Плюс сам `basics` обязан сигналить строкой, если
|
||||||
|
видит, что ступень занижена: он единственный, кто смотрит на дифф целиком на
|
||||||
|
нижних ступенях.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С130. Стоимость прохода — это его цена, умноженная на частоту, и вторая
|
||||||
|
переменная важнее.** [Тема 33](33-review-cost-cut.md) убрала самый дорогой
|
||||||
|
проход, тема 34 — самый частый. Второе дало больше, хотя снятый проход был
|
||||||
|
дешевле каждого отдельного `reimpl`.
|
||||||
|
|
||||||
|
**С131. Урожайность прохода не отвечает на вопрос, где ему стоять.** Меряющая
|
||||||
|
пара осталась самой ценной и всё равно уехала вверх: ценность оправдывает
|
||||||
|
существование прохода, но не его частоту.
|
||||||
|
|
||||||
|
**С132. Замена тяжёлого прохода лёгким записывается как сужение, а не как
|
||||||
|
эквивалент.** `basics` задаёт те же вопросы чтением, и его ответы поэтому слабее
|
||||||
|
— условия вместо оракулов. Назвать это «покрыли то же дешевле» значит соврать
|
||||||
|
себе на первом же прогоне.
|
||||||
|
|
||||||
|
**С133. Ступень, выбираемая по классу изменения, слепа к объёму.** Правило,
|
||||||
|
запрещавшее смотреть на размер, защищало от выбора по ощущению важности — и
|
||||||
|
заодно отправляло трёхстрочную правку и переборку пяти узлов в один профиль.
|
||||||
|
Признаков нужно два: класс отвечает за обратимость, объём — за цену
|
||||||
|
разбирательства.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# 35. Ревизия моделей: переведены двое из девяти, и критерий оказался не тот (2026-08-06)
|
||||||
|
|
||||||
|
Сквозной проход по тринадцати уставам с одним вопросом: кого из девяти
|
||||||
|
`opus`-агентов можно опустить на `sonnet` без потери. Ответ — двоих, и по дороге
|
||||||
|
выяснилось, что критерий, которым конвейер до сих пор раздавал модели, отвечает
|
||||||
|
не на тот вопрос.
|
||||||
|
|
||||||
|
**Р142. Модель выбирается по цене ошибки, а не по роду прохода.** Прежнее
|
||||||
|
деление — applicative против generative — раздаёт модели по тому, **откуда**
|
||||||
|
проход берёт критерий. Но платит проект не за происхождение критерия, а за
|
||||||
|
разбирательство с находкой. Рабочий признак:
|
||||||
|
|
||||||
|
- находка приходит **со ссылкой на записанный источник** (строка спеки, цель в
|
||||||
|
манифесте, значение в конфиге, номер правила) — её опровержение стоит одного
|
||||||
|
открытия файла. Дешёвая модель ошибается здесь **проверяемо**;
|
||||||
|
- находка есть **суждение** («это второй способ», «этот оракул негоден», «эти два
|
||||||
|
документа противоречат») — опровержение стоит рассуждения, а рассуждение стоит
|
||||||
|
триажа или человека.
|
||||||
|
|
||||||
|
Признак объясняет прежнюю раскладку лучше, чем она сама себя: `gate`, `code` и
|
||||||
|
`ops` не потому дёшевы, что применяют чек-лист, а потому, что каждая их находка
|
||||||
|
показывает пальцем на строку.
|
||||||
|
|
||||||
|
**Р143. `doc-code-drift` → `sonnet`.** У него закрытый перечень из восьми
|
||||||
|
правил, и каждое — пара «факт в документе ↔ команда, которой он проверяется».
|
||||||
|
Устав прямо запрещает суждение («верность и полноту не проверяешь»), требует
|
||||||
|
формы «написано X, в коде Y, проверено командой Z» и правила «нечем проверить —
|
||||||
|
не находка». Ложная находка опровергается **той же командой, которая её
|
||||||
|
породила**. Это самый чистый случай признака за весь разбор.
|
||||||
|
|
||||||
|
**Р144. `task-form` → `sonnet`.** Семь пронумерованных правил с таблицами форм и
|
||||||
|
поимённым перечнем подмен. Но решило не это, а потребитель: его находка —
|
||||||
|
готовая формулировка, которую человек читает и отклоняет командой, а не
|
||||||
|
оркестратор, который **молча реализует**. Довод, державший `triage` на верхней
|
||||||
|
модели, здесь не работает вовсе: ошибка стоит строки чтения.
|
||||||
|
|
||||||
|
**Р145. `review-specs` рассмотрен и оставлен на `opus` — по причине, обратной
|
||||||
|
общей.** Он самый частый `opus`-проход конвейера (идёт и в `design`, и на коде,
|
||||||
|
то есть дважды за задачу), и по устройству он applicative: SKILL.md сам называет
|
||||||
|
стадию 1 «два applicative-прохода, оба дешёвые», хотя платит за одного `sonnet`,
|
||||||
|
а за другого `opus`. Расхождение разобрано и закрыто текстом: держит его наверху
|
||||||
|
направление `code → spec`, где надо заметить **отсутствие** — тихий фолбэк,
|
||||||
|
самодеятельный дефолт, проглоченную ошибку. Прочие держат `opus` из-за цены
|
||||||
|
ложных находок, этот — из-за цены пропущенных, а пропуск не оставляет следа
|
||||||
|
нигде: ни в отчёте, ни в границах покрытия.
|
||||||
|
|
||||||
|
**Р146. Остальные шестеро оставлены, и у каждого своя причина.** `adversary` и
|
||||||
|
`rubric` порождают критерий по построению (второй — с запретом открывать код в
|
||||||
|
первой фазе). `architecture` — чистое суждение о структуре. `triage` — сток, его
|
||||||
|
ошибка становится кодом. `doc-consistency` ошибается ровно в ту сторону, которую
|
||||||
|
дороже всего опровергать: путает «упомянуто в двух местах» с «оба утверждают».
|
||||||
|
`basics` заведён час назад, половина его вопросов — суждение, и модель у него
|
||||||
|
выбрана решением оператора в этой же сессии.
|
||||||
|
|
||||||
|
**Р147. Это разбор уставов, а не замер, и так и записано.** `calibration.md`
|
||||||
|
двигает модель инъекцией дефекта; здесь инъекции не было. Двое переведены
|
||||||
|
потому, что их ошибка **обнаруживается той же проверкой, что породила находку**,
|
||||||
|
— то есть цена ошибки ограничена сверху независимо от модели. Для остальных
|
||||||
|
такой границы нет, и трогать их без замера нельзя.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С134. Дешёвая модель безопасна там, где её ошибку опровергает та же команда,
|
||||||
|
что породила находку.** Не «где критерий записан» — записанный критерий бывает и
|
||||||
|
у суждения, и у сверки, а разница между ними в том, чем кончается спор.
|
||||||
|
|
||||||
|
**С135. Ошибка бывает двух родов, и модель защищает от разных.** Ложная находка
|
||||||
|
стоит триажа и видна; пропущенная не стоит ничего сегодня и не видна вовсе.
|
||||||
|
Проход, у которого дороже второе, держится на верхней модели даже будучи
|
||||||
|
applicative.
|
||||||
|
|
||||||
|
**С136. Потребитель находки — часть её цены.** Одна и та же ошибка стоит строки
|
||||||
|
чтения, если её читает человек, и разросшегося кода, если её молча реализует
|
||||||
|
оркестратор. Модель раздаётся с оглядкой на это, а не только на устройство
|
||||||
|
прохода.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# 36. Темы ревью: документ проекта стал направлением проверки (2026-08-06)
|
||||||
|
|
||||||
|
Замечено при сверке документов канона с составом ступеней: **три документа
|
||||||
|
остались без читателя ниже `wide`** — `security.md`, `database.md` и `adr/`.
|
||||||
|
Проект поддерживал их, а на 90% задач их не открывал никто. Причина оказалась не
|
||||||
|
в переезде проходов, а в том, как описан состав прогона.
|
||||||
|
|
||||||
|
**Р148. Тема первична, проход вторичен, и это правило 0 конвейера.** Список тем
|
||||||
|
нигде не был записан: он существовал побочным продуктом списка проходов. Проход
|
||||||
|
уезжал в верхнюю ступень — и тема уезжала с ним **беззвучно**: отчёт честно
|
||||||
|
говорил «`ops` не запускался» и не говорил «эксплуатацию не смотрел никто», а
|
||||||
|
нужно второе. Теперь прогон описывается таблицей «тема → дом → глубина → кто
|
||||||
|
закрывает», и таблица есть в каждом отчёте.
|
||||||
|
|
||||||
|
**Р149. Тема есть документ, и список тем открытый.** Всё, что проект кладёт в
|
||||||
|
`docs/`, становится темой ревью; запретить нельзя, разрешения не надо. Не темы
|
||||||
|
ровно две: `docs/tasks/` и `docs/review.*` (настройка самого конвейера — слой
|
||||||
|
над темами). Отсюда главное следствие: **`docs/` перестал быть документацией и
|
||||||
|
стал конфигурацией конвейера.** Проект настраивает проверку тем, что пишет о
|
||||||
|
себе, а не отдельным файлом настроек, который разошёлся бы с документами.
|
||||||
|
|
||||||
|
Ядро — шесть тем: `requirements`, `autotests`, `conventions`, `architecture`,
|
||||||
|
`security`, `operations`. Их дома канон обещает. Всё сверх — темы проекта, и их
|
||||||
|
разбирает `basics`: именных проходов конечное число, а тем столько, сколько
|
||||||
|
заведёт проект, поэтому приёмник обязателен.
|
||||||
|
|
||||||
|
**Р150. Тема живёт файлом или каталогом, на выбор проекта.** `docs/security.md`
|
||||||
|
и `docs/security/` — одно и то же. Прежде форма была задана поимённо
|
||||||
|
(`conventions`, `research`, `adr` — каталоги, остальные — файлы), и обосновать
|
||||||
|
это было нечем; заодно в TODO висел открытый вопрос «а если `architecture.md`
|
||||||
|
разрастётся». Теперь ответ механический: разросся — стал каталогом с
|
||||||
|
`README.md`, и это не смена версии канона. Обе формы сразу — ошибка, и `docs.py`
|
||||||
|
её ловит: два дома для одного факта расходятся молча.
|
||||||
|
|
||||||
|
**Р151. Ступень выбирает разметчик, а не автор.** Заведён `review-scope`
|
||||||
|
(`sonnet`), стадия 0, до гейта: находит документы, выводит темы, назначает
|
||||||
|
глубины, выбирает ступень с обоснованием. Довод сильнее, чем синхронизация
|
||||||
|
документов: **до сих пор профиль называл тот же оркестратор, который написал
|
||||||
|
код** — то есть в точке выбора глубины проверки разведённости с автором не было
|
||||||
|
вовсе, и решала она под давлением «я почти закончил». Вызывающий пайплайн
|
||||||
|
профиль больше не передаёт.
|
||||||
|
|
||||||
|
Право у разметчика симметричное — поднять и понизить, — но обоснование
|
||||||
|
обязательно всегда, а не только при отступлении от умолчания.
|
||||||
|
|
||||||
|
**Р152. Разметчик передаёт адреса, а не пересказ.** Проект однажды уже держал
|
||||||
|
файл-посредник между документами и проходами (`review-brief.md`) и убрал его:
|
||||||
|
второй дом расходится с первым и выглядит актуальным. Пересказ в задании — тот
|
||||||
|
же посредник, живущий один прогон. Исключение одно: **отсутствие дома** — этого
|
||||||
|
проход сам дёшево не выяснит.
|
||||||
|
|
||||||
|
`sonnet` ему хватает потому, что вывод устроен как **список**: каждый файл в
|
||||||
|
`docs/` обязан попасть в план темой или строкой «не тема, потому что», и план
|
||||||
|
сверяется с `ls docs/` за секунду. Выбор ступени — суждение, но у него три
|
||||||
|
независимых корректора: отрицательный тест `quick`, правило «спорный случай
|
||||||
|
вниз» и сигнал `basics` о заниженной ступени.
|
||||||
|
|
||||||
|
**Р153. `quick` и `standard` совпали составом и разошлись глубиной.** Требование
|
||||||
|
«нижние ступени закрывают все темы, просто не так глубоко» иначе не выполняется:
|
||||||
|
темы одни и те же, а различать ступени больше нечем. Глубин три и они про способ
|
||||||
|
доказательства, а не про старательность: **сверка** (открыть дом, открыть дифф,
|
||||||
|
сравнить), **разбор** (построить сценарий рассуждением), **доказательство**
|
||||||
|
(прогнать, померить, построить путь). Третья есть только в `wide` — она одна и
|
||||||
|
требует машины.
|
||||||
|
|
||||||
|
Цена принята: это единственное место конвейера, где профиль не выводится из
|
||||||
|
списка проходов, поэтому глубина объявляется в отчёте наравне со ступенью.
|
||||||
|
|
||||||
|
**Р154. `review-code` переписан: технический разбор плюс конвенции.** Обнаружено
|
||||||
|
по ходу: **никто не читал код как код.** `specs` сверял с требованиями, `basics`
|
||||||
|
— с отказами окружения, `architecture` — с устройством, а `code` был проходом
|
||||||
|
только по прозаическим конвенциям и прямо объявлял, что дефекты рантайма и
|
||||||
|
логики не его. «Здесь ошибка в логике» не говорил никто, и это была самая
|
||||||
|
крупная дыра конвейера — крупнее любой недосмотренной темы.
|
||||||
|
|
||||||
|
Теперь у прохода две половины: девять классов технического дефекта
|
||||||
|
(необработанная ветка отказа, пустое и нулевое, граница диапазона, перепутанный
|
||||||
|
операнд, неосвобождённый ресурс, изменение под итерацией, неверно применённый
|
||||||
|
интерфейс библиотеки, недостижимая ветка, «сделано соседнее») и прежняя сверка с
|
||||||
|
конвенциями. Модель поднята до `opus` по признаку [темы
|
||||||
|
35](35-model-revision.md): цена **пропущенной** находки — дефект в проде, и она
|
||||||
|
не оставляет следа ни в отчёте, ни в границах покрытия.
|
||||||
|
|
||||||
|
**Р155. Вопросы проекта переадресованы темам.** В `docs/review.*` было «Вопросы
|
||||||
|
к проходам» в форме `ops: <вопрос>` — и когда `ops` уехал в `wide`, вопрос
|
||||||
|
перестал задаваться молча. Стало «Вопросы по темам». Туда же «Недоступно
|
||||||
|
проверке» — по темам, обоими подразделами.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С137. Состав, описанный исполнителями, теряет предмет при перестановке
|
||||||
|
исполнителей.** Список проходов отвечает «кто работал», а нужен ответ «что
|
||||||
|
проверено». Первое выглядит полным ровно тогда, когда второе неверно.
|
||||||
|
|
||||||
|
**С138. Открытый список нуждается в приёмнике, иначе он обещание.** Разрешить
|
||||||
|
проекту завести свою тему и не назначить, кто её разбирает, — то же, что не
|
||||||
|
разрешать.
|
||||||
|
|
||||||
|
**С139. Регулятор глубины проверки нельзя оставлять в руках автора.** Не потому
|
||||||
|
что он злонамерен, а потому что давление «я почти закончил» действует всегда и в
|
||||||
|
одну сторону.
|
||||||
|
|
||||||
|
**С140. Дыру в покрытии находят не там, где ищут находки.** Три осиротевших
|
||||||
|
документа нашлись сверкой канона с составом ступеней, а отсутствие технического
|
||||||
|
ревью кода — сверкой оптик проходов между собой. Ни то ни другое не всплыло бы
|
||||||
|
на прогоне: прогон честно сообщал, что все запущенные проходы отработали.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# 37. `gate` и `autotests` сведены к одному имени (2026-08-07)
|
||||||
|
|
||||||
|
Тема звалась `autotests`, закрывающий её проход — `gate`, и на всех трёх
|
||||||
|
ступенях это была одна и та же клетка таблицы. Одна сущность под двумя именами —
|
||||||
|
та же ошибка, что и два разных под одним, только тише: она не путает, а
|
||||||
|
**теряет**. Вопрос проекта в `docs/review.*` адресуется теме; адресованный
|
||||||
|
проходу — не приезжает никуда, и ровно этот отказ уже случился однажды с `ops`
|
||||||
|
(тема 36, [Р155](36-review-topics-project-docs.md)).
|
||||||
|
|
||||||
|
**Р156. Победило имя темы, а не имя прохода.** Три довода, по убыванию веса:
|
||||||
|
|
||||||
|
1. **Тема первична (правило 0), а имена тем — это имена документов.**
|
||||||
|
`docs/autotests.md` проект напишет: что покрыто, что нарочно нет, где
|
||||||
|
`testdata`. `docs/gate.md` не напишет никто — гейт это команда, а не предмет.
|
||||||
|
2. **Слово «гейт» уже занято дважды** — команда проекта и ребро графа («пока гейт
|
||||||
|
красный, проходы с мнением не идут»). Третье значение сделало бы отчёт нечитаемым:
|
||||||
|
«гейт красный» и «гейт нашёл» — про разное.
|
||||||
|
3. **Тема шире гейта.** «Хватает ли проверок» и «чего в гейте намеренно нет» за
|
||||||
|
пределы красного/зелёного выходят. Назвать целое именем инструмента — тихо его
|
||||||
|
сузить.
|
||||||
|
|
||||||
|
Цена названа честно: `autotests` звучит уже своего содержимого — линт, типы,
|
||||||
|
сканер уязвимостей тестами не являются. Гасится строкой в уставе: тема — это
|
||||||
|
«проверено ли машиной», а не «есть ли тесты», и гейт в ней инструмент, а не
|
||||||
|
граница.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С141. Тема и проход, совпадающие один в один на всех ступенях, обязаны носить
|
||||||
|
одно имя.** Пока имён два, у сущности два адреса, а адресуют её по одному — и
|
||||||
|
какой из двух окажется живым, решает случай.
|
||||||
|
|
||||||
|
**С142. Слово, уже значащее что-то в предметной области проекта, нельзя брать
|
||||||
|
именем роли конвейера.** «Гейт» принадлежит проекту раньше, чем ревью, и спор за
|
||||||
|
него ревью проигрывает.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# 38. Шов между плагинами: канон не называет имён проходов (2026-08-07)
|
||||||
|
|
||||||
|
Замечено при сведении тем документации с ревьюверами: `av-dev-pm` в шести местах
|
||||||
|
называл конвейер поимённо — от прозы канона до **вывода `docs.py` пользователю**
|
||||||
|
(«свои темы проекта: … — их разбирает `review-basics`»). Плагины при этом
|
||||||
|
раздельные: `av-dev-pm` работает без конвейера, `av-dev-pipeline` — без канона,
|
||||||
|
поразрядно деградируя.
|
||||||
|
|
||||||
|
**Р157. Общий словарь — имена тем и имена ступеней, и только они.** Ими проект
|
||||||
|
настраивает ревью: вопросы по темам и триггеры профиля. Имён проходов канон не
|
||||||
|
называет нигде. Направление зависимости при этом несимметрично и это верно:
|
||||||
|
**конвейер называет документы канона поимённо, потому что он их читатель**, а
|
||||||
|
обратной ссылки быть не может — документ живёт дольше, чем раскладка проходов.
|
||||||
|
|
||||||
|
Заодно вычищены описательные адресации того же класса: «архитектурный проход
|
||||||
|
судит», «враждебный проход выдумает», «там идут враждебный, эксплуатационный и
|
||||||
|
архитектурный проходы». Последняя — худшая из них: это утверждение о **составе
|
||||||
|
ступени**, живущее на стороне, которая о составе не знает.
|
||||||
|
|
||||||
|
**Р158. Пример в правиле не должен нарушать само правило.** Объяснение, почему
|
||||||
|
вопросы адресуются темам, звучало так: «вопрос, адресованный `ops`, перестал
|
||||||
|
задаваться в тот день, когда `ops` уехал в верхнюю ступень». Правило про
|
||||||
|
нестабильность имён, иллюстрированное именем. Стало «адресованный проходу» — и
|
||||||
|
работает даже после того, как проход переименуют.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С143. Ссылка из вывода скрипта дороже ссылки из прозы.** Устаревшую строку в
|
||||||
|
документе чинит тот, кто её читает; устаревшее имя в сообщении `docs.py`
|
||||||
|
доезжает до чужого проекта и там объясняется недоумением.
|
||||||
|
|
||||||
|
**С144. Список, который никто не ведёт, честнее списка, который ведут двое.**
|
||||||
|
Читателей документа не перечисляет ни одна сторона — читатель назначается планом
|
||||||
|
прогона. Прежняя ссылка на «таблицу читателей» пережила саму таблицу и обещала
|
||||||
|
то, чего нет, — с той самой правки, которая таблицу и убрала.
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# 39. Спринт без цели — законный случай (2026-08-07)
|
||||||
|
|
||||||
|
Цель была обязательной: `sprint start --goal` требовал слаг, `check` считал
|
||||||
|
ошибкой набор без названной цели, `sprint take` отказывал задаче под чужой
|
||||||
|
целью. Модель описывала только спринт развития — а спринт бывает под багфикс,
|
||||||
|
под техдолг, под здоровье проекта. Такой набор собран **по работоспособности, а
|
||||||
|
не по направлению**, и цели у него нет не по недосмотру.
|
||||||
|
|
||||||
|
Обходной путь существовал и был хуже прямого: завести цель-пустышку («Здоровье
|
||||||
|
проекта») и вешать под неё `fix`-и. Тогда `ROADMAP.md` — документ про то, что
|
||||||
|
приложение умеет, — обрастает строками про то, что оно не ломается, а тег
|
||||||
|
`goal:` перестаёт значить направление.
|
||||||
|
|
||||||
|
**Р159. Цель у спринта необязательна, но её отсутствие — ответ, а не молчание.**
|
||||||
|
`sprint start` принимает `--goal <слаг>` **или** `--no-goal`, и голое отсутствие
|
||||||
|
обоих — отказ с объяснением. Причина в стимуле: цель называет человек, и это
|
||||||
|
единственный продуктовый вопрос всей сессии. Разреши мы заводить спринт просто
|
||||||
|
без флага — забытый флаг, лень спросить и осознанное решение стали бы неотличимы
|
||||||
|
на выходе, а дешевле всего из трёх агенту именно не спрашивать.
|
||||||
|
|
||||||
|
**Р160. В спринте без цели цель не проверяется вовсе.** Набор берёт что угодно
|
||||||
|
готовое к взятию, включая задачи под разными целями: сверять не с чем. Правило
|
||||||
|
«набор служит одной цели» не ослаблено, оно просто не применяется — целей в
|
||||||
|
таком наборе не больше одной, их ноль. Взамен машинной проверки остаётся показ
|
||||||
|
набора человеку до заморозки: у бесцельного спринта это **единственная**
|
||||||
|
проверка состава, и в скилле это сказано прямо.
|
||||||
|
|
||||||
|
**Р161. Признак «спринт идёт» — слаг, а не цель.** Прежде код спрашивал цель и
|
||||||
|
получал заодно ответ про то, открыт ли спринт; теперь эти вопросы разошлись.
|
||||||
|
Слаг подходит на роль признака лучше цели по существу: он есть у любого спринта,
|
||||||
|
потому что без него нечем проставить `sprint:<слаг>`, то есть нечем собрать
|
||||||
|
урожай. Поле «Цель» в шапке остаётся на месте и у бесцельного набора — пишется
|
||||||
|
прозой без ссылки: **«цели нет» и «цель потерялась» обязаны различаться**.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С145. Необязательное поле, которое всё же решают, заводится парой «значение
|
||||||
|
или явный отказ».** Умолчанием тут был бы не выбор, а его отсутствие — и
|
||||||
|
отличить его от забывчивости уже не смог бы никто, включая автора.
|
||||||
|
|
||||||
|
**С146. Признак «сущность существует» нельзя вешать на её необязательное поле.**
|
||||||
|
Пока цель была обязательной, `sprint_goal()` отвечал сразу на два вопроса, и это
|
||||||
|
работало ровно до тех пор, пока второй ответ не понадобился отдельно.
|
||||||
|
|
||||||
|
**С147. Снятая проверка называет, что осталось вместо неё.** Цель не проверяется
|
||||||
|
— значит, за состав отвечают показ человеку и строка доклада; иначе послабление
|
||||||
|
читается как «здесь можно не думать».
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# 40. Три категории документов: не всякий документ — тема ревью (2026-08-07)
|
||||||
|
|
||||||
|
Решение 36 объявило: **каждый документ проекта — тема ревью**. Правило дало
|
||||||
|
открытый список тем и сделало `docs/` конфигурацией конвейера — это работает и
|
||||||
|
остаётся. Но оно же оказалось неверным ровно наполовину, и потому вредным
|
||||||
|
целиком.
|
||||||
|
|
||||||
|
Паспорт и схему хранилища ревью читает, но темами они не являются: по ним нельзя
|
||||||
|
сказать «в этом изменении сделано не так», они задают границу, по которой судит
|
||||||
|
**чужая** тема. Журнал решений и журнал наблюдений ревью изменения не нужны
|
||||||
|
вовсе: ADR объясняет прошлое решение, а не предъявляет требование к изменению.
|
||||||
|
|
||||||
|
Ломалось это механически. Разметчик, применявший правило буквально, обязан был
|
||||||
|
либо завести фантомные темы `passport`, `adr`, `database`, `research` и
|
||||||
|
продублировать ими работу тем `architecture` и `operations`, либо потерять четыре
|
||||||
|
документа молча. Обе ветки случались; в собственном образце плана разметчика
|
||||||
|
`docs/passport.md` не попадал ни строкой, а его же обязательная арифметика
|
||||||
|
покрытия («документов найдено N, все N разнесены») при этом не сходилась.
|
||||||
|
|
||||||
|
**Р162. Разрез один и проверяемый: можно ли по документу сказать «в этом
|
||||||
|
изменении сделано не так».** Отсюда три категории. **Тема** — да, прямо
|
||||||
|
(`conventions`, `security`, `architecture`, свои документы проекта). **Источник
|
||||||
|
темы** — нет, но он задаёт границу для чужой темы (`passport`, `database`,
|
||||||
|
`CLAUDE.md`, `openspec/specs/`). **Процессный документ** — нет, он про то, как
|
||||||
|
мы работаем (`tasks/`, `review.*`, `adr.*`, `research.*`, `.pm.json`).
|
||||||
|
|
||||||
|
**Р163. Открыта одна категория из трёх.** `источник` и `процессный` перечислены
|
||||||
|
поимённо и проектом не пополняются; открыта только `тема`. Прежняя формулировка
|
||||||
|
«не темы ровно две» противоречила собственной раскладке канона — `.pm.json` был
|
||||||
|
третьим, и правило-исправление жило в чужом плагине, в коде `docs.py`. Теперь
|
||||||
|
документ, которого нет в раскладке, — однозначно своя тема проекта, и решать
|
||||||
|
нечего.
|
||||||
|
|
||||||
|
**Р164. «Не судит по нему» и «не открывает» — разные вещи.** `docs/review.*`
|
||||||
|
проходы читают на каждом прогоне: там вопросы по темам, журнал дефектов, типовые
|
||||||
|
узлы, типовые ложноположительные. Это чтение конвейером **своей обвязки**, а не
|
||||||
|
критерия. `adr/`, `research/` и `tasks/` не открывает никто.
|
||||||
|
|
||||||
|
**Р165. Цена решения записана, а не подразумевается.** Расхождение изменения с
|
||||||
|
записанным решением прогоном больше не ловится — это работа сверки документации
|
||||||
|
между спринтами. Измеренные числа проекта из ревью тоже ушли: проход,
|
||||||
|
опирающийся на число, обязан **снять его сам, на этом прогоне**, и приложить
|
||||||
|
команду замера. Обе потери идут обязательными строками в границы покрытия
|
||||||
|
каждого прогона, и пишет их триаж — не проход, потому что проход о том, чего в
|
||||||
|
конвейере нет, пожаловаться не может.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С148. Плоское правило, верное наполовину, хуже двух правил.** Оно не даёт
|
||||||
|
половине случаев легального ответа, и исполнитель выбирает между двумя плохими
|
||||||
|
ветками — фантомной сущностью и молчащей потерей. Заметно это становится не на
|
||||||
|
определении, а на первом же образце вывода.
|
||||||
|
|
||||||
|
**С149. Открытым делается одно множество, а не все.** Открытый список ценен тем,
|
||||||
|
что в него попадает незнакомое; если открыты все категории, незнакомое попадает
|
||||||
|
в произвольную.
|
||||||
|
|
||||||
|
**С150. Отказ читать документ — тоже граница покрытия, и её пишет сток.** Строку
|
||||||
|
«этого не смотрел никто» некому подать снизу: проход, которого нет, отчёта не
|
||||||
|
присылает.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# 41. Разметка задачи: одна величина, посчитанная один раз (2026-08-07)
|
||||||
|
|
||||||
|
Разметка была стадией 0 **ревью кода** и платилась на каждом прогоне. Перед ревью
|
||||||
|
дизайна ту же самую величину — «крупное или незнакомое?» — называл сам пайплайн
|
||||||
|
задачи, то есть оркестратор, который только что довёл предложение до `propose`.
|
||||||
|
Одно и то же измерялось дважды, и один из двух раз без разведённости с автором —
|
||||||
|
ровно в той точке, ради которой разметчик и заведён.
|
||||||
|
|
||||||
|
**Р166. Разметка идёт один раз на задачу, сразу после `propose`.** Её план
|
||||||
|
обслуживает обе стадии ревью: состав ревью дизайна и таблицу тем для ревью кода.
|
||||||
|
Диффа она не видит — кода ещё нет; размер оценивается по дельта-спекам и перечню
|
||||||
|
границ задачи.
|
||||||
|
|
||||||
|
**Р167. Осей две, ступень — максимум по ним.** **Размер** (малое, среднее,
|
||||||
|
крупное) — про объём; **сложность** (знакомое, незнакомое) — про то, известна ли
|
||||||
|
форма решения заранее. Раньше обе были склеены в один вопрос «крупное **или**
|
||||||
|
незнакомое?»: ответ получался тот же, но разметка не могла сказать «среднее, но
|
||||||
|
совершенно знакомое» — а это и есть рабочее умолчание.
|
||||||
|
|
||||||
|
**Р168. Ступень после кода не пересматривается.** Дифф может выйти крупнее
|
||||||
|
ожидания — ступень не двинется. Пересмотр означал бы либо второй запуск
|
||||||
|
разметчика (то, ради устранения чего он и переехал), либо машинный порог,
|
||||||
|
который на нетипичной задаче срабатывает не туда. Расхождение факта с разметкой
|
||||||
|
ловит журнал дефектов, постфактум, — так же, как и всякую другую ошибку выбора
|
||||||
|
ступени.
|
||||||
|
|
||||||
|
**Р169. План на диск не пишется.** Файл-план стал бы четвёртым артефактом рядом
|
||||||
|
с `proposal.md`, `tasks.md` и `design.md`, пережил бы задачу и разошёлся бы с
|
||||||
|
ней молча. Прервался пайплайн — разметка повторяется; это самый дешёвый его
|
||||||
|
проход.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С151. Величина, из которой выводится состав, считается один раз и одним
|
||||||
|
агентом.** Два места, считающие одно и то же, расходятся; расходятся они молча,
|
||||||
|
и побеждает то, у которого меньше разведённости с автором.
|
||||||
|
|
||||||
|
**С152. Разведённость — свойство момента, а не роли.** Тот же агент, спрошенный
|
||||||
|
до написания кода и после, даёт разные ответы; переезд по времени сделал больше,
|
||||||
|
чем сделал бы любой запрет.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# 42. `quick` стал дешевле `standard` тремя способами (2026-08-07)
|
||||||
|
|
||||||
|
`quick` и `standard` совпадали составом (шесть проходов) и различались глубиной
|
||||||
|
трёх тем: сверка против разбора. На практике это означало один проход, задающий
|
||||||
|
на один вопрос меньше, и потолок 4 вместо 2. Нижняя ступень не экономила почти
|
||||||
|
ничего и называлась отдельной ступенью зря.
|
||||||
|
|
||||||
|
Отдельно выяснилось, что дешевизна конвейера держалась на двух заявленных
|
||||||
|
рычагах — узкий вход и потолок находок, — и **оба применялись к одному проходу
|
||||||
|
из шести**. У `specs` и `code` потолка не было вовсе, а вход `code` включал
|
||||||
|
чтение дома конвенций «весь и целиком» на каждой задаче.
|
||||||
|
|
||||||
|
**Р170. `quick` теряет приёмник тем.** Темы `security`, `operations` и
|
||||||
|
`architecture` на этой ступени закрывает `code` сверкой с **записанными
|
||||||
|
инвариантами** `CLAUDE.md`, потолком 1 находка на все три. Это не «глубина ниже»
|
||||||
|
— это **другой дом темы**, куда более узкий, и в плане он так и называется.
|
||||||
|
|
||||||
|
**Р171. Приёмник тем запускается тогда и только тогда, когда ему есть что
|
||||||
|
принимать.** Правило было в `wide` («нет своих тем проекта — не запускается») и
|
||||||
|
теперь распространено на `quick`. Совпадение неслучайное: темы ядра `basics`
|
||||||
|
держит ровно на одной ступени из трёх, а приёмником проектных тем работает на
|
||||||
|
всех.
|
||||||
|
|
||||||
|
**Р172. Вход и потолок применены к каждому проходу с мнением.** На `quick`
|
||||||
|
`specs` читает только дельта-спеку, `code` — только индекс конвенций. Потолки
|
||||||
|
напечатаны и раздельны по половинам `code`: 3 технических, 2 конвенционных, 1 по
|
||||||
|
инвариантам. Раздельность обязательна — конвенционных находок больше по
|
||||||
|
построению, и в общем списке они вытеснили бы техническую половину, чей пропуск
|
||||||
|
дороже.
|
||||||
|
|
||||||
|
**Р173. Сработавший потолок объявляется.** Проход, срезавший находки, говорит
|
||||||
|
строкой, сколько осталось за срезом и какого рода. Молчащий срез неотличим от
|
||||||
|
«больше не нашлось» — тот же класс молчащего пропуска, против которого написан
|
||||||
|
весь конвейер.
|
||||||
|
|
||||||
|
**Р174. Отрицательный тест `quick` стал жёстче, а не мягче.** Вопросы «обратима
|
||||||
|
ли миграция» и «что с записями новой версии после отката» задавал приёмник тем;
|
||||||
|
на `quick` его нет. Значит изменение, которое не откатывается обратной правкой,
|
||||||
|
на `quick` не идёт вовсе — каким бы малым оно ни было.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С153. Ступень, не дающая экономии, не нужна.** Две ступени, различающиеся
|
||||||
|
одним вопросом одного прохода, — это одна ступень с шумом в отчёте.
|
||||||
|
|
||||||
|
**С154. Рычаг, применённый к одному исполнителю, — не рычаг, а исключение.**
|
||||||
|
Заявленный механизм экономии проверяется перечислением: к кому он применён и к
|
||||||
|
кому нет.
|
||||||
|
|
||||||
|
**С155. Проход без потолка выдаёт столько находок, сколько нашёл поверхностей.**
|
||||||
|
Ровно из-за этого был снят проход независимой реализации; тот же механизм
|
||||||
|
работал у `code` и `specs` и не был замечен, потому что счёт никто не считал.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# 43. Ревью дизайна тоже растёт ступенями (2026-08-07)
|
||||||
|
|
||||||
|
Состав ревью дизайна включался одним условием: `specs` всегда, `rubric` и
|
||||||
|
`architecture` — вместе, «при крупном или незнакомом». Значит `standard` получал
|
||||||
|
на предложении ровно один проход, то есть не отличался от `quick` ничем.
|
||||||
|
|
||||||
|
**Р175. Три ступени вместо двух: `quick` — `specs`; `standard` — плюс `rubric`;
|
||||||
|
`wide` — плюс `architecture` и вопрос автору о трёх формах решения.**
|
||||||
|
|
||||||
|
**Р176. Рубрика съехала вниз, архитектура осталась наверху, и это не
|
||||||
|
симметричная правка.** Они зарабатывают на разном. Рубрика порождает **свойства
|
||||||
|
узла** и окупается уже на среднем изменении: её выход уезжает приёмочными
|
||||||
|
критериями в `tasks.md` и работает потом на всей задаче. Архитектура отвечает на
|
||||||
|
вопрос «не появился ли второй способ», а он на среднем знакомом изменении
|
||||||
|
отвечается «нет» ещё до запуска — держать её ниже `wide` значит платить за
|
||||||
|
предсказуемый ответ на каждой задаче.
|
||||||
|
|
||||||
|
**Р177. Тривиальность задачи больше не решает состав ревью.** Раньше она решала,
|
||||||
|
звать ли ревью предложения вовсе; теперь глубину обеих стадий называет ступень,
|
||||||
|
а тривиальная задача просто получает `quick`. «Пропустить ревью дизайна» и
|
||||||
|
«пройти его одним самым дешёвым проходом» — разные вещи: сверка дельта-спек
|
||||||
|
стоит меньше, чем разбор того, что она поймала бы на готовом коде.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С156. Проходы, включаемые одним условием, стоит разводить по тому, на чём они
|
||||||
|
зарабатывают.** Общее условие — признак того, что их не сравнивали между собой,
|
||||||
|
а не того, что они равноценны.
|
||||||
|
|
||||||
|
**С157. Средняя ступень обязана отличаться от нижней на обеих стадиях.** Иначе
|
||||||
|
«рабочее умолчание» отличается от исключения только именем.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# 44. Метка задачи: одно значение, по которому выбираются все ревьюверы (2026-08-07)
|
||||||
|
|
||||||
|
Решения 41–43 развели классификацию на две оси и свели состав обеих стадий ревью
|
||||||
|
к их максимуму. Значения этого максимума назывались `quick`, `standard`, `wide`,
|
||||||
|
а сам он — «ступень». Оба имени описывали **ревью**: как глубоко смотрим, на
|
||||||
|
какой ступеньке идём. Классифицируется же при этом **задача**, и результат
|
||||||
|
классификации принадлежит ей, а не прогону.
|
||||||
|
|
||||||
|
Расхождение не косметическое. Пока величина называлась свойством ревью, её было
|
||||||
|
естественно пересчитать на каждом прогоне — что конвейер и делал, пока разметка
|
||||||
|
не переехала к `propose`. Имя тянуло назад к устройству, из которого её только
|
||||||
|
что вынули.
|
||||||
|
|
||||||
|
**Р178. Классификация выдаёт задаче метку: `small`, `medium` или `large`.**
|
||||||
|
Метка принадлежит задаче, ставится один раз при разметке и дальше только
|
||||||
|
читается. Все проходы обеих стадий получают её в задании и обязаны напечатать в
|
||||||
|
границах покрытия.
|
||||||
|
|
||||||
|
**Р179. Метка — единственный вход выбора исполнителей.** Ни класс задачи, ни её
|
||||||
|
тип, ни тривиальность, ни ощущение важности состав больше не определяют. У
|
||||||
|
конвейера один переключатель, и он напечатан в каждом отчёте.
|
||||||
|
|
||||||
|
**Р180. Слово «ступень» удалено, а не оставлено синонимом.** Два имени одной
|
||||||
|
вещи расходятся — это ровно решение [темы 37](37-gate-and-autotests-one-name.md)
|
||||||
|
про тему и проход. Метка ordered: `small` < `medium` < `large`, и там, где нужен
|
||||||
|
порядок, говорится «младшая» и «старшая метка», а не вводится второе
|
||||||
|
существительное.
|
||||||
|
|
||||||
|
**Р181. Метка — не синоним размера, и это записано там, где ошибиться легче
|
||||||
|
всего.** Совпадают они в одном углу таблицы из трёх: малое **незнакомое**
|
||||||
|
изменение получает `large`, трогая один узел. Поэтому план печатает три строки —
|
||||||
|
размер, сложность, метка, — каждую со своим обоснованием, и выводить одну из
|
||||||
|
другой запрещено. Проход, определивший объём диффа по метке, ошибётся именно на
|
||||||
|
том случае, ради которого верхняя метка и заведена.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С158. Имя величины должно называть её носителя, а не потребителя.** «Ступень
|
||||||
|
ревью» звала пересчитывать себя на каждом прогоне ревью; «метка задачи»
|
||||||
|
считается там же, где живёт задача.
|
||||||
|
|
||||||
|
**С159. Переключатель состава должен быть один и печатный.** Пока их два —
|
||||||
|
тривиальность и ступень, — состав выводится из пересечения, а пересечение нигде
|
||||||
|
не напечатано целиком.
|
||||||
|
|
||||||
|
**С160. Русские слова для осей, английские для значения.** Оси — суждение и
|
||||||
|
читаются прозой (`малое`, `знакомое`); метка — идентификатор, который проходы
|
||||||
|
сравнивают, и потому она английская. Тот же разрез, что «имена файлов
|
||||||
|
английские, текст русский» в каноне, и он же снимает путаницу «крупное» против
|
||||||
|
`large`.
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# 45. Корректор метки, доля `small` и корпус оценки (2026-08-07)
|
||||||
|
|
||||||
|
Три правки по следам тем
|
||||||
|
[41](41-task-sizing-once.md)–[44](44-task-label-single-value.md), и все три
|
||||||
|
закрывают дыры, которые эти решения и открыли.
|
||||||
|
|
||||||
|
**Р182. Сигнал о заниженной метке переехал в `review-code`.** Он жил в
|
||||||
|
`review-basics` — единственном месте. А `basics` с меткой `small` не
|
||||||
|
запускается, если у проекта нет своих тем: значит на типичном проекте задача с
|
||||||
|
меткой `small` шла **без рантайм-проверки** того, что метка верна. Дыра
|
||||||
|
появилась ровно вместе с удешевлением `small` и попала в самую вероятную точку
|
||||||
|
ошибки: занижают туда, где дешевле, а цена занижения там же и выросла — три темы
|
||||||
|
ядра смотрятся только против инвариантов.
|
||||||
|
|
||||||
|
`code` подходит по построению: он идёт при **любой** метке, видит дифф целиком, а
|
||||||
|
на `small` уже читает инварианты — то есть держит в руках весь материал, из
|
||||||
|
которого сигнал выводится. У `basics` сигнал остаётся вторым, подтверждающим: он
|
||||||
|
смотрит оптикой тем и видит то, чего не видно из кода как кода, — что вопросов,
|
||||||
|
отложенных до `large`, накопилось слишком много. Триаж теперь обязан сказать и
|
||||||
|
когда сигнала **нет**: «корректор отработал, возражений нет» и «корректор не
|
||||||
|
запускался» по молчанию неразличимы.
|
||||||
|
|
||||||
|
**Р183. У `small` появилась доля, и она сформулирована сравнением, а не
|
||||||
|
числом.** `small` не должен обгонять `medium`; ориентир — до трети задач.
|
||||||
|
Проверка нужна именно теперь: пока `quick` и `standard` совпадали составом,
|
||||||
|
дрейф между ними не стоил ничего, и её не было. Сейчас он стоит трёх тем ядра. У
|
||||||
|
дрейфа вниз есть стимул, и он назван: метку выбирает не автор, но по описанию,
|
||||||
|
написанному автором, — занижённое описание даёт занижённую метку без чьего-либо
|
||||||
|
умысла.
|
||||||
|
|
||||||
|
**Р184. Размер оценивается по корпусу из пяти источников, а не по
|
||||||
|
дельта-спекам.** Разметчик читал `proposal.md` и `tasks.md`, но `design.md` не
|
||||||
|
открывал вовсе, а метод был описан одной фразой «размер считается по
|
||||||
|
дельта-спекам». Дельты описывают заказанное **поведение** и молчат об объёме
|
||||||
|
работы: шесть шагов в двух узлах видны в `tasks.md`, а факт, что форму решения
|
||||||
|
выбирали из нескольких, — только в `design.md`. Каждый источник получил свою
|
||||||
|
строку по каждой оси, и каждая цифра в обосновании обязана быть привязана к
|
||||||
|
источнику поимённо.
|
||||||
|
|
||||||
|
Отсюда два правила, которых раньше не было. **Расхождение источников по объёму
|
||||||
|
разрешается в пользу большего** — и это не «спорное решается вниз»: то правило
|
||||||
|
разрешает ничью при равных данных, а здесь один источник просто видел больше.
|
||||||
|
**Само расхождение — довод за `незнакомое`:** если о задаче написано так, что
|
||||||
|
источники не сходятся в объёме, форму решения по ней не знают. Отсутствие
|
||||||
|
`design.md` у нетривиальной задачи читается так же — «форму знали заранее» ничем
|
||||||
|
не подтверждено.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С161. Корректор обязан идти чаще, чем корректируемое.** Проверяющий, который
|
||||||
|
запускается реже проверяемого, оставляет дыру именно там, где выбор был самым
|
||||||
|
дешёвым, — то есть там, где ошибаются.
|
||||||
|
|
||||||
|
**С162. Отсутствие сигнала — тоже сигнал, и его надо печатать.** Молчание
|
||||||
|
корректора неотличимо от его отсутствия, а решения по ним разные.
|
||||||
|
|
||||||
|
**С163. Проверка доли формулируется сравнением, а не порогом.** «Меньше, чем
|
||||||
|
`medium`» считается по любому журналу и не требует спорить о числе; порог «не
|
||||||
|
больше 30%» спорен ровно настолько, насколько несопоставимы задачи.
|
||||||
|
|
||||||
|
**С164. Оценка по одному источнику — оценка по остатку.** Источники о задаче
|
||||||
|
отвечают на разные вопросы; пропущенный не ухудшает точность понемногу, а
|
||||||
|
оставляет ось без данных.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# 46. Правило выбора метки съехало из скилла в отдельный документ (2026-08-07)
|
||||||
|
|
||||||
|
**Р185. У правила выбора метки теперь свой дом — `references/review-levels.md`,
|
||||||
|
а в скилле остался диспетчер.** `SKILL.md` конвейера дорос до 1168 строк, и
|
||||||
|
двести с лишним из них отвечали на вопрос, который на обычной задаче не задаётся
|
||||||
|
вовсе: **как** выбирается метка. Метку называет `review-scope` один раз, до
|
||||||
|
обеих стадий; всем остальным нужна не она, а состав по уже названной метке — три
|
||||||
|
строки таблицы. Переехали правило двух осей, «спорное решается вниз», «максимум
|
||||||
|
по поверхности», разбор того, чем `small` дешевле `medium`, и обе проверки
|
||||||
|
долей. Остались таблица состава, схема процесса и раздача тем.
|
||||||
|
|
||||||
|
**Форма выбрана одна на все метки, а не по документу на метку.** Предлагался
|
||||||
|
разрез по образцу типов задач в `av-dev-pm:tasks`, где у `fix`, `feature` и
|
||||||
|
`chore` по своему файлу. Аналогия не переносится, и по двум причинам. Типы задач
|
||||||
|
**разъединены** — общее вынесено в `task-format.md`, а в файле типа лежит только
|
||||||
|
своё; метки же **вложены**: `medium` это `small` плюс два прохода, `large` —
|
||||||
|
`medium` плюс доказательство. Три файла повторяли бы костяк трижды, а `copies.py`
|
||||||
|
такое не ловит: он сверяет дословные копии по маркерам, тогда как здесь вышли бы
|
||||||
|
почти-копии с намеренными мелкими отличиями — расхождение, неотличимое от
|
||||||
|
задуманного. Вторая причина сильнее первой: ценность этого текста **в
|
||||||
|
сравнении**. Читателю нужно не «что делает `small`», а «чем `small` отличается от
|
||||||
|
`medium`» — на этот вопрос отвечают и выбор метки, и «спорное вниз», и корректор.
|
||||||
|
Сравнение, разложенное по трём файлам, не читается.
|
||||||
|
|
||||||
|
**Механика рычагов осталась в скилле, а не уехала с меткой.** Непуск, вход и
|
||||||
|
потолок общие для всех проходов и всех меток, их дом — раздел «Модель по
|
||||||
|
проходу». В переехавшем тексте от них только то, что они делают с `small`, и
|
||||||
|
ссылка на дом; точные потолки не продублированы.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С165. Дом правила — там, где правило выбирают, а не там, где его применяют.**
|
||||||
|
Применяют состав на каждой задаче, выбирают метку один раз; текст, обслуживающий
|
||||||
|
выбор, в потоке применения лежит мёртвым грузом.
|
||||||
|
|
||||||
|
**С166. Вложенные вещи не режутся по файлу на вещь.** Разъединённое (типы задач)
|
||||||
|
режется, вложенное (метки) — нет: разрез вложенного даёт дублирование общей
|
||||||
|
части, а дублирование намеренно неточное машина не сверит.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# 47. OpenSpec заводится скиллом, а его конфиг — часть канона (2026-08-07)
|
||||||
|
|
||||||
|
**Р186. `init` заводит OpenSpec сам, а не оставляет это человеку.** Каталог
|
||||||
|
`openspec/` был предпосылкой, о которой канон говорил, но за которой не следил:
|
||||||
|
`openspec/specs/` объявлен домом темы `requirements`, `config.yaml` описан
|
||||||
|
абзацем — а заводилось всё руками, и не проверялось ничего. Новый проект выходил
|
||||||
|
из `init` с полным каноном документов и без каталога, без которого не работают
|
||||||
|
ни `opsx:propose`, ни ревью дизайна, ни сверка требований. Команда названа
|
||||||
|
поимённо (`openspec init --tools claude`) в трёх местах — скилле, каноне и
|
||||||
|
отказе скрипта: отказ без команды заставляет искать её в другом месте.
|
||||||
|
|
||||||
|
**Р187. Файл из коробки хуже отсутствующего, и потому проверяется машиной.**
|
||||||
|
`openspec init` кладёт `config.yaml`, где `context` и `rules` —
|
||||||
|
закомментированный пример на английском. Такой файл читается как настроенный: он
|
||||||
|
есть, он валиден, имя правильное. Работает он как пустой, и узнаётся это по уже
|
||||||
|
написанному предложению — на другом языке, с capability по имени пакета, без
|
||||||
|
единого `SHALL`. `docs.py` проверяет четыре вещи, и каждая про молчащий пробел:
|
||||||
|
каталог есть; имя именно `config.yaml` (`config.yml` OpenSpec не читает и об
|
||||||
|
этом не сообщает); `context` и `rules.specs` не остались примером, а правила
|
||||||
|
называют `SHALL`; `context` называет `passport` и `CLAUDE.md`.
|
||||||
|
|
||||||
|
**Р188. Форма конфига — маршрутизатор, и это разрез, а не пожелание.**
|
||||||
|
Утверждение, которое можно опровергнуть, открыв другой файл проекта, — пересказ;
|
||||||
|
строка, которая говорит, какой файл открыть, — ссылка. `context` читается при
|
||||||
|
порождении **каждого** артефакта, туда удобно дописать «чтобы агент знал», и
|
||||||
|
именно поэтому в нём заводятся вторые дома инвариантов, конвенций, гейта и
|
||||||
|
правил ревью. Машина этот разрез не проверяет — отличить ссылку от пересказа она
|
||||||
|
не умеет, — и он отдан `doc-consistency` отдельным абзацем правила «один факт —
|
||||||
|
один дом», с `config.yaml`, добавленным ему во вход.
|
||||||
|
|
||||||
|
**Обязательными сделаны ровно два адреса — паспорт и `CLAUDE.md`.** Причина в
|
||||||
|
порядке работы: предложение пишется **до** того, как кто-либо откроет `docs/`, и
|
||||||
|
без этих двух строк его пишут, не зная ни границы домена, ни инвариантов.
|
||||||
|
Длинный список адресов превратил бы `context` во второй дом ровно тем способом,
|
||||||
|
против которого правило и заведено.
|
||||||
|
|
||||||
|
**Образец конфига лёг в канон, а не в конвейер**, как планировалось решением
|
||||||
|
[Р3](01-openspec-status.md). Форма документа принадлежит тому, кто владеет
|
||||||
|
каноном документов; конвейер её читатель. Иначе `av-dev-pipeline` завёл бы у
|
||||||
|
себя описание файла, который заводит и проверяет `av-dev-pm`, — тот же шов, что
|
||||||
|
разбирали, убирая имена проходов из канона.
|
||||||
|
|
||||||
|
## Что из этого следует
|
||||||
|
|
||||||
|
**С167. Предпосылка, за которой никто не следит, — не предпосылка, а
|
||||||
|
пожелание.** Если условие названо обязательным, его должен кто-то заводить и
|
||||||
|
кто-то проверять; иначе оно живёт ровно до первого проекта, где о нём забыли.
|
||||||
|
|
||||||
|
**С168. Заполненная форма и заполненный смысл — разные вещи, и первая маскирует
|
||||||
|
вторую.** Файл на месте, валиден, с правильным именем — и пуст по существу: это
|
||||||
|
худший вид пробела, потому что выглядит он как его отсутствие.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user