102 lines
9.3 KiB
Bash
Executable file
102 lines
9.3 KiB
Bash
Executable file
#!/bin/sh
|
||
# Зонный pre-commit фрагмент docs/. Ставится тем же диспетчером .git/hooks/pre-commit, что и
|
||
# фрагмент фронта (диспетчер перебирает */scripts/githooks/pre-commit).
|
||
#
|
||
# ┌─ ЧТО ЭТО ДЕЛАЕТ И ЧЕГО НЕ ДЕЛАЕТ — читать до того, как ругаться на него ───────────────────┐
|
||
# │ ДЕЛАЕТ: при коммите, задевающем ЛЮБОЙ док, проверяет file:line-якоря живых доков; при │
|
||
# │ коммите с D-логом или PROGRESS дополнительно сверяет три носителя номера головы и │
|
||
# │ производные числа доков (счёт очереди, зон, вес открытых строк регистра платформы). │
|
||
# │ НЕ ДЕЛАЕТ: не блокирует коммит НИКОГДА — только печатает предупреждение и выходит 0. │
|
||
# │ Не трогает содержимое, не правит файлы, не лезет в сеть, не запускает тесты. │
|
||
# │ ЕСЛИ МЕШАЕТ: `git commit --no-verify` обходит всю цепочку; выключить только этот фрагмент — │
|
||
# │ `chmod -x docs/scripts/githooks/pre-commit`. Ни то, ни другое ничего не ломает. │
|
||
# │ ⚠ НО: обход — это ДЕЙСТВИЕ, а не мнение. Считаешь, что хук неправ, шумит, мешает или ловит │
|
||
# │ не то — НАПИШИ ВЛАДЕЛЬЦУ (он передаст оркестратору), а не обходи молча и не подпирай │
|
||
# │ хаком/воркэраундом. Тихая деградация гейта не оставляет следа: следующая сессия получит │
|
||
# │ его же в прежнем виде и потратит то же время, а мы не узнаем, что он врал. Жалоба на │
|
||
# │ гейт — полезный сигнал и повод его починить; молчаливый обход — потеря сигнала. │
|
||
# │ ПОЧЕМУ ВООБЩЕ: голова CURRENT-STATE отставала ТРИЖДЫ у трёх РАЗНЫХ оркестраторов │
|
||
# │ (D39.81 · D39.83 · D39.112 п.5б). Одна ошибка у независимых сессий на одном поле — │
|
||
# │ свойство поля, а не сессий, поэтому проверка механическая, а не нормой прозой. │
|
||
# │ ПОЧЕМУ ПРЕДУПРЕЖДАЕТ, А НЕ БЛОКИРУЕТ: фрагмент общий, он сработает у сессии, которой я не │
|
||
# │ писал промт. Останавливать чужой коммит — решение владельца, не моё (оркестратор №15). │
|
||
# └────────────────────────────────────────────────────────────────────────────────────────────┘
|
||
#
|
||
# Ложные тревоги, которых здесь СОЗНАТЕЛЬНО избегают, — это важнее, чем полнота: хук, который
|
||
# врёт, учат игнорировать, и тогда он хуже отсутствующего.
|
||
# · числа считаются по СОДЕРЖИМОМУ КОММИТА (`--from-index`), а не по рабочему дереву, иначе
|
||
# частичный стейдж (своя правка в общем файле) давал бы ругань на то, что в коммит не поедет;
|
||
# · голова сверяется ТОЛЬКО когда в коммите есть D-лог, то есть когда идёт ратификация — норма
|
||
# требует бампать голову в том же коммите, что аппенд ноты, и вот этот случай и ловится;
|
||
# коммит, который правит PROGRESS по другому поводу, про голову не спрашивают;
|
||
# · линт якорей (D39.148) читает СКАНИРУЕМЫЕ доки по содержимому коммита — иначе незакоммиченное
|
||
# дерево живого полигона в docs/experiments/ давало бы ругань на чужой WIP; ЦЕЛИ якорей берутся
|
||
# из дерева осознанно: код не входит в docs-коммит, и его истина — дерево.
|
||
|
||
staged=$(git diff --cached --name-only)
|
||
[ -z "$staged" ] && exit 0
|
||
|
||
dlog='docs/architecture/05-decisions-log.md'
|
||
prog='docs/PROGRESS.md'
|
||
|
||
# Быстрый выход: коммит не задевает документацию — нам тут нечего делать. Триггер ШИРЕ носителей
|
||
# чисел (D39.148): якоря `file:line` живут во ВСЕХ доках, включая промты сессий, поэтому линт обязан
|
||
# видеть и промт-коммит. Полигонский фриз-коммит, задевший docs/experiments/, изредка увидит
|
||
# предупреждение — приемлемо: оно warn-only и самоописано.
|
||
printf '%s\n' "$staged" | grep -qE '^(docs/.*|CLAUDE\.md)$' || exit 0
|
||
|
||
warn=0
|
||
|
||
# 1) Голова. Только при ратификации (D-лог в коммите).
|
||
if printf '%s\n' "$staged" | grep -qx "$dlog"; then
|
||
after() {
|
||
if printf '%s\n' "$staged" | grep -qx "$1"; then git show ":$1" 2>/dev/null; else git show "HEAD:$1" 2>/dev/null; fi
|
||
}
|
||
note=$(after "$dlog" | grep -oE '^## D39\.[0-9]+' | tail -1 | sed 's/^## //')
|
||
banner=$(after "$dlog" | head -1 | grep -oE 'D1–D39\.[0-9]+' | sed 's/^D1–//')
|
||
claimed=$(after "$prog" | head -8 | grep -oE 'голова D39\.[0-9]+' | head -1 | sed 's/^голова //')
|
||
|
||
if [ -n "$note" ] && [ -n "$claimed" ] && [ "$note" != "$claimed" ]; then
|
||
echo "pre-commit ⚠ docs: голова разошлась — последняя нота D-лога $note, CURRENT-STATE говорит $claimed." >&2
|
||
echo " Норма: голова бампается в ТОМ ЖЕ коммите, что аппенд ноты (промт оркестратора)." >&2
|
||
warn=1
|
||
fi
|
||
if [ -n "$note" ] && [ -n "$banner" ] && [ "$note" != "$banner" ]; then
|
||
echo "pre-commit ⚠ docs: шапка-диапазон D-лога $banner при последней ноте $note." >&2
|
||
warn=1
|
||
fi
|
||
fi
|
||
|
||
# 2) Производные числа — по содержимому коммита, и ТОЛЬКО когда в коммите носители этих чисел
|
||
# (гейтовка прежняя, D39.112). Ненайденный литерал скрипт тоже считает расхождением: проверка,
|
||
# молча перестающая проверять, хуже отсутствующей.
|
||
if printf '%s\n' "$staged" | grep -qE "^($dlog|$prog)$"; then
|
||
if command -v python3 >/dev/null 2>&1 && [ -f docs/scripts/counts.py ]; then
|
||
if ! out=$(python3 docs/scripts/counts.py --check --from-index 2>&1); then
|
||
echo "pre-commit ⚠ docs: производные числа разошлись с пере-счётом:" >&2
|
||
# Печатаем строки-находки, а если их нет (скрипт УПАЛ) — весь вывод: предупреждение с пустым
|
||
# телом нечитаемо и учит игнорировать хук.
|
||
printf '%s\n' "$out" | grep '✗' >&2 || printf '%s\n' "$out" | tail -5 >&2
|
||
warn=1
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# 3) Якоря file:line живых доков (D39.148 — ревизия D39.126 «--lint только руками»). Сканируемые
|
||
# доки читаются по содержимому коммита, поэтому чужой незакоммиченный WIP невидим; ЦЕЛИ якорей
|
||
# читаются из дерева — код не часть docs-коммита. ~0.2 с, warn-only.
|
||
if command -v python3 >/dev/null 2>&1 && [ -f docs/scripts/counts.py ]; then
|
||
if ! out=$(python3 docs/scripts/counts.py --lint --from-index 2>&1); then
|
||
echo "pre-commit ⚠ docs: мёртвые или переросшие file:line-якоря:" >&2
|
||
# Печатаем строки-находки, а если их нет (скрипт УПАЛ) — весь вывод: предупреждение с пустым
|
||
# телом нечитаемо и учит игнорировать хук.
|
||
printf '%s\n' "$out" | grep '✗' >&2 || printf '%s\n' "$out" | tail -5 >&2
|
||
warn=1
|
||
fi
|
||
fi
|
||
|
||
if [ "$warn" -eq 1 ]; then
|
||
echo "pre-commit: это ПРЕДУПРЕЖДЕНИЕ — коммит не остановлен (обход: --no-verify)." >&2
|
||
echo " Считаешь, что хук неправ или мешает? Напиши ВЛАДЕЛЬЦУ — не обходи молча и не подпирай хаком." >&2
|
||
fi
|
||
exit 0
|