гейт коммита: пять машинных проверок встали в pre-commit
Проверки существовали и запускались руками — то есть тогда, когда о них вспоминали. Каждая из них ловит ровно тот класс поломок, который не виден при чтении: фронтматтер разбирает загрузчик скиллов, а не человек; копия правила расходится с домом молча; диаграмма mermaid выглядит правдоподобно и падает на рендере; ruff и pyrefly стерегут ноль внешних зависимостей, без которого tasks.py и docs.py перестают работать в чужом проекте. Полагаться на память в таком наборе — значит узнавать о поломке от того, кто скачал плагин. Ставится lefthook (конфиг в lefthook.yml, `lefthook install` один раз на клон), пять задач в parallel. Glob разводит две половины: правка одних скриптов не платит за рендер диаграмм (~15 секунд), правка документов не гоняет линтеры. Внутри своей половины проверяется весь репозиторий, а не изменённые файлы — и расхождение копии, и находка ruff в соседнем файле это ровно тот случай, когда правка сломала не себя. Два свойства названы в README и в шапке конфига, чтобы не выяснялись отладкой. Первое: судится рабочее дерево, а не индекс — скрипты написаны как обход репозитория и про git add не знают, поэтому частичный коммит при грязном дереве проверяется по тому, что на диске. Второе: обход разовый — LEFTHOOK=0, и он законен ровно для случая, когда найденное нечем чинить прямо сейчас. Заодно поправлена строка README про диаграммы: «поэтому он не в гейте, а в руках того, кто правит диаграмму» — с этой правкой она перестала быть верной. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -228,6 +228,7 @@ claude plugin uninstall <плагин>@av-dev-skills --scope project
|
|||||||
<plugin>/agents/ charter'ы сабагентов
|
<plugin>/agents/ charter'ы сабагентов
|
||||||
scripts/ проверки репозитория: копии, диаграммы, фронтматтеры
|
scripts/ проверки репозитория: копии, диаграммы, фронтматтеры
|
||||||
pyproject.toml линтеры скриптов, только для этого репозитория
|
pyproject.toml линтеры скриптов, только для этого репозитория
|
||||||
|
lefthook.yml гейт коммита: проверки документов
|
||||||
```
|
```
|
||||||
|
|
||||||
## Проверка скриптов
|
## Проверка скриптов
|
||||||
@@ -315,11 +316,45 @@ uv run python scripts/diagrams.py # 0 рендерятся, 1 нет, 3 нет
|
|||||||
```
|
```
|
||||||
|
|
||||||
Рендерит `mmdc` с PATH или `npx --yes @mermaid-js/mermaid-cli`; ни того ни
|
Рендерит `mmdc` с PATH или `npx --yes @mermaid-js/mermaid-cli`; ни того ни
|
||||||
другого нет — код 3, а не молчаливый успех. Прогон занимает секунды на блок,
|
другого нет — код 3, а не молчаливый успех. Прогон занимает секунды на блок —
|
||||||
поэтому он не в гейте, а в руках того, кто правит диаграмму.
|
это самая дорогая из трёх проверок, и в гейте коммита она стоит **под glob по
|
||||||
|
markdown**: правка одних скриптов проходит мгновенно.
|
||||||
|
|
||||||
Чего проверка **не** ловит — расхождение диаграммы с прозой вокруг неё. Дословного
|
Чего проверка **не** ловит — расхождение диаграммы с прозой вокруг неё. Дословного
|
||||||
соответствия между текстом и графом нет, сличать нечего, и держится это
|
соответствия между текстом и графом нет, сличать нечего, и держится это
|
||||||
правилом старшинства, записанным рядом с каждой диаграммой: **в конвейере ревью
|
правилом старшинства, записанным рядом с каждой диаграммой: **в конвейере ревью
|
||||||
старший граф** (он и есть алгоритм планировщика, проза его объясняет), **в
|
старший граф** (он и есть алгоритм планировщика, проза его объясняет), **в
|
||||||
остальных местах старшая проза** (диаграмма там сводка).
|
остальных местах старшая проза** (диаграмма там сводка).
|
||||||
|
|
||||||
|
## Гейт коммита
|
||||||
|
|
||||||
|
Все пять проверок стоят в `pre-commit` через [lefthook](https://lefthook.dev) —
|
||||||
|
конфиг в [lefthook.yml](lefthook.yml), ставится один раз на клон:
|
||||||
|
|
||||||
|
```
|
||||||
|
lefthook install # пишет .git/hooks/pre-commit
|
||||||
|
lefthook run pre-commit # прогнать руками, не коммитя
|
||||||
|
```
|
||||||
|
|
||||||
|
| Проверка | Когда идёт | Сколько |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| фронтматтеры | правка `*.md` | миллисекунды |
|
||||||
|
| копии правил | правка `*.md` | миллисекунды |
|
||||||
|
| диаграммы | правка `*.md` | ~15 с на весь репозиторий |
|
||||||
|
| `ruff check .` | правка `*.py` | доли секунды |
|
||||||
|
| `pyrefly check` | правка `*.py` | доли секунды |
|
||||||
|
|
||||||
|
Glob разводит две половины: коммит, трогающий одни скрипты, не платит за рендер
|
||||||
|
диаграмм, а коммит в документы не гоняет линтеры. Внутри своей половины
|
||||||
|
проверяется **весь репозиторий**, а не изменённые файлы: и расхождение копии, и
|
||||||
|
находка ruff в соседнем файле — это ровно тот случай, когда правка сломала не
|
||||||
|
себя.
|
||||||
|
|
||||||
|
Два свойства, о которых стоит знать заранее:
|
||||||
|
|
||||||
|
- **судится рабочее дерево, а не индекс.** Скрипты обходят репозиторий целиком
|
||||||
|
и про `git add` не знают: частичный коммит при грязном дереве проверяется по
|
||||||
|
тому, что на диске. Это цена того, что проверки — обход, а не фильтр файлов, и
|
||||||
|
она принята: расхождение копий и битая диаграмма ловятся именно обходом;
|
||||||
|
- **обход разовый — `LEFTHOOK=0 git commit …`.** Он законен ровно для случая,
|
||||||
|
когда найденное нечем чинить прямо сейчас; молча пропущенная проверка — нет.
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Гейт коммита: всё, что в этом репозитории проверяется машиной.
|
||||||
|
#
|
||||||
|
# Документы — то, что читает не человек, а машина, и чья поломка **не видна при
|
||||||
|
# чтении**: фронтматтер (его разбирает загрузчик скиллов), помеченная копия
|
||||||
|
# правила (расходится молча) и диаграмма mermaid (текст правдоподобен, рендер
|
||||||
|
# падает). Скрипты — ruff и pyrefly: они же стерегут ноль внешних зависимостей,
|
||||||
|
# без которого tasks.py и docs.py перестают работать в чужом проекте.
|
||||||
|
# Всё остальное (проза, устройство, язык) — предмет ревью, а не хука.
|
||||||
|
#
|
||||||
|
# Скрипты читают **рабочее дерево целиком**, а не индекс: частичный коммит при
|
||||||
|
# грязном дереве судится по тому, что на диске. Это осознанно — они и написаны
|
||||||
|
# как обход репозитория, а не как фильтр по файлам.
|
||||||
|
#
|
||||||
|
# Ставится `lefthook install` (см. README, «Гейт коммита»). Обойти разово —
|
||||||
|
# `LEFTHOOK=0 git commit …`; обход законен ровно для того случая, когда чинить
|
||||||
|
# найденное нечем прямо сейчас.
|
||||||
|
|
||||||
|
pre-commit:
|
||||||
|
parallel: true
|
||||||
|
jobs:
|
||||||
|
- name: фронтматтеры
|
||||||
|
glob: "*.md"
|
||||||
|
run: python3 scripts/frontmatter.py
|
||||||
|
|
||||||
|
- name: копии правил
|
||||||
|
glob: "*.md"
|
||||||
|
run: python3 scripts/copies.py
|
||||||
|
|
||||||
|
# Секунды, а не миллисекунды: рендер идёт настоящим mermaid-cli. Поэтому
|
||||||
|
# glob — правка одних только скриптов проходит гейт мгновенно. Нет ни
|
||||||
|
# `mmdc`, ни `npx` — код 3, коммит отказан: «не проверено» здесь не то же
|
||||||
|
# самое, что «проверено и сошлось».
|
||||||
|
- name: диаграммы
|
||||||
|
glob: "*.md"
|
||||||
|
run: python3 scripts/diagrams.py
|
||||||
|
|
||||||
|
# Скрипты — под своим glob и через uv: версии линтеров прибиты точно, и
|
||||||
|
# `uv run` берёт именно их, а не то, что оказалось в PATH. Обе проверки
|
||||||
|
# укладываются в доли секунды на весь репозиторий, поэтому проверяется он
|
||||||
|
# целиком, а не изменённые файлы: находка в чужом файле здесь означает, что
|
||||||
|
# правка сломала соседа.
|
||||||
|
- name: ruff
|
||||||
|
glob: "*.py"
|
||||||
|
run: uv run ruff check .
|
||||||
|
|
||||||
|
- name: pyrefly
|
||||||
|
glob: "*.py"
|
||||||
|
run: uv run pyrefly check
|
||||||
Reference in New Issue
Block a user