адреса чужих документов сверяются с перечнем владельца

Адрес принадлежит одному плагину, а называют его все: docs/* стоит в
сорока местах конвейера, tasks/ROADMAP.md — в четырёх местах канона.
Переименование в каноне до них не доходит, и заметить это нечем:
протухший адрес попадает в механизм честной деградации ревью и выходит
правдоподобной строкой «документа в проекте нет», а не поломкой.

Перечень берётся из константы владельца — той, по которой он и так
проверяет раскладку. Судится упразднённое, а не незнакомое: список тем
канона открытый, и «нет такого имени» опровергнуть нечем; зато карта
переездов RETIRED и есть перечень запрещённого. Рядом одна догадка —
почти совпавшее имя как опечатка, порог замерен (законные до 0.64,
опечатки от 0.91).

Первый прогон: одна настоящая находка — REMAINING иллюстрировал
смысловой дубль адресом docs/specs/, упразднённым в версии 1 канона.

В гейте без glob: перечень лежит в .py, упоминания в .md, и коммит с
переименованием трогает только первую сторону.
This commit is contained in:
av
2026-08-09 15:12:46 +03:00
parent c6be879831
commit 872732989a
7 changed files with 302 additions and 31 deletions
+44 -6
View File
@@ -259,7 +259,7 @@ claude plugin uninstall <плагин>@av-dev-skills --scope project
<plugin>/skills/<skill>/scripts/ tasks.py, docs.py, openspec.py
<plugin>/agents/ charter'ы сабагентов
shared/ дома правил, общих для нескольких плагинов
scripts/ проверки репозитория: копии, диаграммы, фронтматтеры
scripts/ проверки репозитория: копии, адреса, диаграммы, фронтматтеры
pyproject.toml линтеры скриптов, только для этого репозитория
lefthook.yml гейт коммита: проверки документов
```
@@ -352,6 +352,40 @@ uv run python scripts/copies.py # 0 сошлось, 1 расхождение
на тот файл, дом остался без копий, разметка сломана. Чего он **не** ловит —
копию, которую забыли пометить: помечать — по-прежнему решение человека.
## Проверка адресов документов
Адрес документа принадлежит одному плагину, а называют его все: `docs/*` стоит
примерно в сорока местах конвейера, `tasks/ROADMAP.md` — в четырёх местах канона.
Переименование в каноне до этих мест само не доходит.
```
python3 scripts/addresses.py # весь репозиторий
# 0 сошлось, 1 упразднённый адрес или опечатка, 3 перечень владельца недоступен
```
**Зачем машина, а не аккуратность.** Прогон ревью умеет честно деградировать:
дома темы нет — в границах покрытия появляется строка «документа в проекте нет»
с названной ценой. Протухший адрес попадает ровно в эту машинерию и выходит
**правдоподобным отчётом**, а не поломкой. Громкий признак ошибки деградацией
убран, и здесь он возвращается гейтом.
Перечень берётся из **константы владельца** — той, по которой он и так проверяет
раскладку (`docs.py`, `tasks.py`). Второй перечень прозой был бы вторым домом
ровно того сорта, против которого написан канон.
Судится **упразднённое, а не незнакомое**, и это следует из канона: список тем
открытый, всё, что проект кладёт в `docs/` сверх закрытых категорий, — законная
тема, и опровергнуть её нечем. Зато переименование ловится точно: канон, убирая
слот, кладёт его в карту переездов, и она здесь и есть перечень запрещённого.
Рядом единственная догадка — имя, **почти** совпавшее с каноническим: `securty`
это опечатка вероятнее, чем новая тема. Порог замерен по репозиторию: законные
имена дают до 0.64, опечатки — от 0.91.
Не проверяются журналы (они описывают прошлые состояния и задним числом не
переписываются), адреса `openspec/*` (раскладка чужого инструмента, владельца у
нас нет) и упоминания в комментариях скриптов — сверяется только markdown. Эти
границы скрипт печатает сам.
## Проверка диаграмм
Диаграммы `mermaid` живут исходником в markdown — картинок в репозитории нет.
@@ -381,7 +415,7 @@ uv run python scripts/diagrams.py A.md B.md # только названные
## Гейт коммита
Все пять проверок стоят в `pre-commit` через [lefthook](https://lefthook.dev) —
Все шесть проверок стоят в `pre-commit` через [lefthook](https://lefthook.dev) —
конфиг в [lefthook.yml](lefthook.yml), ставится один раз на клон:
```
@@ -393,6 +427,7 @@ lefthook run pre-commit # прогнать руками, не коммитя
| --- | --- | --- | --- |
| фронтматтеры | правка `*.md` | весь репозиторий | миллисекунды |
| копии правил | правка `*.md` | весь репозиторий | миллисекунды |
| адреса документов | **каждый коммит** | весь репозиторий | ~0.07 с |
| диаграммы | правка `*.md` | staged-файлы | ~1 с на файл |
| `ruff check --fix` | правка `*.py` | staged-файлы | доли секунды |
| `pyrefly check` | правка `*.py` | staged-файлы | доли секунды |
@@ -402,10 +437,13 @@ Glob разводит две половины: коммит, трогающий
**Судятся staged-файлы, а не рабочее дерево** — гейт обязан проверять то, что
уедет в историю, а не то, что случайно лежит рядом на диске. Исключений два, и
оба про существо, а не про удобство: `copies.py` сверяет копию с домом, а дом
лежит в другом файле, которого в индексе может не быть (список staged дал бы
«копии дословны» ровно там, где правка дома их и разошлась), а `frontmatter.py`
обходит весь репозиторий за сотые доли секунды — экономить тут нечего.
три, и все про существо, а не про удобство: `copies.py` сверяет копию с домом, а
дом лежит в другом файле, которого в индексе может не быть (список staged дал бы
«копии дословны» ровно там, где правка дома их и разошлась); `frontmatter.py`
обходит весь репозиторий за сотые доли секунды — экономить тут нечего;
`addresses.py` идёт **без glob вовсе**, потому что сводит две стороны: перечень
адресов лежит в `*.py` владельца, а упоминания — в `*.md` соседей, и коммит с
переименованием документа трогает только первую.
**`ruff` чинит безопасное сам, и починка доносится до этого же коммита**
(`stage_fixed: true`). Иначе исправленный файл остался бы в рабочем дереве, а в