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",
|
||||
"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",
|
||||
|
||||
-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`.
|
||||
|
||||
Что решено и почему — [DECISIONS.md](DECISIONS.md). Что осталось сделать —
|
||||
[TODO.md](TODO.md) и [REMAINING.md](REMAINING.md). Как процесс дошёл до текущей
|
||||
формы — [HISTORY.md](HISTORY.md).
|
||||
Что решено и почему — [журнал решений](decisions/README.md).
|
||||
|
||||
## Плагины
|
||||
|
||||
Плагина два: **av-dev** — весь процесс, и **av-dev-git** — сообщения коммитов.
|
||||
Второй работает в любом репозитории, в том числе не приведённом к канону. До 13 августа 2026 процесс жил тремя плагинами
|
||||
(`av-dev-docs`, `av-dev-tasks`, `av-dev-code`); раскол делался под раздельную
|
||||
установку, она не понадобилась ни разу, и плагины слились — тема 64
|
||||
[DECISIONS.md](DECISIONS.md).
|
||||
установку, она не понадобилась ни разу, и плагины слились —
|
||||
[тема 64](decisions/64-three-plugins-merged.md) журнала решений.
|
||||
|
||||
Имя **скилла** несёт префикс прежнего плагина: `doc-`, `task-`, `code-`. Вызов
|
||||
выходит вида `/av-dev:<скилл>`.
|
||||
Имя **скилла** несёт префикс материала, с которым он работает: `doc-`, `task-`,
|
||||
`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-canon` — привести проект к канону документов: `check` / `adopt` /
|
||||
`upgrade`, плюс скрипт `docs.py`. Он же ведёт журнал версий раскладки —
|
||||
общий, и на документы, и на каталог задач;
|
||||
- `doc-healthcheck` — здоровье документации **судом, а не машиной**: не
|
||||
разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон
|
||||
разом — `doc-consistency` (документы между собой и с openspec) и
|
||||
`doc-code-drift` (факты против кода) — и разбирает урожай порциями. Дорого,
|
||||
поэтому не на каждой задаче. Язык документов вычитывает отдельный агент
|
||||
`doc-wording`, и зовут его не отсюда, а те, кто только что писал текст:
|
||||
`doc-sync`, `doc-init` и `doc-canon`;
|
||||
`doc-sync`, `doc-init` и `canon`;
|
||||
- `doc-sync` — содержимое канона по ходу разработки: ADR из архивного
|
||||
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
||||
архитектуры.
|
||||
|
||||
**Учёт работ.** Владеет каталогом задач.
|
||||
|
||||
- `task-track` — задачи и цели каталогом markdown-файлов, у каждой записи тип
|
||||
(`goal`, `feature`, `fix`, `chore`, `research`), и тип задаёт её схему;
|
||||
- `task-track` — задачи каталогом markdown-файлов: у каждой тип (`feature`,
|
||||
`fix`, `chore`, `research`), задающий её схему, а у проекта — стадия (`build`
|
||||
или `support`), решающая, что значит порядок строк беклога;
|
||||
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
||||
`task-wording` (язык записей);
|
||||
- `task-groom` — груминг беклога: что сейчас самое важное и что перестало быть
|
||||
@@ -114,10 +121,10 @@ flowchart TB
|
||||
tp["code-resolve<br/>3 сценария: разведка,<br/>решение, обслуживание"] --> rp["code-review<br/>10 агентов-проходов"]
|
||||
osp["code-openspec<br/>заводит и проверяет openspec/"]
|
||||
end
|
||||
subgraph docsp["документы, владеют docs/"]
|
||||
canon["canon<br/>форма раскладки всего проекта"]
|
||||
subgraph docsp["документы, владеют содержимым docs/"]
|
||||
direction LR
|
||||
init["doc-init"]
|
||||
canon["doc-canon"]
|
||||
docs["doc-sync"]
|
||||
hc["doc-healthcheck"]
|
||||
end
|
||||
@@ -152,18 +159,20 @@ flowchart TB
|
||||
`docs/`, каталог задач, `openspec/`. Тогда вызывающий называет строкой, чего теперь не делает
|
||||
никто, и работу не останавливает. Правило целиком —
|
||||
[shared/absence.md](av-dev/shared/absence.md): оно нужно почти каждому скиллу, и
|
||||
ни один им не владеет. Там же, в `shared/`, живут язык проектных текстов и
|
||||
словарь сопровождения; скиллы читают их по ссылке, а дословной копией они
|
||||
уезжают только в уставы вычитки — туда, где текст обязан лежать внутри промпта.
|
||||
ни один им не владеет. Там же, в `shared/`, живут язык проектных текстов,
|
||||
словарь сопровождения и **перечень осей процесса**
|
||||
[axes.md](av-dev/shared/axes.md) — какие закрытые словари правят ходом работы,
|
||||
где дом каждого и чего он **не** решает. Скиллы читают эти дома по ссылке, а
|
||||
дословной копией оттуда уезжает лишь то, что обязано лежать внутри промпта.
|
||||
|
||||
## Канон документов проекта
|
||||
## Канон раскладки проекта
|
||||
|
||||
Все проекты приводятся к одной раскладке — так проще ориентироваться, когда
|
||||
проектов много, и рядом OpenSpec тоже держит строгую структуру. `CLAUDE.md` плюс
|
||||
`docs/` (паспорт, архитектура, база, безопасность, конвенции, разведка, ADR,
|
||||
ревью, задачи) и `openspec/` — **раскладка целиком, роли документов и правило
|
||||
единственного дома живут одним домом**:
|
||||
[canon.md](av-dev/skills/doc-canon/references/canon.md). Здесь она не
|
||||
[canon.md](av-dev/skills/canon/references/canon.md). Здесь она не
|
||||
пересказывается: копия перечня путей уже расходилась с домом, и как раз в
|
||||
обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает
|
||||
нарушением.
|
||||
@@ -181,9 +190,9 @@ flowchart TB
|
||||
«тема → её дом → что оттуда берётся» —
|
||||
[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` в корне репозитория** — вместе с
|
||||
настройками: `[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'а.** Вызов
|
||||
разрешается по каталогу, а сообщение о промахе говорит «нет такого скилла»,
|
||||
а не «имя не то»;
|
||||
@@ -426,7 +446,7 @@ python3 scripts/copies.py # 0 сошлось, 1 расхождение, 2 р
|
||||
когда он решает; и в скелетах, уезжающих в репозиторий проекта.
|
||||
|
||||
**Дом ставится в `shared/` только тогда, когда владельца нет.** У адресов
|
||||
владелец есть: раскладку `docs/` держит `doc-canon`, каталог задач —
|
||||
владелец есть: раскладку `docs/` держит `canon`, каталог задач —
|
||||
`task-track`, и переносить их наружу значило бы отобрать у владельца его же
|
||||
предмет. Общее без владельца живёт в `shared/`; чужое с владельцем остаётся
|
||||
дома, а потребитель на него ссылается.
|
||||
@@ -464,7 +484,7 @@ python3 scripts/resync.py # переписать тела всех разо
|
||||
## Проверка адресов документов
|
||||
|
||||
Адрес документа принадлежит одному плагину, а называют его все: `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), ставится один раз на клон:
|
||||
|
||||
```
|
||||
@@ -537,6 +557,7 @@ lefthook run pre-commit # прогнать руками, не коммитя
|
||||
| фронтматтеры | правка `*.md` или `*.json` | весь репозиторий | миллисекунды |
|
||||
| копии правил | правка `*.md` | весь репозиторий | миллисекунды |
|
||||
| адреса документов | **каждый коммит** | весь репозиторий | ~0.07 с |
|
||||
| журнал решений | **каждый коммит** | весь репозиторий | ~0.09 с |
|
||||
| диаграммы | правка `*.md` | staged-файлы | ~1 с на файл |
|
||||
| `ruff check --fix` | правка `*.py` | staged-файлы | доли секунды |
|
||||
| `pyrefly check` | правка `*.py` | staged-файлы | доли секунды |
|
||||
@@ -552,7 +573,16 @@ Glob разводит две половины: коммит, трогающий
|
||||
обходит весь репозиторий за сотые доли секунды — экономить тут нечего;
|
||||
`addresses.py` идёт **без glob вовсе**, потому что сводит две стороны: перечень
|
||||
адресов лежит в `*.py` владельца, а упоминания — в `*.md` соседей, и коммит с
|
||||
переименованием документа трогает только первую.
|
||||
переименованием документа трогает только первую; `decisions.py` — по той же
|
||||
причине: файл темы и ссылки на него лежат порознь, и переименование темы трогает
|
||||
только одну сторону.
|
||||
|
||||
**Что судит `decisions.py`.** Номер записи журнала обязан быть один: буквенные
|
||||
метки решений выродились до пятибуквенных и разъехались молча — `АЕАКЛ` означала
|
||||
и тему 53, и тему 65. И подпись ссылки обязана называть свою цель: в
|
||||
`[тема 5](decisions/05-project-start-lifecycle.md)` номер записан дважды, словом и путём, —
|
||||
дубль неизбежный (иначе ссылку не прочитать, не открыв) и потому сверяемый, как
|
||||
всякая копия.
|
||||
|
||||
**`ruff` чинит безопасное сам, и починка доносится до этого же коммита**
|
||||
(`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",
|
||||
"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": {
|
||||
"name": "Anton Vakhrushev",
|
||||
"email": "anwinged@gmail.com"
|
||||
|
||||
@@ -15,19 +15,19 @@ color: yellow
|
||||
машина, а что человек», и её правая колонка — твой устав дословно.
|
||||
|
||||
Карта домов, по которой ты судишь о правиле 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` |
|
||||
| почему решено так | `adr/`, источник — архивный `design.md` либо записка разведки |
|
||||
| граница домена, «чем не является» | `passport.md` |
|
||||
| инвариант и его severity | `CLAUDE.md` |
|
||||
| что приложение умеет и чего не умеет; порядок работ | `tasks/ROADMAP.md` |
|
||||
| что осталось сделать и в каком порядке | `tasks/BACKLOG.md` |
|
||||
| измеренное число | `research/` |
|
||||
| настройка с числовым значением | `database.md` |
|
||||
| периметр и модель угроз | `security.md` |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
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
|
||||
model: sonnet
|
||||
color: green
|
||||
|
||||
@@ -213,11 +213,15 @@ color: green
|
||||
- **незнакомое** — форму предстоит нащупать по ходу. Признак один и
|
||||
проверяемый: **перед работой нельзя назвать, какие узлы будут тронуты**.
|
||||
|
||||
| | знакомое | незнакомое |
|
||||
<!-- копия: матрица-метки из av-dev/skills/code-review/references/review-levels.md -->
|
||||
|
||||
| | **знакомое** — форму решения можно назвать до начала | **незнакомое** — форму предстоит нащупать по ходу |
|
||||
|---|---|---|
|
||||
| **малое** | `small` | `large` |
|
||||
| **среднее** | `medium` | `large` |
|
||||
| **крупное** | `large` | `large` |
|
||||
| **малое** — один узел | `small` | `large` |
|
||||
| **среднее** — несколько узлов одного слоя | `medium` | `large` |
|
||||
| **крупное** — несколько слоёв, перенос ответственности, большой рефакторинг | `large` | `large` |
|
||||
|
||||
<!-- /копия: матрица-метки -->
|
||||
|
||||
**Метка — не синоним размера, и это главная ловушка таблицы.** Размер `малое` и
|
||||
метка `small` совпадают только в левом верхнем углу: малое **незнакомое**
|
||||
@@ -272,6 +276,8 @@ color: green
|
||||
**Ревью кода — раздача тем.** Кто закрывает тему, зависит от метки. Раскладка
|
||||
жёсткая, выдумывать её не надо:
|
||||
|
||||
<!-- копия: тема-метка-глубина из av-dev/skills/code-review/SKILL.md -->
|
||||
|
||||
| Тема | `small` | `medium` | `large` |
|
||||
|---|---|---|---|
|
||||
| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор |
|
||||
@@ -282,6 +288,8 @@ color: green
|
||||
| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство |
|
||||
| тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор |
|
||||
|
||||
<!-- /копия: тема-метка-глубина -->
|
||||
|
||||
Две глубины, которые ты назначаешь:
|
||||
|
||||
- **сверка** — открыть дом, открыть дифф, сравнить. Один-два вопроса на тему,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: review-triage
|
||||
description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Сверяет план разметки задачи с пришедшими отчётами: тема, размеченная и оставшаяся без отчёта, — находка о самом прогоне. Формирует итоговый отчёт с планом, перечнем проходов и обязательной секцией границ покрытия."
|
||||
description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Сверяет план с пришедшими отчётами: тема, стоявшая в плане и оставшаяся без отчёта, — находка о самом прогоне; на прогоне без метки план даёт сценарий обслуживания, а не разметчик. Формирует итоговый отчёт с планом, перечнем проходов и обязательной секцией границ покрытия."
|
||||
tools: Read, Grep, Glob, Bash, Write
|
||||
model: opus
|
||||
color: yellow
|
||||
@@ -21,18 +21,26 @@ color: yellow
|
||||
|
||||
## Вход
|
||||
|
||||
Сырые выводы всех запущенных проходов, `git diff <база>..HEAD`, **план разметки
|
||||
задачи** (агент `review-scope`, один запуск после `propose`) и режим прогона.
|
||||
Дельта-спеки — по мере надобности.
|
||||
Сырые выводы всех запущенных проходов, `git diff <база>..HEAD`, **план прогона**
|
||||
и режим. Дельта-спеки — по мере надобности.
|
||||
|
||||
План — это таблица «тема → дом → глубина → кто закрывает» плюс размер, сложность
|
||||
и метка с обоснованием. Он твой главный инструмент сверки: ты единственный, кто
|
||||
видит и то, что размечено, и то, что пришло.
|
||||
План — таблица «тема → дом → глубина → кто закрывает». Он твой главный инструмент
|
||||
сверки: ты единственный, кто видит и то, что заявлено, и то, что пришло.
|
||||
|
||||
**Плана нет — ты не запускаешься, и исключений нет.** Сверка размеченного с
|
||||
пришедшим — твоя единственная защита от молчащего пропуска, и без плана она не
|
||||
выполняется вовсе. Отчёт, собранный без неё, выглядит полным ровно настолько же,
|
||||
насколько и неполный.
|
||||
**Откуда план приходит, зависит от режима, и режимов два.**
|
||||
|
||||
- **С меткой** — план собрал `review-scope` (один запуск после `propose`), и к
|
||||
таблице прилагаются размер, сложность и метка с обоснованием.
|
||||
- **Без метки** — так идёт прогон сценария обслуживания: изменение не меняет
|
||||
поведения, размечать нечего, и разметчик не запускается вовсе. План
|
||||
**фиксирован сценарием** (`av-dev:code-resolve`, `references/maintain.md`), а
|
||||
размера, сложности и метки не существует. Не ищи их и не подставляй: в отчёте
|
||||
на их месте — строка «прогон без метки, план сценария».
|
||||
|
||||
**Плана нет ни от разметчика, ни от сценария — ты не запускаешься, и исключений
|
||||
нет.** Сверка заявленного с пришедшим — твоя единственная защита от молчащего
|
||||
пропуска, и без плана она не выполняется вовсе. Отчёт, собранный без неё,
|
||||
выглядит полным ровно настолько же, насколько и неполный.
|
||||
|
||||
Из документов проекта тебе нужны:
|
||||
|
||||
@@ -174,7 +182,9 @@ severity:
|
||||
|
||||
**Сигнала нет — тоже скажи строкой.** «Корректор метки отработал, возражений
|
||||
нет» и «корректор не запускался» — разные вещи, и отличить их по молчанию
|
||||
нельзя.
|
||||
нельзя. **На прогоне без метки корректору нечего поднимать**, и это третье
|
||||
состояние: пиши «метки нет, корректор неприменим», а не «не запускался» —
|
||||
последнее читается как пропуск.
|
||||
|
||||
## Границы покрытия — не сокращаются
|
||||
|
||||
@@ -238,9 +248,12 @@ severity:
|
||||
Строго секциями из контракта: `Блокирует мердж` (≤3) / `Стоит исправить сейчас`
|
||||
(≤4) / `Гипотезы без доказательства` / `Promote candidates` / `Границы покрытия`.
|
||||
|
||||
Перед секциями — сводка: размер, сложность и метка с обоснованием разметки и режим прогона,
|
||||
состояние гейта, **план с исходом по каждой теме**, сколько находок пришло на
|
||||
вход и сколько осталось.
|
||||
Перед секциями — сводка: режим прогона, состояние гейта, **план с исходом по
|
||||
каждой теме**, сколько находок пришло на вход и сколько осталось. На прогоне
|
||||
**с меткой** к этому добавляются размер, сложность и метка с обоснованием
|
||||
разметки; на прогоне **без метки** их место занимает строка «прогон без метки,
|
||||
план сценария обслуживания» — выдумывать метку задним числом нельзя, её никто
|
||||
не снимал.
|
||||
|
||||
## Ограничения
|
||||
|
||||
|
||||
+19
-44
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: task-form
|
||||
description: "Проверка формы записи каталога задач по существу: тип, разошедшийся с содержанием записи, форма заголовка по типу (цель — что приложение будет уметь, задача — что нужно сделать, разведка — о чём она), «зачем», пересказывающее заголовок вместо состояния и боли, раздел «Затрагивает» с замыслом вместо границ, критерий приёмки с оракулом только на словах, предписание процесса в теле, и связь задачи со строкой «Завершения» её цели. Читает файл цели, на которую ссылается задача. Отдаёт готовые формулировки на замену и ничего не правит сам. Язык текста (залог, оценки, стоп-слова, англицизмы) смотрит отдельный агент task-wording. Использовать после заведения или разбора пачки записей, при проверке готовности перед взятием в работу (tasks.py ready) и на груминге. Только чтение."
|
||||
description: "Проверка формы записи каталога задач по существу: тип, разошедшийся с содержанием записи, форма заголовка по типу (задача — что нужно сделать, разведка — о чём она), «зачем», пересказывающее заголовок вместо состояния и боли, раздел «Затрагивает» с замыслом вместо границ, критерий приёмки с оракулом только на словах, предписание процесса в теле. Отдаёт готовые формулировки на замену и ничего не правит сам. Язык текста (залог, оценки, стоп-слова, англицизмы) смотрит отдельный агент task-wording. Использовать после заведения или разбора пачки записей, при проверке готовности перед взятием в работу (tasks.py ready) и на груминге. Только чтение."
|
||||
tools: Read, Grep, Glob
|
||||
model: sonnet
|
||||
color: green
|
||||
@@ -11,8 +11,8 @@ color: green
|
||||
открывая код.
|
||||
|
||||
Оптика — смысл записи в её собственных рамках. Ты **не** судишь, нужна ли
|
||||
задача, верно ли выбрана цель и не крупна ли она: это разбор, и его ведёт
|
||||
человек со скиллом `task-track`.
|
||||
задача и не крупна ли она: это разбор, и его ведёт человек со скиллом
|
||||
`task-track`.
|
||||
|
||||
Границу с языком держи твёрдо. **Залог, оценки, стоп-слова, англицизмы, жаргон**
|
||||
— у агента `task-wording`, и тебе они не поручены даже там, где бросаются в
|
||||
@@ -28,8 +28,6 @@ color: green
|
||||
## Что тебе дают
|
||||
|
||||
Список файлов записей (`tasks/items/<slug>.md`) или каталог задач целиком.
|
||||
Каталог тебе нужен и сам по себе: задача несёт тег `goal:<слаг>`, и **файл цели
|
||||
ты открываешь**, иначе седьмое правило не проверить.
|
||||
|
||||
Документы проекта — паспорт, архитектура, конвенции — если зовущий их назвал.
|
||||
По ним видно, названа ли граница именем, которое в проекте существует.
|
||||
@@ -43,20 +41,15 @@ color: green
|
||||
|
||||
| Тип | Отвечает на | Форма |
|
||||
| --- | --- | --- |
|
||||
| 🎯 `goal` | что приложение будет уметь | утверждение о возможности: «Соперником может быть компьютер» |
|
||||
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | глагол в неопределённой форме, допускается «не» перед ним: «Печатать поле одним куском кода» |
|
||||
| 🔬 `research` | о чём разведка | назывное, без обещания: «Подсказка следующего хода» |
|
||||
|
||||
Описательный заголовок задачи («Лишние символы молча отбрасываются») называет
|
||||
**состояние** и одинаково читается как жалоба и как задание. Заголовок цели в
|
||||
форме действия («Сделать соперника-компьютер») превращает роадмап в список
|
||||
работ — а он список возможностей.
|
||||
**состояние** и одинаково читается как жалоба и как задание.
|
||||
|
||||
**Область работ — не цель.** «Работа со слиянием», «Рефакторинг вывода» не
|
||||
отвечают ни на один из трёх вопросов; предложи возможность, которую эта работа
|
||||
создаёт, и скажи, если из текста её не видно. **Свойство поведения —
|
||||
законная возможность**: «исход слияния не зависит от порядка доставки» — цель,
|
||||
а не абстракция.
|
||||
**Область работ — не задача.** «Работа со слиянием», «Рефакторинг вывода» не
|
||||
отвечают ни на один из двух вопросов; предложи формулировку, называющую, что
|
||||
нужно сделать, и скажи, если из текста этого не видно.
|
||||
|
||||
2. **Тип сходится с тем, что в записи написано.** Тип — первое поле меты, и он
|
||||
решает, каких разделов запись требует; разошедшийся тип врёт ровно там, где
|
||||
@@ -68,7 +61,7 @@ color: green
|
||||
- **`feature`, после которой снаружи ничего не меняется**, — это `chore`, и
|
||||
сказать это честно дешевле, чем выдумывать пользовательскую пользу;
|
||||
- **`chore`, меняющий наблюдаемое поведение**, — это `feature` или `fix`, и у
|
||||
них другие требования (цель, воспроизведение);
|
||||
последнего другие требования (воспроизведение);
|
||||
- **`research`, у которого «Вопрос» — это тема, а не вопрос.** «Разобраться с
|
||||
выводом в терминалах» вопросом не является: на него нельзя ответить. Пока
|
||||
вопроса нет, запись остаётся сырьём — и это законное состояние, но назови
|
||||
@@ -105,34 +98,19 @@ color: green
|
||||
постановке. Он же путь понизить требования решением, принятым до
|
||||
проектирования.
|
||||
|
||||
7. **Задача называет, какую строку «Завершения» своей цели она двигает.**
|
||||
Открой файл цели из тега `goal:<слаг>` и сверь. Три исхода, и все три —
|
||||
разные находки:
|
||||
|
||||
- **строка не названа** — допиши предложение, какая это строка, если из текста
|
||||
задачи видно; не видно — так и скажи;
|
||||
- **строки с таким смыслом в «Завершении» нет** — либо задача не про эту цель,
|
||||
либо у цели неполное «Завершение». Назови оба варианта, выбирать не тебе;
|
||||
- **строка «Завершения», к которой не относится ни одна поданная задача**, —
|
||||
это незакрытая часть возможности. Скажи о ней отдельно, вне списка находок
|
||||
по файлам: это про набор, а не про запись.
|
||||
|
||||
У задачи **без цели** (`fix`, `chore`, `research`) правило не применяется
|
||||
вовсе — они служат работоспособности, а не направлению.
|
||||
|
||||
## Чего ты не проверяешь
|
||||
|
||||
Не своё бывает двух разных родов, и поступают с ними по-разному.
|
||||
|
||||
**Чужому подрядчику — строкой в границах покрытия.** Язык у `task-wording`;
|
||||
согласованность документов канона между собой у `doc-consistency`, их
|
||||
соответствие коду у `doc-code-drift` — до задач эти двое не доходят вовсе, но
|
||||
если ты открыл цель и увидел расхождение в самом документе, оно их. Увидел —
|
||||
назови в конце одной строкой, чтобы находка не пропала, но находкой не оформляй.
|
||||
соответствие коду у `doc-code-drift` — до задач эти двое не доходят вовсе.
|
||||
Увидел не своё — назови в конце одной строкой, чтобы находка не пропала, но
|
||||
находкой не оформляй.
|
||||
|
||||
**Машинной проверке — вообще ничего.** Всё, что ловит `tasks.py check` (состав и
|
||||
написание секций, теги, тег `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
|
||||
description: "Вычитка языка записей каталога задач по информационному стилю — задачи, цели, строки индексов и причины отказа. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в слаге. Отдаёт готовые формулировки на замену и ничего не правит сам. Форму записи (заголовок по типу, «зачем», границы, оракулы) смотрит отдельный агент task-form, документы проекта вычитывает doc-wording. Использовать после заведения или разбора пачки записей, до взятия в работу и на переоценке беклога. Только чтение."
|
||||
description: "Вычитка языка записей каталога задач по информационному стилю — задачи, строки индекса и причины отказа. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в слаге. Отдаёт готовые формулировки на замену и ничего не правит сам. Форму записи (заголовок по типу, «зачем», границы, оракулы) смотрит отдельный агент task-form, документы проекта вычитывает doc-wording. Использовать после заведения или разбора пачки записей, до взятия в работу и на переоценке беклога. Только чтение."
|
||||
tools: Read, Grep, Glob
|
||||
model: sonnet
|
||||
color: green
|
||||
---
|
||||
|
||||
Ты — **вычитка языка записей каталога задач**: задач, целей, строк индексов и
|
||||
причин отказа. Оптика — слова и фразы, а не то, что запись описывает: ты не
|
||||
судишь, нужна ли задача, верно ли выбрана цель и правильно ли запись оформлена.
|
||||
Ты — **вычитка языка записей каталога задач**: задач, строк индекса и причин
|
||||
отказа. Оптика — слова и фразы, а не то, что запись описывает: ты не судишь,
|
||||
нужна ли задача и правильно ли она оформлена.
|
||||
|
||||
Границу держи твёрдо, и она у тебя одна. **Форму записи** — тип, заголовок по
|
||||
типу, «зачем» вместо пересказа, раздел «Затрагивает», годность оракулов, связь
|
||||
со строкой «Завершения» цели — смотрит `task-form`, и тебе она не поручена даже
|
||||
типу, «зачем» вместо пересказа, раздел «Затрагивает», годность оракулов —
|
||||
смотрит `task-form`, и тебе она не поручена даже
|
||||
там, где бросается в глаза. Отсюда же исключение, и оно **против** тебя:
|
||||
неудачное слово **в заголовке** судит `task-form`, потому что заголовок целиком
|
||||
его. Увидел не по своей части — скажи одной строкой в конце доклада, не
|
||||
@@ -27,9 +27,8 @@ color: green
|
||||
|
||||
## Что тебе дают
|
||||
|
||||
Список записей или каталог задач: файлы `items/<slug>.md`, а с ними — индексы
|
||||
(`BACKLOG.md`, `ROADMAP.md`, `REJECTED.md`), где та же запись представлена
|
||||
строкой. **Строка индекса вычитывается наравне с файлом**: по ней запись
|
||||
Список записей или каталог задач: файлы `items/<slug>.md`, а с ними —
|
||||
`BACKLOG.md` и `REJECTED.md`, где та же запись представлена строкой. **Строка индекса вычитывается наравне с файлом**: по ней запись
|
||||
выбирают, не открывая тела, и «зачем» в ней повторяется дословно.
|
||||
|
||||
Плюс, если зовущий их назвал, документы проекта — паспорт, архитектура,
|
||||
@@ -200,8 +199,8 @@ color: green
|
||||
критериев `check` только считает — поимённо их судит `tasks.py ready`, и это
|
||||
тоже не твоя находка: твоя — язык того, что уже написано.
|
||||
|
||||
**Содержание работы**: нужна ли задача, верно ли выбрана цель, не крупна ли она,
|
||||
достаточна ли декомпозиция. Это разбор, и его ведёт человек со скиллом `task-track`.
|
||||
**Содержание работы**: нужна ли задача, не крупна ли она, достаточна ли
|
||||
декомпозиция. Это разбор, и его ведёт человек со скиллом `task-track`.
|
||||
|
||||
**Полезное действие, параллельность и работающий заголовок** — тоже не твои.
|
||||
Они в доктрине языка, судит их человек: находка по ним требует увидеть текст
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||
|
||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||
копию из `.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]
|
||||
dir = "tasks" # каталог задач от корня репозитория
|
||||
stage = "build" # стадия проекта: build | support
|
||||
items = "items" # имена частей каталога — необязательны
|
||||
backlog = "BACKLOG.md"
|
||||
roadmap = "ROADMAP.md"
|
||||
|
||||
Коды выхода зовущих скриптов — общий словарь av-dev; отсюда возвращается
|
||||
исключение `ConfigError`, а решает по нему вызывающий.
|
||||
@@ -55,8 +55,8 @@ LEGACY = ("docs/.docs.json", "docs/.pm.json")
|
||||
LEGACY_TASKS = ".tasks.json"
|
||||
|
||||
# Версия раскладки — одна на плагин. Журнал версий — references/changelog.md
|
||||
# скилла `doc-canon`, повышает его операция `upgrade`.
|
||||
VERSION = 1
|
||||
# скилла `canon`, повышает его операция `upgrade`.
|
||||
VERSION = 3
|
||||
|
||||
VERSION_KEY = "version"
|
||||
|
||||
@@ -280,6 +280,33 @@ def merge_section(root: Path, name: str, values: dict) -> list[str]:
|
||||
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:
|
||||
"""Ключи секции, которые уже есть и разошлись с тем, что мы собирались дать.
|
||||
|
||||
@@ -317,7 +344,12 @@ def skeleton(number: int, docs: dict | None = None, tasks: dict | None = None) -
|
||||
out += ["", "[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):
|
||||
out.append(f"{key} = {quote(tasks[key])}")
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
@@ -21,9 +21,18 @@
|
||||
проверяет вовсе, — а вложенных блоков разметка не знает, и внутри `язык-правила`
|
||||
его было бы не забрать отдельно.
|
||||
|
||||
Правила — для всего, что пишется словами: задачи и цели, документы канона,
|
||||
решения ADR, записки разведки, сообщения коммитов. Не для кода и не для
|
||||
сообщений программы пользователю — там свои конвенции проекта.
|
||||
Правила — для всего, что пишется словами **в этом плагине**: задачи, документы
|
||||
канона, решения ADR, записки разведки. Не для кода и не для сообщений программы
|
||||
пользователю — там свои конвенции проекта.
|
||||
|
||||
**Сообщения коммитов сюда не входят, и это названо намеренно.** Их форму держит
|
||||
отдельный плагин `av-dev-git`, скилл `commit`, и она с этими правилами
|
||||
расходится по существу: там предписан результат страдательным залогом
|
||||
(«добавлены», «обновлено»), здесь — действие активным. Расхождение осознанное:
|
||||
строка коммита отвечает на «что стало», а не на «что я сделал», и читают её в
|
||||
`git log` подряд сотнями. Объявлять юрисдикцию над чужим плагином, до которого
|
||||
отсюда нет и ссылки, значило бы завести правило, нарушение которого никто не
|
||||
увидит.
|
||||
|
||||
Основа — **информационный стиль** Максима Ильяхова ([учебник
|
||||
бюро](https://bureau.ru/projects/book-text/), книга «Пиши, сокращай»). Он
|
||||
@@ -228,8 +237,8 @@
|
||||
## Доклад вычитки
|
||||
|
||||
Не правило языка, а **контракт прохода**: форма, в которой находка приходит к
|
||||
человеку. Живёт здесь потому, что проходов вычитки несколько — по одному на
|
||||
плагин, — и разойтись формой они не должны.
|
||||
человеку. Живёт здесь потому, что проходов вычитки два — `doc-wording` по документам и
|
||||
`task-wording` по записям задач, — и разойтись формой они не должны.
|
||||
|
||||
<!-- дом: вычитка-доклад -->
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Сопровождение и эксплуатация
|
||||
|
||||
**Это дом.** Словарь «чем держат проект» назван в трёх местах трёх разных
|
||||
скиллов: секция `Сопровождение` в роадмапе (`task-track`), раздел «Эксплуатация»
|
||||
в `architecture.md` (`doc-canon`) и тема ревью `operations` (`code-review`). Ни
|
||||
скиллов: задачи типа `chore` (`task-track`), раздел «Эксплуатация»
|
||||
в `architecture.md` (`canon`) и тема ревью `operations` (`code-review`). Ни
|
||||
один из трёх им не владеет, поэтому дом стоит в `shared/`.
|
||||
|
||||
Три перечня «чем держат проект» уже разъезжались — на «метриках и логах» против
|
||||
@@ -18,16 +18,18 @@
|
||||
|
||||
| Место | Уровень | Что там |
|
||||
| --- | --- | --- |
|
||||
| `ROADMAP.md`, секция `Сопровождение` | план | **работы**, которые собираемся делать: цели и их задачи |
|
||||
| `BACKLOG.md`, задачи `chore` | план | **работы**, которые собираемся делать |
|
||||
| `architecture.md`, раздел «Эксплуатация» | состояние | **как устроено сейчас**: где работает, что рядом, кто перезапускает |
|
||||
| тема ревью `operations` | оптика | **чем проверяем**: «это упало через неделю на проде» |
|
||||
|
||||
Слово **«поддержка» не употребляется вовсе** — в нём слышится помощь
|
||||
пользователю, а это другая работа.
|
||||
пользователю, а это другая работа. По той же причине им не названа и **стадия
|
||||
проекта**: у неё имя «доработка» (`support`), словарь — `task-track`, «Две
|
||||
стадии».
|
||||
|
||||
**Граница с возможностями проходит по тому, кто наблюдает.** «Приложение
|
||||
сообщает о своём состоянии» — возможность приложения, её место среди прочих
|
||||
целей: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в разные
|
||||
секции роадмапа, и это верно — секции отвечают на разные вопросы.
|
||||
задач: наблюдает пользователь сервиса. «Дежурный видит состояние на одном
|
||||
экране» — сопровождение: наблюдаем мы. Одни и те же метрики попадают в задачи
|
||||
разных типов, и это верно — типы отвечают на разные вопросы.
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: doc-canon
|
||||
description: Привести проект к канону документов av-dev и держать его в соответствии — три операции одной машиной сравнения. check — что разошлось с текущей версией канона; adopt — перевод проекта из любой прежней раскладки (docs/specs, drafts, backlog, BRIEF.md, review-brief) в канон с переносом файлов; upgrade — повышение проекта с версии канона N до текущей по журналу версий. Использовать, когда просят проверить документацию проекта, перевести проект на канон, обновить его под новую версию канона или когда пришли в старый проект и надо понять, что в нём не так. Заведение нового проекта с нуля — скилл av-dev:doc-init.
|
||||
name: canon
|
||||
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` | проект в чужой раскладке | перенос в канон |
|
||||
| `upgrade` | канон вырос, проект отстал | по журналу версий |
|
||||
|
||||
**Имя без префикса, и это не случайность.** Остальные скиллы названы по
|
||||
материалу, с которым работают, — `doc-`, `task-`, `code-`; этот работает не с
|
||||
материалом, а с **формой**, и она у всех частей проекта одна. `check` сверяет
|
||||
раскладку документов, `adopt` заводит все части сразу и зовёт владельцев каталога
|
||||
задач и `openspec/`, `upgrade` повышает **всю** раскладку одним журналом версий —
|
||||
и документы, и каталог задач. Содержимое при этом не его: документы ведёт
|
||||
`av-dev:doc-sync`, записи задач — `av-dev:task-track`.
|
||||
|
||||
**Определение канона — [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 version --dir <корень> # версия раскладки: скрипта и проекта
|
||||
@@ -58,11 +66,30 @@ python3 $ds bump --dir <корень> # поднять вер
|
||||
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
|
||||
верна.
|
||||
|
||||
**Коды выхода — тот же словарь, что у `tasks.py`:** 0 сошлось, 1 дрейф, 2 ошибка
|
||||
употребления, 3 окружение, 4 внутренний сбой. Ветвись на коде, а не на тексте.
|
||||
**Копия.** Дом словаря — `shared/axes.md` в репозитории плагина: словарь общий
|
||||
для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл.
|
||||
|
||||
Различать 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 не запускается: спеки не с чем сверять |
|
||||
|
||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||
поведении.
|
||||
@@ -228,10 +255,11 @@ capability), `openspec/config.yaml`.
|
||||
|
||||
**Задачи `docs.py` не проверяет** — их ведёт другой скилл, и согласованность
|
||||
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
|
||||
его отчёт идёт в доклад отдельной строкой, и пункт «задачи без цели» в нём
|
||||
зелёным не станет: цели не сочиняются адаптацией (запрет записан у того, кто
|
||||
ведёт задачи), их проставляет человек порциями переоценки на первом груминге —
|
||||
скилл `av-dev:task-groom`.
|
||||
его отчёт идёт в доклад отдельной строкой, и зелёным он сразу не станет:
|
||||
у перенесённых записей нет критериев приёмки, а `check` без объявленной
|
||||
**стадии** отказывает вовсе. Стадию называет человек (`tasks.py stage
|
||||
build|support`) — машина её не выводит: список пунктов одинаково выглядит и
|
||||
планом стройки, и очередью правок.
|
||||
|
||||
### 5. Объяви переходное состояние
|
||||
|
||||
+31
-28
@@ -6,7 +6,7 @@
|
||||
[журнала](changelog.md). Литерал в шапке был третьим и отстал на первом же
|
||||
повышении — версию 13 он пережил, объявляя канон двенадцатым.
|
||||
|
||||
Это **единственный дом определения канона**. Скиллы `doc-init`, `doc-canon` и `doc-sync`
|
||||
Это **единственный дом определения канона**. Скиллы `doc-init`, `canon` и `doc-sync`
|
||||
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
||||
работать будет то, которое прочитали последним. Меняется канон — меняется этот
|
||||
файл и появляется запись в [changelog.md](changelog.md).
|
||||
@@ -19,7 +19,7 @@
|
||||
Рядом лежит OpenSpec, у которого структура тоже строгая.
|
||||
|
||||
Цена принята сознательно: плагин не переносится на чужой репозиторий как есть —
|
||||
чужой репозиторий **приводится** к канону скиллом `doc-canon`.
|
||||
чужой репозиторий **приводится** к канону скиллом `canon`.
|
||||
|
||||
Раскладка отвечает, **где** текст лежит и на какой вопрос отвечает. Каким он
|
||||
должен быть **словами** — общий для всех документов канона файл
|
||||
@@ -29,7 +29,7 @@
|
||||
## Сопровождение и эксплуатация — целое и часть
|
||||
|
||||
Словарь этой темы — [shared/operations.md](../../../shared/operations.md):
|
||||
целое и часть, три места одной темы (секция роадмапа, раздел архитектуры, тема
|
||||
целое и часть, три места одной темы (задачи `chore`, раздел архитектуры, тема
|
||||
ревью `operations`) и граница с возможностями проекта. Здесь он не
|
||||
пересказывается: копия жила рядом с домом в одном дереве и была ровно тем
|
||||
вторым домом, против которого правило и написано.
|
||||
@@ -71,6 +71,9 @@ openspec/
|
||||
|
||||
## Три категории документов
|
||||
|
||||
Категория документа — ось процесса; перечень осей и того, чего каждая **не**
|
||||
решает, — [shared/axes.md](../../../shared/axes.md).
|
||||
|
||||
Раньше здесь стояло плоское правило «каждый документ `docs/` — тема ревью». Оно
|
||||
неверно ровно наполовину: паспорт и схема хранилища ревью нужны, но темами не
|
||||
являются, а журнал решений и журнал наблюдений ревью изменения не нужны вовсе.
|
||||
@@ -348,42 +351,42 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
скриптом и своими проверками; где каталог лежит и как названы его части, говорит
|
||||
секция `[tasks]` в `.av-dev.toml`. Версия там одна на всю раскладку: канон и
|
||||
каталог задач двигаются вместе, потому что ведёт их один плагин.
|
||||
Канон **резервирует место** в `docs/` и внутрь не смотрит:
|
||||
`docs.py` каталог не открывает, его отсутствия не считает дрейфом и согласованность
|
||||
задач не проверяет. Проект, не заведший каталог задач, их не ведёт
|
||||
Каталог лежит **в корне репозитория, вне `docs/`** — места в `docs/` канон ему
|
||||
больше не резервирует (переезд — запись 11 закрытого журнала), а внутрь не
|
||||
смотрит и подавно: `docs.py` каталог не открывает, его отсутствия не считает
|
||||
дрейфом и согласованность задач не проверяет. Проект, не заведший каталог задач, их не ведёт
|
||||
вовсе, и отказом это быть не может.
|
||||
|
||||
Раскладку, форму записи и команды держит скилл `av-dev:task-track`. Ниже — то,
|
||||
от чего зависит, читается ли проект как продукт: канон высказывается об этом
|
||||
потому, что роадмап отвечает на вопрос о **системе**, а не о работах.
|
||||
от чего зависит, читается ли проект как продукт.
|
||||
|
||||
**`ROADMAP.md` отвечает на «что приложение уже умеет и чего ещё не умеет».** Это
|
||||
не очередь работ: цель — **возможность приложения**, задача — шаг к ней.
|
||||
Достигнутая цель из роадмапа **не исчезает** — строка с датой переезжает в
|
||||
секцию достигнутого, потому что «что умеет» и есть половина вопроса, ради
|
||||
которого документ открывают. Вторым домом поведения роадмап при этом не
|
||||
становится: нормативное поведение живёт в `openspec/specs/`, роадмап отвечает,
|
||||
**когда и в каком порядке** оно появилось.
|
||||
**Индекс работ один — `BACKLOG.md`, и «что приложение уже умеет» он не
|
||||
отвечает.** На этот вопрос отвечают `openspec/specs/` (нормативное поведение) и
|
||||
`git log` индекса (когда и в каком порядке оно появилось). Роадмапа в каноне
|
||||
нет: половину своего вопроса он дублировал беклогом, а вторую — спеками.
|
||||
|
||||
**У каждой записи есть тип, и тип решает, что с ней можно делать.** Дом типа —
|
||||
**У проекта есть стадия, и она решает, что значит порядок строк беклога:**
|
||||
`build` — зависимость, `support` — важность. Канон её называет, потому что от
|
||||
неё зависит, читается ли список работ как план стройки или как очередь правок;
|
||||
механика — `task-track`, «Две стадии».
|
||||
|
||||
**У каждой задачи есть тип, и тип решает, что с ней можно делать.** Дом типа —
|
||||
поле меты `Тип` первой строкой; эмодзи в заголовке от него производна. Словарь
|
||||
закрыт:
|
||||
|
||||
| Тип | Что это |
|
||||
| --- | --- |
|
||||
| 🎯 `goal` | возможность приложения |
|
||||
| ✨ `feature` | снаружи появляется то, чего не было |
|
||||
| 🐞 `fix` | поведение расходится с заявленным |
|
||||
| 🧹 `chore` | обслуживание, поведение не меняется |
|
||||
| 🔬 `research` | исход — знание, а не изменение |
|
||||
|
||||
**Схемы записи здесь нет намеренно.** Какие разделы тип требует, нужна ли ему
|
||||
цель и берётся ли он в работу — скилл `av-dev:task-track`, раздел «Тип
|
||||
записи», подробно — по файлу на тип в его `references/task-<тип>.md`. Канон
|
||||
фиксирует **словарь**, потому что
|
||||
от него зависит, читается ли проект как продукт; схема — механика ведения задач,
|
||||
и второй её экземпляр разошёлся бы с первым (он и разошёлся: канон успел
|
||||
объявить цель у `fix` запрещённой, хотя она там необязательна).
|
||||
**Схемы записи здесь нет намеренно.** Какие разделы тип требует — скилл
|
||||
`av-dev:task-track`, раздел «Тип записи», подробно — по файлу на тип в его
|
||||
`references/task-<тип>.md`. Канон фиксирует **словарь**, потому что от него
|
||||
зависит, читается ли проект как продукт; схема — механика ведения задач, и второй
|
||||
её экземпляр разошёлся бы с первым (он и разошёлся: канон успел объявить цель у
|
||||
`fix` запрещённой, хотя она была необязательна, — и целей теперь нет вовсе).
|
||||
|
||||
Схема требуется **к взятию в работу**, а не к заведению: беклог пополняется чаще,
|
||||
чем разбирается, и требование на входе выгоняло бы в заметки то, что должно
|
||||
@@ -451,7 +454,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
| почему решено так | `adr/`, источник — архивный `design.md` либо записка разведки |
|
||||
| граница домена, «чем не является» | `passport.md` |
|
||||
| инвариант и его severity | `CLAUDE.md` |
|
||||
| что приложение умеет и чего не умеет; порядок работ | `tasks/ROADMAP.md` |
|
||||
| что осталось сделать и в каком порядке | `tasks/BACKLOG.md` |
|
||||
| измеренное число | `research/` |
|
||||
| настройка с числовым значением | `database.md` |
|
||||
| периметр и модель угроз | `security.md` |
|
||||
@@ -485,8 +488,8 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
| --- | --- |
|
||||
| `docs/review-brief.md` | документы канона и есть бриф; остаток — в `review.md` |
|
||||
| `docs/specs/` | `openspec/specs/` (поведение) и `architecture.md` (обзор) |
|
||||
| `docs/drafts/` | идея → запись `research`; отказ → ADR; порядок → `ROADMAP.md`; размышление → `opsx:explore` |
|
||||
| `docs/plan.md` | `tasks/ROADMAP.md` |
|
||||
| `docs/drafts/` | идея → запись `research`; отказ → ADR; порядок → `BACKLOG.md`; размышление → `opsx:explore` |
|
||||
| `docs/plan.md` | `tasks/BACKLOG.md` |
|
||||
| `BRIEF.md` | `passport.md` |
|
||||
| `docs/backlog/` | `tasks/` в корне репозитория |
|
||||
| `docs/review-journal.md`, `docs/review/journal.md` | `docs/review.md` |
|
||||
@@ -524,7 +527,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
разрез, что между `task-form` и `task-wording`.
|
||||
|
||||
**Зовутся оба одинаково и одним скиллом — `av-dev:doc-healthcheck`, на весь
|
||||
канон разом; шагом `adopt` и шагом `upgrade` его зовёт `doc-canon`.** Не на синке
|
||||
канон разом; шагом `adopt` и шагом `upgrade` его зовёт `canon`.** Не на синке
|
||||
документации: `doc-consistency` на
|
||||
`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` о таком
|
||||
плейсхолдере напоминает.
|
||||
@@ -32,7 +32,7 @@
|
||||
# Паспорт проекта
|
||||
|
||||
Зачем это и для кого. [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`
|
||||
@@ -6,12 +6,8 @@
|
||||
маркеры долга и две сверки с кодом. Смысловые дубли и оставшееся в архитектуре
|
||||
поведение судит агент — скрипт об этом говорит вслух в конце отчёта.
|
||||
|
||||
Коды выхода — тот же словарь, что у tasks.py:
|
||||
0 сошлось
|
||||
1 дрейф раскладки (рабочая ситуация, чинится)
|
||||
2 ошибка употребления
|
||||
3 окружение: не тот каталог, битый конфиг
|
||||
4 внутренний сбой
|
||||
Коды выхода — общий словарь скриптов av-dev; дом словаря и разбор «дрейф
|
||||
против окружения» — av-dev/shared/axes.md. Значения — в константах ниже.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -131,10 +127,10 @@ NOT_DOCS = {".docs.json", ".pm.json", "tasks"}
|
||||
RETIRED = {
|
||||
"review-brief.md": "документы канона и есть бриф; остаток — в review",
|
||||
"review-journal.md": "→ документ review",
|
||||
"plan.md": "→ tasks/ROADMAP.md (ведёт скилл task-track)",
|
||||
"plan.md": "→ tasks/BACKLOG.md (ведёт скилл task-track)",
|
||||
"local-research.md": "→ документ research",
|
||||
"specs": "поведение → openspec/specs/, обзор → тема architecture",
|
||||
"drafts": "идея → запись research, отказ → ADR, порядок → ROADMAP.md",
|
||||
"drafts": "идея → запись research, отказ → ADR, порядок → BACKLOG.md",
|
||||
"backlog": "→ tasks/ в корне репозитория (ведёт скилл task-track)",
|
||||
}
|
||||
|
||||
@@ -320,7 +316,7 @@ def check_version(root: Path, cfg: dict, rep: Report) -> None:
|
||||
if got < LAYOUT_VERSION:
|
||||
rep.error(
|
||||
f"проект приведён к раскладке версии {got}, текущая — {LAYOUT_VERSION}:"
|
||||
f" нужно повышение (скилл av-dev:doc-canon, операция upgrade)"
|
||||
f" нужно повышение (скилл av-dev:canon, операция upgrade)"
|
||||
)
|
||||
elif got > LAYOUT_VERSION:
|
||||
rep.error(
|
||||
@@ -377,7 +373,7 @@ def check_legacy(root: Path, rep: Report) -> None:
|
||||
f"нет {CONFIG}, а настройки лежат по прежней раскладке"
|
||||
f" ({', '.join(legacy)}) — она осталась от трёх плагинов, которые"
|
||||
f" слились в один: перенеси значения и удали старые файлы операцией"
|
||||
f" upgrade скилла av-dev:doc-canon (журнал, версия 1). Прежние имена не"
|
||||
f" upgrade скилла av-dev:canon (журнал, версия 1). Прежние имена не"
|
||||
f" читаются, поэтому в этом прогоне всё остальное проверено так, будто"
|
||||
f" настроек нет вовсе"
|
||||
)
|
||||
@@ -706,9 +702,22 @@ def cmd_bump(args: argparse.Namespace) -> int:
|
||||
if was is not None and was > LAYOUT_VERSION:
|
||||
fail(ENV, f"проект на версии {was}, скрипт знает {LAYOUT_VERSION}:"
|
||||
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 'не была объявлена'}"
|
||||
f" → {LAYOUT_VERSION} в {CONFIG}")
|
||||
f" → {target} в {CONFIG}")
|
||||
if target < LAYOUT_VERSION:
|
||||
print(f" до текущей ({LAYOUT_VERSION}) осталось записей журнала:"
|
||||
f" {LAYOUT_VERSION - target}. Пройди шаги следующей и позови bump"
|
||||
f" снова — по разу на запись")
|
||||
return OK
|
||||
|
||||
|
||||
@@ -74,10 +74,30 @@ python3 $os check --dir <корень> # форма config.yaml в проек
|
||||
python3 $os form # слепок формы против живого OpenSpec
|
||||
```
|
||||
|
||||
**Коды выхода — общий словарь скриптов av-dev:** 0 сошлось, 1 дрейф, 2 ошибка
|
||||
употребления, 3 окружение, 4 внутренний сбой. Ветвись на коде, а не на тексте.
|
||||
Различать 1 и 3 обязательно: «форма разошлась» — рабочая ситуация, «openspec не
|
||||
отвечает» — нерабочая.
|
||||
**Копия.** Дом словаря — `shared/axes.md` в репозитории плагина: словарь общий
|
||||
для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл.
|
||||
|
||||
<!-- копия: коды-выхода из av-dev/shared/axes.md -->
|
||||
|
||||
**Коды выхода — общий словарь всех скриптов `av-dev`. Ветвись на коде, а не на
|
||||
тексте вывода.**
|
||||
|
||||
| Код | Что случилось |
|
||||
| --- | --- |
|
||||
| 0 | сошлось |
|
||||
| 1 | дрейф: рабочая ситуация, чинится |
|
||||
| 2 | ошибка употребления: аргументы или нарушенное правило |
|
||||
| 3 | окружение: не тот каталог, битый конфиг, нет инструмента |
|
||||
| 4 | внутренний сбой — дефект скрипта, доложить |
|
||||
|
||||
**Различать 1 и 3 обязательно.** «Дрейф» — рабочая ситуация, и чинится она
|
||||
правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет.
|
||||
Одинаковая реакция на них неверна в обоих случаях.
|
||||
|
||||
<!-- /копия: коды-выхода -->
|
||||
|
||||
Здесь это значит: «форма разошлась» — рабочая ситуация, «openspec не отвечает» —
|
||||
нерабочая.
|
||||
|
||||
`check` проверяет форму, и каждая проверка — про молчащий пробел, а не про вкус:
|
||||
каталог есть; имя именно `config.yaml` (`config.yml` OpenSpec не читает и об этом
|
||||
@@ -118,7 +138,7 @@ python3 $os form # слепок формы против жив
|
||||
## Кто зовёт этот скилл
|
||||
|
||||
- `av-dev:doc-init` — шагом заведения нового проекта, до первого документа;
|
||||
- `av-dev:doc-canon` в режиме `adopt` — если на переводимом проекте каталога нет
|
||||
- `av-dev:canon` в режиме `adopt` — если на переводимом проекте каталога нет
|
||||
или `config.yaml` остался примером;
|
||||
- `av-dev:code-resolve` и `av-dev:code-review` — не вызовом по ходу, а отсылкой:
|
||||
OpenSpec у обоих жёсткая предпосылка, и на проекте без каталога оба посылают
|
||||
@@ -140,7 +160,7 @@ python3 $os form # слепок формы против жив
|
||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||
|
||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||
поведении.
|
||||
@@ -168,7 +188,7 @@ python3 $os form # слепок формы против жив
|
||||
## Чего этот скилл не делает
|
||||
|
||||
- **Не пишет спеки и предложения.** Это `opsx:propose` и конвейер задачи.
|
||||
- **Не ведёт документы канона** — их дом скилл `av-dev:doc-canon`, и адреса в
|
||||
- **Не ведёт документы канона** — их дом скилл `av-dev:canon`, и адреса в
|
||||
`context` только на них ссылаются.
|
||||
- **Не чинит расхождение формы с версией OpenSpec в проекте.** Оно чинится в
|
||||
плагине: константы скрипта, образец здесь, запись в журнал версий канона.
|
||||
|
||||
@@ -16,12 +16,8 @@
|
||||
PyYAML в стандартной библиотеке нет. Всё проверяемое различимо построчно,
|
||||
комментарии отброшены, ключи верхнего уровня стоят в первой колонке.
|
||||
|
||||
Коды выхода — общий словарь скриптов av-dev:
|
||||
0 сошлось
|
||||
1 дрейф: форма разошлась с ожидаемой
|
||||
2 ошибка употребления: аргументы
|
||||
3 окружение: не тот каталог, инструмент не отвечает
|
||||
4 внутренний сбой
|
||||
Коды выхода — общий словарь скриптов av-dev; дом словаря и разбор «дрейф
|
||||
против окружения» — av-dev/shared/axes.md. Значения — в константах ниже.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -220,7 +216,7 @@ def check_form(root: Path, rep: Report) -> None:
|
||||
rep.skip(
|
||||
f"{where} в проекте нет — ссылка на него в context не "
|
||||
f"требуется. Документы канона проект не завёл, и без них "
|
||||
f"конвейер работает вслепую: заводит их av-dev:doc-canon"
|
||||
f"конвейер работает вслепую: заводит их av-dev:canon"
|
||||
)
|
||||
continue
|
||||
if pointer not in live:
|
||||
|
||||
@@ -41,12 +41,20 @@ description: "Взять одну задачу и довести её до за
|
||||
делает скилл `av-dev:code-openspec`. **Сценариям разведки и обслуживания
|
||||
OpenSpec не нужен** — они не заводят change; `opsx:explore` берётся разведкой,
|
||||
если плагин есть.
|
||||
- **Проектные копии этих скиллов и агентов удаляются при установке плагина**
|
||||
(`.claude/skills/` — голые имена `resolve`, `review`, а у проектов прошлого
|
||||
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`, и с префиксом
|
||||
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`;
|
||||
`.claude/agents/<проект>-review-*.md`). Две копии одного скилла расходятся, и
|
||||
побеждает та, что короче названа.
|
||||
- **Проектные копии этих скиллов и агентов удаляются при установке плагина.**
|
||||
|
||||
<!-- копия: проектные-копии из README.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 не запускается: спеки не с чем сверять |
|
||||
|
||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||
поведении.
|
||||
@@ -97,7 +105,7 @@ description: "Взять одну задачу и довести её до за
|
||||
карта «что где» — `references/project-facts.md` конвейера ревью.
|
||||
|
||||
**Документов канона нет — проект к нему не приведён.** Скажи это строкой и
|
||||
предложи скилл `av-dev:doc-canon`: одна операция на проект против поразрядной
|
||||
предложи скилл `av-dev:canon`: одна операция на проект против поразрядной
|
||||
деградации на каждой задаче. Работу при этом не останавливай.
|
||||
|
||||
## Вход
|
||||
@@ -108,8 +116,7 @@ description: "Взять одну задачу и довести её до за
|
||||
|
||||
**Запись из каталога сперва проверяется на готовность, и проверяет её машина.**
|
||||
Вызови 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:doc-sync`. Закрытие — работа этого скилла, и это осознанное решение с
|
||||
названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек на
|
||||
груминге (`av-dev:task-groom`) возвращает задачу `reopen` с причиной, а доклад
|
||||
названной ценой: **приёмщик и исполнитель совпали**. Закрытие поэтому **не окончательно** — человек
|
||||
возвращает задачу `reopen` с причиной (на доработке это делают грумингом,
|
||||
`av-dev:task-groom`, на стройке — сразу, как заметили), а доклад
|
||||
по критериям приёмки становится единственным, по чему приёмка вообще возможна.
|
||||
- **Определением ценности.** «Нужна ли эта функциональность» — не вопрос этого
|
||||
скилла ни на одном шаге и ни в одном сценарии. Чекпоинт решения спрашивает «так
|
||||
|
||||
@@ -81,10 +81,15 @@
|
||||
- **чем задача становится** — `fix` или `feature`, с причиной из разреза выше;
|
||||
- **что уже сделано** и что из этого лежит в рабочем дереве.
|
||||
|
||||
Проверка на простой язык та же, что у чекпоинтов двух других сценариев: **в
|
||||
тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||
Проверка на простой язык — общая у трёх сценариев:
|
||||
|
||||
<!-- копия: чекпоинт-простой-язык из av-dev/skills/code-resolve/references/solve.md -->
|
||||
|
||||
**в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||
по-русски, и нет слов, которых нет в паспорте проекта.**
|
||||
|
||||
<!-- /копия: чекпоинт-простой-язык -->
|
||||
|
||||
**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` поднимается, шаг
|
||||
сборки отрабатывает, хук ставится на чистом клоне. Правка, которую нельзя
|
||||
сборки отрабатывает, хук ставится на чистом клоне. **У оснастки, заводимой
|
||||
впервые, прежнего нет** — тогда проверяется, что заведённое работает на чистом
|
||||
клоне, и в докладе это называется своим именем, а не «регрессий не найдено». Правка, которую нельзя
|
||||
проверить ничем, кроме «у меня локально работает», называется в докладе строкой.
|
||||
|
||||
### 4. Ревью — план фиксирован сценарием
|
||||
@@ -259,12 +277,16 @@ Change ты не передаёшь — его нет.
|
||||
Поэтому план у сценария **свой и постоянный**, и глубину он называет сам —
|
||||
проходы берут её из метки, а метки здесь нет:
|
||||
|
||||
<!-- дом: план-без-метки -->
|
||||
|
||||
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда |
|
||||
| `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда |
|
||||
| `conventions` + технический разбор | `conventions.*` | `review-code` | вход `small`: только индекс конвенций; потолки 3 технических и 2 конвенционных; **третья половина включена** — сверка с инвариантами `CLAUDE.md`, потолок 1 | дифф трогает код, а не только оснастку |
|
||||
|
||||
<!-- /дом: план-без-метки -->
|
||||
|
||||
**Глубина названа в плане потому, что иначе её неоткуда взять.** Вход и потолки
|
||||
`review-code` заданы меткой, у `review-basics` меткой задана и сама возможность
|
||||
запуска; на прогоне без метки оба взяли бы их наугад — то есть по-разному от
|
||||
@@ -324,16 +346,16 @@ Change ты не передаёшь — его нет.
|
||||
**`adr/` этот сценарий не пополняет, и это не пропуск.** У ADR закрытый список
|
||||
источников — архивный `design.md` либо записка разведки, — и ни того ни другого
|
||||
обслуживание не производит. Поэтому триггеры ADR здесь работают **стоп-признаком,
|
||||
а не поводом завести запись**: сработал дорогой откат, намеренный отказ от
|
||||
очевидного подхода или пересмотр прежнего решения — сценарий выбран неверно,
|
||||
объявляй исход **нужна разведка** и останавливайся. Решение с ценой обязано
|
||||
а не поводом завести запись**: сработал любой из них — сценарий выбран неверно,
|
||||
объявляй исход **нужна разведка** и останавливайся. Перечень триггеров — в
|
||||
[canon.md](../../canon/references/canon.md#adr) и здесь не пересказывается. Решение с ценой обязано
|
||||
пройти чекпоинт вариантов, а не появиться в коммите обслуживания, чей смысл —
|
||||
«ничего не решали, поменяли оснастку».
|
||||
|
||||
Список документов и их триггеров здесь не дублируется — он в чек-листе скилла
|
||||
`av-dev:doc-sync`; копия уже однажды разошлась с оригиналом. Документов канона в
|
||||
проекте нет — синка нет вовсе: скажи это исходом и предложи завести канон
|
||||
скиллом `av-dev:doc-canon`.
|
||||
скиллом `av-dev:canon`.
|
||||
|
||||
### 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`. Твоё — содержание ответа, их —
|
||||
форма и дом.
|
||||
@@ -227,9 +227,14 @@ git и читается диффом, а второй стоп на каждой
|
||||
отвергнутое (отвергнутый вариант с названной причиной — половина будущего ADR);
|
||||
приносить один вариант и называть это выбором.
|
||||
|
||||
Проверка на простой язык та же, что у чекпоинта решения: **в тексте нет `SHALL`,
|
||||
нет имён файлов и функций там, где вещь называется по-русски, и нет слов, которых
|
||||
нет в паспорте проекта.**
|
||||
Проверка на простой язык — общая у трёх сценариев:
|
||||
|
||||
<!-- копия: чекпоинт-простой-язык из av-dev/skills/code-resolve/references/solve.md -->
|
||||
|
||||
**в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется
|
||||
по-русски, и нет слов, которых нет в паспорте проекта.**
|
||||
|
||||
<!-- /копия: чекпоинт-простой-язык -->
|
||||
|
||||
Исходы чекпоинта:
|
||||
|
||||
@@ -255,8 +260,8 @@ git и читается диффом, а второй стоп на каждой
|
||||
- **ответ на вопрос** — по адресу из шага 1;
|
||||
- **отвергнутые варианты с причинами.** Это половина будущего ADR и единственная
|
||||
защита от повторной разведки того же самого;
|
||||
- **решение с ценой — в ADR**, если оно проходит триггер канона (дорогой откат,
|
||||
намеренный отказ, пересмотр прежнего решения). У разведки, кончившейся без
|
||||
- **решение с ценой — в ADR**, если оно проходит [триггер
|
||||
канона](../../canon/references/canon.md#adr). У разведки, кончившейся без
|
||||
кода, `design.md` не будет никогда, поэтому здесь ADR цитирует **записку
|
||||
разведки**, а не архивный change; канон это допускает прямо, и в записи
|
||||
источник называется.
|
||||
@@ -267,7 +272,7 @@ git и читается диффом, а второй стоп на каждой
|
||||
перечня адресов неотличим от доклада о ненаписанном.
|
||||
|
||||
**Документов канона в проекте нет** — писать ответ некуда: назови это исходом,
|
||||
предложи завести канон скиллом `av-dev:doc-canon` и оставь ответ в докладе
|
||||
предложи завести канон скиллом `av-dev:canon` и оставь ответ в докладе
|
||||
целиком, чтобы работа не пропала. Заводить `docs/` мимо канона нельзя.
|
||||
|
||||
### 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` — он заводит каталог и заменяет
|
||||
пример в `config.yaml` настройкой. Его же зовут `av-dev:doc-init` на новом
|
||||
проекте и `av-dev:doc-canon` в режиме `adopt` — на переводимом.
|
||||
проекте и `av-dev:canon` в режиме `adopt` — на переводимом.
|
||||
**Предпосылка эта — про изменение поведения, а не про всякий прогон:**
|
||||
сценарий обслуживания зовёт конвейер без change и без дельта-спек, и ни один
|
||||
проход его плана на них не завязан. См. «Прогон без change».
|
||||
- **Документы канона** — см. следующий раздел.
|
||||
- **Проектные копии этих скиллов и агентов удаляются при установке.** Если в
|
||||
проекте уже лежат свои `.claude/skills/review`,
|
||||
`.claude/skills/review-pipeline`, `.claude/skills/task-pipeline`,
|
||||
`.claude/skills/task-batch`, `.claude/skills/resolve` или
|
||||
`.claude/agents/<проект>-review-*.md` — снеси их. Иначе короткое имя разрешится
|
||||
в устаревшую проектную копию, молча и без признаков подмены.
|
||||
- **Проектные копии этих скиллов и агентов удаляются при установке.**
|
||||
|
||||
<!-- копия: проектные-копии из README.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 не запускается: спеки не с чем сверять |
|
||||
|
||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||
поведении.
|
||||
@@ -132,7 +140,7 @@ description: "Конвейер ревью изменения, устроенны
|
||||
критерий, по которому судит изменение. `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`
|
||||
или `large`. Это **единственный вход, по которому конвейер выбирает
|
||||
исполнителей**: и на дизайне, и на коде состав читается из неё, а не из класса
|
||||
@@ -330,6 +343,8 @@ charter'а, а модель потом двигает калибровка, и
|
||||
|
||||
Ревью кода:
|
||||
|
||||
<!-- дом: тема-метка-глубина -->
|
||||
|
||||
| Тема | `small` | `medium` | `large` |
|
||||
|---|---|---|---|
|
||||
| `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор |
|
||||
@@ -340,6 +355,8 @@ charter'а, а модель потом двигает калибровка, и
|
||||
| `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство |
|
||||
| тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор |
|
||||
|
||||
<!-- /дом: тема-метка-глубина -->
|
||||
|
||||
Весь процесс с выбором исполнителей на каждом этапе — одной схемой. **Метка
|
||||
считается один раз, в узле разметки, и дальше только читается:**
|
||||
|
||||
@@ -402,7 +419,7 @@ flowchart TD
|
||||
|
||||
Отсюда состав обеих стадий:
|
||||
|
||||
| Метка | Когда | Ревью дизайна | Ревью кода: стадии | Проходов всего | Доля задач |
|
||||
| Метка | Когда | Ревью дизайна | Ревью кода: ступени | Проходов всего | Доля задач |
|
||||
|---|---|---|---|---|---|
|
||||
| `small` | малое **и** знакомое: багфикс, локальная правка, доки | `specs` | 1, 2, 5 (+3 при своих темах) | **5–6** | **до трети, и меньше, чем `medium`** |
|
||||
| `medium` | **рабочее умолчание**: среднее и знакомое | `specs`, `rubric` | 1, 2, 3, 5 | **7** | **большинство** |
|
||||
@@ -447,8 +464,8 @@ flowchart TD
|
||||
— и очередь между ними была бы платой ни за что.
|
||||
|
||||
Рёбер два вида, и они разной природы. Путать их нельзя: первое про
|
||||
**осмысленность** (без плана задание не определено, на красном гейте проход с мнением
|
||||
проход не о чем), второе про **железо**.
|
||||
**осмысленность** (без плана задание не определено, а на красном гейте проходу с
|
||||
мнением не о чем судить), второе про **железо**.
|
||||
|
||||
| Ребро | Смысл | Между кем |
|
||||
|---|---|---|
|
||||
@@ -463,7 +480,7 @@ flowchart TD
|
||||
```mermaid
|
||||
flowchart TD
|
||||
plan[/"план разметки задачи<br/>(готов до ревью кода)"/]
|
||||
autotests["autotests<br/>(стадия 1, держит машину)"]
|
||||
autotests["autotests<br/>(ступень 1, держит машину)"]
|
||||
specs["specs"]
|
||||
code["code"]
|
||||
basics["basics<br/>(medium: темы ядра и свои;<br/>small, large: только свои темы проекта)"]
|
||||
@@ -513,7 +530,7 @@ flowchart TD
|
||||
|
||||
Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск.
|
||||
Проходы, заявившие его, сериализуются между собой при любой метке и на любой
|
||||
стадии; порядок внутри цепочки произволен.
|
||||
ступени; порядок внутри цепочки произволен.
|
||||
|
||||
| Проход | Держит машину | Почему |
|
||||
|---|---|---|
|
||||
@@ -653,6 +670,30 @@ flowchart TD
|
||||
(тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит **без
|
||||
change**: у работы, не меняющей поведения, дельта-спек нет по построению.
|
||||
|
||||
**Копия.** Дом оси — `shared/axes.md` в репозитории плагина: режим делят конвейер,
|
||||
сценарий обслуживания и два устава, и ни один из них им не владеет. Правится дом,
|
||||
а не этот файл.
|
||||
|
||||
<!-- копия: режим-прогона из av-dev/shared/axes.md -->
|
||||
|
||||
**Прогон ревью идёт в одном из двух режимов, и режим — не глубина.**
|
||||
|
||||
- **С меткой** — обычный прогон по change: разметку сделал `review-scope`, состав
|
||||
обеих стадий выведен из метки.
|
||||
- **Без метки** — прогон сценария обслуживания: change нет, размечать нечего,
|
||||
план фиксирован и назван сценарием. Разметчик не запускается вовсе.
|
||||
|
||||
**Без метки — не то же самое, что `small`.** `small` — это суждение о размере и
|
||||
сложности, снятое с изменения; отсутствие метки — утверждение, что снимать её
|
||||
не с чего. Проход, подставивший себе `small` там, где метки нет, вывел бы
|
||||
глубину из ничего.
|
||||
|
||||
**Режим правит не только состав, но и саму возможность запуска.** Проход, у
|
||||
которого запуск задан меткой, без метки не имеет ответа на вопрос «запускаться
|
||||
ли» — и ответ ему даёт план сценария, а не умолчание.
|
||||
|
||||
<!-- /копия: режим-прогона -->
|
||||
|
||||
**Метка на таком прогоне не назначается, и разметчик не зовётся.** Обе его оси
|
||||
здесь не определены: размер он выводит из `proposal.md`, `design.md`, `tasks.md`
|
||||
и дельта-спек, а сложность — из формы решения, которая у обслуживания либо
|
||||
@@ -664,11 +705,15 @@ change**: у работы, не меняющей поведения, дельт
|
||||
называет глубину и вход каждого прохода** — их обычный источник метка, и без неё
|
||||
проходы взяли бы их наугад:
|
||||
|
||||
| Тема | Кто закрывает | Глубина и вход | Когда |
|
||||
|---|---|---|---|
|
||||
| `autotests` | `review-autotests` | как обычно | всегда |
|
||||
| `operations` | `review-basics` | сверка, потолок 2 | всегда |
|
||||
| `conventions` + технический разбор | `review-code` | вход `small` (индекс конвенций), потолки 3 и 2, третья половина включена — потолок 1 | дифф трогает код |
|
||||
<!-- копия: план-без-метки из av-dev/skills/code-resolve/references/maintain.md -->
|
||||
|
||||
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `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`; не объявил — это строка границ покрытия, а
|
||||
не догадка прохода.
|
||||
|
||||
## Стадия 1 — Автотесты (обязательна при любой метке)
|
||||
## Ступень 1 — Автотесты (обязательна при любой метке)
|
||||
|
||||
Агент `review-autotests`, тема `autotests`. Запускает команду гейта из семантики
|
||||
гейта в `CLAUDE.md` и интерпретирует вывод.
|
||||
@@ -714,11 +759,11 @@ change**: у работы, не меняющей поведения, дельт
|
||||
Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу
|
||||
запрещено списывать такой отказ в мелочь.
|
||||
|
||||
## Стадия 2 — Сверка (обязательна при любой метке)
|
||||
## Ступень 2 — Сверка (обязательна при любой метке)
|
||||
|
||||
Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра
|
||||
между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со
|
||||
стадией 3 или 4 — той, что в метки.
|
||||
стадией 3 или 4 — той, которую назначила метка.
|
||||
|
||||
- `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек
|
||||
предлагаемого изменения**, а не из proposal, сообщения коммита или описания
|
||||
@@ -728,7 +773,7 @@ change**: у работы, не меняющей поведения, дельт
|
||||
обычном входе: необработанная ветка отказа, пустое значение, граница диапазона,
|
||||
перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс
|
||||
библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть,
|
||||
которая **не выражается правилом**: механизируемое уже проверила стадия 1.
|
||||
которая **не выражается правилом**: механизируемое уже проверила ступень 1.
|
||||
**На `small` у него есть третья, узкая обязанность** — сверить дифф с
|
||||
записанными инвариантами `CLAUDE.md` по темам `security`, `operations` и
|
||||
`architecture`, потому что с этой меткой `basics` не идёт. Потолок 1 находка
|
||||
@@ -756,7 +801,7 @@ change**: у работы, не меняющей поведения, дельт
|
||||
недосмотренной темы.
|
||||
|
||||
Recall темы `conventions` равен длине конвенций проекта — это предел любой
|
||||
сверки, и ровно ради него существуют стадии 3 и 4.
|
||||
сверки, и ровно ради него существуют ступени 3 и 4.
|
||||
|
||||
**Оба прохода на верхней модели, и по одной причине — цене пропуска.** У `specs`
|
||||
это направление `code → spec`: надо заметить **отсутствие** — тихий фолбэк,
|
||||
@@ -765,7 +810,7 @@ Recall темы `conventions` равен длине конвенций прое
|
||||
границах покрытия; прочие проходы с мнением держат `opus` из-за цены **ложных**
|
||||
находок, эти двое — из-за цены пропущенных.
|
||||
|
||||
## Стадия 3 — Темы (`medium` целиком; `small` и `large` — только свои темы проекта)
|
||||
## Ступень 3 — Темы (`medium` целиком; `small` и `large` — только свои темы проекта)
|
||||
|
||||
Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не
|
||||
меряет — уходит одним сообщением вместе со стадией 2, сразу после зелёного гейта.
|
||||
@@ -802,7 +847,7 @@ Recall темы `conventions` равен длине конвенций прое
|
||||
взгляда на ось времени — значит изменение, которое не откатывается обратной
|
||||
правкой, на `small` не идёт вовсе, каким бы малым оно ни было.
|
||||
|
||||
## Стадия 4 — Доказательство (только `large`)
|
||||
## Ступень 4 — Доказательство (только `large`)
|
||||
|
||||
Три прохода, и все три уходят сразу после зелёного гейта, в одном ряду со
|
||||
стадией 2. Каждый берёт свою тему и доводит её до **доказательства**:
|
||||
@@ -831,7 +876,7 @@ Recall темы `conventions` равен длине конвенций прое
|
||||
ось времени и эксплуатации. Ровно поэтому они и стоят денег: оракул добывается
|
||||
запуском, а запуск — это машина, цепочка и часы.
|
||||
|
||||
Раньше эта пара стояла в `medium`, то есть на большинстве задач. Стадия
|
||||
Раньше эта пара стояла в `medium`, то есть на большинстве задач. Ступень
|
||||
переехала в `large` **сознательно и по цене, а не потому, что перестала находить**:
|
||||
она осталась самой ценной, но её ценность оплачивается на каждой задаче, а
|
||||
получается — на немногих. Что из-за этого перестало проверяться на младших метках, названо в «Честном пределе» и обязано идти строкой в границы покрытия
|
||||
@@ -849,7 +894,7 @@ Recall темы `conventions` равен длине конвенций прое
|
||||
записки; для архитектурного — что граница домена берётся из `passport.*`, а не из
|
||||
истории решений. Обе потери названы в «Честном пределе».
|
||||
|
||||
**Условие стадии и есть условие метки `large`:** изменение крупное **или**
|
||||
**Условие ступени и есть условие метки `large`:** изменение крупное **или**
|
||||
незнакомое — любая из двух осей. Разведены они не для красоты: у архитектурного
|
||||
прохода работа появляется от **размера** (трогается несколько слоёв разом или в
|
||||
проекте становится больше сущностей, чем было), у меряющей пары — от
|
||||
@@ -870,7 +915,7 @@ Recall темы `conventions` равен длине конвенций прое
|
||||
конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс
|
||||
секция «дешевле переделать до мерджа».
|
||||
|
||||
## Стадия 5 — Triage (обязательна)
|
||||
## Ступень 5 — Triage (обязательна)
|
||||
|
||||
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
|
||||
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
|
||||
@@ -1113,7 +1158,7 @@ flowchart TD
|
||||
и где это лежит в документах проекта; таблица поразрядной деградации.
|
||||
- [references/review-levels.md](references/review-levels.md) — дом правила выбора
|
||||
метки: две оси, спорное вниз, чем `small` дешевле, доли как проверка правила.
|
||||
- Skill `av-dev:doc-canon` — приведение проекта к канону документов.
|
||||
- Skill `av-dev:canon` — приведение проекта к канону документов.
|
||||
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
||||
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
|
||||
- [references/calibration.md](references/calibration.md) — калибровка инъекцией, вердикты keep/retune/drop.
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
|
||||
## Шкала severity
|
||||
|
||||
Severity — ось процесса; перечень осей — [shared/axes.md](../../../shared/axes.md).
|
||||
|
||||
| Severity | Что это | Пример |
|
||||
|---|---|---|
|
||||
| `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`, который кладёт
|
||||
в проект `av-dev:doc-canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы
|
||||
в проект `av-dev:canon`, повторяет её дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы
|
||||
здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе
|
||||
проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет.
|
||||
Дословность сверяет `scripts/copies.py` маркетплейса по маркерам ниже — но
|
||||
|
||||
@@ -5,20 +5,26 @@
|
||||
его достаточно. Здесь то, что читают, когда метку **выбирают, оспаривают или
|
||||
калибруют**.
|
||||
|
||||
Применяет правило `review-scope` при разметке задачи — не автор изменения. Его
|
||||
рабочая выжимка лежит в уставе агента; расходиться она с этим файлом не вправе, а
|
||||
при расхождении прав этот.
|
||||
Применяет правило `review-scope` при разметке задачи — не автор изменения. Сама
|
||||
матрица уехала в его устав **помеченной копией**, и дословность её держит
|
||||
`copies.py`, а не обещание: прежде здесь стояло «расходиться не вправе», и
|
||||
подкреплено это было ничем. Проза вокруг матрицы — отрицательный тест `small`,
|
||||
доли, цена — принадлежит месту и живёт только здесь.
|
||||
|
||||
## Правило выбора — две оси, а не один вопрос
|
||||
|
||||
**Оси две, они измеряют разное, и метка есть максимум по ним.**
|
||||
|
||||
<!-- дом: матрица-метки -->
|
||||
|
||||
| | **знакомое** — форму решения можно назвать до начала | **незнакомое** — форму предстоит нащупать по ходу |
|
||||
|---|---|---|
|
||||
| **малое** — один узел | `small` | `large` |
|
||||
| **среднее** — несколько узлов одного слоя | `medium` | `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
|
||||
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`;
|
||||
- **вернулись к проекту после перерыва** — прежде чем опираться на написанное;
|
||||
- **перед тем как опереться на документ в решении**, если оно дорогое;
|
||||
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `doc-canon` сам.
|
||||
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `canon` сам.
|
||||
|
||||
**Не на каждой задаче и не на каждом синке документации.** Цена реальная:
|
||||
`doc-consistency` идёт на `opus`, потому что сличение утверждений — суждение;
|
||||
@@ -52,7 +52,7 @@ check` и его скрипт; здесь начинается там, где к
|
||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||
|
||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||
поведении.
|
||||
@@ -132,15 +132,15 @@ check` и его скрипт; здесь начинается там, где к
|
||||
идёт из его собственного отчёта — перечень фактов у него закрытый, и он
|
||||
называет, какие из них проверить было нечем.
|
||||
- Канона в проекте нет вовсе — это исход, а не пустой прогон: скажи строкой и
|
||||
предложи `av-dev:doc-canon`.
|
||||
предложи `av-dev:canon`.
|
||||
|
||||
## Чего этот скилл не делает
|
||||
|
||||
- **Не проверяет раскладку, версию и ссылки** — это `doc-canon check`, там машина.
|
||||
- **Не проверяет раскладку, версию и ссылки** — это `canon check`, там машина.
|
||||
- **Не судит язык** документов: залог, англицизмы, жаргон, термин без дома — это
|
||||
агент `doc-wording`, и зовут его отдельно, по пачке правленных документов.
|
||||
Звонящие у него названные — последний шаг синка в `av-dev:doc-sync`, шаг 9
|
||||
`av-dev:doc-init` и шаг вычитки в обоих режимах `doc-canon`, — просто ни один из
|
||||
`av-dev:doc-init` и шаг вычитки в обоих режимах `canon`, — просто ни один из
|
||||
них не здесь. У него другой ритм: он нужен там, где текст только что писали, а
|
||||
не там, где он год лежал. Оркестровать его нечем — он один и работает по
|
||||
названному списку.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
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` узнаёт только плейсхолдер оттуда.
|
||||
|
||||
## Что `init` физически не может произвести
|
||||
@@ -32,10 +32,10 @@ description: "Завести новый проект — сессия вопро
|
||||
Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет,
|
||||
заводится первой задачей». Проход читает её как факт.
|
||||
|
||||
**`tasks/ROADMAP.md` в таблице нет намеренно.** Первые цели `init` собирает
|
||||
интервью (блок 6), но записывает их не он: каталогом задач и формой целей владеет
|
||||
`av-dev:task-track`, и это шаг 7. Человек от каталога задач отказался — цели
|
||||
остаются списком в докладе, роадмапа в проекте не появляется, и это говорится
|
||||
**`tasks/BACKLOG.md` в таблице нет намеренно.** Первый план работ `init`
|
||||
собирает интервью (блок 6), но записывает его не он: каталогом задач владеет
|
||||
`av-dev:task-track`, и это шаг 7. Человек от каталога задач отказался — план
|
||||
остаётся списком в докладе, беклога в проекте не появляется, и это говорится
|
||||
строкой.
|
||||
|
||||
## Порядок интервью — зависимость, а не удобство
|
||||
@@ -54,9 +54,11 @@ description: "Завести новый проект — сессия вопро
|
||||
проекте нельзя откатить — деплой, выкладка наружу, перезапись данных.
|
||||
5. **Чем краснеет гейт.** Какие проверки обязательны; что красит безусловно;
|
||||
чего в гейте намеренно не будет и кто тогда это гоняет.
|
||||
6. **Первые цели.** Возможности приложения, а не задачи: три-пять целей в
|
||||
`Запланировано`, каждая — ответ на «что приложение будет уметь», с
|
||||
обоснованием очереди прозой.
|
||||
6. **Первые шаги стройки.** Новый проект по определению начинается со стадии
|
||||
`build`: приложения ещё нет. Собери **список от базы к деталям** — что нужно
|
||||
сделать, чтобы приложение заработало, — и обоснуй **порядок**: он значит
|
||||
зависимость, а не важность. Пять-десять шагов достаточно: план дописывается
|
||||
по ходу стройки, и это законно.
|
||||
|
||||
### Как вести
|
||||
|
||||
@@ -90,7 +92,7 @@ description: "Завести новый проект — сессия вопро
|
||||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||||
|
||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||
поведении.
|
||||
@@ -133,12 +135,12 @@ description: "Завести новый проект — сессия вопро
|
||||
5. Напиши заполняемые документы. **Бриф переезжает в `passport.md`** и
|
||||
отдельным файлом не остаётся: два дома для одного замысла разойдутся на
|
||||
первом же уточнении.
|
||||
6. Заведи скелет остальных по [скелетам](../doc-canon/references/skeletons.md) —
|
||||
6. Заведи скелет остальных по [скелетам](../canon/references/skeletons.md) —
|
||||
каждый с честной строкой.
|
||||
7. Каталог задач и первые цели — **вызови скилл `av-dev:task-track`**: он владеет
|
||||
форматом целей и задач. Не разрешился — учёт задач остаётся владельцу, и это
|
||||
тоже строка доклада.
|
||||
8. `docs.py check` из скилла `doc-canon` — до отсутствия дрейфа. Замечания о
|
||||
7. Каталог задач и первые шаги — **вызови скилл `av-dev:task-track`**: он владеет
|
||||
форматом задач и стадией (`init --stage build`). Не разрешился — учёт задач
|
||||
остаётся владельцу, и это тоже строка доклада.
|
||||
8. `docs.py check` из скилла `canon` — до отсутствия дрейфа. Замечания о
|
||||
незаполненных плейсхолдерах остаются: их закрывает не `init`, а работа.
|
||||
9. **Вычитай написанное — агент `doc-wording`**, по пачке заполненных документов
|
||||
(`passport.md`, `CLAUDE.md`, `security.md`). Здесь он нужен сильнее, чем где
|
||||
@@ -152,7 +154,7 @@ description: "Завести новый проект — сессия вопро
|
||||
## Что дальше
|
||||
|
||||
- Содержимое канона по ходу разработки ведёт скилл `doc-sync`.
|
||||
- Раскладку проверяет `doc-canon check`.
|
||||
- Раскладку проверяет `canon check`.
|
||||
- Первую задачу берёт конвейер проекта; `architecture.md` и `conventions/`
|
||||
наполняются его шагом синка, а не заранее.
|
||||
|
||||
@@ -160,6 +162,6 @@ description: "Завести новый проект — сессия вопро
|
||||
|
||||
- **Не проектирует систему.** Архитектура выводится из кода, а не наоборот.
|
||||
- **Не пишет код** и не заводит сборку.
|
||||
- **Не переводит существующий проект** — это `doc-canon adopt`. Признак: в
|
||||
- **Не переводит существующий проект** — это `canon adopt`. Признак: в
|
||||
репозитории уже есть документация или беклог в какой-то раскладке.
|
||||
- **Не решает за человека**, что важно: цель, границы и периметр — его ответы.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
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`.
|
||||
Определение канона и роли документов — [канон](../doc-canon/references/canon.md),
|
||||
Скилл владеет **содержимым** документов канона; раскладкой владеет `canon`.
|
||||
Определение канона и роли документов — [канон](../canon/references/canon.md),
|
||||
здесь не пересказывается.
|
||||
|
||||
Главный вызывающий — **шаг синка документации в конвейере задачи**: скилл
|
||||
@@ -36,7 +36,7 @@ description: Вести содержимое документов канона
|
||||
| `openspec/specs/` | всегда при изменении поведения | вливает `opsx:archive` |
|
||||
| `database.md` | тронуты миграции | `docs.py check --base` |
|
||||
| `architecture.md` | новый компонент, граница, внешняя зависимость, изменилось окружение | `docs.py`: capability без упоминания |
|
||||
| `adr/` | дорогой откат, намеренный отказ, пересмотр прежнего | нет — только этот чек-лист |
|
||||
| `adr/` | сработал триггер канона (перечень — [canon.md](../canon/references/canon.md#adr)) | нет — только этот чек-лист |
|
||||
| `research/` | узнали новое о внешнем формате или данных | нет |
|
||||
| `security.md` | новый недоверенный вход, токен, путь наружу, сдвиг периметра | нет |
|
||||
| `conventions/` | находка принята и не специфична для одного места | промоут |
|
||||
@@ -106,11 +106,11 @@ description: Вести содержимое документов канона
|
||||
`av-dev:code-resolve`, сценарий разведки: решение, принятое разведкой (намеренный отказ, выбор
|
||||
подхода, «проверили и не делаем»), `design.md` не имеет по построению — change по
|
||||
нему не будет никогда. Промоут при этом тот же: цитата и ссылка, но на записку.
|
||||
Перечень источников закрыт и живёт в [каноне](../doc-canon/references/canon.md),
|
||||
Перечень источников закрыт и живёт в [каноне](../canon/references/canon.md),
|
||||
раздел `adr/`.
|
||||
|
||||
**Триггер заведения, форма имени и правило замены — в
|
||||
[каноне](../doc-canon/references/canon.md), раздел `adr/`.** Здесь они не
|
||||
[каноне](../canon/references/canon.md), раздел `adr/`.** Здесь они не
|
||||
повторяются: копия правила расходится с оригиналом на первой же смене версии
|
||||
канона, а расходится незаметно.
|
||||
|
||||
@@ -126,7 +126,7 @@ description: Вести содержимое документов канона
|
||||
|
||||
Обзор не держит поведение — его нормативный дом `openspec/specs/`. **Форма
|
||||
маркера долга и правило «гейт от них не краснеет» — в
|
||||
[каноне](../doc-canon/references/canon.md), раздел `architecture.md`.**
|
||||
[каноне](../canon/references/canon.md), раздел `architecture.md`.**
|
||||
|
||||
Разбирается порциями: раздел вычищает та задача, которая его касается.
|
||||
Содержимое не выбрасывается, а переезжает — требования в дельта-спеку 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 не запускается: спеки не с чем сверять |
|
||||
|
||||
**Свой скилл зовётся полным именем** — `av-dev:doc-canon`, `av-dev:task-track`,
|
||||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||||
поведении.
|
||||
@@ -191,7 +191,7 @@ description: Вести содержимое документов канона
|
||||
|
||||
Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку
|
||||
конвейера. **Что в каком и в какой форме — в
|
||||
[каноне](../doc-canon/references/canon.md), раздел `review.md`**; подробности формы
|
||||
[каноне](../canon/references/canon.md), раздел `review.md`**; подробности формы
|
||||
записи и выбор адреса, куда она ведёт, — у конвейера ревью: `Skill
|
||||
av-dev:code-review`, его `references/review-journal.md`.
|
||||
|
||||
@@ -205,7 +205,7 @@ av-dev:code-review`, его `references/review-journal.md`.
|
||||
Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком
|
||||
принадлежит конвейеру ревью — его `references/promote.md`, читается через
|
||||
`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`.
|
||||
- **Не заводит недостающие документы** — их скелет кладёт `doc-canon adopt` или
|
||||
- **Не проверяет раскладку** — это `canon`.
|
||||
- **Не заводит недостающие документы** — их скелет кладёт `canon adopt` или
|
||||
`doc-init`.
|
||||
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
|
||||
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.
|
||||
|
||||
@@ -27,7 +27,7 @@ description: "Груминг беклога — интерактивный ра
|
||||
## Три правила, из которых всё следует
|
||||
|
||||
1. **Порядок назначает человек, машина его не выводит.** Ни давность, ни тип, ни
|
||||
число задач под целью приоритетом не являются. Единственное место в очереди,
|
||||
размер секции приоритетом не являются. Единственное место в очереди,
|
||||
назначенное не человеком, — конец секции у сырья, и оно из очереди изъято
|
||||
(`task-track`, правило 4).
|
||||
2. **Порция важнее охвата.** Тридцать задач за заход — это усталость и
|
||||
@@ -37,6 +37,36 @@ description: "Груминг беклога — интерактивный ра
|
||||
`--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» — брать сегодня нечего.
|
||||
**На стройке этот признак читается иначе**: он значит «план ещё не дописан», а
|
||||
не «пора грумить».
|
||||
|
||||
Порога в неделях нет намеренно: счётчик простоя пришлось бы вести руками, а
|
||||
решает всё равно человек. Признак — **очередь перестала быть твоей**: взялся
|
||||
@@ -122,7 +154,7 @@ flowchart TD
|
||||
**3. Что перестало быть важным.** Порциями по 5–8. Сперва то, что решается
|
||||
фактом и не требует ничьего суждения (сделано попутно, отменено решением,
|
||||
дублируется, симптомы одного дефекта), затем то, что решает человек (жива ли,
|
||||
та ли цель, задача ли это ещё).
|
||||
задача ли это ещё).
|
||||
|
||||
**4. Что важно сейчас.** Расстановка порядка — `move --after <слаг>` и
|
||||
`move --first`. Разбирается **не весь беклог, а верх очереди**: первые три-пять
|
||||
@@ -146,15 +178,15 @@ flowchart TD
|
||||
кодом стоит меньше, чем та же работа через квартал;
|
||||
- **что дорожает от ожидания** — данные копятся, миграция усложняется, внешний
|
||||
срок приближается;
|
||||
- **цель, которую человек назвал следующей.**
|
||||
- **то, что человек назвал следующим.**
|
||||
|
||||
Довод **записывается причиной** (`move --after <слаг> --reason …`). Порядок без
|
||||
причины — это порядок, который на следующем груминге назначат заново с нуля.
|
||||
|
||||
**Цель и приоритет — независимые оси.** Очередь может идти поперёк целей, и это
|
||||
законно: задачи одной цели не обязаны стоять подряд. Но если под целью годами
|
||||
ничего не поднимается наверх — это разговор про цель, а не про очередь, и он
|
||||
идёт на шаге 3.
|
||||
**Тема и приоритет — независимые оси.** Очередь может идти поперёк полок, и это
|
||||
законно: задачи одной темы не обязаны стоять подряд. Но если из одной полки
|
||||
годами ничего не поднимается наверх — это разговор про саму работу, а не про
|
||||
очередь, и он идёт на шаге 3.
|
||||
|
||||
## Документы устаревают тем же ходом работы
|
||||
|
||||
@@ -193,9 +225,9 @@ flowchart TD
|
||||
- **независимый отчёт ревью** — артефакт, написанный не исполнителем: отчёт
|
||||
триажа в
|
||||
`openspec/changes/archive/<id>/review/` (до архивации — `changes/<id>/review/`);
|
||||
- **`reopen <слаг> --reason`** — закрытие не окончательно. Заметил на груминге,
|
||||
что закрытая задача сделана не тем, чем обещала, — возвращай, это штатная
|
||||
операция, а не скандал. Индексы под git: `git log -p` по беклогу показывает,
|
||||
- **`reopen <слаг> --reason`** — закрытие не окончательно. Заметил, что закрытая
|
||||
задача сделана не тем, чем обещала, — возвращай, это штатная операция, а не
|
||||
скандал, и ждать груминга она не требует (на стройке его и не будет). Индексы под git: `git log -p` по беклогу показывает,
|
||||
что и когда закрыто, потому что закрытие коммитится отдельным коммитом учёта.
|
||||
|
||||
Известные обходы:
|
||||
@@ -231,11 +263,11 @@ flowchart TD
|
||||
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
|
||||
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
|
||||
- **Что перестало быть важным**: удалено как реализованное (со ссылками), ушло
|
||||
без реализации (с причинами), понижено до сырья, слито, сменило тип или цель.
|
||||
без реализации (с причинами), понижено до сырья, слито, сменило тип.
|
||||
- **Что важно сейчас**: верх очереди по каждой секции — слаги в порядке, и по
|
||||
каждому движению довод одной строкой.
|
||||
- **Границы покрытия**: сколько задач не трогали и какие именно секции, теги или
|
||||
цели остались — иначе доклад читается как «беклог разобран».
|
||||
- **Границы покрытия**: сколько задач не трогали и какие именно секции или теги
|
||||
остались — иначе доклад читается как «беклог разобран».
|
||||
- `tasks.py check` после правок — результат строкой.
|
||||
|
||||
## Чего этот скилл не делает
|
||||
@@ -244,4 +276,4 @@ flowchart TD
|
||||
себе — формат и содержимое ведёт `task-track` (груминг зовёт его операции). Не решает
|
||||
за человека, что важно: он готовит развилки и рекомендует. Не принимает
|
||||
закрытые задачи отдельным ритуалом — `reopen` есть, момента у него нет. Не судит
|
||||
документы проекта — это скиллы `av-dev:doc-canon` и `av-dev:doc-healthcheck`.
|
||||
документы проекта — это скиллы `av-dev:canon` и `av-dev:doc-healthcheck`.
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
проверки на нужность. Свежесть меряется git'ом, как и залежалость, — датой
|
||||
появления файла в истории;
|
||||
2. дальше **по залежалости** — `list --stale`;
|
||||
3. по потребности — одна секция целиком, один тег (партия ревью), одна цель
|
||||
(`--goal`), список от человека.
|
||||
3. по потребности — одна секция целиком, один тег (партия ревью), список от
|
||||
человека.
|
||||
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
||||
Между порциями — промежуточный доклад.
|
||||
|
||||
@@ -84,20 +84,14 @@
|
||||
|
||||
6. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
|
||||
сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
|
||||
7. **Та ли цель — и нужна ли она вообще.** `feature`, которой не находится цель,
|
||||
— кандидат на выход: новая возможность вне цели это возможность, которой никто
|
||||
не заказывал. Операционной задаче (`fix`, `chore`, `research`) цель не нужна,
|
||||
и выдумывать её здесь не надо.
|
||||
|
||||
**Отменяется и сама цель** — когда замысел оказался неверен, а не когда
|
||||
задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую
|
||||
либо закрыть своей причиной, либо перевесить на другую цель, и только потом
|
||||
закрыть цель. Порядок и почему он такой —
|
||||
[task-goal.md](../../task-track/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель).
|
||||
7. **Тот ли тип.** Заводилась починкой, а после разбора оказалось, что
|
||||
поведение никогда и не было заявлено, — это `feature`. Тип, оставшийся от
|
||||
прошлой формулировки, врёт ровно там, где по нему отбирают, и требует не тех
|
||||
разделов.
|
||||
8. **Задача ли это по-прежнему.** Не проходит `ready` по существу, а не по
|
||||
недописанным разделам → `edit <slug> --type research` и опустошённый раздел
|
||||
«Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач под
|
||||
той же целью, дальше декомпозиция.
|
||||
«Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач,
|
||||
дальше декомпозиция.
|
||||
9. **Не подешевела ли она.** Сделанная с прошлого раза работа меняет цену
|
||||
**других** задач: рядом с только что тронутым кодом та же работа стоит меньше.
|
||||
Это довод и на шаге 4 — задача, внезапно подешевевшая, поднимается в очереди
|
||||
@@ -111,7 +105,7 @@
|
||||
нигде не хранится.
|
||||
|
||||
Задача из верхних строк `--stale`, которую и этот заход оставляет без изменений,
|
||||
**либо двигается (меняет цель, поднимается в очереди, уходит с причиной), либо
|
||||
**либо двигается (меняет полку, поднимается в очереди, уходит с причиной), либо
|
||||
остаётся с явно записанной причиной**, почему её держим (`move <slug> --reason
|
||||
…` — без `--section` секция берётся текущая). Молчаливое «оставить как есть» на
|
||||
давно неподвижной задаче — это решение не принимать решение; запись причины
|
||||
@@ -123,9 +117,8 @@
|
||||
Разбирается **верх очереди**, а не весь беклог: первые три-пять строк каждой
|
||||
секции. Ниже пятой строки порядок всё равно перестаёт что-либо значить.
|
||||
|
||||
1. **Покажи текущий верх** — `list --index backlog`, по секциям, в том порядке,
|
||||
в каком строки лежат. Плюс состояние проекта из роадмапа: секция `Готово`
|
||||
отвечает на «где мы», `Запланировано` — на «куда шли».
|
||||
1. **Покажи текущий верх** — `list`, по секциям, в том порядке, в каком строки
|
||||
лежат.
|
||||
2. **Спрашивай сравнением, а не оценкой.** «Что из этих двух делают раньше»
|
||||
имеет проверяемый ответ, «насколько важна эта задача» — нет. Веди попарно и
|
||||
сверху: что первое, что после него.
|
||||
@@ -133,7 +126,7 @@
|
||||
или `move <slug> --first --reason …`. Довод берётся из перечня в
|
||||
[SKILL.md](../SKILL.md#приоритет-как-его-расставляют): сломано сейчас,
|
||||
разблокирует остальное, дешевеет от сделанного, дорожает от ожидания,
|
||||
названная цель.
|
||||
названо человеком.
|
||||
4. **Проверь верх на готовность** — `tasks.py ready <слаг> …` по первым строкам.
|
||||
Задача, стоящая первой и не проходящая `ready`, — это очередь, которая врёт:
|
||||
взять её нельзя. Либо дописывается здесь же, либо уступает место.
|
||||
|
||||
+254
-258
@@ -1,13 +1,13 @@
|
||||
---
|
||||
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`, а этот скилл лишь даёт ему операции; и выполнением
|
||||
@@ -17,57 +17,53 @@ description: Ведение задач и целей как каталога mar
|
||||
|
||||
Ситуация не покрыта инструкцией — решай по ним.
|
||||
|
||||
0. **Цель — возможность приложения, задача — шаг к ней.** Цель отвечает на «что
|
||||
приложение будет уметь», её «Завершение» — наблюдаемый признак того, что уже
|
||||
умеет; задача отвечает на «что для этого нужно сделать». Оценивается проект
|
||||
по **поведению**, а не по внутреннему устройству, поэтому и роадмап отвечает
|
||||
не «сколько работ осталось», а «что уже умеет и чего ещё не умеет».
|
||||
Свойство поведения — тоже возможность: «сообщает о своём состоянии»,
|
||||
«исход слияния не зависит от порядка доставки» — законные цели.
|
||||
1. **Беклог гниёт с той стороны, где его пополняют.** Заведение — самая частая
|
||||
операция и с худшим отказом: из одного разговора рождается пять файлов, а
|
||||
переоценка потом разгребает то, чего не надо было заводить. Дедупликация и
|
||||
фильтр на входе дешевле любой чистки. Заводим только то, что **не делаем
|
||||
сейчас** и о потере чего пожалеем.
|
||||
2. **Файл — источник истины, индексы производны.** Разошлись — неправы индексы.
|
||||
0. **Стадия решает, что значит порядок строк.** Проект живёт в одной из двух
|
||||
стадий, и обе ведут один и тот же беклог, но читают его по-разному.
|
||||
На **стройке** (`build`) беклог это план от базы к деталям: порядок —
|
||||
зависимость, «раньше нельзя». На **доработке** (`support`) беклог это очередь
|
||||
правок: порядок — важность, «раньше лучше». Из этого следует остальное —
|
||||
сколько у беклога секций, как его пополняют, что значит его опустошение и
|
||||
нужен ли груминг. Стадия объявлена ключом `[tasks] stage`; молчание ответом
|
||||
не считается, и `check` без неё отказывает.
|
||||
1. **Беклог гниёт с той стороны, где его пополняют — на доработке.** Заведение
|
||||
там самая частая операция и с худшим отказом: из одного разговора рождается
|
||||
пять файлов, а переоценка потом разгребает то, чего не надо было заводить.
|
||||
Дедупликация и фильтр на входе дешевле любой чистки: заводим только то, что
|
||||
**не делаем сейчас** и о потере чего пожалеем.
|
||||
|
||||
**На стройке правило не применяется**, и это не послабление. Список стройки
|
||||
пишется вперёд целиком — он и есть замысел, — а фильтр «не заводи то, чего не
|
||||
делаешь сейчас» запретил бы написать план дальше первого шага. Дедуп остаётся
|
||||
в обеих стадиях: две записи об одном плохи всегда.
|
||||
2. **Файл — источник истины, индекс производен.** Разошлись — неправ индекс.
|
||||
Согласованность механизируема и проверяется командой, а не вниманием: всё,
|
||||
что ловит `tasks.py check`, не должно попадать ни в чек-лист, ни в промпт.
|
||||
Поэтому **«зачем» живёт в мете файла**, а строка индекса его лишь
|
||||
повторяет: пока поле лежало только в индексе, восстановление пропавшей
|
||||
строки теряло его молча и навсегда. Единственное исключение намеренное: **в каком
|
||||
индексе лежит запись, знают индексы** — поля-состояния в файле нет. И
|
||||
**порядок строк в беклоге**: приоритет это свойство очереди, а не задачи, и в
|
||||
файле ему места нет (правило 4).
|
||||
строки теряло его молча и навсегда. Единственное исключение намеренное:
|
||||
**порядок строк в беклоге** — он свойство списка, а не задачи, и в файле ему
|
||||
места нет (правило 4).
|
||||
3. **Причина переживает запись.** Выкинутая без причины задача вернётся через
|
||||
квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не
|
||||
оставляет ничего, поэтому у неё есть `REJECTED.md`.
|
||||
4. **Приоритет — это порядок строк, а цель есть не у всякой задачи.** Очередь
|
||||
внутри секции беклога значима: **первая строка — то, что делают следующим**.
|
||||
Приоритет назначает человек на груминге, машина его не выводит и не угадывает.
|
||||
4. **Порядок строк — единственное, чего в файле нет.** Он значим в обеих
|
||||
стадиях, и назначает его человек: на стройке — раскладывая шаги по
|
||||
зависимости, на доработке — на груминге. Машина порядок не выводит и не
|
||||
угадывает; всё, что она делает сама, — ставит машинную позицию в **конец**
|
||||
секции и говорит об этом вслух.
|
||||
|
||||
Прежде здесь стояло «порядка нет, есть цель», и обосновано это было тем, что
|
||||
на «что делать дальше» отвечает **набор спринта**. Набора больше нет, а
|
||||
вопрос остался — и без порядка отвечать на него стало нечем.
|
||||
|
||||
**Дом приоритета — индекс, а не файл.** Это то же исключение из правила 2,
|
||||
что и «в каком индексе лежит запись»: приоритет — свойство очереди. Положи он
|
||||
в файл числом, и два соседних файла смогли бы утверждать одно и то же место,
|
||||
а строка индекса — противоречить обоим.
|
||||
|
||||
Цель обязательна там, где она и есть содержание работы, — у **новой
|
||||
возможности** (`feature`). Починка, техдолг и разведка служат
|
||||
работоспособности, а не направлению, и живут без цели законно. Придуманная им
|
||||
цель — то же враньё, от которого спасает тип. **Цель и приоритет —
|
||||
независимые оси:** очередь может идти поперёк целей, и это законно.
|
||||
**Дом порядка — индекс, а не файл.** Положи его в файл числом, и два соседних
|
||||
файла смогли бы утверждать одно и то же место, а строка индекса —
|
||||
противоречить обоим.
|
||||
|
||||
Одно место в очереди назначено **не человеком, а типом**: **сырьё**
|
||||
(`research` без раздела «Вопрос») стоит в конце своей категории. Его не берут,
|
||||
(`research` без раздела «Вопрос») стоит в конце своей секции. Его не берут,
|
||||
и между берущимся оно каждый раз требует открыть файл, чтобы это понять. Раз
|
||||
это выводится, проверяет и чинит это машина.
|
||||
5. **Тип решает, что с записью можно делать.** Тип — единственная ось и первое
|
||||
поле меты: от него зависит, какие разделы обязательны в теле, нужна ли цель,
|
||||
берётся ли запись в работу и в каком индексе живёт её строка. Словарь закрыт;
|
||||
ни один тип не подошёл — значит, в записи их два, и её надо разделить.
|
||||
5. **Тип решает, что с задачей можно делать.** Тип — вторая ось и первое
|
||||
поле меты: от него зависит, какие разделы обязательны в теле и берётся ли
|
||||
запись в работу. Словарь закрыт; ни один тип не подошёл — значит, в записи их
|
||||
два, и её надо разделить.
|
||||
|
||||
## Раскладка
|
||||
|
||||
@@ -79,55 +75,23 @@ description: Ведение задач и целей как каталога mar
|
||||
|
||||
```
|
||||
tasks/
|
||||
items/ задачи и цели файлами, <slug>.md, слаги английские
|
||||
ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет
|
||||
BACKLOG.md что можно взять — только задачи, целей здесь нет.
|
||||
Порядок строк в секции значим: это очередь
|
||||
items/ задачи файлами, <slug>.md, слаги английские
|
||||
BACKLOG.md что можно взять. Порядок строк в секции значим,
|
||||
и значит он разное на разных стадиях
|
||||
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
|
||||
```
|
||||
|
||||
Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `ROADMAP.md` — то,
|
||||
подо что берут.** Цель в работу взять нельзя — берут её задачи, — поэтому в
|
||||
списке берущихся ей не место.
|
||||
**Индекс один.** `REJECTED.md` индексом не считается: он не говорит, где запись
|
||||
числится, — это кладбище ушедшего.
|
||||
|
||||
**Четыре секции роадмапа, и последняя отвечает на половину вопроса:**
|
||||
**Секции беклога называет проект**, и `check` проверяет у них ровно две вещи:
|
||||
что секция есть хоть одна и что на стройке она **одна**. Смысла секции не несут
|
||||
— это полки домена (`Ядро`, `Инфра`), — и подгонять их имена под свой вкус
|
||||
скрипт права не имеет. Поле меты, называющее полку, зовётся **Категория**.
|
||||
|
||||
| Секция | Англ. | Что в ней |
|
||||
| --- | --- | --- |
|
||||
| `Запланировано` | `Planned` | очередь значима и обосновывается прозой рядом |
|
||||
| `Направления` | `Directions` | очереди нет, тянутся долго |
|
||||
| `Сопровождение` | `Operations` | чем держат проект: инструмент, процесс, эксплуатация — не возможности приложения, и потому отдельно |
|
||||
| `Готово` | `Done` | достигнутые цели — строкой с датой, **без ссылки на файл**: файл удалён, поведение живёт в спеках |
|
||||
|
||||
**Порядок тоже канонический, и `Готово` стоит последним не из скромности.**
|
||||
Достигнутое **копится**: через год этой секции больше, чем всех остальных
|
||||
вместе. Стоя первой, она отодвигает за экран ровно то, ради чего роадмап
|
||||
открывают чаще всего, — что делается сейчас и что дальше. Порядок проверяет
|
||||
`check`, переставляет `check --fix`.
|
||||
|
||||
**Секции роадмапа канонические, категории беклога — нет**, и разница не в любви к
|
||||
единообразию. У каждой секции роадмапа свой смысл, в достигнутое пишет сам `close`, и
|
||||
роадмап, названный по-своему, читался бы только своим автором. Категории беклога
|
||||
(`Ядро`, `Инфра`) смысла не несут — это полки домена, и остаются делом проекта.
|
||||
Отсюда и разные имена поля меты: у цели **Секция** (часть роадмапа — состояние
|
||||
очереди), у задачи **Категория** (полка домена, на которой она лежит).
|
||||
|
||||
Отсюда четыре правила, которые проверяет `tasks.py check`: **состав закреплён**
|
||||
(чужая секция — ошибка, а не вольность), **все четыре обязаны быть** (нет
|
||||
секции — нет ответа на её часть вопроса), **язык один на весь индекс**, **порядок
|
||||
канонический**. `--roadmap-sections` у `init` нет: выбирать нечего.
|
||||
|
||||
**Заголовок секции отбит пустой строкой с обеих сторон и написан с прописной.**
|
||||
Во всех индексах одинаково, включая категории беклога, которые проект называет сам.
|
||||
Написание канонических секций правит `check --fix` (заодно и ссылку на секцию в
|
||||
мете файлов: имя секции принадлежит заголовку индекса, файл на неё только
|
||||
ссылается); отбивку и порядок он правит везде.
|
||||
|
||||
Оговорка про `Сопровождение`: слово `окружение` сюда не годится — в
|
||||
`architecture.md` оно уже значит боевое окружение приложения, и одно слово в двух
|
||||
смыслах развело бы документы канона. А `Разработка`, стоявшая тут раньше,
|
||||
называла слишком много: роадмап **весь** про разработку, и секция с таким именем
|
||||
не отличалась от остальных ничем.
|
||||
**Заголовок секции отбит пустой строкой с обеих сторон и написан с прописной**;
|
||||
отбивку правит `check --fix`. Написание секции в мете файлов он тоже правит: имя
|
||||
секции принадлежит заголовку индекса, файл на неё только ссылается.
|
||||
|
||||
**Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (работа не может
|
||||
продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и
|
||||
@@ -138,53 +102,31 @@ tasks/
|
||||
который переезжает с такой секцией, её надо удалить** — это единственное место,
|
||||
где это сказано.
|
||||
|
||||
**Запись живёт в одном индексе за раз.** Индексов два, и выбирает между ними
|
||||
тип: цель в роадмапе, задача в беклоге. Сменился тип — строка переезжает
|
||||
(`edit --type`). Файл в `items/` при этом **не двигается**: он и есть запись,
|
||||
индексы лишь показывают, где она числится и в каком порядке стоит.
|
||||
|
||||
**Порядок строк в беклоге — приоритет**, и он единственное, чего в файле нет
|
||||
(правило 4). Отсюда следствие для всякой машинной правки индекса:
|
||||
восстановленная или перенесённая строка встаёт **в конец своей секции**, и
|
||||
скрипт об этом говорит. Молчаливая вставка выдала бы машинную позицию за
|
||||
решение человека — а решение это его.
|
||||
**Порядок строк — единственное, чего в файле нет** (правило 4). Отсюда следствие
|
||||
для всякой машинной правки индекса: восстановленная или перенесённая строка
|
||||
встаёт **в конец своей секции**, и скрипт об этом говорит. Молчаливая вставка
|
||||
выдала бы машинную позицию за решение человека — а решение это его.
|
||||
|
||||
**У сделанной задачи записи не остаётся** — файл и строка удаляются (`close
|
||||
--implemented`). Ей хватает коммита и документации проекта; вторая запись была
|
||||
бы вторым домом для того же факта. Вопрос «что было сделано и когда» отвечается
|
||||
даром: индексы лежат под git, а закрытие коммитится отдельным коммитом учёта —
|
||||
даром: индекс лежит под git, а закрытие коммитится отдельным коммитом учёта —
|
||||
`git log -p tasks/BACKLOG.md` отдаёт историю без отдельного журнала.
|
||||
|
||||
**У достигнутой цели запись остаётся, и это единственное исключение.** Файл
|
||||
удаляется так же, а строка переезжает в секцию `Готово` с датой. Причина в том,
|
||||
что цель — не работа, а **возможность**: «что приложение умеет» это половина
|
||||
вопроса, ради которого роадмап и открывают, и стирать её вместе с файлом значит
|
||||
оставить инструмент, отвечающий только «что осталось». Вторым домом это не
|
||||
становится: поведение живёт в `openspec/specs/`, а роадмап отвечает **когда и в
|
||||
каком порядке оно появилось** — другой вопрос. Ссылки на файл в строке нет
|
||||
намеренно: файл удалён, а битая ссылка — законная ошибка `check`.
|
||||
|
||||
Куда запись может переехать и какой командой — весь набор переходов:
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
state "BACKLOG.md — что берут" as B
|
||||
state "ROADMAP.md — подо что берут" as P
|
||||
state "REJECTED.md — ушла без реализации" as R
|
||||
state "записи нет — реализована" as D
|
||||
state "ROADMAP.md, «умеет» — цель достигнута" as A
|
||||
|
||||
[*] --> B: add --type feature|fix|chore|research
|
||||
[*] --> P: add --type goal
|
||||
B --> P: edit --type goal --section
|
||||
P --> B: edit --type feature|fix|chore|research --section
|
||||
B --> B: move --after | --first | --section
|
||||
B --> D: close --implemented
|
||||
P --> A: close --implemented
|
||||
B --> R: close --reason
|
||||
P --> R: close --reason
|
||||
D --> 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` его не подставляет:
|
||||
какая стадия у проекта, знает человек, а подставленное умолчание соврало бы ровно
|
||||
там, где по нему принимают решение.
|
||||
|
||||
**Граница проходит по тому, кто наблюдает, а не по теме.** «Приложение сообщает
|
||||
о своём состоянии» — возможность: наблюдает пользователь сервиса, и цели место
|
||||
среди прочих. «Дежурный видит состояние на одном экране» — сопровождение:
|
||||
наблюдаем мы. Одна и та же наблюдаемость попадает в разные секции, и это верно —
|
||||
секции отвечают на разные вопросы.
|
||||
**Секций на стройке одна, и это не педантизм.** Порядок там — зависимость, и
|
||||
разложенный по полкам список перестаёт быть планом: два шага из разных секций
|
||||
уже не сравнить. На доработке полки законны — правки независимы, и очередь
|
||||
внутри полки самостоятельна.
|
||||
|
||||
**Сопровождение и эксплуатация — целое и часть**, а не синонимы, и та же тема
|
||||
живёт ещё в двух местах: разделе «Эксплуатация» в `architecture.md` и теме ревью
|
||||
`operations`. Словарь у всех трёх общий, и дом у него один:
|
||||
[shared/operations.md](../../shared/operations.md) — читается по ссылке.
|
||||
Пересказывать его своими словами нельзя: три перечня «чем держат проект» уже
|
||||
разъезжались на «метриках и логах» против «мониторинга».
|
||||
**Переход — событие, а не настройка.** `tasks.py stage support` переносит остаток
|
||||
беклога в первую новую секцию, правит «Категорию» в файлах и говорит, что порядок
|
||||
с этого момента значит другое. Датой ему служит коммит: отдельного журнала ради
|
||||
одной строки не заводится. Признак созревания наблюдаемый — беклог стройки
|
||||
исчерпан, и `check` об этом напоминает; **запретить переход раньше скрипт не
|
||||
берётся**: «приложение построено» решает человек, а не счётчик строк.
|
||||
|
||||
Секция выбирается так: очередь значима и обоснована прозой — `Запланировано`;
|
||||
тянется долго и очереди не имеет — `Направления`; не про приложение, а про то,
|
||||
чем его держат, — `Сопровождение`; в `Готово` кладёт сам `close`.
|
||||
Обратный переход (`stage build`) разрешён и устроен так же. Он редок — проект
|
||||
уходит на стройку заново разве что при переделке замысла целиком, — но
|
||||
запрещать его было бы запретом на то, что иногда и правда случается.
|
||||
|
||||
- **Список задач цели выводится, а не хранится.** В теле цели — зачем она и что
|
||||
считается её завершением; перечня задач там нет. Он был бы третьим индексом и
|
||||
поехал бы на первой же закрытой задаче, а `check` про него не знает. Связь
|
||||
однонаправленна: задача несёт тег `goal:<слаг>`, перечень даёт
|
||||
`tasks.py list --goal <слаг>`.
|
||||
- **Статус цели выводится.** Цель достигнута, когда у неё не осталось открытых
|
||||
задач; `[x]`/`[~]` руками не ведутся, а `close` цели с живыми задачами
|
||||
скрипт запретит. Достижение — `close <цель> --implemented`: файл удаляется,
|
||||
строка с датой переезжает в `Готово`. Ошиблись — `reopen` вернёт файл и
|
||||
**снимет строку достигнутого**, чтобы роадмап не утверждал того, чего нет. Единственная оговорка: цель без задач неотличима — «ещё не
|
||||
разобрана» или «всё закрыто». Различает **тег `decomposed`** в мете
|
||||
цели: он ставится, когда цель разложена на задачи. Тег, а не строка в теле —
|
||||
потому что проверяется механически: `check` **напоминает** о нём у пустой цели
|
||||
(замечанием, не ошибкой — неразобранная цель это законное состояние), а `check
|
||||
--fix` сам проставляет его цели, у которой задачи есть.
|
||||
- **Тип `[epic]` упразднён.** Он был зонтиком между целью и задачами — «задача,
|
||||
которая не мерджится целиком». Зонтик теперь цель, а слишком крупный шаг просто
|
||||
дробится на шаги помельче под той же целью, и промежуточному типу места не
|
||||
осталось. Замер подтвердил: ноль употреблений на 97 записей двух живых
|
||||
проектов. Встретился в чужом беклоге — это цель либо набор задач, и `check`
|
||||
назовёт его неизвестным типом.
|
||||
## Чего у задач больше нет
|
||||
|
||||
**Тип `goal` и `ROADMAP.md` упразднены.** Цель была зонтиком над параллельными
|
||||
направлениями: она нужна там, где список работ нельзя выстроить в один порядок,
|
||||
и очередь идёт поперёк направлений. У проекта, который ведёт один человек, такого
|
||||
не бывает — на стройке список линеен по зависимости, на доработке правки
|
||||
независимы, — и зонтик не стоял ни над чем.
|
||||
|
||||
Роадмап при этом отвечал на свой вопрос наполовину: «чего ещё не умеет» — это
|
||||
«что осталось в беклоге», то есть пересказ второго индекса. Вторая половина, «что
|
||||
уже умеет», живёт в двух домах и без него: нормативное поведение — в
|
||||
`openspec/specs/`, а когда и в каком порядке оно появилось — в `git log` индекса
|
||||
и коммитах задач.
|
||||
|
||||
Вместе с целью ушли: секция `Готово` (её ответ дают спеки и история), теги
|
||||
`goal:<слаг>` и `decomposed`, поле меты `Секция` (осталась `Категория`), раздел
|
||||
`Завершение`, ключи `[tasks] roadmap` и `[tasks] completion_heading`, флаги
|
||||
`add --goal`, `list --goal`, `list --index`, `edit --goal`, `edit --section` и
|
||||
`init --roadmap`. Встретились в проекте —
|
||||
`check` назовёт их поимённо, `check --fix` снимет теги, а запись типа `goal`
|
||||
оставит человеку: во что она превращается — в задачу или в ничто, — машина не
|
||||
решает.
|
||||
|
||||
**Тип `[epic]` упразднён раньше и не вернулся.** Слишком крупный шаг дробится на
|
||||
шаги помельче, стоящие в списке подряд.
|
||||
|
||||
## Тип записи
|
||||
|
||||
**Тип — единственная ось, и он решает, что с записью можно делать.** Дом типа —
|
||||
**Тип решает, что с задачей можно делать.** Вторая ось скилла — первая стадия.
|
||||
Перечень осей всего процесса и того, чего каждая **не** решает, —
|
||||
[shared/axes.md](../../shared/axes.md). Дом типа —
|
||||
**поле меты `Тип` первой строкой**; эмодзи в заголовке H1 от него производна, её
|
||||
ставит `add` и чинит `check --fix`.
|
||||
|
||||
| Тип | Обязательные разделы | Цель | В работу | Устав |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 🎯 `goal` | `Завершение` | — | нет | [task-goal.md](references/task-goal.md) |
|
||||
| ✨ `feature` | `Затрагивает`, `Критерии приёмки` | **обязательна** | да | [task-feature.md](references/task-feature.md) |
|
||||
| 🐞 `fix` | `Воспроизведение`, `Затрагивает`, `Критерии приёмки` | необязательна | да | [task-fix.md](references/task-fix.md) |
|
||||
| 🧹 `chore` | `Затрагивает`, `Критерии приёмки` | нет | да | [task-chore.md](references/task-chore.md) |
|
||||
| 🔬 `research` | `Вопрос`, `Куда ляжет ответ` | нет | да | [task-research.md](references/task-research.md) |
|
||||
| Тип | Обязательные разделы | Устав |
|
||||
| --- | --- | --- |
|
||||
| ✨ `feature` | `Затрагивает`, `Критерии приёмки` | [task-feature.md](references/task-feature.md) |
|
||||
| 🐞 `fix` | `Воспроизведение`, `Затрагивает`, `Критерии приёмки` | [task-fix.md](references/task-fix.md) |
|
||||
| 🧹 `chore` | `Затрагивает`, `Критерии приёмки` | [task-chore.md](references/task-chore.md) |
|
||||
| 🔬 `research` | `Вопрос`, `Куда ляжет ответ` | [task-research.md](references/task-research.md) |
|
||||
|
||||
Берутся в работу все четыре: записи, которую нельзя взять, больше не существует.
|
||||
|
||||
Сверх обязательных у любой задачи допустимы `Рамки` и `Вопросы`. Раздел не из
|
||||
схемы своего типа — **замечание, а не ошибка**: свой раздел законная вольность
|
||||
проекта, но `Воспроизведение` у `chore` почти всегда значит, что тип проставлен
|
||||
не тот, и сказать об этом стоит, не запрещая.
|
||||
|
||||
**Осей было две, и ортогональность у них была фальшивой.** Тип записи
|
||||
**Прежних осей было две, и ортогональность у них была фальшивой.** Тип записи
|
||||
(`goal`/`idea`/`task`) и род работы (`kind:<род>` тегом) давали двенадцать клеток
|
||||
произведения, из которых законны были шесть: у цели род запрещён, у задачи
|
||||
обязателен, у идеи пуст. Плюс алгоритм работы крепится не к `task`, а к `fix` и
|
||||
@@ -299,7 +248,8 @@ stateDiagram-v2
|
||||
изменения, а не по типу задачи: `chore` бывает миграцией схемы, `fix` — правкой
|
||||
публичного контракта. Правило «предписание процесса в теле задачи снимается»
|
||||
типом не отменяется, а подтверждается: он описывает работу, а не то, как её
|
||||
проверять.
|
||||
проверять. **Стадия проекта их тоже не выбирает**: изменение на стройке ничем не
|
||||
проще того же изменения на доработке, и метку ему по-прежнему назначает разметка.
|
||||
|
||||
**Одно исполнителю тип всё же говорит — каким сценарием работу вести, и то не
|
||||
один.** Скилл `av-dev:code-resolve` выбирает сценарий связкой из двух
|
||||
@@ -314,11 +264,10 @@ stateDiagram-v2
|
||||
Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы
|
||||
задачу можно было **оценить, не открывая код**.
|
||||
|
||||
**Заголовок отвечает на вопрос своего типа.** Вопросов три, поэтому и форм три:
|
||||
**Заголовок отвечает на вопрос своего типа.** Вопросов два, поэтому и форм две:
|
||||
|
||||
| Тип | Отвечает на | Пример |
|
||||
| --- | --- | --- |
|
||||
| 🎯 `goal` | что приложение будет уметь | Соперником может быть компьютер |
|
||||
| ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | Печатать поле одним куском кода |
|
||||
| 🔬 `research` | о чём разведка | Подсказка следующего хода |
|
||||
|
||||
@@ -331,10 +280,6 @@ stateDiagram-v2
|
||||
исход знание, а что делать — ещё неизвестно, и заголовок-действие обещал бы
|
||||
решённость, которой нет.
|
||||
|
||||
Из этого же правила растёт разница индексов: роадмап — список возможностей,
|
||||
беклог — список работ, и если заголовки перепутать формами, каждый из них
|
||||
начинает читаться как другой.
|
||||
|
||||
`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 list --dir D [--stale] [--section S] [--type T] [--tag a,b] [--goal S] [--raw] [--index …] [--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 edit S --dir D [--title T] [--why «зачем»] [--type T] [--goal G] [--add-tag a,b] [--rm-tag c]
|
||||
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 feature|fix|chore|research [--section S] [--why «зачем»] [--tag a,b]
|
||||
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 close S --dir D --reason R # в REJECTED.md + удалить (ушла без реализации)
|
||||
python3 $tk close S --dir D --implemented # просто удалить (реализована и закоммичена)
|
||||
python3 $tk reopen S --dir D --reason R # вернуть закрытую: приёмка не сошлась
|
||||
python3 $tk ready S… --dir D # схема типа выполнена — можно брать в работу
|
||||
python3 $tk init --dir D [--sections …] [--items …] [--backlog …] …
|
||||
python3 $tk adopt scan --from … | apply --plan … # разовая адаптация, references/adopt.md
|
||||
python3 $tk stage --dir D # показать стадию
|
||||
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` в репозитории плагина: словарь общий
|
||||
для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл.
|
||||
|
||||
| Код | Что случилось | Что делать |
|
||||
| --- | --- | --- |
|
||||
| 0 | сошлось / сделано | дальше по сценарию |
|
||||
| 1 | **только `check`:** найден дрейф индексов и файлов | `check --fix`, остаток разобрать |
|
||||
| 2 | ошибка употребления: аргументы или нарушенное правило | читать сообщение, это отказ по существу |
|
||||
| 3 | окружение: каталог не найден, конфиг битый или мимо диска | чинится путём или `.av-dev.toml` в корне, повтор не поможет |
|
||||
| 4 | внутренний сбой | дефект скрипта, доложить |
|
||||
<!-- копия: коды-выхода из av-dev/shared/axes.md -->
|
||||
|
||||
Различать 1 и 3 обязательно: «дрейф в беклоге» — рабочая ситуация, «каталога
|
||||
нет» — нерабочая, и одинаковая реакция на них была бы неверна в обоих случаях.
|
||||
**Коды выхода — общий словарь всех скриптов `av-dev`. Ветвись на коде, а не на
|
||||
тексте вывода.**
|
||||
|
||||
Тип — английское ключевое слово `goal` / `feature` / `fix` / `chore` /
|
||||
`research` (как и прочие токены команд), у `add` **обязательное**: без него
|
||||
неизвестно, какой шаблон тела класть. Текст задачи при этом русский, а эмодзи в
|
||||
заголовке ставит скрипт.
|
||||
| Код | Что случилось |
|
||||
| --- | --- |
|
||||
| 0 | сошлось |
|
||||
| 1 | дрейф: рабочая ситуация, чинится |
|
||||
| 2 | ошибка употребления: аргументы или нарушенное правило |
|
||||
| 3 | окружение: не тот каталог, битый конфиг, нет инструмента |
|
||||
| 4 | внутренний сбой — дефект скрипта, доложить |
|
||||
|
||||
**Мутации правят файл и индексы заодно** — руками строку индекса или мету
|
||||
не пиши, зови `add`/`edit`/`move`/`close`/`reopen`. Смена заголовка, «зачем», типа,
|
||||
цели и **тегов** — это `edit`: он держит H1 (вместе с эмодзи), мету и индекс
|
||||
**Различать 1 и 3 обязательно.** «Дрейф» — рабочая ситуация, и чинится она
|
||||
правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет.
|
||||
Одинаковая реакция на них неверна в обоих случаях.
|
||||
|
||||
<!-- /копия: коды-выхода -->
|
||||
|
||||
Здесь это значит: код 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` (после ответа на вопрос снимается
|
||||
`question`), смена цели — `--goal`, типа — `--type`; оба заменяют прежнее
|
||||
значение, а не добавляют второе.
|
||||
`question`), смена типа — `--type`; оба заменяют прежнее значение, а не
|
||||
добавляют второе.
|
||||
|
||||
**Переезд между индексами — следствие смены типа, а не отдельная команда.**
|
||||
`edit <slug> --type goal --section <часть роадмапа>` переносит строку из
|
||||
`BACKLOG.md` в `ROADMAP.md` (и обратно — задачным типом плюс
|
||||
`--section <категория беклога>`);
|
||||
`move` двигает только внутри одного индекса и пишет причину. `--section` у
|
||||
`edit` работает **только** при таком переезде — иначе он отсылает к `move`,
|
||||
потому что смена секции без причины и есть тот дрейф, который потом никто не
|
||||
объяснит.
|
||||
**Секцию меняет только `move`, и он пишет причину**: смена полки без причины и
|
||||
есть тот дрейф, который потом никто не объяснит.
|
||||
|
||||
**`move --after <слаг>` и `move --first` — это и есть расстановка приоритета.**
|
||||
**`move --after <слаг>` и `move --first` — это и есть расстановка порядка.**
|
||||
Порядок строк в секции значим (правило 4), и двигают его только этой командой:
|
||||
руками поправленная строка не оставляет причины, а причина здесь и есть половина
|
||||
решения.
|
||||
решения. Что именно этот порядок значит, говорит стадия: на стройке `--after`
|
||||
называет зависимость, на доработке — приоритет.
|
||||
|
||||
Тело задачи скрипт не трогает:
|
||||
`add` кладёт заголовок, мета-блок и шаблон с подсказками, тело дописываешь
|
||||
@@ -446,18 +403,23 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
правки**, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся
|
||||
чини `check --fix` — он детерминированно правит то, где истина однозначна (тип в
|
||||
своё поле, эмодзи заголовка, имя поля места, секция, заголовок, дубли, «зачем» из
|
||||
индекса в файл, старая форма меты, пометка `decomposed` у цели с задачами, сырьё
|
||||
в конец категории), а неоднозначное (задача сразу в двух индексах, нечего
|
||||
восстанавливать, **тип, которого неоткуда взять**) печатает отдельной пометкой
|
||||
индекса в файл, старая форма меты, снятые теги упразднённых целей, сырьё
|
||||
в конец секции), а неоднозначное (нечего восстанавливать, **тип, которого
|
||||
неоткуда взять**, запись типа `goal`) печатает отдельной пометкой
|
||||
`НЕОДНОЗНАЧНО` — это тебе, и это идёт строкой доклада. **Ссылка на исчезнувший
|
||||
файл в пометку не попадает:** `--fix` её просто не трогает, и она остаётся
|
||||
`ОШИБКА` обычного `check` — то есть видна, но в докладе её надо назвать отдельно.
|
||||
|
||||
`--fix` правит **и файлы** — там, где источник ровно один и выбирать не из чего:
|
||||
тип переезжает из прежнего дома (тег `kind:`, префикс `[goal]`/`[idea]`) в поле
|
||||
меты, заголовок получает эмодзи, поле места — имя по типу, «зачем», оставшееся
|
||||
только в индексе, переезжает в мету, цель с задачами получает `decomposed`.
|
||||
Каждый случай печатается поимённо.
|
||||
меты, заголовок получает эмодзи, поле места зовётся «Категория», «зачем»,
|
||||
оставшееся только в индексе, переезжает в мету, теги `goal:` и `decomposed`
|
||||
снимаются. Каждый случай печатается поимённо.
|
||||
|
||||
**Ни стадию, ни состав секций `--fix` не трогает.** Стадию он подставить не
|
||||
может — это решение человека; секции стройки не сливает — в каком порядке пойдут
|
||||
строки слитых полок, знает тоже только человек, а порядок здесь и есть
|
||||
содержание.
|
||||
|
||||
**Тип, который не выводится ниоткуда, `--fix` не угадывает.** `feature` от
|
||||
`chore` машина не отличает, и подставленное наугад значение врало бы ровно там,
|
||||
@@ -468,7 +430,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
работу — там, где по ней принимают решение; `check` поимённо о ней не говорит, а
|
||||
считает: строка здоровья **«схема типа не выполнена: 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); там же тест «готова к
|
||||
взятию». Схема и алгоритм каждого типа — по файлу на тип:
|
||||
[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) ·
|
||||
[research](references/task-research.md).
|
||||
|
||||
@@ -496,7 +458,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
|
||||
Формат каталога задач меняется, и проект должен знать, к какой версии он
|
||||
приведён. Число живёт ключом `version` в `.av-dev.toml` в корне репозитория,
|
||||
журнал версий — [журнал скилла `doc-canon`](../doc-canon/references/changelog.md),
|
||||
журнал версий — [журнал скилла `canon`](../canon/references/changelog.md),
|
||||
сверяет их `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. **Фильтр.** Делаем прямо сейчас — не заводим. Не пожалеем о потере — не
|
||||
заводим. Родилось три кандидата — покажи их и спроси, какие заводить: молча
|
||||
заведённая пачка и есть тот самый отказ из правила 1.
|
||||
0. **Посмотри стадию** — `stage`. От неё зависят шаг 1 и место новой строки: на
|
||||
доработке беклог пополняют по одной и с фильтром, на стройке пишут планом.
|
||||
1. **Фильтр — на доработке.** Делаем прямо сейчас — не заводим. Не пожалеем о
|
||||
потере — не заводим. Родилось три кандидата — покажи их и спроси, какие
|
||||
заводить: молча заведённая пачка и есть тот самый отказ из правила 1.
|
||||
**На стройке фильтра нет**: план пишется вперёд целиком, и «этого мы сейчас
|
||||
не делаем» — не довод против шага, а описание всякого шага, кроме первого.
|
||||
2. **Дедуп.** `list` плюс поиск по слагам, полю «зачем» и телам (`grep -ril`),
|
||||
**включая `REJECTED.md`**. Нашлось среди живых — **дописываем в существующий
|
||||
файл**, а не заводим соседний. Нашлось в `REJECTED.md` — покажи пользователю
|
||||
@@ -526,7 +492,6 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
переоценки.
|
||||
3. **Тип** — `--type` обязателен, и он же первое содержательное решение:
|
||||
|
||||
- возможность приложения, а не шаг к ней → `goal`;
|
||||
- снаружи появляется то, чего не было → `feature`;
|
||||
- поведение расходится с заявленным и **воспроизводится** → `fix`
|
||||
(не воспроизводится → `research`);
|
||||
@@ -535,12 +500,12 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
|
||||
Не подходит ни один — в записи их два, разбирай. Не проходит тест готовности
|
||||
(см. task-format) — это **сырьё**: `--type research`, раздел «Вопрос» пока
|
||||
пуст, место в конце категории. Не делается одним заходом — это не эпик, а
|
||||
несколько задач под одной целью: дроби сразу.
|
||||
4. **Цель — если тип её требует.** У `feature` должен быть `--goal <слаг>`:
|
||||
новая возможность и есть содержание цели. Подходящей нет — либо она
|
||||
заводится (`--type goal`), либо перед тобой не `feature`. У `fix`, `chore` и
|
||||
`research` цели может не быть вовсе, и придумывать её не надо.
|
||||
пуст, место в конце секции. Не делается одним заходом — дроби на шаги
|
||||
помельче и ставь их в списке подряд.
|
||||
4. **Место в списке.** `add` кладёт строку в конец секции всегда. На стройке это
|
||||
почти наверняка не то место: порядок там зависимость, и новый шаг чаще всего
|
||||
встаёт в середину — `move <слаг> --after <слаг>`. На доработке конец списка
|
||||
законен: место в очереди назначает груминг, а не заведение.
|
||||
5. `add …`, затем допиши тело редактором **по схеме своего типа** — шаблон её
|
||||
уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем
|
||||
нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый
|
||||
@@ -554,18 +519,46 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
пять файлов из одной мысли, а сорок файлов из сорока сырых находок. Защита та
|
||||
же, что в самом ревью: кластеризация по причине, дедуп против живых и
|
||||
`REJECTED.md`, находка без свидетельства → сырьё (`research`), а не задача, и карта кластеров
|
||||
пользователю до создания файлов. Порядок, отображение серьёзности и привязка к
|
||||
целям — [references/from-review.md](references/from-review.md).
|
||||
пользователю до создания файлов. **Стадию смотри и здесь**, тем же нулевым
|
||||
шагом: от неё зависит, куда ляжет тяжёлая находка — наверх очереди или после
|
||||
своей зависимости. Порядок и отображение серьёзности —
|
||||
[references/from-review.md](references/from-review.md).
|
||||
|
||||
### Прийти в репозиторий, где задачи уже как-то ведутся
|
||||
|
||||
Разовая операция: вывести каталог задач из старой раскладки беклога, `TODO.md`,
|
||||
заметок или списка шагов роадмапа — [references/adopt.md](references/adopt.md).
|
||||
заметок или списка шагов плана — [references/adopt.md](references/adopt.md).
|
||||
Сюда же относится переименование транслитных слагов в английские: оно делается
|
||||
**одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу.
|
||||
|
||||
Если переводить надо не только задачи, а весь `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-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индексов; документы проекта — только как словарь |
|
||||
| `task-form` | заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса | только `items/` |
|
||||
| `task-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индекса; документы проекта — только как словарь |
|
||||
|
||||
Разделены они не по охвату, а **по глубине**. Язык проверяется по словам и
|
||||
фразам, поштучно; форма записи требует понять, что задача делает, и открыть
|
||||
цель, на которую она ссылается. Слитый проход одну половину делает дорогой, а
|
||||
вторую — поверхностной.
|
||||
фразам, поштучно; форма записи требует понять, что задача делает. Слитый проход
|
||||
одну половину делает дорогой, а вторую — поверхностной.
|
||||
|
||||
Модель у обоих одна, `sonnet`, и это не отменяет разреза. Оба судят по
|
||||
**записанному правилу** — семь пунктов формы против правил языка, — а их находка
|
||||
**записанному правилу** — шесть пунктов формы против правил языка, — а их находка
|
||||
приходит готовой формулировкой, которую читает и отклоняет человек, а не молча
|
||||
реализует оркестратор. Ошибка здесь стоит строки чтения, и платить за неё верхней
|
||||
моделью не за что.
|
||||
@@ -670,23 +662,25 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
- **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда:
|
||||
раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога
|
||||
нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект
|
||||
действительно новый, а перевод чужой раскладки делает `av-dev:doc-canon`.
|
||||
действительно новый, а перевод чужой раскладки делает `av-dev:canon`.
|
||||
У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и
|
||||
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
|
||||
- **Версия и настройки живут в `.av-dev.toml` в корне репозитория** — версия
|
||||
ключом `version`, настройки каталога секцией `[tasks]`: `dir` — где каталог
|
||||
лежит, плюс **имена** файлов и заголовков, и последние только если отличаются
|
||||
от умолчания. Неизвестный ключ в секции — код 3 на любой команде, так что
|
||||
лишнее слово останавливает работу с задачами целиком.
|
||||
лежит, `stage` — стадия проекта, плюс **имена** файлов и заголовков, и
|
||||
последние только если отличаются от умолчания. Неизвестный ключ в секции — код
|
||||
3 на любой команде, так что лишнее слово останавливает работу с задачами
|
||||
целиком.
|
||||
|
||||
Дом в корне, а не внутри каталога задач, по двум причинам: настройка, лежащая
|
||||
внутри настраиваемого каталога, не смогла бы сказать, **где он**; и версия
|
||||
одна на весь плагин, а корень есть и у проекта без `docs/`. Прежние
|
||||
`<каталог задач>/.tasks.json` и `docs/.docs.json` не читаются — увидев их,
|
||||
скрипт говорит «прежняя раскладка» и зовёт `upgrade`.
|
||||
- **Секции беклога** берутся из заголовков `##` индекса как есть; их количество
|
||||
и названия — дело проекта (умолчание `Ядро` / `Инфра`). **В конфиге их нет** —
|
||||
второй список разошёлся бы с заголовками молча.
|
||||
- **Секции беклога** берутся из заголовков `##` индекса как есть; их названия —
|
||||
дело проекта (умолчание `План` на стройке, `Ядро` / `Инфра` на доработке), а
|
||||
количество ограничено стадией: на стройке секция одна. **В конфиге секций
|
||||
нет** — второй список разошёлся бы с заголовками молча.
|
||||
|
||||
### Вызов из другого плагина
|
||||
|
||||
@@ -723,8 +717,10 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
|
||||
- **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным
|
||||
предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть,
|
||||
под какую цель отнести, какая рамка разведки верна — решение пользователя. Слаг,
|
||||
формулировка, порядок строк в индексе — механика, делаем сами.
|
||||
какая рамка разведки верна, пора ли менять стадию — решение пользователя. Слаг
|
||||
и формулировка — механика, делаем сами. **Порядок строк механикой не
|
||||
считается** ни на одной стадии: на стройке он зависимость, на доработке
|
||||
приоритет, и оба называет человек.
|
||||
- **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа;
|
||||
решений больше — веди **несколько итераций** диалога по ≤3, а не один
|
||||
перегруженный запрос. Между итерациями применяй уже решённое.
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
# Адаптация каталога задач
|
||||
|
||||
Проект, где задачи уже как-то ведутся, и из имеющегося материала **выводится**
|
||||
заполненный каталог задач: цели, задачи, кладбище, индексы. Операция разовая —
|
||||
заполненный каталог задач: задачи, кладбище, индекс. Операция разовая —
|
||||
после неё проект живёт скиллами `task-track` и `task-groom`.
|
||||
|
||||
**Это часть приведения проекта к канону.** Раскладку `docs/` целиком ведёт скилл
|
||||
`av-dev:doc-canon`; он же зовёт этот сценарий на шаге «каталог задач», потому что
|
||||
форматом задач владеет `task-track`, а не `doc-canon`. Отдельно сценарий вызывается,
|
||||
`av-dev:canon`; он же зовёт этот сценарий на шаге «каталог задач», потому что
|
||||
форматом задач владеет `task-track`, а не `canon`. Отдельно сценарий вызывается,
|
||||
когда переводить надо **только** задачи.
|
||||
|
||||
Вход какой угодно: старая раскладка `av-dev-backlog` (индекс `README.md`,
|
||||
кладбище `CLOSED.md`, приоритеты секциями, транслитные слаги, файлы рядом с
|
||||
индексом), `TODO.md`, россыпь заметок, раздел «планы» в `README.md`, список
|
||||
шагов роадмапа проекта.
|
||||
шагов плана проекта.
|
||||
|
||||
## Три правила, из которых всё следует
|
||||
|
||||
1. **Сперва карта, потом файлы.** Человеку показывается, что найдено, как
|
||||
разложилось по целям и **что не разложилось**, — и только после подтверждения
|
||||
1. **Сперва карта, потом файлы.** Человеку показывается, что найдено, в каком
|
||||
порядке разложилось и **что не разложилось**, — и только после подтверждения
|
||||
пишется хоть один файл. Это то же правило, что у интейка находок ревью:
|
||||
массовое заведение записей без подтверждения — самый дорогой отказ, потому
|
||||
что разгребает его потом переоценка.
|
||||
@@ -38,7 +38,7 @@
|
||||
tk="$CLAUDE_PLUGIN_ROOT/skills/task-track/scripts/tasks.py"
|
||||
|
||||
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 \
|
||||
--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` в
|
||||
`tie-break-equal-completeness` может только тот, кто понимает смысл. `scan`
|
||||
честно говорит: проверить надо **все** слаги, признаки транслита — эвристика;
|
||||
- **цели.** Шаги роадмапа — готовые цели в **`Запланировано`** (очередь и
|
||||
обоснование у них уже есть); тематические скопления задач — цели в
|
||||
**`Направления`** («прочность слияния»,
|
||||
«журнал и пересборка»). Предлагаешь ты, назначает человек;
|
||||
- **стадия.** `--stage` называет, чем этот беклог будет: планом стройки или
|
||||
очередью правок. Машине это не выводится — она видит список пунктов, а не то,
|
||||
построено приложение или нет;
|
||||
- **порядок.** Нумерованные шаги источника `scan` сохраняет по номерам, прочие
|
||||
ставит следом. Дальше порядок — твоё суждение и подтверждение человека: на
|
||||
стройке это зависимость, на доработке важность;
|
||||
- **что вообще не задача.** Обоснование порядка шагов, абзац прозой, заголовок
|
||||
раздела — это не пункты беклога, и они уходят в «не разложилось» с причиной.
|
||||
|
||||
## Порядок
|
||||
|
||||
1. **Осмотрись.** Где лежат задачи, роадмап, заметки. Каталог задач по канону —
|
||||
всегда `tasks`. Секции беклога (`--sections`) — по умолчанию
|
||||
`Ядро,Инфра`; если у проекта деление другое по существу, оно называется
|
||||
здесь, а не подгоняется под умолчание, и становится **заголовками `##`
|
||||
индекса** — их единственным домом. В `.av-dev.toml` секции не пишутся: там
|
||||
версия формата и имена частей, а второй список секций разошёлся бы с
|
||||
заголовками молча.
|
||||
1. **Осмотрись и назови стадию.** Где лежат задачи, план, заметки; построено
|
||||
приложение или строится. Каталог задач по канону — всегда `tasks`. Секции
|
||||
беклога (`--sections`) — на стройке ровно одна (умолчание `План`), на
|
||||
доработке сколько нужно (умолчание `Ядро,Инфра`); если у проекта деление
|
||||
другое по существу, оно называется здесь, а не подгоняется под умолчание, и
|
||||
становится **заголовками `##` индекса** — их единственным домом. В
|
||||
`.av-dev.toml` секции не пишутся: там версия, стадия и имена частей, а второй
|
||||
список секций разошёлся бы с заголовками молча.
|
||||
2. **`adopt scan`** по всем источникам разом. Один прогон, одна карта: два
|
||||
прохода дадут два несогласованных состояния.
|
||||
3. **Заполни карту**: `slug` (английский), `section`, `goal` у каждой записи;
|
||||
список `goals` — из шагов роадмапа и из тем. Закрытый шаг целью не
|
||||
заводится. Пустой `goal` законен у `fix`, `chore` и `research` — они служат
|
||||
работоспособности, а не направлению; у `feature` цель обязательна.
|
||||
3. **Заполни карту**: `slug` (английский), `type` и `section` у каждой записи, и
|
||||
**порядок `items`** — он уедет в индекс как есть. Пункт, помеченный
|
||||
закрытым, не переносится вовсе.
|
||||
4. **Покажи человеку карту** через `AskUserQuestion`, ≤3 вопроса за итерацию,
|
||||
рекомендация первым вариантом. Показывается: сколько записей, предлагаемые
|
||||
цели (порядок и темы) с обоснованием, спорные отнесения, список «не
|
||||
разложилось». Массовые механические решения (слаги, порядок строк) не
|
||||
выносятся — это механика.
|
||||
рекомендация первым вариантом. Показывается: сколько записей, предлагаемый
|
||||
порядок с обоснованием, спорные отнесения, список «не разложилось». Слаги не
|
||||
выносятся — это механика; **порядок выносится всегда**, потому что механикой
|
||||
он не является ни на одной стадии.
|
||||
5. **`adopt apply`.** `--refs` перечисляет **всё**, где могут стоять ссылки на
|
||||
слаги: документация, архив изменений, `CLAUDE.md`, `README.md`. Скрипт
|
||||
посчитает и покажет, сколько ссылок поправлено и по каким слагам.
|
||||
6. **`tasks.py check`** и доклад.
|
||||
|
||||
`apply` отказывается писать поверх живого каталога и проверяет карту целиком
|
||||
**до** первой записи: неверная секция, дубль слага, цель, которой нет в карте —
|
||||
всё это отказ до того, как на диске появился хотя бы один файл.
|
||||
**до** первой записи: неверная секция, дубль слага, неназванный тип, две секции
|
||||
при стадии `build` — всё это отказ до того, как на диске появился хотя бы один
|
||||
файл.
|
||||
|
||||
## Переходное состояние — объявляется, а не заминается
|
||||
|
||||
Сразу после адаптации задачи в большинстве своём **не готовы к взятию**: у них
|
||||
нет критериев приёмки, а у части может не быть цели. Это нормально, но обязано
|
||||
быть названо, иначе следующий агент примет пустой беклог за поломку.
|
||||
нет критериев приёмки. Это нормально, но обязано быть названо, иначе следующий
|
||||
агент примет пустой беклог за поломку.
|
||||
|
||||
`apply` печатает состояние по факту: сколько задач без цели (это **ошибки**
|
||||
`check`) и сколько не собрало разделы своего типа (для `check` это не ошибка, а
|
||||
строка здоровья, но `ready` такую задачу не пропустит). Закрывается это
|
||||
**порциями груминга** — скилл
|
||||
`groom`, 5–8 задач за порцию: проставить цели, превратить «готово, когда» в
|
||||
критерии с оракулами, вынуть вопросы из прозы в раздел «Вопросы». Там же
|
||||
беклогу впервые назначается **порядок**: после адаптации его нет вовсе, а
|
||||
очередь и есть то, ради чего каталог заводят.
|
||||
`apply` печатает состояние по факту: сколько задач не собрало разделы своего типа
|
||||
(для `check` это не ошибка, а строка здоровья, но `ready` такую задачу не
|
||||
пропустит). Закрывается это **порциями по 5–8 задач**: превратить «готово,
|
||||
когда» в критерии с оракулами, вынуть вопросы из прозы в раздел «Вопросы». На
|
||||
доработке это груминг (скилл `av-dev:task-groom`), на стройке — гигиена полей
|
||||
этого скилла: груминга там нет.
|
||||
|
||||
**Порядок строк проверяется глазами отдельно.** На стройке он выведен из
|
||||
нумерации источника, и там, где её не было, он случаен. На доработке машина
|
||||
важности не знает вовсе — очередь расставляется первым же грумингом.
|
||||
|
||||
Готовность к первой задаче — не «`check` зелёный», а «`ready` пропускает хотя бы
|
||||
верхние строки очереди».
|
||||
@@ -113,18 +118,20 @@ python3 $tk adopt apply --plan tasks-adopt-plan.json \
|
||||
дело человека, удалять чужое молча нельзя. В доклад идёт готовая команда.
|
||||
- **Не переписывает подписи ссылок.** `[docs/backlog](tasks/BACKLOG.md)` —
|
||||
цель поправлена, текст остался; это правится глазами, и таких мест немного.
|
||||
- **Не сочиняет критерии приёмки и не придумывает цели**, которых в материале
|
||||
нет. Придуманная цель хуже отсутствующей: под неё заведут задачи.
|
||||
- **Не сочиняет критерии приёмки** и не додумывает пользы, которой в материале
|
||||
нет.
|
||||
- **Не выводит стадию.** Список пунктов одинаково выглядит и планом стройки, и
|
||||
очередью правок; отвечает `--stage`, а называет его человек.
|
||||
- **Не трогает историю.** В коммитах старые слаги остаются, и это нормально.
|
||||
|
||||
## Доклад
|
||||
|
||||
- Источники и что в каждом распознано (раскладка, индекс, кладбище, секции).
|
||||
- Сколько записей перенесено, сколько целей заведено (порядок / темы) и откуда
|
||||
каждая выведена.
|
||||
- Стадия и сколько записей перенесено; откуда взялся порядок (нумерация
|
||||
источника или суждение).
|
||||
- **Переименования**: сколько слагов, сколько ссылок поправлено и в скольких
|
||||
файлах — числом, а не «поправлены ссылки».
|
||||
- **Не разложилось**: поимённо, с причиной.
|
||||
- Переходное состояние: сколько задач без цели, сколько без критериев, чем и за
|
||||
сколько порций закрывается.
|
||||
- Переходное состояние: сколько задач без критериев, чем и за сколько порций
|
||||
закрывается.
|
||||
- `tasks.py check` — результат строкой.
|
||||
|
||||
@@ -50,17 +50,12 @@
|
||||
заведённое и уже выкинутое. Нашлось среди живых — дописываем находку в
|
||||
существующий файл. Нашлось в `REJECTED.md` — это сигнал: причина отказа могла
|
||||
устареть, выноси пользователю, а не заводи молча заново.
|
||||
4. **Разложи по целям — там, где цель нужна.** Большинство находок ревью это
|
||||
`fix` и `chore`, и **цель им не требуется**: они служат работоспособности, а
|
||||
не направлению. Придуманная им цель —
|
||||
ровно то враньё, от которого спасает тип.
|
||||
|
||||
Цель обязательна у находки, которая оказалась **новой возможностью**
|
||||
(`feature`): нашлось поведение, которого никто не заказывал, и его надо
|
||||
либо заказать целью, либо убрать. Подходящей цели нет — заведи её
|
||||
(`add --type goal --section Направления`) в том же проходе.
|
||||
4. **Проставь типы.** Большинство находок ревью это `fix` и `chore`. Находка,
|
||||
оказавшаяся **новой возможностью** (`feature`), — отдельный случай: нашлось
|
||||
поведение, которого никто не заказывал, и решение тут не «завести задачу», а
|
||||
«заказать или убрать». Выноси такую пользователю отдельно от прочих.
|
||||
5. **Покажи карту до создания файлов.** Кластер → задача / сырьё / строка в
|
||||
пакетный файл / уже заведено / отброшено, и под какую цель — пачкой через
|
||||
пакетный файл / уже заведено / отброшено — пачкой через
|
||||
`AskUserQuestion`. Это тот же барьер, что и «три кандидата» в интейке из
|
||||
диалога: массовое заведение файлов без подтверждения — ровно тот отказ, ради
|
||||
которого интейк из ревью и выделен. Дешёвая мелочь по явному согласию может
|
||||
@@ -88,8 +83,16 @@
|
||||
[скилле груминга](../../task-groom/SKILL.md#приоритет-как-его-расставляют), и
|
||||
серьёзность попадает ровно в один из них.
|
||||
|
||||
- **тяжёлая находка со свидетельством о сломанном сейчас** → задача под ту цель,
|
||||
которой она угрожает, и **первой строкой секции**: `move <слаг> --first
|
||||
**Всё это — про доработку.** На стройке порядок строк значит зависимость, и
|
||||
`--first` там означает «ни от чего не зависит», а не «важнее всех»: находка,
|
||||
поднятая наверх, встанет перед собственной зависимостью. Место находке на стройке
|
||||
называет зависимость — `move --after <шаг, после которого её можно делать>`, — а
|
||||
серьёзность идёт **причиной в мете** и разбирается ближайшим пересмотром плана
|
||||
(`task-track`, «Пересмотр плана стройки»). Груминга там нет, и откладывать «до
|
||||
него» некуда.
|
||||
|
||||
- **тяжёлая находка со свидетельством о сломанном сейчас** → задача
|
||||
**первой строкой секции**: `move <слаг> --first
|
||||
--reason «сломано сейчас: …»`. Это довод «что сломано сейчас» из перечня
|
||||
груминга — единственный, который не требует сравнения с соседями по очереди,
|
||||
потому что сломанное дорожает само. Позицию всё равно назначает человек, и
|
||||
@@ -124,7 +127,7 @@
|
||||
## Доклад
|
||||
|
||||
- Источник (какое ревью/аудит, сколько находок на входе).
|
||||
- Свёрнуто в задачи: N кластеров из M находок, со слагами, целями и тегом партии.
|
||||
- Свёрнуто в задачи: N кластеров из M находок, со слагами и тегом партии.
|
||||
- Что не заведено и почему: починено инлайн, уже заведено, стало сырьём, ушло в
|
||||
`REJECTED.md`.
|
||||
- Поимённая сверка: находок на входе N, исход есть у N.
|
||||
|
||||
@@ -8,14 +8,19 @@
|
||||
|
||||
Задачу можно дробить, только если части удовлетворяют **обоим** условиям:
|
||||
|
||||
1. **Мерджатся независимо.** Часть Б не требует, чтобы часть А была уже влита.
|
||||
Есть порядок «сперва А, потом Б, иначе не собрать» → это не декомпозиция, а
|
||||
план реализации: шаги остаются **внутри одного файла**.
|
||||
1. **Каждая мерджится сама по себе.** Часть, после которой дерево не собирается
|
||||
или поведение сломано до прихода соседней, — не часть, а половина.
|
||||
2. **Каждая — самостоятельный шаг.** Часть, осмысленная только в комплекте с
|
||||
другой, — не задача. Проверяй тестом «готова к взятию» (task-format): какую
|
||||
строку «Завершения» цели двигает **именно эта часть** и какие у неё
|
||||
собственные критерии приёмки. У операционных частей (`fix`, `chore`,
|
||||
`research`) цели может не быть — тогда достаточно собственных критериев.
|
||||
другой, — не задача. Проверяй тестом «готова к взятию» (task-format): свои
|
||||
критерии приёмки у неё есть или нет.
|
||||
|
||||
**Порядок между частями законен на стройке и подозрителен на доработке**, и это
|
||||
единственное, что стадия здесь меняет. Беклог стройки **весь** состоит из
|
||||
упорядоченных зависимостью шагов: «сперва А, потом Б» — не повод не дробить, а
|
||||
описание того, как этот список устроен, и части просто встают подряд. На
|
||||
доработке правки независимы, и обнаруженный порядок «иначе не собрать» чаще
|
||||
всего значит, что перед тобой не декомпозиция, а план реализации: шаги остаются
|
||||
**внутри одного файла**.
|
||||
|
||||
Не проходит хотя бы одно — **не дроби**. Ложная декомпозиция плодит файлы,
|
||||
которые нельзя взять поодиночке, и переоценка потом склеивает их обратно.
|
||||
@@ -45,37 +50,29 @@
|
||||
гигиена полей снимает. Шов пользуется меткой как **признаком**, что в задаче
|
||||
две разнородные работы; решение о метке остаётся за конвейером.
|
||||
|
||||
**Цель наследуется.** Все части несут `goal:` родителя: декомпозиция не меняет
|
||||
того, чему работа служит. Если у части цель другая — это признак, что дробили не
|
||||
по той границе, либо что часть вообще из другой работы.
|
||||
|
||||
## Что делать с родителем
|
||||
|
||||
После разделения родитель **не остаётся** третьей висящей строкой:
|
||||
|
||||
- части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
|
||||
`REJECTED.md` здесь — не «выкинули», а именно тот след, что переживает запись:
|
||||
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
|
||||
наследников, а не археологией git;
|
||||
- родитель осмыслен как **возможность**, а не как шаг → это цель, и **строка
|
||||
переезжает**: `edit <slug> --type goal --section <часть роадмапа>` снимает её с
|
||||
`BACKLOG.md` и вставляет в `ROADMAP.md`. Файл в `items/` при этом не двигается —
|
||||
он и есть запись. Части получают `--goal <слаг родителя>`, а закрывать родителя
|
||||
нечем и незачем: он не выкинут, он стал целью.
|
||||
части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
|
||||
`REJECTED.md` здесь — не «выкинули», а именно тот след, что переживает запись:
|
||||
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
|
||||
наследников, а не археологией git.
|
||||
|
||||
**Промежуточного зонтика между целью и задачей нет.** Тип `epic` упразднён:
|
||||
роль зонтика играет цель, а слишком крупный шаг дробится на шаги помельче под
|
||||
той же целью. Если частям нужен общий заголовок — значит у них общая
|
||||
возможность, и её надо назвать целью, а не заводить временный тип.
|
||||
**Зонтика над частями нет никакого.** Тип `epic` упразднён, цель, игравшая его
|
||||
роль после него, — тоже. Если частям нужен общий заголовок, у них общее место в
|
||||
списке: они встают подряд, и соседство и есть тот ответ, ради которого заводили
|
||||
зонтик.
|
||||
|
||||
## Когда декомпозиция случается посреди работы
|
||||
|
||||
Задача, которая **оказалась крупнее задачи**, распознаётся до того, как под неё
|
||||
заведено предложение об изменении: иначе его придётся выбрасывать. Она выходит
|
||||
из работы на декомпозицию, а её строка возвращается в беклог с причиной
|
||||
(`move … --reason "крупнее задачи"`). Части заводятся сразу под той же целью, и
|
||||
**место в очереди им назначает человек**: машина поставит их в конец секции, а
|
||||
крупная задача редко распадается на что-то менее срочное, чем была сама.
|
||||
(`move … --reason "крупнее задачи"`). **Место в списке частям назначает
|
||||
человек**: машина поставит их в конец секции, а на стройке место наследуется от
|
||||
родителя (`move --after`), да и на доработке крупная задача редко распадается на
|
||||
что-то менее срочное, чем была сама.
|
||||
|
||||
## Мозговой штурм сырья
|
||||
|
||||
@@ -96,9 +93,9 @@ Applicative-штурм («перечисли задачи, следующие и
|
||||
applicative.
|
||||
2. **Вынеси формы пользователю** через `AskUserQuestion` с компромиссами. Рамку
|
||||
выбирает он: это продуктовое решение, не механика.
|
||||
3. **Назови цель.** Выбранная форма служит цели — существующей или новой. Идея,
|
||||
для которой цель не находится, скорее всего уезжает в `REJECTED.md`, а не
|
||||
заводится задачей.
|
||||
3. **Назови пользу.** Выбранная форма отвечает на «что станет наблюдаемо иначе».
|
||||
Идея, для которой такого ответа не находится, скорее всего уезжает в
|
||||
`REJECTED.md`, а не заводится задачей.
|
||||
4. **Только выбранную форму** дроби по тесту декомпозиции выше и проставь
|
||||
критерии приёмки: без них наследники останутся идеями под другим именем.
|
||||
|
||||
@@ -109,8 +106,8 @@ Applicative-штурм («перечисли задачи, следующие и
|
||||
## Доклад
|
||||
|
||||
- Идея/задача на входе, выбранная рамка (для штурма), задачи-наследники со
|
||||
слагами, целями и секциями.
|
||||
- Судьба родителя: удалён / стал целью / выкинут с причиной.
|
||||
слагами, секциями и местом в списке.
|
||||
- Судьба родителя: удалён / выкинут с причиной.
|
||||
- `tasks.py check` после правок.
|
||||
- Границы покрытия: какие постановки рассмотрены и какие сознательно отброшены —
|
||||
чтобы штурм не пришлось повторять с нуля.
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
|
||||
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
||||
| Поле места | **Категория** — полка домена беклога |
|
||||
| Цель (`goal:<слаг>`) | нет: цель — это возможность, а здесь её не появляется |
|
||||
| Индекс | `BACKLOG.md` |
|
||||
| Берётся в работу | да |
|
||||
|
||||
@@ -43,7 +42,7 @@
|
||||
## Алгоритм
|
||||
|
||||
1. **Проверить, что поведение не меняется.** Меняется — это `feature` или `fix`,
|
||||
и у неё другие требования (цель, воспроизведение).
|
||||
и у последнего другие требования (воспроизведение).
|
||||
2. **Назвать, что перестанет мешать** — одной фразой, адресуясь разработчику.
|
||||
«Прибраться в модуле X» — не ответ: непонятно, что изменится.
|
||||
3. **Назвать границы** в `Затрагивает`. У обслуживания они часто не в коде:
|
||||
@@ -55,9 +54,6 @@
|
||||
5. **Проверить, что это не «заодно».** Обслуживание любит склеиваться в пачку
|
||||
(«обновить зависимости и переписать сборку и убрать мёртвый код»). Не
|
||||
мерджится порознь — это несколько задач ([split.md](split.md)).
|
||||
6. **Цель не проставлять.** `chore` служит работоспособности, а не направлению.
|
||||
Работа по сопровождению проекта при этом видна в роадмапе — секцией
|
||||
`Сопровождение`, но целью не становится.
|
||||
|
||||
## Кто такую задачу решает
|
||||
|
||||
|
||||
@@ -15,18 +15,13 @@
|
||||
| Обязательные разделы | `Затрагивает`, `Критерии приёмки` |
|
||||
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
||||
| Поле места | **Категория** — полка домена беклога |
|
||||
| Цель (`goal:<слаг>`) | **обязательна** |
|
||||
| Индекс | `BACKLOG.md` |
|
||||
| Берётся в работу | да |
|
||||
|
||||
**Цель обязательна, и это единственный тип, у которого так.** Новая возможность
|
||||
и есть содержание цели: подходящей нет — либо она заводится, либо перед тобой не
|
||||
`feature`. `ready` без цели откажет.
|
||||
|
||||
## Алгоритм
|
||||
|
||||
1. **Найти цель или завести её.** Задача без цели, названная функцией, — самый
|
||||
частый способ пронести в беклог работу, которой никто не заказывал.
|
||||
1. **Проверить, что возможность и правда новая.** Поведение расходится с уже
|
||||
заявленным — это `fix`, а не `feature`, и требования у него другие.
|
||||
2. **Назвать границы** в разделе `Затрагивает`: эндпоинт или команда, таблица и
|
||||
миграция, формат на диске, публичный тип пакета, внешний сервис. Названы
|
||||
**границы, а не замысел**: «переписать хранилище на новый драйвер» — замысел,
|
||||
@@ -35,13 +30,12 @@
|
||||
3. **Написать критерии приёмки** — 2–5 проверяемых утверждений списком, у
|
||||
каждого назван оракул. Не «работает корректно», а «повторный прогон даёт тот
|
||||
же отпечаток — оракул: команда сверки».
|
||||
4. **Сказать, какую строку «Завершения» цели задача двигает.** Одной строкой в
|
||||
теле. Это защита от задачи «отрефакторить X»: она проваливается не потому,
|
||||
что невидима снаружи, а потому, что не находит строки, к которой относится.
|
||||
4. **Поставить её на место в списке.** На стройке место называет зависимость:
|
||||
`move <слаг> --after <шаг, без которого нельзя>`. На доработке место в
|
||||
очереди назначает груминг, и конец списка законен.
|
||||
5. **Проверить, что задача одна.** Отвечается всё, но задача не делается одним
|
||||
заходом и не мерджится целиком — это несколько задач под одной целью, дроби
|
||||
сразу ([split.md](split.md)). Промежуточного зонтика между целью и задачей
|
||||
нет.
|
||||
заходом и не мерджится целиком — это несколько задач, дроби сразу
|
||||
([split.md](split.md)) и ставь их в списке подряд.
|
||||
6. **Реализация** — дело конвейера проекта, не этого скилла. Закрывается
|
||||
`close <слаг> --implemented`: файл и строка удаляются, суть переезжает в
|
||||
`openspec/specs/` и документацию.
|
||||
@@ -49,8 +43,8 @@
|
||||
## Что видит машина, а что человек
|
||||
|
||||
Схему типа судит `ready` на входе в работу: **наличие непустого** раздела
|
||||
`Затрагивает`, **число** критериев (меньше двух — отказ, больше пяти —
|
||||
замечание) и цель. Наличие оракула проверяется **эвристикой** — словом «оракул»
|
||||
`Затрагивает` и **число** критериев (меньше двух — отказ, больше пяти —
|
||||
замечание). Наличие оракула проверяется **эвристикой** — словом «оракул»
|
||||
в пункте. `check` этого поимённо не говорит, а считает строкой здоровья
|
||||
(`SKILL.md`, «Что механизировано, а что нет»).
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
| Обязательные разделы | **`Воспроизведение`**, `Затрагивает`, `Критерии приёмки` |
|
||||
| Допустимые сверх того | `Рамки`, `Вопросы` |
|
||||
| Поле места | **Категория** — полка домена беклога |
|
||||
| Цель (`goal:<слаг>`) | необязательна |
|
||||
| Индекс | `BACKLOG.md` |
|
||||
| Берётся в работу | да |
|
||||
|
||||
@@ -54,9 +53,7 @@
|
||||
почти всегда есть парный критерий: **прежнее поведение не сломалось**
|
||||
(«ввод `а1` принимается по-прежнему»). Без него починка чинит одно и ломает
|
||||
соседнее.
|
||||
6. **Цель не выдумывать.** `fix` служит работоспособности, а не направлению.
|
||||
Придуманная цель — то же враньё, от которого спасает тип.
|
||||
7. **Записать дефект в журнал** `docs/review.md` с пометкой «проскочил / пойман
|
||||
6. **Записать дефект в журнал** `docs/review.md` с пометкой «проскочил / пойман
|
||||
ревью». Проскочившие — проверочный набор для калибровки конвейера; пойманные с
|
||||
оракулом — лучшая опора для прохода ревью: проектные, воспроизводимые,
|
||||
однажды оказавшиеся правдой.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Формат записей и индексов
|
||||
# Формат записей и индекса
|
||||
|
||||
Заголовок, мета-блок и строку индекса ставит `tasks.py add` — руками их не
|
||||
пишут. Этот файл описывает **общую форму** любой записи и то, что проверяет
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
| Тип | Файл | Одной строкой |
|
||||
| --- | --- | --- |
|
||||
| 🎯 `goal` | [task-goal.md](task-goal.md) | возможность приложения |
|
||||
| ✨ `feature` | [task-feature.md](task-feature.md) | снаружи появляется то, чего не было |
|
||||
| 🐞 `fix` | [task-fix.md](task-fix.md) | поведение расходится с заявленным |
|
||||
| 🧹 `chore` | [task-chore.md](task-chore.md) | обслуживание, поведение не меняется |
|
||||
@@ -25,7 +24,6 @@
|
||||
- **Тип:** fix
|
||||
- **Категория:** Ядро — вернулась из работы: остаток писал нерешённое в журнал
|
||||
- **Зачем:** ввод «а1б2» ходит в a1 — игрок не видит, что ошибся, и винит игру
|
||||
- **Теги:** goal:merge-robustness
|
||||
|
||||
Разбор хода читает первые два символа и молча выбрасывает остаток строки.
|
||||
|
||||
@@ -55,8 +53,8 @@
|
||||
**эмодзи типа**, и она **производна**: её ставит `add` и чинит `check --fix`
|
||||
по полю меты. Второго дома у типа нет — эмодзи это его отображение, как
|
||||
строка индекса это отображение файла.
|
||||
- **Форма заголовка — по типу.** Цель отвечает на «что приложение будет уметь»;
|
||||
`feature`, `fix` и `chore` — на «что нужно сделать», глаголом в неопределённой
|
||||
- **Форма заголовка — по типу.** `feature`, `fix` и `chore` отвечают на «что
|
||||
нужно сделать», глаголом в неопределённой
|
||||
форме, перед ним допускается «не»; `research` называет предмет разведки и
|
||||
формы действия **не несёт намеренно**. Почему так — SKILL.md, «Как написана
|
||||
задача». `check` считает заголовки не в форме действия и печатает число в
|
||||
@@ -67,9 +65,8 @@
|
||||
теги необязательны. Нераспознанные поля сохраняются: скрипт правит свои и не
|
||||
трогает чужие.
|
||||
- **Тип — первым полем.** Он решает, что у записи вообще может быть: какие
|
||||
разделы обязательны, нужна ли цель, берётся ли она в работу, — и читается
|
||||
раньше всего остального. Словарь **закрыт**: `goal` | `feature` | `fix` |
|
||||
`chore` | `research`. Не подходит ни один — это сигнал, что в записи их два и
|
||||
разделы обязательны и берётся ли она в работу, — и читается раньше всего
|
||||
остального. Словарь **закрыт**: `feature` | `fix` | `chore` | `research`. Не подходит ни один — это сигнал, что в записи их два и
|
||||
её надо разделить.
|
||||
- **«Зачем» отвечает на «зачем нужна эта задача»** — состояние, остаток, боль.
|
||||
Не пересказ задачи: пересказ уже есть по ссылке. Живёт здесь, а не только в
|
||||
@@ -86,19 +83,16 @@
|
||||
Тело — не план реализации и не спецификация: принятое и реализованное переезжает
|
||||
в документацию проекта, а файл задачи удаляется.
|
||||
|
||||
### Поле места: «Категория» и «Секция»
|
||||
### Поле места: «Категория»
|
||||
|
||||
Поле называет, **где числится строка**, и имя у него **зависит от типа**:
|
||||
Поле называет **секцию беклога, в которой числится строка** — полку домена
|
||||
(`Ядро`, `Инфра`, …), куда задачу положили и куда вернут, если она уйдёт в работу
|
||||
и вернётся. **На стройке секция одна**, и поле называет её же: различать ей
|
||||
нечего, но производность от заголовка индекса сохраняется и там.
|
||||
|
||||
| Тип | Поле | Значения | Что это |
|
||||
| --- | --- | --- | --- |
|
||||
| `goal` | **Секция** | `Запланировано`, `Направления`, `Сопровождение` | часть роадмапа: состояние очереди |
|
||||
| прочие | **Категория** | секции беклога проекта (`Ядро`, `Инфра`, …) | полка домена, на которой задача лежит |
|
||||
|
||||
Разные имена потому, что это **разные вещи**. У задачи это полка: куда её
|
||||
положили и куда вернут, если она уйдёт в работу и вернётся. У цели оно называет
|
||||
не полку, а место в очереди работ. Одно имя на два смысла их и смешивало; `check` называет
|
||||
несовпадение дрейфом, `check --fix` переименовывает.
|
||||
Прежнее имя поля — **«Секция»**: так оно называлось у целей, указывая на часть
|
||||
роадмапа. Разбор его по-прежнему принимает, `check` называет дрейфом, `check
|
||||
--fix` переименовывает.
|
||||
|
||||
Имя самого места принадлежит **заголовку индекса** — файл на него лишь
|
||||
ссылается, и принадлежность сверяется по нижнему регистру.
|
||||
@@ -111,14 +105,18 @@
|
||||
| --- | --- |
|
||||
| префикс `[goal]` / `[idea]` в H1 | поле **Тип** + эмодзи в H1; `[idea]` → `research` |
|
||||
| тег `kind:<род>` | поле **Тип** (род работы стал типом) |
|
||||
| поле **Секция** у задачи | поле **Категория** |
|
||||
| поле **Секция** | поле **Категория** |
|
||||
| теги `goal:<слаг>` и `decomposed` | сняты: целей больше нет |
|
||||
| поле **Хук** | поле **Зачем** |
|
||||
| мета одной строкой через `·` | мета списком, поле на строку |
|
||||
|
||||
Единственное, чего `--fix` не делает сам, — **проставить тип записи, у которой
|
||||
его неоткуда взять**: `feature` от `chore` машина не отличает, и подставленное
|
||||
наугад значение врало бы ровно там, где по нему принимают решение. Такие записи
|
||||
он называет поимённо пометкой `НЕОДНОЗНАЧНО`.
|
||||
Чего `--fix` не делает сам — **решает за человека, каким быть типу**. Случаев
|
||||
три, и все три уезжают пометкой `НЕОДНОЗНАЧНО`: тип, которого неоткуда взять
|
||||
(`feature` от `chore` машина не отличает); тип вне словаря; и запись типа `goal`
|
||||
— целей больше нет, а во что превращается эта, в задачу или в ничто, машина не
|
||||
знает. Подставленное наугад значение врало бы ровно там, где по нему принимают
|
||||
решение. Мёртвые теги и имя поля места при этом снимаются у **любой** записи,
|
||||
включая ту, чей тип остался неразобранным.
|
||||
|
||||
### Затрагивает
|
||||
|
||||
@@ -147,8 +145,8 @@
|
||||
забыли назвать, она от отсутствующей не отличает. Раздела нет — отказ во взятии:
|
||||
оценивать нечем.
|
||||
|
||||
**У `goal` и `research` раздела нет** — у первой границы называют её задачи, у
|
||||
второй они становятся известны, когда из разведки родятся задачи.
|
||||
**У `research` раздела нет** — её границы становятся известны, когда из разведки
|
||||
родятся задачи.
|
||||
|
||||
### Критерии приёмки
|
||||
|
||||
@@ -166,8 +164,7 @@
|
||||
что проверено больше проверенного, хуже, чем не проверять вовсе.
|
||||
|
||||
**У `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, без ведущих, хвостовых и двойных дефисов
|
||||
@@ -261,9 +220,9 @@
|
||||
проходом по всем местам, где слаг упомянут, иначе останутся битые ссылки,
|
||||
которых никто не проверяет.
|
||||
|
||||
## Индексы
|
||||
## Индекс
|
||||
|
||||
Строка везде одной формы:
|
||||
Строка одной формы:
|
||||
|
||||
```markdown
|
||||
- [🐞 Заголовок дословно](items/slug.md) — зачем
|
||||
@@ -276,52 +235,47 @@
|
||||
|
||||
| Файл | Что отвечает | Секции |
|
||||
| --- | --- | --- |
|
||||
| `ROADMAP.md` | что приложение уже умеет и чего ещё не умеет | канонические и в этом порядке: `Запланировано`, `Направления`, `Сопровождение`, `Готово` (англ. `Planned`, `Directions`, `Operations`, `Done`) |
|
||||
| `BACKLOG.md` | что **можно взять** — только задачи, **в порядке очереди** | категории проекта (по умолчанию Ядро/Инфра) |
|
||||
| `BACKLOG.md` | что **можно взять**, в значимом порядке | называет проект; на стройке ровно одна (умолчание `План`), на доработке сколько нужно (умолчание `Ядро`/`Инфра`) |
|
||||
| `REJECTED.md` | что ушло без реализации и почему | — |
|
||||
|
||||
Секции — **единственные заголовки `##` в индексе**: любой другой `##` в
|
||||
преамбуле проверка сочтёт секцией.
|
||||
|
||||
**Порядок строк внутри секции беклога значим: это очередь.** Первая строка — то,
|
||||
что делают следующим; назначает порядок человек на груминге, и двигают его
|
||||
`move --after` и `move --first`. Одно место из очереди изъято и **производно от
|
||||
типа и заполненности**: **сырьё** (`research` без раздела «Вопрос») стоит в конце
|
||||
своей секции, потому что его не берут, и между берущимся оно каждый раз требует
|
||||
**Порядок строк внутри секции значим, и стадия решает, что он значит:** на
|
||||
стройке зависимость, на доработке важность (SKILL.md, «Две стадии»). Назначает
|
||||
его человек — раскладывая шаги или на груминге, — и двигают его `move --after`
|
||||
и `move --first`. Одно место из очереди изъято и **производно от типа и
|
||||
заполненности**: **сырьё** (`research` без раздела «Вопрос») стоит в конце своей
|
||||
секции, потому что его не берут, и между берущимся оно каждый раз требует
|
||||
открыть файл, чтобы это понять. Проверяет `check`, переставляет `check --fix`,
|
||||
и человек этот порядок не назначает — иначе он был бы приоритетом, которого
|
||||
здесь нет.
|
||||
и человек этот порядок не назначает — иначе он был бы решением, которого здесь
|
||||
нет.
|
||||
|
||||
**Секции «блокеры» среди них нет.** Блокер — состояние, а не полка: он живёт до
|
||||
ответа человека, а следы остаются вопросами в файлах задач.
|
||||
Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила
|
||||
бы правилу «эскалируем немедленно», поэтому `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`
|
||||
|
||||
@@ -346,27 +300,24 @@ SKILL.md. Порядок закреплён потому, что `Готово`
|
||||
Разметка сверх типа. Тип полем, потому что он один и обязателен; теги — потому
|
||||
что их много и `list --tag` уже умеет отбирать по ним порцию разбора.
|
||||
|
||||
- `goal:<слаг>` — цель, которой служит задача. Обязателен **у `feature`**:
|
||||
новая возможность и есть содержание цели. У `fix`, `chore` и `research` его
|
||||
может не быть — они служат работоспособности, а не направлению.
|
||||
- `question` — в файле есть неразобранный раздел «Вопросы».
|
||||
- `decomposed` — на цели: разложена на задачи (см. «Файл цели»).
|
||||
|
||||
Тега `kind:<род>` больше нет: род работы стал типом. Оставшийся в файле `check`
|
||||
называет дрейфом, а `check --fix` снимает, перенеся значение в поле «Тип».
|
||||
Тегов `kind:<род>`, `goal:<слаг>` и `decomposed` больше нет: род работы стал
|
||||
типом, а цели упразднены. Оставшиеся в файле `check` называет дрейфом, а `check
|
||||
--fix` снимает (значение `kind:` при этом переезжает в поле «Тип»).
|
||||
|
||||
Отбор — `list --tag a,b`: перечисленные через запятую теги требуются **все
|
||||
сразу** (это И, не ИЛИ). Тег, которого нет ни у одной задачи, `list` называет
|
||||
вслух: молчаливый ноль читается как «таких задач нет», а чаще это опечатка.
|
||||
|
||||
Свои теги проект заводит свободно (партия ревью `review-ГГГГ-ММ-ДД`, тема,
|
||||
источник) — словарь не фиксирован. В индексы теги не выносим: индексы
|
||||
производны, отбор делает `list --tag`, а не глаза.
|
||||
источник) — словарь не фиксирован. В индекс теги не выносим: он
|
||||
производен, отбор делает `list --tag`, а не глаза.
|
||||
|
||||
## Тест «готова к взятию»
|
||||
|
||||
Задача готова, если из файла отвечаются четыре вопроса. Первый и четвёртый —
|
||||
общие, второй и третий у каждого типа свои и перечислены в его файле.
|
||||
Задача готова, если из файла отвечаются три вопроса. Первый общий, второй и
|
||||
третий у каждого типа свои и перечислены в его файле.
|
||||
|
||||
1. **Что станет наблюдаемо иначе**, когда она сделана — снаружи: пользователю,
|
||||
владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет
|
||||
@@ -379,26 +330,13 @@ SKILL.md. Порядок закреплён потому, что `Готово`
|
||||
`chore` — `Затрагивает`.
|
||||
3. **По чему видно, что закончено** — критерии приёмки с оракулами;
|
||||
у `research` вместо них `Куда ляжет ответ`.
|
||||
4. **Какую часть «Завершения» своей цели она двигает** — у задачи с целью.
|
||||
Строкой: «двигает пункт 2 «Завершения» — накопительная метрика перестаёт
|
||||
уменьшаться». Это и есть защита от задачи «отрефакторить X»: она проваливает
|
||||
тест не потому, что невидима снаружи, а потому, что не находит строки, к
|
||||
которой относится. Заодно видно обратное — достаточен ли набор задач для
|
||||
цели: строка «Завершения», к которой не относится ни одна задача, это
|
||||
незакрытая часть возможности.
|
||||
|
||||
**У задачи без цели** (`fix`, `chore`, `research`) вопрос не задаётся: они
|
||||
служат работоспособности, а не направлению.
|
||||
|
||||
Не отвечается первый, второй или третий вопрос → это ещё не задача, а **сырьё**:
|
||||
тип `research` без раздела «Вопрос», место — конец секции, работа над ним —
|
||||
штурм. Не отвечается четвёртый у `feature` → либо цель есть и не проставлена,
|
||||
либо это не новая возможность.
|
||||
Не отвечается любой из трёх → это ещё не задача, а **сырьё**: тип `research` без
|
||||
раздела «Вопрос», место — конец секции, работа над ним — штурм.
|
||||
|
||||
Отвечается всё, но задача не делается одним заходом и не мерджится целиком →
|
||||
это **несколько задач под одной целью**, дроби сразу. Промежуточного зонтика
|
||||
между целью и задачей нет: тип `[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` |
|
||||
| Берётся в работу | да — **но только с заполненным «Вопросом»** |
|
||||
|
||||
@@ -43,7 +42,8 @@
|
||||
| Место в секции беклога | **конец**, `check --fix` сносит туда сам | среди прочих |
|
||||
| `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