линтеры скриптов: ruff и pyrefly через uv, починены 41 находка

Скрипты остаются на голом python 3.12 без зависимостей: pyproject.toml живёт
только в этом репозитории и держит линтеры, а не зависимости скриптов.

Ноль зависимостей охраняется дважды: banned-api у ruff ловит частые соблазны по
имени, pyrefly видит окружение без ничего и не разрешает любой сторонний импорт.

Версии прибиты точно, uv.lock под git: обновление линтера меняет набор находок,
а находки правятся руками в скриптах, которые уезжают в чужие проекты.

RUF001–003 выключены — весь текст русский, 311 срабатываний из 338 шум.
av-dev-backlog исключён: заморожен до удаления, правка без выгоды.

Из 41 находки содержательных две: мёртвая ques в check и два места, где
find_entry_index может вернуть None прямо в list.pop и range. По ревью там
стоит raise, а не continue: тихий пропуск превратил бы сломанный инвариант в
отчёт «индексы согласованы».

os из tasks.py ушёл целиком, fail() в docs.py объявлен NoReturn.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-08-03 15:28:10 +03:00
co-authored by Claude Opus 5
parent 68218208b7
commit c692436b91
8 changed files with 278 additions and 42 deletions
+64
View File
@@ -695,3 +695,67 @@ healthlog.
желательным. Пересобрать на шаге 1.7.
39. **Замер — единственный шаг, который нельзя переставить.** Всё остальное в
порядке 1–5 можно тасовать; шаг 3 стоит перед шагом 5 жёстко.
## 9. Линтеры скриптов (2026-08-03)
### Что было
Три скрипта на python, 3600 строк, ни одной проверки. `tasks.py` — 2450 строк,
которые ходят по файловой системе, переименовывают и удаляют файлы задач.
Требование к самим скриптам прежнее и не обсуждается: **голый `python3` 3.12,
ноль внешних зависимостей** — они лежат рядом со скиллами и запускаются в
чужом проекте, где ничего ставить нельзя.
### Решено
**DD. `pyproject.toml` в корне `dev-skills`, зависимости через `uv`.** Файл
живёт только здесь и не уезжает никуда: он держит **линтеры**, а не зависимости
скриптов. Скрипты остаются запускаемыми любым `python3` — это проверено прогоном
всех операций через `/usr/bin/python3`, а не через `.venv`.
**EE. Ноль зависимостей охраняется двумя способами, и главный — второй.**
`banned-api` у ruff ловит частые соблазны по имени (`requests`, `yaml`,
`pydantic`, `click`, `rich`) — список заведомо неполный. Настоящий страж —
pyrefly: в окружении нет ничего, кроме линтеров, поэтому **любой** сторонний
импорт у него не разрешается. Первый способ даёт понятное сообщение, второй —
полноту.
**FF. Версии линтеров прибиты точно** (`ruff==0.16.1`, `pyrefly==1.2.0`) плюс
`uv.lock` в git. Обновление линтера меняет набор находок, а находки правятся
руками в скриптах, которые уезжают в чужие проекты. Обновление обязано быть
отдельной осознанной правкой, а не побочным эффектом `uv sync`.
**GG. `RUF001``RUF003` выключены.** Весь текст скриптов русский: сообщения,
докстроки, комментарии. «Похожая на латиницу кириллица» здесь норма, а не
опечатка, и три этих правила давали **311 срабатываний из 338** — шум, в котором
тонут остальные 27.
**HH. `av-dev-backlog` исключён из проверки.** Плагин помечен устаревшим и живёт
до перевода последнего проекта, после чего удаляется целиком. Шесть его находок
косметические (`os.replace`, `l` как имя), а правка замороженного кода без
тестов — риск без выгоды. Исключение уходит вместе с плагином.
**II. Голый `except Exception` разрешён только помеченный.** Правило `BLE`
включено, а два места последнего рубежа (`main` обоих скриптов, код выхода 4 по
словарю) несут `# noqa: BLE001` с причиной. Так третий такой except не
появляется молча.
### Что из этого следует
40. **Найдено и починено 27 находок ruff и 14 pyrefly.** Содержательных две:
мёртвая переменная `ques` в `check` (вычислялась и не использовалась —
вопросы проверяет `questions_open`) и два места в `check --fix`, где
`find_entry_index` может вернуть `None`, а результат идёт прямо в
`list.pop` и в `range`. Оба сегодня недостижимы, и недостижимость держалась
на рассуждении о вызывающем коде, а не на проверке.
*Поправлено по ревью:* там стоит `raise`, а не `continue`. Тихий пропуск
превратил бы сломанный инвариант в отчёт «индексы согласованы» — то есть в
враньё; громкий отказ кодом 4 честнее.
41. **`os` из `tasks.py` ушёл целиком.** `os.replace``Path.replace`,
`os.path.basename``Path.name`; импорт стал не нужен.
42. **`fail()` в `docs.py` объявлен `NoReturn`.** Без этого `read_config`
выглядел как возвращающий неинициализированное значение — и это ровно то,
что читатель кода тоже не мог знать наверняка.
43. **Проверка не входит ни в один гейт.** CI у репозитория нет, хука нет;
запускается руками командой из README. Заводить хук ради двух скриптов,
которые правятся раз в месяц, — плата ритуалом без выгоды.