хвост задачи: отражение молча, новое — по слову человека

Синк документации делил правки по документам, а делить их надо по роду.
Отражение сделанного (вливание дельт, миграция, компонент в обзоре) пишется
молча: без правки документ станет ложным. Новая запись и новая норма — ADR,
конвенция, записка разведки, инвариант, периметр, дефект в журнале — только
предлагаются, а пишет их третий такт шага 6 после слова человека.

Реплика при этом одна на весь хвост: вопрос про урожай ревью переехал с шага 5
на шаг 6 и слился с предложениями синка — решение одно, «что из найденного
переживёт задачу». Плановых стопов в сценарии решения стало ровно два, и оба
про решения человека.

Сверка документов получила счётчик: doc-healthcheck оставляет след ключом
[healthcheck] last в .av-dev.toml, синк считает по нему задачи с прошлого
прогона и говорит строкой. Прежде признак «десяток задач» держался в памяти,
то есть не срабатывал.

Журнал — тема 78.
This commit is contained in:
av
2026-08-23 19:06:06 +03:00
parent 3c89d7111d
commit a4bc9191e7
12 changed files with 462 additions and 62 deletions
+15
View File
@@ -295,6 +295,11 @@ kebab-case.** Причина не эстетическая: имя файла с
«заменено на».
<!-- /дом: adr-когда-заводить -->
**Сработавший триггер даёт предложение, а не запись.** Заводит ADR человек своим
словом — правило и его причина в скилле `av-dev:doc-sync`, раздел «Два рода
правок». Канон здесь отвечает за другое: за то, при каких условиях предлагать
вообще есть что.
Не заводится для рутины и для того, что видно из кода и `git log`.
Записи неизменяемы: передумали — заводится новая, старая получает статус.
@@ -553,6 +558,9 @@ migrations = "internal/store/migrations" # если БД есть
[tasks]
dir = "tasks" # каталог задач от корня репозитория
[healthcheck]
last = "a1b2c3d" # сверка документов: коммит прошлого прогона
```
`version` — версия раскладки, под которую проект приведён, целым числом:
@@ -563,6 +571,13 @@ dir = "tasks" # каталог задач от корн
делает сверку с `database.md`. `[tasks]` — где лежит каталог задач и как названы
его части; состав ключей описывает скилл `task-track`.
`[healthcheck] last` — коммит, на котором в последний раз проходила сверка
документов; ставит его сам `av-dev:doc-healthcheck` последним шагом прогона, а
читает `av-dev:doc-sync`, чтобы сосчитать задачи, сделанные с тех пор. **Ключ
необязательный и в скелете его нет намеренно**: у нового проекта сверок не было,
и пустое значение врало бы про это меньше, чем отсутствие ключа, только на вид.
Отсутствие читается однозначно — «не сверялись ни разу».
**Формат TOML взят ради комментариев.** Файл лежит в репозитории проекта, и
человек, открывший его через полгода, обязан прочитать в нём, что означает
число. JSON комментариев не знает, и объяснение приходилось держать в другом
+11 -4
View File
@@ -1,6 +1,6 @@
---
name: code-resolve
description: "Взять одну задачу и довести её до закрытия. Одна точка входа, три сценария, и выбирает сценарий сам скилл, прочитав постановку. Способ известен и меняется поведение — сценарий решения: цикл Spec Driven Development (opsx propose → чекпоинт с объяснением человеческим языком, где форму решения одобряет человек → opsx apply → ревью кода постоянным составом → archive → синк документации → коммит → закрытие). Способ известен, а спека не меняется (тип chore: тулчейн, зависимости, сборка, гит-хуки, перенос, чистка) — сценарий обслуживания: правка → гейт со сверкой состава проверок → ревью фиксированным планом без change (autotests, operations, плюс conventions и техника, если тронут код) → синк документации → коммит → закрытие; планового стопа нет, change не заводится. Нашлась дельта-спека — задача оказалась шире своего типа: стоп с объяснением простым языком и двумя решениями человека, переформулировать запись в fix или feature и решать её процессом того типа следующим прогоном либо прекратить работу. Способа нет, постановка мутная, тип research — сценарий разведки: вопрос и рамки → чтение документов, кода и внешних источников (можно opsx:explore) → чекпоинт вариантов: 2–4 способа решить, цена каждого, что становится невозможным, рекомендация → ответ уезжает в документы канона, исход — в задачи → вычитка написанного → коммит → закрытие. Разведка кода не пишет и change не заводит, а выбранный способ реализуется следующим прогоном. На входе путь к файлу задачи, её слаг или просто текст постановки: размеченная запись не обязательна — текст берётся так же, как его берёт opsx:propose, и текстом идут все три сценария. Использовать, когда просят взять, сделать или решить задачу — хоть записью из каталога, хоть описанием прямо в разговоре, — обновить зависимости или сборку, разобраться, изучить, сравнить подходы, проработать сырую идею, ответить на вопрос из беклога."
description: "Взять одну задачу и довести её до закрытия. Одна точка входа, три сценария, и выбирает сценарий сам скилл, прочитав постановку. Способ известен и меняется поведение — сценарий решения: цикл Spec Driven Development (opsx propose → чекпоинт с объяснением человеческим языком, где форму решения одобряет человек → opsx apply → ревью кода постоянным составом → archive и отражение в документах молча → одна реплика о новом, где человек решает, что заводится: ADR, конвенция, задачи из урожая ревью → письмо одобренного → коммит → закрытие). Плановых стопов у сценария два, и оба про решения человека: чекпоинт до кода решает форму решения, реплика после кода — что из найденного переживёт задачу. Способ известен, а спека не меняется (тип chore: тулчейн, зависимости, сборка, гит-хуки, перенос, чистка) — сценарий обслуживания: правка → гейт со сверкой состава проверок → ревью фиксированным планом без change (autotests, operations, плюс conventions и техника, если тронут код) → синк документации, где почти всё письмо — отражение фактов и идёт молча → коммит → закрытие; планового стопа нет, change не заводится. Нашлась дельта-спека — задача оказалась шире своего типа: стоп с объяснением простым языком и двумя решениями человека, переформулировать запись в fix или feature и решать её процессом того типа следующим прогоном либо прекратить работу. Способа нет, постановка мутная, тип research — сценарий разведки: вопрос и рамки → чтение документов, кода и внешних источников (можно opsx:explore) → чекпоинт вариантов: 2–4 способа решить, цена каждого, что становится невозможным, рекомендация → ответ уезжает в документы канона, исход — в задачи → вычитка написанного → коммит → закрытие. Разведка кода не пишет и change не заводит, а выбранный способ реализуется следующим прогоном. На входе путь к файлу задачи, её слаг или просто текст постановки: размеченная запись не обязательна — текст берётся так же, как его берёт opsx:propose, и текстом идут все три сценария. Использовать, когда просят взять, сделать или решить задачу — хоть записью из каталога, хоть описанием прямо в разговоре, — обновить зависимости или сборку, разобраться, изучить, сравнить подходы, проработать сырую идею, ответить на вопрос из беклога."
---
# Работа над одной задачей
@@ -302,7 +302,8 @@ flowchart TD
| код — `opsx:apply`, вместе с гейтом до зелёного и поведенческой верификацией | [solve](references/solve.md), шаг 4 |
| правки по находкам триажа, помеченным `инлайн` | [solve](references/solve.md), шаг 5; [maintain](references/maintain.md), шаг 4 |
| правка оснастки в сценарии обслуживания | [maintain](references/maintain.md), шаг 2 |
| архивация change и синк документации`opsx:archive` и `av-dev:doc-sync` | [solve](references/solve.md), шаг 6; [maintain](references/maintain.md), шаг 5 |
| архивация change и отражение в документах`opsx:archive` и `av-dev:doc-sync` | [solve](references/solve.md), шаг 6, такт 1; [maintain](references/maintain.md), шаг 5 |
| письмо одобренного нового и задачи из урожая | [solve](references/solve.md), шаг 6, такт 3 |
**Остальное остаётся оркестратору, и перечень закрыт:** выбор сценария и стопы,
чекпоинт, вызовы `av-dev:code-review`, `av-dev-git:commit` и `av-dev:task-track`,
@@ -363,8 +364,14 @@ flowchart TD
**Чек-лист синка — единственное исключение из «не длиннее экрана».** Он приходит
из хвостового агента целиком и целиком уезжает в доклад: тронутые документы
поимённо, нетронутые — одной строкой с общей причиной. Сжать его своими словами
значит потерять принуждённое отрицание, ради которого шаг и существует.
поимённо, предложенное — строкой с основанием, нетронутые — одной строкой с общей
причиной. Сжать его своими словами значит потерять принуждённое отрицание, ради
которого шаг и существует.
**Предложения из этого чек-листа оркестратор не исполняет сам.** Они уезжают в
реплику человеку вместе с урожаем ревью, и написанным становится только то, что
он назвал (`solve.md`, шаг 6, такт второй). Агент, вернувший предложение, свою
работу сделал — заведение нового не его решение и не твоё.
**Возврату на слово не верят, и перечитывать за агентом дифф для этого не надо.**
Верят независимым артефактам: зелёному гейту, отчёту триажа, ревью следующего
@@ -381,6 +381,16 @@ ADR: список источников канон закрыл двумя — а
Нетронутые группируются одной строкой. Возврат приходит в этой же форме и уезжает
в доклад целиком: тронутое без нетронутого не отличается от невыполненного шага.
**Второе правило синка тоже действует: отражение пишется молча, новое
предлагается** (дом — раздел «Два рода правок» скилла `av-dev:doc-sync`). Здесь
оно почти ничего не стоит: обслуживание двигает **факты** — команды, шаги гейта,
зависимости поимённо, пути, имя ветки, числа настроек, — а факт в документе,
разошедшийся с кодом, это отражение по определению. Реплика человеку на этом
сценарии редка, и поводов у неё два: **новый запрет или инвариант в `CLAUDE.md`**
и **сужение проверок в `review.*`**. Второе спрашивать не надо — проверки сузил
человек, слово по ним сказано; первое надо, потому что запрет свяжет все будущие
задачи. Нового нет — реплики нет, и шаг кончается возвратом агента.
**Здесь этот шаг весит больше, чем в решении, и вот почему.** Обслуживание не
меняет поведения — значит, почти всё, что оно меняет, это документация: команды,
шаги гейта, зависимости поимённо, пути, имя основной ветки, настройки с числовым
@@ -281,6 +281,13 @@ git и читается диффом, а второй стоп на каждой
незачем. Но **названо должно быть каждое место, куда ты писал**, — доклад без
перечня адресов неотличим от доклада о ненаписанном.
**Правило «новое по слову» здесь тоже не задаёт второго вопроса**, хотя ответ
разведки — новое от первой до последней строки. Слово уже сказано **чекпоинтом
вариантов**: человек выбрал вариант и тем самым заказал запись. Спросить ещё раз
значило бы переспросить только что одобренное — и заодно предложить выбросить
работу, ради которой прогон и шёл. Что записать нового сверх выбранного —
например ADR по решению с ценой, — предлагается, как везде.
**Документов канона в проекте нет** — писать ответ некуда: назови это исходом,
предложи завести канон скиллом `av-dev:canon` и оставь ответ в докладе
целиком, чтобы работа не пропала. Заводить `docs/` мимо канона нельзя.
+113 -29
View File
@@ -28,14 +28,17 @@ flowchart TD
s3(["3. ЧЕКПОИНТ: объяснение<br/>в чём проблема, как решаем,<br/>чем рискуем"])
s4["4. opsx:apply — код, гейт,<br/>поведенческая верификация — агентом"]
s5["5. ревью кода — постоянный состав<br/>+ отработка замечаний агентом"]
s6["6. opsx:archive + синк документации —<br/>одним агентом"]
s6["6. opsx:archive + отражение в документах —<br/>одним агентом"]
s6q(["РЕПЛИКА: что заводим из нового —<br/>ADR, конвенция, задачи из урожая"])
s6b["6b. письмо одобренного и задачи —<br/>тем же агентом"]
s7["7. коммит работы — av-dev-git:commit"]
s8["8. закрыть задачу — av-dev:task-track,<br/>вторым коммитом учёта"]
in --> s1
s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8
s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s6q --> s6b --> s7 --> s8
s3 -.->|"скорректировать:<br/>правка спек и дизайна"| s3
s5 -.->|"находка отменяет дизайн:<br/>меняются дельта-спеки"| s3
s6 -.->|"нового нет:<br/>реплики нет"| s7
```
Схема — **сводка**: содержание каждого шага в его разделе ниже, и при
@@ -64,7 +67,8 @@ flowchart TD
и без дома названы в границах покрытия;
3. решение прошло чекпоинт и не разошлось с одобренным — либо разошлось, и
чекпоинт был пройден заново;
4. change заархивирован, дельты влиты в актуальные спеки;
4. change заархивирован, дельты влиты в актуальные спеки, и по **каждому**
документу канона назван исход — правка, предложение или отрицание с причиной;
5. коммит сделан в текущую ветку;
6. **критерии приёмки, если проект их дал, выписаны поимённо, и по каждому назван
оракул и наблюдаемый исход** — «прогнал вот это, увидел вот то». Это **доклад,
@@ -278,10 +282,15 @@ flowchart TD
откуда взялась.
**Задачи из урожая заводятся только тогда, когда человек сказал «заводим».**
Покажи список одной репликой и спроси. Сказал — зови `av-dev:task-track`, у него
на этот вход отдельный сценарий «задачи из ревью и аудита»: своя нарезка, свой
формат, свои правила дублей, и передавать находку туда надо дословно. Не сказал —
урожай остаётся строками доклада, и это исход, а не потеря.
Спрашивается это **не здесь, а на шаге 6** — там же, где спрашивается новое в
документах, и той же одной репликой: два вопроса подряд про одно и то же («что из
найденного заводим») стоили бы человеку двух переключений вместо одного. Сюда
урожай складывается, а не выносится.
Сказал «заводим» — зовёт `av-dev:task-track` агент шага 6, у него на этот вход
отдельный сценарий «задачи из ревью и аудита»: своя нарезка, свой формат, свои
правила дублей, и передавать находку туда надо дословно. Не сказал — урожай
остаётся строками доклада, и это исход, а не потеря.
**Молча беклог не наполняется.** Очередь работ ведёт человек, и задача, заведённая
за него по ходу чужого прогона, отнимает у него ровно то решение, ради которого
@@ -308,37 +317,62 @@ flowchart TD
единственный **независимый** артефакт о составе прогона: своей прозе здесь верить
нельзя — её написал тот, кто мог проход и пропустить.
### 6. Архивация и синк документации — одним агентом
### 6. Архивация и документы — отражение молча, новое по слову
**Оба шага уходят одному агенту, и это один запуск** (SKILL.md, «Кто пишет»).
Работа здесь письменная от начала до конца: `opsx:archive` вливает дельты в
актуальные спеки, `av-dev:doc-sync` правит документы канона, и обе правки идут по
Шаг идёт **в три такта**, и агент запускается в нём дважды. Причина одна: письмо
в документы бывает двух родов, а спрашивается только один.
**Копия.** Дом правила — раздел «Два рода правок» скилла `av-dev:doc-sync`.
Правится дом, а не этот файл.
<!-- копия: синк-род-правки из av-dev/skills/doc-sync/SKILL.md -->
- **Отражение** — документ уже описывает эту вещь, и без правки он **станет
ложным**: миграция написана, а `database.md` её не знает; компонент заведён, а
`architecture.md` перечисляет прежние. Такая правка ничего не решает, она
договаривает решённое на чекпоинте и уже стоящее в коде. **Пишется молча.**
- **Новое** — в каноне заводится запись или норма, которой не было: ADR, правило
в конвенциях, записка в `research/`, инвариант в `CLAUDE.md`, сдвиг периметра в
`security.md`, граница в `passport.md`, дефект в журнале `review.md`. Такая
запись переживёт задачу и свяжет следующие. **Пишется только по слову
человека.**
<!-- /копия: синк-род-правки -->
#### Такт первый — агент: архив и отражение
**Оба скилла уходят одному агенту, и это один запуск** (SKILL.md, «Кто пишет»).
Работа письменная от начала до конца: `opsx:archive` вливает дельты в актуальные
спеки, `av-dev:doc-sync` идёт по чек-листу и пишет отражение, и обе правки — по
чек-листам своих скиллов, а не по суждению оркестратора. Разнесённые по двум
запускам, они стоили бы двух заданий, двух возвратов и паузы между ними — при том
что второй шаг читает ровно то, что оставил первый.
запускам, они стоили бы двух заданий, двух возвратов и паузы между ними — при
том что второй читает ровно то, что оставил первый.
В задании: корень проекта, идентификатор change, база диффа и **порядок**
сначала `opsx:archive` с `openspec validate --strict` перед ним, затем
`av-dev:doc-sync`. Плюс требование довести гейт проекта до зелёного после правок:
документы у многих проектов гейт проверяет, и красный гейт здесь остановил бы
коммит следующим шагом.
`av-dev:doc-sync`.
**Вычитку языка зовёт сам `av-dev:doc-sync`** — агента `doc-wording` по пачке
правленых документов. Отдельным запуском её тянуть не надо, и в задании она не
называется: правило живёт в том скилле, а не здесь.
**Вычитка и гейт идут последним тактом, в котором писали.** Вернул непустой
список предложений — оба ждут третьего такта; список пуст — этот такт последний,
и оба идут в нём. Гонять гейт дважды подряд по одному дереву незачем, а вычитывать
пачку, которая сейчас пополнится, — тем более. Вычитку зовёт сам
`av-dev:doc-sync` (агента `doc-wording` по пачке правленого), и правило живёт в
том скилле; гейт до зелёного доводит агент, потому что красный гейт остановил бы
коммит следующим шагом — документы у многих проектов он проверяет.
**Возврат — адреса тронутого, исход валидации, чек-лист синка и исход гейта.**
Чек-лист уезжает в доклад целиком, и переписывать его своими словами нельзя — это
единственный след того, что каждый документ был назван.
**Возврат — чек-лист, адреса тронутого, исход валидации и исход гейта, если он
гонялся.**
Чек-лист уезжает в доклад целиком, и переписывать его своими словами нельзя —
это единственный след того, что каждый документ был назван.
**Правило, которое задаёт его форму, одно и оно жёсткое: принуждённое
отрицание.** Доклад обязан назвать
**каждый** документ канона — либо чем он обновлён, либо «не требуется, потому
что…». Нетронутые группируются одной строкой с общей причиной. Список триггеров
прозой уже проверен на живом проекте и дал 6 записей ADR на 43 изменения;
работает только обязательное отрицание. **Требование стоит в задании агента**
без него возврат придёт перечнем тронутого, а тронутое без нетронутого не
отличается от невыполненного шага.
отрицание.** Против **каждого** документа канона стоит одно из трёх — чем он
обновлён, что по нему предлагается, либо «не требуется, потому что…».
Нетронутые группируются одной строкой с общей причиной. Список триггеров прозой
уже проверен на живом проекте и дал 6 записей ADR на 43 изменения; работает
только обязательное отрицание. **Требование стоит в задании агента** без него
возврат придёт перечнем тронутого, а тронутое без нетронутого не отличается от
невыполненного шага.
**Список документов и их триггеров здесь не дублируется** — он в чек-листе скилла
`av-dev:doc-sync`, и копия уже однажды разошлась с оригиналом, потеряв два
@@ -349,6 +383,45 @@ flowchart TD
задачу нельзя: документ, заведённый мимо канона, окажется вторым домом ровно
тому, что канон потом заведёт своим.
#### Такт второй — одна реплика человеку на весь хвост
Покажи **одним списком** всё, что заводится нового:
- **предложения синка** — ADR, конвенция, записка в `research/`, инвариант,
периметр, дефект в журнал. Каждое строкой: что заведём, куда и на каком
основании;
- **урожай ревью с шага 5** — отложенные находки, из которых получаются задачи:
формулировка, оракул, откуда взялась.
Человек отвечает разом. **Нового нет — реплики нет**, и шаг кончился первым
тактом; у большинства задач так и выходит.
**Реплика одна, и делить её нельзя.** Спросить про ADR на синке, а про задачи
отдельно — значит взять с человека два переключения там, где решение одно: что из
найденного этой задачей переживёт её. Ровно поэтому вопрос про урожай и перенесён
сюда с шага 5.
**Спрашиваешь, а не советуешь по каждому пункту.** Основание уже названо строкой,
и второй абзац уговоров превращает реплику в чтение. Человек вправе ответить
«ничего» — это исход, а не потеря: находки остаются строками доклада.
#### Такт третий — тот же агент: письмо одобренного
Запускается **только если человек что-то одобрил**. В задании:
- **одобренные записи дословно** — формулировка, источник, основание; сочинять
заново нельзя, ADR цитирует решение из архивного `design.md`, а не пересказывает
его;
- **задачи из урожая** — вызовом `av-dev:task-track`, сценарий «задачи из ревью и
аудита», находка передаётся дословно вместе с оракулом;
- **вычитка** `doc-wording` по всей пачке правленого — и первого такта, и этого;
- **гейт проекта до зелёного** после правок.
**Отвергнутое не пишется никуда.** Ни в один документ, ни отдельной записью «от
такого-то отказались»: журнала отвергнутого канон не держит, и заведение его
здесь было бы ровно тем новым, которого человек только что не заказал. Отказ
идёт строкой доклада.
### 7. Коммит
Коммить **в текущую ветку** (`git rev-parse --abbrev-ref HEAD`), сам ветку не
@@ -397,6 +470,11 @@ change. Заводить запись задним числом, чтобы её
- **`Урожай`** — отложенные находки списком (формулировка, оракул, откуда
взялась) и **что человек по нему решил**: заведены задачи или список остался в
докладе;
- **что заведено нового в документах** — одобренное по именам записей, и **что
предложено и отвергнуто**, тоже по именам. Отказ виден только здесь: в
документы он не пишется;
- **сигнал сверки** — строка синка о том, сколько задач сделано с прошлого
прогона `av-dev:doc-healthcheck`, либо что сверки не было ни разу;
- **сколько находок ушло инлайном и сколько развилкой** — числом. По нему видно,
во что прогон обошёлся человеку;
- **одна строка границ покрытия**: какой режим гонялся, какие проходы не
@@ -420,6 +498,12 @@ change. Заводить запись задним числом, чтобы её
`av-dev:task-track` — по слову человека, у него на этот вход отдельный сценарий
«задачи из ревью и аудита». Каталога задач в
проекте нет — урожай остаётся списком в докладе, и это говорится строкой.
- **Стопов у сценария два, и оба про решения человека, а не про ход работ.**
Чекпоинт шага 3 решает форму решения **до** кода; реплика шага 6 решает, что из
найденного переживёт задачу. Между ними прогон идёт сам: правки инлайном чинятся
молча, отражение в документах пишется молча. Третьего стопа заводить нельзя —
прогон, останавливающийся чаще, теряет ровно то время, ради которого короткие
итерации и выбраны.
- **Способ решения ты не выбираешь.** Он приходит известным: из постановки, из
разведки, от человека. Выбор между двумя подходами с разной ценой делается в
разведке, у своего чекпоинта, — не по ходу этого сценария.
+9 -3
View File
@@ -831,8 +831,12 @@ change»: сверять исход с планом триаж обязан и
отчёте: формулировка, оракул, откуда взялась (какой проход, какой change).
**Задачи из урожая заводятся только по слову человека, и это правило, а не
вежливость.** Список показывается ему одной репликой; сказал «заводим» — зови
`av-dev:task-track`, у него на этот вход отдельный сценарий «задачи из ревью и
вежливость.** Спрашивает не конвейер: список уезжает вызывающему и показывается
человеку **одной репликой на весь хвост задачи** — вместе с тем новым, что
предлагает записать синк документации (`av-dev:code-resolve`,
`references/solve.md`, шаг 6). Два вопроса про одно и то же — «что из найденного
переживёт задачу» — стоили бы двух переключений вместо одного. Сказал «заводим» —
зовётся `av-dev:task-track`, у него на этот вход отдельный сценарий «задачи из ревью и
аудита»: свой формат, кластеризация по причине, дедуп против беклога и кладбища.
Не сказал — урожай остаётся строками доклада, и это исход, а не потеря. Прогон,
заводящий задачи сам, наполняет беклог работой, которую никто не выбирал; на
@@ -844,7 +848,9 @@ change»: сверять исход с планом триаж обязан и
- `Promote candidates` — по процедуре [references/promote.md](references/promote.md):
находка → конвенция → правило линтера → **удаление формулировки из конвенций**.
Третий шаг обязателен.
Третий шаг обязателен. **Сама конвенция заводится по слову человека** — той же
репликой, что и задачи из урожая: её строка станет входом каждого следующего
прогона, и из всего, что пишет хвост задачи, она связывает дальше всего.
- Дефект, проскочивший ревью и всплывший позже, идёт в журнал проекта
([references/review-journal.md](references/review-journal.md)) — сразу, не
ретроспективно: теряется именно то, почему дефект не поймали.
@@ -52,6 +52,13 @@ flowchart TD
тема относится к поведению системы, а не к тому, как мы пишем код, — это не
конвенция, а требование: заводится дельта-спека обычным путём.
**Конвенция заводится по слову человека, и это не формальность.** Одна её строка
становится входом каждого следующего прогона ревью и критерием для всех будущих
задач — из всего, что пишет хвост задачи, конвенция связывает дальше всего.
В цикле задачи она поэтому **предлагается**, а не заводится: строка предложения
называет проверяемое свойство и проход, который его нашёл, и по этой паре человек
решает (`av-dev:code-resolve`, `references/solve.md`, шаг 6, такт второй).
Промоут идёт **тем же путём, что change → spec**: правка попадает в тот же
коммит, что и исправление кода, с пометкой в сообщении — история промоутов
остаётся видна в `git log` по файлу конвенций.
@@ -75,6 +82,12 @@ flowchart TD
хук блокирует любой коммит, и правило снимут первым же раздражённым движением.
Приводить код в соответствие — часть шага 2, отдельным коммитом.
**Отсюда и место шага 2: он не помещается в хвост чужой задачи.** Конфиг,
сканер и приведение кода к зелёному — это работа размером с задачу, и сделанная
попутно она удваивает прогон, который человек заводил ради другого. Согласованный
промоут даёт **строку конвенции сейчас** и **задачу `chore` на механизацию**;
задачу заводит `av-dev:task-track` тем же словом, что и саму конвенцию.
## Шаг 3. Удаление из конвенций и из промптов
**Шаг, который пропускают чаще всего, и единственный, ради которого затевались
+29 -2
View File
@@ -1,6 +1,6 @@
---
name: doc-healthcheck
description: "Проверка здоровья документации проекта судом, а не машиной: не разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом — doc-consistency (один факт в двух домах, прямое противоречие, поведение в architecture.md вместо спек, ADR без парного статуса, число без происхождения) и doc-code-drift (протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки, capability). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию раскладки проверяет скилл av-dev:canon, язык документов — агент doc-wording."
description: "Проверка здоровья документации проекта судом, а не машиной: не разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом — doc-consistency (один факт в двух домах, прямое противоречие, поведение в architecture.md вместо спек, ADR без парного статуса, число без происхождения) и doc-code-drift (протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки, capability). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Прогон оставляет след — ключ [healthcheck] last в .av-dev.toml, — и по нему синк документации считает, сколько задач сделано с прошлой сверки, и выдаёт сигнал строкой. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию раскладки проверяет скилл av-dev:canon, язык документов — агент doc-wording."
---
# Здоровье документации
@@ -20,7 +20,10 @@ check` и его скрипт; здесь начинается там, где к
- **с прошлой сверки сделан десяток задач.** Документы протухают ровно от
сделанной работы: переименованная цель сборки, ушедшая зависимость, второй
способ делать то, что обзор объявил единственным, факт, дописанный в
`architecture.md` и уже живущий в `CLAUDE.md`;
`architecture.md` и уже живущий в `CLAUDE.md`. **Этот признак считается, а не
вспоминается**: счёт ведёт синк документации по следу прошлого прогона и
выдаёт строкой на каждой сделанной задаче (`av-dev:doc-sync`, раздел «Сигнал
сверки»);
- **вернулись к проекту после перерыва** — прежде чем опираться на написанное;
- **перед тем как опереться на документ в решении**, если оно дорогое;
- шагом `adopt` и шагом `upgrade` — их зовёт скилл `canon` сам.
@@ -123,6 +126,28 @@ check` и его скрипт; здесь начинается там, где к
прогоне. Класс ошибок, который повторяется, идёт в `docs/review.*`, раздел
настройки, — там дом типовых ложноположительных.
## След прогона
**Последним шагом прогон правит `.av-dev.toml`** — ключ `last` в секции
`[healthcheck]`: хеш коммита `HEAD` и дата комментарием рядом. Состав ключей —
[канон](../canon/references/canon.md), раздел `.av-dev.toml`; правится **строка**,
а не файл целиком.
**Без следа признак «десяток задач» не считается никем.** Так и было: сверку
звали по памяти, то есть не звали — тот же прозаический триггер, что дал 6
записей ADR на 43 изменения. След превращает признак в число, которое
`av-dev:doc-sync` считает командой
`git rev-list --count <last>..HEAD -- openspec/changes/archive` и говорит вслух
на каждой задаче.
Ключ **необязательный и заводится сам** — первым же прогоном сверки; проекту для
этого делать нечего. Его отсутствие значит «сверки не было ни разу», и синк
говорит это отдельной строкой.
**Позвал одного агента из двух — след всё равно ставится, но в докладе назван
неполным.** Иначе следующая сверка отсчитывалась бы от прогона, который смотрел
половину.
## Доклад
- **Кого позвал** — обоих или одного, и почему одного.
@@ -147,3 +172,5 @@ check` и его скрипт; здесь начинается там, где к
- **Не правит документы за агентов** — они возвращают формулировки, решение
подставить принимает человек или ты по его правилу.
- **Не заводит задачи** — этим владеет `av-dev:task-track`.
- **Не решает, когда себя звать.** Признак считает синк и говорит строкой; часы
на прогон тратит человек своим словом.
+139 -23
View File
@@ -1,6 +1,6 @@
---
name: doc-sync
description: Вести содержимое документов канона по ходу разработки — синк после сделанной задачи с построчным отчётом по каждому документу, заведение ADR промоутом из архивного design.md или из записки разведки, запись наблюдения в research, запись дефекта и настройки конвейера в review.md, чистка architecture.md от поведения с маркерами долга. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл av-dev:canon.
description: "Вести содержимое документов канона по ходу разработки. Правки двух родов, и спрашивается один: отражение сделанного (вливание дельт, миграция в database.md, компонент в architecture.md) пишется молча, а новая запись и новая норма (ADR, правило в conventions, записка в research, инвариант CLAUDE.md, периметр security.md, граница passport.md, дефект в review.md) только предлагается — пишет её второй запуск после слова человека. Построчный отчёт по каждому документу остаётся: каждый назван либо правкой, либо предложением, либо отрицанием с причиной. ADR и записка разведки — промоут цитатой из архивного design.md или записки, а не второе сочинение. Синк же считает и выдаёт строкой сигнал сверки: сколько задач сделано с прошлого прогона av-dev:doc-healthcheck. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл av-dev:canon."
---
# Ведение содержимого канона
@@ -27,32 +27,84 @@ description: Вести содержимое документов канона
Это тот же приём, что «границы покрытия» в отчёте ревью и «пустое называется
пустым» в каноне.
## Два рода правок, и спрашивается один
Второе правило, поперёк первого: **пройти по всем документам обязан ты, а
завести новое — человек**. Признак проверяемый и читается одним вопросом: **что
станет с документом, если правку не сделать**.
<!-- дом: синк-род-правки -->
- **Отражение** — документ уже описывает эту вещь, и без правки он **станет
ложным**: миграция написана, а `database.md` её не знает; компонент заведён, а
`architecture.md` перечисляет прежние. Такая правка ничего не решает, она
договаривает решённое на чекпоинте и уже стоящее в коде. **Пишется молча.**
- **Новое** — в каноне заводится запись или норма, которой не было: ADR, правило
в конвенциях, записка в `research/`, инвариант в `CLAUDE.md`, сдвиг периметра в
`security.md`, граница в `passport.md`, дефект в журнале `review.md`. Такая
запись переживёт задачу и свяжет следующие. **Пишется только по слову
человека.**
<!-- /дом: синк-род-правки -->
**Показывается новое одной репликой и одним списком.** Каждый пункт — строкой:
что заведём, куда и на каком основании. Человек отвечает разом, и одобренное
пишет **следующий заход синка** — в цикле задачи это третий такт шага 6
(`av-dev:code-resolve`, `references/solve.md`). **Нового нет — реплики нет**, и
это обычный исход: у большинства задач хвост состоит из одного отражения.
**«По слову» — это по слову, а не вторым вопросом.** Человек уже сказал в этом
прогоне «заведи ADR», сам решил сузить проверки, сам одобрил формулировку
конвенции — слово сказано, и переспрашивать нечего: запись идёт как одобренная, а
в докладе стоит, чьим решением. Предложение существует ради нового, которое
заметил ты, а не ради ритуала.
**Отказ человека — строка доклада и всё.** В документы он не пишется: журнала
отвергнутых ADR и снятых конвенций канон не держит, и заведение такого журнала
здесь было бы ровно тем новым, которого никто не заказывал.
**Отрицание от этого не ослабло.** Документ, по которому нечего предложить,
по-прежнему обязан быть назван — просто раньше отрицание читал отчёт, а теперь
человек, и читает он его **до** того, как что-то написано. Обязанность та же:
пропуск неотличим от «не требуется», пока отрицание не сказано вслух.
## Чек-лист синка
Идёт сверху вниз; каждая строка попадает в доклад.
| Документ | Обновляется, когда | Проверка |
| --- | --- | --- |
| `openspec/specs/` | всегда при изменении поведения | вливает `opsx:archive` |
| `database.md` | тронуты миграции | `docs.py check --base` |
| `architecture.md` | новый компонент, граница, внешняя зависимость, изменилось окружение | `docs.py`: capability без упоминания |
| `adr/` | сработал триггер канона (перечень — [canon.md](../canon/references/canon.md#adr)) | нет — только этот чек-лист |
| `research/` | узнали новое о внешнем формате или данных | нет |
| `security.md` | новый недоверенный вход, токен, путь наружу, сдвиг периметра | нет |
| `conventions/` | находка принята и не специфична для одного места | промоут |
| `review.md` | дефект воспроизведён; сузили или расширили проверку | нет |
| `passport.md` | новый потребитель, сдвиг границы «чем не является» | нет |
| `CLAUDE.md` | изменился инвариант, гейт, запрет, необратимое | нет |
| Документ | Род | Обновляется, когда | Проверка |
| --- | --- | --- | --- |
| `openspec/specs/` | отражение | всегда при изменении поведения | вливает `opsx:archive` |
| `database.md` | отражение | тронуты миграции | `docs.py check --base` |
| `architecture.md` | отражение | новый компонент, граница, внешняя зависимость, изменилось окружение | `docs.py`: capability без упоминания |
| `adr/` | новое | сработал триггер канона (перечень — [canon.md](../canon/references/canon.md#adr)) | нет — только этот чек-лист |
| `research/` | новое | узнали новое о внешнем формате или данных | нет |
| `security.md` | новое | новый недоверенный вход, токен, путь наружу, сдвиг периметра | нет |
| `conventions/` | новое | находка принята и не специфична для одного места | промоут |
| `review.md` | новое | дефект воспроизведён; сузили или расширили проверку | нет |
| `passport.md` | новое | новый потребитель, сдвиг границы «чем не является» | нет |
| `CLAUDE.md` | новое | изменился инвариант, гейт, запрет, необратимое | нет |
**Разрез в таблице не произволен.** Ложным без правки становится ровно тот
документ, который описывает **состояние системы**, — потому отражений в чек-листе
и мало. Остальные задают норму или хранят память: им не с чем разойтись, пока в
них не написано новое.
Пример доклада:
```
Синк документации:
Синк документации.
Отражено, записано:
- openspec/specs/ — влиты дельты change add-bucket-reindex
- architecture.md — добавлен воркер свёртки, ссылка на capability reindex
- database.md — миграция 00006, таблица bucket
- adr/ — заведён ADR-2026-08-03-queue-as-table: отказ от внешней очереди
- research/ — новое о формате не узнано
- passport, security, conventions, review — не требуется: изменение внутреннее
Предложено, жду слова:
- adr/ — отказ от внешней очереди в пользу таблицы; источник: архивный
design.md; триггер: намеренный отказ от очевидного подхода
Не требуется: research, security, conventions, review, passport, CLAUDE.md —
периметр не двигался, инварианты те же, новое о внешних данных не узнано.
Сверка документов: с прошлой (a1b2c3d, 2026-07-30) сделано 11 задач — пора
звать av-dev:doc-healthcheck.
```
## Сверка — не здесь, а в `av-dev:doc-healthcheck`
@@ -118,9 +170,16 @@ description: Вести содержимое документов канона
нет**. Строка «adr/ — не требуется: решение рутинное» и есть то, ради чего
чек-лист существует; её отсутствие неотличимо от «забыл посмотреть».
Порядок работы: открой источник — архивный `design.md` change либо записку
разведки, — найди в нём решение, проходящее триггер, процитируй его и причину,
сошлись на источник, добавь строку в индекс `docs/adr/README.md` сверху.
**Запись — новое, и заводится она по слову** (раздел «Два рода правок»).
Сработавший триггер даёт не файл, а строку предложения: какое решение, из какого
источника, каким из трёх триггеров прошло. Своей записи ADR не стоит ничего, а
каталог решений читают как список того, что в проекте всерьёз, — и разбавленный
рутиной он перестаёт им быть.
Порядок работы после «да»: открой источник — архивный `design.md` change либо
записку разведки, — найди в нём решение, проходящее триггер, процитируй его и
причину, сошлись на источник, добавь строку в индекс `docs/adr/README.md`
сверху.
## Чистка `architecture.md`
@@ -197,9 +256,16 @@ av-dev:code-review`, его `references/review-journal.md`.
Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим».
Со временем теряется не факт, а то, почему дефект не поймали, — единственное,
ради чего журнал есть. И решение сузить проверки (перестали звать проход,
переселили его в другой скилл) обязано попасть в раздел настройки, а не остаться
в отчёте ревью.
ради чего журнал есть.
«Сразу» и «по слову» здесь не спорят: запись — новое, и она идёт предложением, но
**предложением этого прогона**, а не следующего. Отложить её до «когда починим»
нельзя ни с чьего согласия: чинится дефект, а теряется причина промаха.
**Решение сузить проверки** (перестали звать проход, переселили его в другой
скилл) обязано попасть в раздел настройки, а не остаться в отчёте ревью. Второй
раз оно не спрашивается: такое решение принимает человек по определению, и слово
по нему уже сказано — сказано тогда, когда проверку сузили.
## Промоут в конвенции
@@ -216,6 +282,53 @@ av-dev:code-review`, его `references/review-journal.md`.
На синке это отдельная строка: «conventions/ — правило X механизировано,
формулировка удалена» либо «не требуется».
**Конвенция — самое дорогое из нового, и на синке она только предлагается.**
Одна её строка становится входом каждого следующего прогона ревью и критерием
для всех будущих задач; находка, доехавшая до конвенции по инерции хвоста, потом
годами разменивается на внимание прохода. Предложение называет **проверяемое
свойство и проход, который его нашёл**, — по этой паре человек и решает.
**Шаг 2 в хвост задачи не помещается.** Механизация правила — конфиг линтера или
сканер, плюс приведение кода к зелёному — это работа размером с задачу, и делать
её попутно значит удваивать чужой прогон. Согласованный промоут даёт строку
конвенции сейчас и **задачу типа `chore`** на механизацию — заводит её
`av-dev:task-track`, и заводится она тем же словом человека, что и сама
конвенция.
## Сигнал сверки — строка, а не вызов
Сверку документов (`av-dev:doc-healthcheck`) зовёт человек по признаку **«с
прошлой сверки сделан десяток задач»**. Признак наблюдаемый, но считать его было
нечем: следа у сверки не оставалось, и «десяток» держался в чьей-то памяти. Это
ровно тот прозаический триггер, который дал 6 записей ADR на 43 изменения, — и
здесь он не срабатывал по той же причине.
**След оставляет сама сверка** — ключ `[healthcheck] last` в `.av-dev.toml`
(состав ключей — [canon.md](../canon/references/canon.md), раздел
`.av-dev.toml`). **Считает синк**, и вот чем:
```sh
git rev-list --count <last>..HEAD -- openspec/changes/archive
```
Коммит, тронувший архив, — это доехавшая до конца задача, так что счёт идёт в
задачах, а не в правках. `openspec` в проекте нет — считай коммиты
(`git rev-list --count <last>..HEAD`) и **скажи, что считал коммиты**: число
другого рода, и молчаливая подмена сделала бы признак вдвое чувствительнее.
Строка доклада обязательна всегда, и вариантов у неё три:
- **счёт меньше десятка** — «с прошлой сверки N задач, звать рано»;
- **счёт от десятка** — «с прошлой сверки N задач, пора звать
`av-dev:doc-healthcheck`»;
- **ключа нет** — «сверка документов не проводилась ни разу», и это самый
сильный из трёх сигналов, а не отсутствие данных.
**Сам не зовёшь.** Прогон сверки идёт по всему канону и держит `opus`; решение о
таких часах принимает тот, кто их оплачивает. Синк, позвавший её сам, превратил
бы самую дорогую проверку процесса в церемонию хвоста задачи — против чего она и
вынесена в отдельный скилл.
## Чего этот скилл не делает
- **Не проверяет раскладку** — это `canon`.
@@ -223,3 +336,6 @@ av-dev:code-review`, его `references/review-journal.md`.
`doc-init`.
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.
- **Не заводит новое молча** — ни ADR, ни конвенцию, ни записку. Молча идёт
только отражение, и признак у него один: без правки документ станет ложным.
- **Не зовёт сверку документов** — считает и говорит строкой; зовёт человек.