diff --git a/av-dev/skills/canon/references/canon.md b/av-dev/skills/canon/references/canon.md index a5b2608..97a2255 100644 --- a/av-dev/skills/canon/references/canon.md +++ b/av-dev/skills/canon/references/canon.md @@ -555,12 +555,10 @@ version = 1 # версия раскладки [docs] migrations = "internal/store/migrations" # если БД есть +healthcheck_last = "a1b2c3d" # сверка документов: коммит прошлого прогона [tasks] dir = "tasks" # каталог задач от корня репозитория - -[healthcheck] -last = "a1b2c3d" # сверка документов: коммит прошлого прогона ``` `version` — версия раскладки, под которую проект приведён, целым числом: @@ -571,13 +569,18 @@ last = "a1b2c3d" # сверка документов: ко делает сверку с `database.md`. `[tasks]` — где лежит каталог задач и как названы его части; состав ключей описывает скилл `task-track`. -`[healthcheck] last` — коммит, на котором в последний раз проходила сверка +`[docs] healthcheck_last` — коммит, на котором в последний раз проходила сверка документов; ставит его сам `av-dev:doc-healthcheck` последним шагом прогона, а читает `av-dev:doc-sync`, чтобы сосчитать задачи, сделанные с тех пор. **Ключ необязательный и в скелете его нет намеренно**: у нового проекта сверок не было, и пустое значение врало бы про это меньше, чем отсутствие ключа, только на вид. Отсутствие читается однозначно — «не сверялись ни разу». +Секции он достался по смыслу: `[docs]` — настройки проверок документов, а сверка +документов и есть такая проверка. Своя секция верхнего уровня стоила бы правки +общего читателя `shared/config.py` и сделала бы файл, объявленный «версией и +настройками», хранилищем состояния. + **Формат TOML взят ради комментариев.** Файл лежит в репозитории проекта, и человек, открывший его через полгода, обязан прочитать в нём, что означает число. JSON комментариев не знает, и объяснение приходилось держать в другом @@ -592,6 +595,20 @@ last = "a1b2c3d" # сверка документов: ко читаются: два дома для одной версии расходятся молча. Увидев их, `docs.py` и `tasks.py` говорят «прежняя раскладка» и зовут `upgrade` — версия 1 журнала. -Ключей будет больше по мере роста проверок; неизвестный ключ `docs.py` -игнорирует, отсутствующий — считает «проверка неприменима» и говорит об этом -строкой, а не молчит. +Ключей будет больше по мере роста проверок, но **заводятся они только вместе с +правкой скрипта**: неизвестный ключ — не безмолвный пропуск, а **отказ кодом +3**. Верхний уровень стережёт `shared/config.py` (`TOP_KEYS`), секцию `[docs]` — +`docs.py` (`DOCS_KEYS`), секцию `[tasks]` — `tasks.py`. Довод у отказа +проверяемый: ключ, положенный не в ту секцию, при молчаливом пропуске не значит +ничего — проверка объявляет себя неприменимой, отчёт выходит зелёным, и на месте +настройки оказывается тишина. + +**Здесь это правило однажды соврало, и цена была немедленной.** Абзац обещал, что +неизвестный ключ игнорируется; по этому обещанию скилл сверки завёл себе секцию +`[healthcheck]` верхнего уровня — и первый же её прогон сделал бы нерабочими +`docs.py`, `tasks.py` и гейт проекта, который их зовёт. Отсюда и порядок: **новый +ключ заводится правкой константы в скрипте-владельце, и только потом появляется +здесь**. + +**Отсутствующий** ключ — другое дело: он значит «проверка неприменима», и скрипт +говорит об этом строкой, а не молчит. diff --git a/av-dev/skills/canon/scripts/docs.py b/av-dev/skills/canon/scripts/docs.py index 7644591..310a259 100644 --- a/av-dev/skills/canon/scripts/docs.py +++ b/av-dev/skills/canon/scripts/docs.py @@ -296,7 +296,15 @@ def read_config(root: Path, rep: Report) -> dict: # Ключи секции `[docs]`. Секцию знает этот скрипт, а не общий читатель: ключ # заводится вместе с проверкой, которая его читает. -DOCS_KEYS = ("migrations",) +# +# `healthcheck_last` — коммит прошлой сверки документов; пишет его скилл +# `av-dev:doc-healthcheck`, читает `av-dev:doc-sync`, чтобы сосчитать задачи с +# тех пор. Здесь он стоит **только чтобы файл не отвергли**: неизвестный ключ — +# отказ кодом 3, то есть ключ, заведённый скиллом мимо этой константы, сделал бы +# нерабочими и `docs.py`, и `tasks.py`, и гейт проекта, который их зовёт. +# Проверки, читающей его, у скрипта нет и не предполагается: значение — след +# работы человека, а не настройка. +DOCS_KEYS = ("migrations", "healthcheck_last") def docs_cfg(cfg: dict) -> dict: diff --git a/av-dev/skills/doc-healthcheck/SKILL.md b/av-dev/skills/doc-healthcheck/SKILL.md index 7a84ef2..a49ae7c 100644 --- a/av-dev/skills/doc-healthcheck/SKILL.md +++ b/av-dev/skills/doc-healthcheck/SKILL.md @@ -1,6 +1,6 @@ --- name: doc-healthcheck -description: "Проверка здоровья документации проекта судом, а не машиной: не разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом — doc-consistency (один факт в двух домах, прямое противоречие, поведение в architecture.md вместо спек, ADR без парного статуса, число без происхождения) и doc-code-drift (протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки, capability). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Прогон оставляет след — ключ [healthcheck] last в .av-dev.toml, — и по нему синк документации считает, сколько задач сделано с прошлой сверки, и выдаёт сигнал строкой. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию раскладки проверяет скилл av-dev:canon, язык документов — агент doc-wording." +description: "Проверка здоровья документации проекта судом, а не машиной: не разошлись ли документы между собой и с кодом. Зовёт двух агентов на весь канон разом — doc-consistency (один факт в двух домах, прямое противоречие, поведение в architecture.md вместо спек, ADR без парного статуса, число без происхождения) и doc-code-drift (протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки, capability). Разбирает урожай порциями: строка на замену идёт в документ сразу, работа больше абзаца становится задачей. Прогон оставляет след — ключ [docs] healthcheck_last в .av-dev.toml, — и по нему синк документации считает, сколько задач сделано с прошлой сверки, и выдаёт сигнал строкой. Использовать, когда с прошлой сверки сделан десяток задач, когда вернулись к проекту после перерыва, перед тем как опереться на документ в решении, а также шагом adopt и upgrade. Дорого — не на каждой задаче. Раскладку и версию раскладки проверяет скилл av-dev:canon, язык документов — агент doc-wording." --- # Здоровье документации @@ -128,11 +128,17 @@ check` и его скрипт; здесь начинается там, где к ## След прогона -**Последним шагом прогон правит `.av-dev.toml`** — ключ `last` в секции -`[healthcheck]`: хеш коммита `HEAD` и дата комментарием рядом. Состав ключей — +**Последним шагом прогон правит `.av-dev.toml`** — ключ `healthcheck_last` в +секции `[docs]`: хеш коммита `HEAD` и дата комментарием рядом. Состав ключей — [канон](../canon/references/canon.md), раздел `.av-dev.toml`; правится **строка**, а не файл целиком. +**Секцию и имя ключа не выбирай сам.** Неизвестный ключ `.av-dev.toml` — отказ +кодом 3, а не пропуск: ключ, заведённый мимо константы скрипта-владельца, роняет +`docs.py`, `tasks.py` и гейт проекта разом. Этот ключ там уже назван +(`DOCS_KEYS` в `av-dev/skills/canon/scripts/docs.py`), а любой другой пришлось бы +заводить правкой скрипта. + **Без следа признак «десяток задач» не считается никем.** Так и было: сверку звали по памяти, то есть не звали — тот же прозаический триггер, что дал 6 записей ADR на 43 изменения. След превращает признак в число, которое diff --git a/av-dev/skills/doc-sync/SKILL.md b/av-dev/skills/doc-sync/SKILL.md index eb3a177..f00a543 100644 --- a/av-dev/skills/doc-sync/SKILL.md +++ b/av-dev/skills/doc-sync/SKILL.md @@ -1,6 +1,6 @@ --- name: doc-sync -description: "Вести содержимое документов канона по ходу разработки. Правки двух родов, и спрашивается один: отражение сделанного (вливание дельт, миграция в database.md, компонент в architecture.md) пишется молча, а новая запись и новая норма (ADR, правило в conventions, записка в research, инвариант CLAUDE.md, периметр security.md, граница passport.md, дефект в review.md) только предлагается — пишет её второй запуск после слова человека. Построчный отчёт по каждому документу остаётся: каждый назван либо правкой, либо предложением, либо отрицанием с причиной. ADR и записка разведки — промоут цитатой из архивного design.md или записки, а не второе сочинение. Синк же считает и выдаёт строкой сигнал сверки: сколько задач сделано с прошлого прогона av-dev:doc-healthcheck. Использовать, когда задача сделана и надо обновить документацию, когда просят завести 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, читая след в ключе [docs] healthcheck_last. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл av-dev:canon." --- # Ведение содержимого канона @@ -303,9 +303,9 @@ av-dev:code-review`, его `references/review-journal.md`. ровно тот прозаический триггер, который дал 6 записей ADR на 43 изменения, — и здесь он не срабатывал по той же причине. -**След оставляет сама сверка** — ключ `[healthcheck] last` в `.av-dev.toml` -(состав ключей — [canon.md](../canon/references/canon.md), раздел -`.av-dev.toml`). **Считает синк**, и вот чем: +**След оставляет сама сверка** — ключ `healthcheck_last` в секции `[docs]` +файла `.av-dev.toml` (состав ключей — [canon.md](../canon/references/canon.md), +раздел `.av-dev.toml`). **Считает синк**, и вот чем: ```sh git rev-list --count ..HEAD -- openspec/changes/archive diff --git a/decisions/79-healthcheck-trace-moved-into-docs-section.md b/decisions/79-healthcheck-trace-moved-into-docs-section.md new file mode 100644 index 0000000..2b90f72 --- /dev/null +++ b/decisions/79-healthcheck-trace-moved-into-docs-section.md @@ -0,0 +1,58 @@ +# 79. След сверки переехал в секцию docs; ключ верхнего уровня ронял скрипты (2026-08-23) + +## Что было + +Тема 78 завела след сверки документов ключом `last` в секции `[healthcheck]` +файла `.av-dev.toml`. Ревью трансформации — три прохода по скиллам и документам — +нашло, что такого ключа не переживает ни один скрипт плагина. + +Проверено запуском, а не рассуждением. В проекте с `[healthcheck] last` команда +`docs.py check` печатает `ОТКАЗ: .av-dev.toml: неизвестные ключи верхнего уровня: +healthcheck` и выходит кодом 3; `tasks.py check` — то же самое. Верхний уровень +файла стережёт `shared/config.py` константой `TOP_KEYS`, и неизвестный ключ там — +**отказ**, а не пропуск. То есть первый же прогон сверки сделал бы нерабочими +`canon check/adopt/upgrade`, весь `task-track` вместе с закрытием задачи и гейт +проекта, который их зовёт. + +**Корень — не описка, а ложное обещание документа.** Раздел `.av-dev.toml` в +каноне утверждал: «неизвестный ключ `docs.py` игнорирует». Утверждение было +неверным и старше темы 78; при проектировании следа опёрлись на него и скрипт не +открыли, хотя докстринг `check_keys` говорит прямо обратное: «неизвестный ключ — +отказ, а не безмолвный пропуск». + +## Решено + +**Р319. След живёт ключом `healthcheck_last` в секции `[docs]`.** Секция досталась +ему по смыслу: `[docs]` — настройки проверок документов, а сверка документов и +есть такая проверка. Своя секция верхнего уровня стоила бы правки общего читателя +`shared/config.py` и превратила бы файл, объявленный «версией и настройками», в +хранилище состояния. + +**Р320. Новый ключ `.av-dev.toml` заводится правкой константы скрипта-владельца, +и только потом появляется в каноне.** Порядок именно такой, а не обратный: +владелец ключа — тот скрипт, чья константа его перечисляет (`TOP_KEYS` в +`shared/config.py`, `DOCS_KEYS` в `docs.py`, свой список в `tasks.py`). + +**Р321. Канон приведён к скрипту, а не скрипт к канону.** Отказ кодом 3 — +поведение верное: ключ, положенный не в ту секцию, при молчаливом пропуске не +значит ничего, проверка объявляет себя неприменимой, отчёт выходит зелёным, и на +месте настройки оказывается тишина. Ложное обещание снято, на его месте — правило +Р320 и названная цена этой ошибки. + +## Следствия + +**С282. Версия раскладки не двигается.** Ключ необязательный и заводится сам +первым прогоном сверки; проекту делать нечего, а запись журнала версий нужна ради +пункта «что сделать проекту». + +**С283. Правило «своей прозе здесь верить нельзя» распространяется и на +собственную документацию плагина.** Оно написано про возврат агента, но работает +шире: утверждение документа о поведении скрипта проверяется **запуском скрипта**, +и стоит эта проверка одной команды. Здесь её не сделали, и цена вышла в рабочую +поломку у всякого, кто позвал бы сверку. + +**С284. Гейт репозитория такого класса дефектов не ловит и ловить не может.** Он +судит форму: фронтматтеры, дословность копий, адреса, номера журнала, рендер +диаграмм. Согласованность прозы с поведением скрипта — суждение, и добыло его +ревью, а не хук. Это довод в пользу того, чтобы прогон ревью по трансформации +шёл не «если останется время», а следом за ней. diff --git a/decisions/README.md b/decisions/README.md index 5087629..9ed97c4 100644 --- a/decisions/README.md +++ b/decisions/README.md @@ -130,3 +130,4 @@ | 76 | [Лёгкий проход в цикле, тяжёлые — в отдельном скилле](76-proof-in-cycle-deep-review-apart.md) | 2026-08-23 | | 77 | [Цикл задачи проверяет механику; метки сняты](77-cycle-checks-mechanics-labels-dropped.md) | 2026-08-23 | | 78 | [Хвост задачи: отражение молча, новое — по слову](78-tail-reflection-silent-new-by-word.md) | 2026-08-23 | +| 79 | [След сверки переехал в секцию docs; ключ верхнего уровня ронял скрипты](79-healthcheck-trace-moved-into-docs-section.md) | 2026-08-23 |