agent-quality-kit 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +155 -0
  3. package/kit/docs/ai/agent-harness-playbook.md +596 -0
  4. package/kit/docs/ai/ai-native-development.md +371 -0
  5. package/kit/docs/ai/ai-sdlc.md +221 -0
  6. package/kit/docs/ai/anthropic-ai-native-sdlc-2026-08.md +294 -0
  7. package/kit/docs/ai/app-owner-strategy.md +921 -0
  8. package/kit/docs/ai/deep-research-2026-07.md +161 -0
  9. package/kit/docs/ai/harness-best-practices.md +385 -0
  10. package/kit/docs/ai/index.md +64 -0
  11. package/kit/docs/ai/project-baseline.md +261 -0
  12. package/kit/docs/ai/quality-gates-checklist.md +322 -0
  13. package/kit/docs/ai/sources-building-with-agents.md +111 -0
  14. package/kit/docs/ai/stream-2026-08-ai-coding-panel.md +304 -0
  15. package/kit/docs/ready-made-rules.md +170 -0
  16. package/kit/gates/README.md +231 -0
  17. package/kit/gates/_skip.sh +75 -0
  18. package/kit/gates/commit-explains-itself/README.md +45 -0
  19. package/kit/gates/commit-explains-itself/check.sh +63 -0
  20. package/kit/gates/commit-explains-itself/gate.yml +10 -0
  21. package/kit/gates/commit-explains-itself/green/COMMIT_MSG +6 -0
  22. package/kit/gates/commit-explains-itself/red/COMMIT_MSG +3 -0
  23. package/kit/gates/complexity-limit/README.md +37 -0
  24. package/kit/gates/complexity-limit/check.sh +44 -0
  25. package/kit/gates/complexity-limit/gate.yml +13 -0
  26. package/kit/gates/complexity-limit/green/flat.py +10 -0
  27. package/kit/gates/complexity-limit/red/deep.py +9 -0
  28. package/kit/gates/dead-code/README.md +30 -0
  29. package/kit/gates/dead-code/gate.yml +23 -0
  30. package/kit/gates/dead-code/green/mod.py +9 -0
  31. package/kit/gates/dead-code/red/mod.py +9 -0
  32. package/kit/gates/deps-are-pinned/README.md +29 -0
  33. package/kit/gates/deps-are-pinned/check.sh +49 -0
  34. package/kit/gates/deps-are-pinned/gate.yml +9 -0
  35. package/kit/gates/deps-are-pinned/green/nodep-go/go.mod +3 -0
  36. package/kit/gates/deps-are-pinned/green/package-lock.json +3 -0
  37. package/kit/gates/deps-are-pinned/green/package.json +4 -0
  38. package/kit/gates/deps-are-pinned/green/requirements.txt +2 -0
  39. package/kit/gates/deps-are-pinned/red/package.json +4 -0
  40. package/kit/gates/deps-are-pinned/red/requirements.txt +2 -0
  41. package/kit/gates/deps-are-pinned/red/withdep-go/go.mod +5 -0
  42. package/kit/gates/duplicate-code/README.md +40 -0
  43. package/kit/gates/duplicate-code/check.sh +58 -0
  44. package/kit/gates/duplicate-code/gate.yml +12 -0
  45. package/kit/gates/duplicate-code/green/common.py +9 -0
  46. package/kit/gates/duplicate-code/green/use.py +9 -0
  47. package/kit/gates/duplicate-code/red/a.py +12 -0
  48. package/kit/gates/duplicate-code/red/b.py +12 -0
  49. package/kit/gates/entry-links-exist/README.md +22 -0
  50. package/kit/gates/entry-links-exist/check.sh +24 -0
  51. package/kit/gates/entry-links-exist/gate.yml +16 -0
  52. package/kit/gates/entry-links-exist/green/AGENTS.md +5 -0
  53. package/kit/gates/entry-links-exist/green/rules/general.md +3 -0
  54. package/kit/gates/entry-links-exist/red/AGENTS.md +3 -0
  55. package/kit/gates/file-size-limit/README.md +22 -0
  56. package/kit/gates/file-size-limit/check.sh +34 -0
  57. package/kit/gates/file-size-limit/gate.yml +9 -0
  58. package/kit/gates/file-size-limit/green/a.py +251 -0
  59. package/kit/gates/file-size-limit/green/b.py +251 -0
  60. package/kit/gates/file-size-limit/red/big.py +601 -0
  61. package/kit/gates/gate-has-samples/README.md +29 -0
  62. package/kit/gates/gate-has-samples/check.sh +48 -0
  63. package/kit/gates/gate-has-samples/gate.yml +9 -0
  64. package/kit/gates/gate-has-samples/green/.aqk.yml +10 -0
  65. package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/check.sh +2 -0
  66. package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/green/good.py +2 -0
  67. package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/red/bad.py +1 -0
  68. package/kit/gates/gate-has-samples/red/.aqk.yml +10 -0
  69. package/kit/gates/gate-has-samples/red/gates/no-print-in-prod/check.sh +2 -0
  70. package/kit/gates/gates-are-runnable/README.md +23 -0
  71. package/kit/gates/gates-are-runnable/check.sh +35 -0
  72. package/kit/gates/gates-are-runnable/gate.yml +9 -0
  73. package/kit/gates/gates-are-runnable/green/.aqk.yml +9 -0
  74. package/kit/gates/gates-are-runnable/green/checks/lint.sh +2 -0
  75. package/kit/gates/gates-are-runnable/red/.aqk.yml +6 -0
  76. package/kit/gates/gates-run-in-ci/README.md +29 -0
  77. package/kit/gates/gates-run-in-ci/check.sh +42 -0
  78. package/kit/gates/gates-run-in-ci/gate.yml +12 -0
  79. package/kit/gates/gates-run-in-ci/green/.aqk.yml +6 -0
  80. package/kit/gates/gates-run-in-ci/green/.github/workflows/ci.yml +7 -0
  81. package/kit/gates/gates-run-in-ci/green/checks/lint.sh +2 -0
  82. package/kit/gates/gates-run-in-ci/red/.aqk.yml +6 -0
  83. package/kit/gates/gates-run-in-ci/red/.github/workflows/ci.yml +7 -0
  84. package/kit/gates/gates-run-in-ci/red/checks/lint.sh +2 -0
  85. package/kit/gates/lesson-has-outcome/README.md +37 -0
  86. package/kit/gates/lesson-has-outcome/check.sh +50 -0
  87. package/kit/gates/lesson-has-outcome/gate.yml +11 -0
  88. package/kit/gates/lesson-has-outcome/green/.aqk.yml +2 -0
  89. package/kit/gates/lesson-has-outcome/green/incidents/README.md +32 -0
  90. package/kit/gates/lesson-has-outcome/red/.aqk.yml +2 -0
  91. package/kit/gates/lesson-has-outcome/red/incidents/README.md +13 -0
  92. package/kit/gates/no-print-in-prod/README.md +44 -0
  93. package/kit/gates/no-print-in-prod/check.sh +36 -0
  94. package/kit/gates/no-print-in-prod/gate.yml +15 -0
  95. package/kit/gates/no-print-in-prod/green/docs.ts +15 -0
  96. package/kit/gates/no-print-in-prod/green/legacy.py +9 -0
  97. package/kit/gates/no-print-in-prod/green/main.go +8 -0
  98. package/kit/gates/no-print-in-prod/green/main.rs +4 -0
  99. package/kit/gates/no-print-in-prod/green/service.py +8 -0
  100. package/kit/gates/no-print-in-prod/red/main.go +8 -0
  101. package/kit/gates/no-print-in-prod/red/main.rs +4 -0
  102. package/kit/gates/no-print-in-prod/red/service.py +3 -0
  103. package/kit/gates/secrets-not-in-code/README.md +29 -0
  104. package/kit/gates/secrets-not-in-code/check.sh +18 -0
  105. package/kit/gates/secrets-not-in-code/gate.yml +9 -0
  106. package/kit/gates/secrets-not-in-code/green/settings.py +4 -0
  107. package/kit/gates/secrets-not-in-code/red/settings.py +2 -0
  108. package/kit/gates/swallowed-error/README.md +26 -0
  109. package/kit/gates/swallowed-error/check.sh +54 -0
  110. package/kit/gates/swallowed-error/gate.yml +12 -0
  111. package/kit/gates/swallowed-error/green/loader.py +11 -0
  112. package/kit/gates/swallowed-error/green/run.js +8 -0
  113. package/kit/gates/swallowed-error/red/loader.py +5 -0
  114. package/kit/gates/swallowed-error/red/run.js +3 -0
  115. package/kit/gates/todo-without-task/README.md +26 -0
  116. package/kit/gates/todo-without-task/check.sh +19 -0
  117. package/kit/gates/todo-without-task/gate.yml +12 -0
  118. package/kit/gates/todo-without-task/green/order.py +9 -0
  119. package/kit/gates/todo-without-task/red/order.py +8 -0
  120. package/kit/ratchet/ratchet.sh +62 -0
  121. package/kit/rules/general.md +55 -0
  122. package/kit/rules/security.md +33 -0
  123. package/kit/rules/testing.md +46 -0
  124. package/package.json +41 -0
  125. package/tool/commands/doctor.mjs +230 -0
  126. package/tool/commands/gates.mjs +445 -0
  127. package/tool/commands/project.mjs +316 -0
  128. package/tool/lib/core.mjs +98 -0
  129. package/tool/lib/manifest.mjs +140 -0
  130. package/tool/lib/repo.mjs +270 -0
  131. package/tool/lib/templates.mjs +187 -0
  132. package/tool/program.mjs +81 -0
  133. package/tool/selfcheck/conditional.sh +24 -0
  134. package/tool/selfcheck/gates.sh +127 -0
  135. package/tool/selfcheck/smoke.sh +539 -0
  136. package/tool/selfcheck/syntax.sh +23 -0
  137. package/tool/selfcheck/units.mjs +105 -0
@@ -0,0 +1,596 @@
1
+ <!-- источник: audit_project/docs/ai/agent-harness-playbook.md -->
2
+ # Плейбук: харнес для разработки с ИИ-агентом (воспроизводимый опыт)
3
+
4
+ > **Что это.** Методичка «как поднять новый проект, где код пишет агент, а человек рулит».
5
+ >
6
+ > ⚠️ **Честно о переносимости.** Разделы 0, 4, 6–8, 11–18 не зависят от языка. Разделы 2 и 3 —
7
+ > **конкретика Python и GitHub Actions**: это снимок с живого проекта, а не универсальный рецепт.
8
+ > На другом стеке читать их как образец рассуждения, а не как список команд.
9
+ > Дистиллят того, что мы внедрили и проверили на memo + чего решили НЕ внедрять и почему + что
10
+ > обязаны внедрить позже (с триггерами). Формат — TODO-чеклист: открой на новом проекте и иди сверху вниз.
11
+ > Живой эталон каждого конфига — этот репозиторий (пути указаны у каждого пункта).
12
+ >
13
+ > **Источники:** reference-проект `audit_project`; gap-анализ обвязки 2026; AIE World's Fair 2026
14
+ > (харнес > модель, receipts/replay, production evals, review debt); Karpathy Sequoia Ascent 2026;
15
+ > Hashimoto/OpenAI harness engineering; наш опыт memo (все грабли — из реальных инцидентов).
16
+
17
+ ---
18
+
19
+ ## 0. Философия (прочитай, прежде чем копировать конфиги)
20
+
21
+ 1. **Харнес важнее модели.** Узкое место — не интеллект модели, а среда: гейты, трейсы, границы,
22
+ обратная связь. Та же модель в хорошем харнесе работает кратно лучше.
23
+ 2. **Гейты вместо надзора.** Качество держат детерминированные автоматические проверки с
24
+ fix-инструкцией в тексте ошибки — не «постараюсь лучше» и не построчный надзор человека.
25
+ Повторный баг одного класса → **новый гейт** (тест/линтер/хук), а не воспитание агента.
26
+ 3. **Гейт стережёт существующий артефакт.** Не ставь линтер на код, которого нет, — это мёртвое
27
+ правило, шум и ложное чувство защищённости. Каждому отложенному инструменту — **триггер** (§8).
28
+ 4. **Гейты — в первом коммите.** Гейт, добавленный потом, — долг (весь старый код красный);
29
+ добавленный сразу — множитель. День 0 нового репо = день установки харнеса (§2–§5), код — потом.
30
+ 5. **60–70% бюджета агента — в обвязку и техдолг**, не в фичи. Скучно, но после 1–2 недель
31
+ «наведения порядка» фичи начинают ехать кратно быстрее.
32
+ 6. **Детерминизм > LLM-judge.** Всё, что можно проверить кодом, — проверяем кодом. LLM-проверки —
33
+ только там, где код не справляется (и калибруем по человеку).
34
+ 7. **Чего нет в контексте — не существует.** Знание проекта живёт в репо (доки, правила, скрипты),
35
+ а не в голове и не в чате. Команды — copy-paste-исполнимые, не «запусти тесты».
36
+ 8. **Docker-first.** Всё исполняется в контейнерах. Локальные запуски мимо compose плодят мусор
37
+ (`dist/`, логи) и «у меня работает». Правило: `make up`, не `npm run dev` на хосте.
38
+ 9. ⚠️ **Review debt — риск №1 делегирования.** Код генерится быстрее, чем человек его понимает.
39
+ Гейты ловят механику, но НЕ ловят «согласился на дизайн, которого не понял». Противоядие:
40
+ заставляй агента **объяснять цепочку целиком** (архитектуру, «что с чем связано»), решения — в ADR.
41
+ 10. ⚠️ **Переинжиниринг харнеса — реальная ловушка.** Харнес — средство. Собрал базовый гейт →
42
+ СТОП, строй продукт. Остальное — из трения (баг повторился → гейт).
43
+ 11. **Мышление можно аутсорсить, понимание — нельзя** (Karpathy). Человек остаётся владельцем
44
+ «зачем», инвариантов и вкуса; агент — исполнения.
45
+
46
+ ---
47
+
48
+ ## 1. Чек-лист «День 0» — краткая карта (детали в §2–§5)
49
+
50
+ - [ ] §2.1 git-гигиена: `.gitignore`, `.gitattributes` (LF для `*.sh`!), `.env`/`.env.example`
51
+ - [ ] §2.2 `pyproject.toml`: uv + ruff (полный набор групп) + mypy + pytest
52
+ - [ ] §2.3 `.pre-commit-config.yaml` + `bandit.yaml` + `pre-commit install`
53
+ - [ ] §2.4 `scripts/arch-lint.sh` (свой линтер: print/TODO/NBSP/лимиты строк)
54
+ - [ ] §2.5 `Makefile` (up/down/logs/migrate/test/lint/format/typecheck/arch-lint/precommit)
55
+ - [ ] §3 CI: `.github/workflows/ci.yml` (статика · security · тесты)
56
+ - [ ] §4 `.claude/`: CLAUDE.md, settings.json (allow/ask/**deny**), 3 хука, rules/, агенты-критики
57
+ - [ ] §5 Docker: compose (web+db+frontend), healthcheck, `.dockerignore`
58
+ - [ ] §6 Правила кода и §7 правила тестов — скопировать в `.claude/rules/`
59
+ - [ ] §8 Прочитать таблицу отложенного арсенала, выписать триггеры под свой проект
60
+ - [ ] §10 Если на Windows — прочитать грабли ДО первого хука
61
+
62
+ ---
63
+
64
+ ## 2. Python-фундамент
65
+
66
+ ### 2.1. Git-гигиена (5 минут, спасает часы)
67
+
68
+ - [ ] `.gitattributes`: `*.sh text eol=lf` — **обязательно**. CRLF ломает shebang
69
+ (`#!/usr/bin/env bash\r`) в контейнере/WSL/хуках. Грабля реальная (memo, 2026-07).
70
+ - [ ] exec-бит скриптам через git (Windows его не хранит):
71
+ `git update-index --chmod=+x путь/скрипт.sh`
72
+ - [ ] `.env` в `.gitignore`; рядом `.env.example` с дев-дефолтами и комментами «как получить секрет».
73
+ - [ ] Секреты в коде — никогда; только `os.environ`. (Двойная защита: deny на чтение `.env`
74
+ в §4.2 + gitleaks/detect-private-key в §2.3/§3.)
75
+
76
+ ### 2.2. `pyproject.toml` — всё в одном файле (эталон: `pyproject.toml` memo)
77
+
78
+ - [ ] **uv** как менеджер (lock = воспроизводимое окружение; `[tool.uv] package = false` для приложения).
79
+ - [ ] dev-группа: `pytest`, `pytest-django`(если Django), `ruff`, `mypy`, `pre-commit`.
80
+ - [ ] **ruff** — линтер+форматтер одним тулом. Группы и ЗАЧЕМ каждая:
81
+
82
+ | Группа | Что ловит | Почему обязательна для кода агента |
83
+ |---|---|---|
84
+ | `E,F` | база pycodestyle/pyflakes | неиспользуемые импорты/переменные — агент их плодит |
85
+ | `I` | сортировка импортов | детерминизм диффов |
86
+ | `B` | bugbear: мутабельные дефолты, ловушки | классические «правдоподобные» баги LLM |
87
+ | `UP` | устаревший синтаксис | агент тянет паттерны из старых данных |
88
+ | `DJ` | Django-специфика (`__str__`, порядок Meta) | реально ловил у нас DJ008/DJ012 |
89
+ | `SIM` | упрощения | анти-слоп: агентный код ×2.3 многословнее |
90
+ | `RUF` | ruff-специфика | — |
91
+ | `DTZ` | naive datetime | бомба в любом дат-домене (аудит, отчёты) |
92
+ | `T20` | запрет `print` | агенты обожают print-отладку |
93
+ | `PT` | стиль pytest (составные assert и пр.) | точная диагностика падений |
94
+ | `RET` | лишние return/else | анти-слоп |
95
+ | `LOG,G` | корректность logging | f-string в логгере, потерянные exc_info |
96
+ | `C90` | цикломатическая сложность, `max-complexity=10` | агент сам режет монолитные функции |
97
+
98
+ - [ ] ignore: `RUF001/002/003` (если код/комменты на русском — иначе шум «кириллица похожа на
99
+ латиницу»), `RUF012` (Django class-level атрибуты — идиоматика).
100
+ - [ ] per-file-ignores: `*/migrations/* = ["ALL"]`, `manage.py = ["I001","T20"]`,
101
+ `scripts/* = ["T20"]`, тесты — `F401`.
102
+ - [ ] **mypy лёгкий** (не strict на старте): `ignore_missing_imports=true`,
103
+ `check_untyped_defs=true`, files = только прод-пакеты. Strict + django-stubs — по триггеру §8.
104
+
105
+ ### 2.3. `.pre-commit-config.yaml` — гейт коммита (эталон: наш файл)
106
+
107
+ Принцип: **pre-commit = быстрая кроссплатформенная статика (секунды)**; тяжёлое (Go/Docker-бинари,
108
+ тесты с БД) — в CI на Linux. Иначе на Windows гейт развалится или все будут его обходить.
109
+
110
+ - [ ] `pre-commit-hooks`: `end-of-file-fixer` (exclude: `uv.lock`, прозаические `docs/`),
111
+ `trailing-whitespace` (exclude: `docs/`), `check-merge-conflict`, **`detect-private-key`**,
112
+ `check-added-large-files`, `check-yaml`, `check-toml`.
113
+ - [ ] Если продукт выдаёт СВОИ секреты (API-ключи) — **`.gitleaks.toml` с правилом под их формат**
114
+ (префикс типа `xxx_sk_live_` и делает ключ обнаружимым сканером; эталон: наш `.gitleaks.toml`).
115
+ - [ ] `ruff` (`--fix`) + `ruff-format`.
116
+ - [ ] `bandit` (+ `bandit.yaml`: exclude tests/migrations; skips пустой) — статик-аудит безопасности.
117
+ - [ ] `shellcheck-py` — **весь bash под статикой** (хуки, скрипты). Bash — самый багоопасный язык в репо.
118
+ - [ ] `check-jsonschema → check-github-workflows` — сломанный CI-yaml ловится локально.
119
+ - [ ] local: `arch-lint` (§2.4).
120
+ - [ ] Установить: `uv run pre-commit install`. Прогнать всё: `uv run pre-commit run --all-files`.
121
+ - [ ] ⚠️ Вайтспейс-фиксеры НЕ пускать в прозаические доки/сырые данные (`exclude: ^docs/`) —
122
+ иначе хук «редактирует» чужую работу при каждом прогоне.
123
+
124
+ ### 2.4. `scripts/arch-lint.sh` — свой линтер инвариантов (эталон: наш файл)
125
+
126
+ То, чего нет в готовых линтерах. Каждая ошибка — **с fix-инструкцией в тексте**:
127
+
128
+ - [ ] Запрет `print()` (py) / `console.log` (js/jsx) в прод-коде → «замени на logging».
129
+ - [ ] Запрет `TODO/FIXME/HACK` → «заведи задачу в spec/plans/, маркер убери».
130
+ - [ ] Запрет NBSP (невидимый \xc2\xa0 — агент вставляет из markdown).
131
+ - [ ] **Лимиты размера** (агент уходит в рефакторинг/теряется на больших файлах):
132
+ прод-`.py` > **500** строк = error · компонент `.jsx/.tsx` > **300** = warn →
133
+ декомпозиция · тест-файл > **800** = error → разбить по доменам / `parametrize`.
134
+ - [ ] Два режима: с аргументами (pre-commit, только staged) / без (полный скан, `make arch-lint`).
135
+
136
+ ### 2.5. `Makefile` — единый интерфейс команд (агенту и человеку)
137
+
138
+ - [ ] `up/down/logs/migrate` (docker compose), `test` (pytest В КОНТЕЙНЕРЕ — реальный стек),
139
+ `test-local`, `lint`, `format`, `typecheck`, `arch-lint`, `precommit`.
140
+ - [ ] Все команды copy-paste-исполнимые; `make help` перечисляет. Это «CLAUDE.md для рук».
141
+
142
+ ---
143
+
144
+ ## 3. CI (GitHub Actions) — эталон: `.github/workflows/ci.yml`
145
+
146
+ Три параллельные джобы, fail-fast (дешёвое — первым):
147
+
148
+ - [ ] **quality**: `uv sync --frozen` → `ruff check` → `ruff format --check` → `arch-lint`.
149
+ - [ ] **security**: `gitleaks dir .` (секреты, сильнее detect-private-key) · `hadolint` на все
150
+ Dockerfile · `actionlint` (валидация самих workflow) · `pip-audit` по `uv export`
151
+ (известные CVE в зависимостях). Всё — скачиванием бинарей, на Linux это секунды.
152
+ - [ ] **tests**: сервис `postgres:17` c healthcheck → `makemigrations --check --dry-run`
153
+ (**поменял модель — забыл миграцию** = красный) → `migrate` → `pytest`.
154
+ - [ ] Branch protection на main: merge только через зелёный CI (включить в настройках GitHub).
155
+ - [ ] Nightly-джобы — по триггерам §8 (mutmut, trivy, schemathesis, lychee).
156
+
157
+ ---
158
+
159
+ ## 4. Харнес агента — `.claude/` (самая недооценённая часть)
160
+
161
+ ### 4.1. `CLAUDE.md` — конституция, < 200 строк
162
+
163
+ - [ ] Только: карта проекта (указатель на README/доки), язык, ключевые правила-принципы,
164
+ команды НЕ дублировать (они в Makefile). Раздутый CLAUDE.md агент игнорирует.
165
+ - [ ] **Эмпирика 2026 (ETH Zurich + Vercel):** LLM-сгенерированные контекст-файлы СНИЖАЮТ успех (~−3%)
166
+ и удорожают на 20–23%; курируемые человеком дают +4 п.п. Критерий каждой строки: «агент не может
167
+ открыть это сам» — иначе удалить. Skills НЕ срабатывают сами в 56% случаев → правила подключать
168
+ явными указателями («перед задачей Y читай docs/X»), не надеждой на автотриггер. Бюджет ~150–200
169
+ надёжных инструкций на разговор («CLAUDE.md = RAM, docs/ = диск»).
170
+ - [ ] Обязательные правила (проверены на memo):
171
+ - «Код — только после принятого плана и явного го» (человек владеет scope).
172
+ - «Путь файла в первой строке» (`# file: ...`) — агент и человек всегда знают, где находятся.
173
+ - «Развилки: варианты + рекомендация» — против молчаливых решений (анти-review-debt).
174
+ - «Статус — в git/коде, НЕ в доках» (прогресс в доках гниёт). Доки — только durable.
175
+ - «Обратимое и под гейтом → делай + запиши почему; необратимое → жди» (автономия с границами).
176
+ - «e2e на поведение > бесконтрольные юнит»; «повторный баг → новый гейт».
177
+ - Ссылки на `.claude/rules/*` (правило 14 в memo).
178
+
179
+ ### 4.2. `settings.json` — permissions тремя слоями (эталон: наш)
180
+
181
+ - [ ] `allow`: базовые тулы (Bash, Read, Edit, Write, Glob, Grep, Web*).
182
+ - [ ] `ask`: обратимое-но-опасное — `rm*`, `git reset --hard`, `git clean`, `push --force`,
183
+ `branch -D`, PowerShell-эквиваленты (`Remove-Item` и др. — на Windows у агента ДВЕ оболочки!).
184
+ - [ ] `deny` (жёсткое «нет», не обсуждается): **`Read(./.env)`, `Read(./.env.*)`,
185
+ `Read(./secrets/**)`** (секрет не должен попасть в контекст агента = в логи API) +
186
+ force-push / reset --hard / `docker volume rm` / `docker system prune` / `rm -rf /` и `~`.
187
+ - [ ] hooks-секция: PreToolUse → block-dangerous; PostToolUse(Write|Edit) → auto-format;
188
+ Stop → stop-gate. (Код — §4.3.)
189
+
190
+ ### 4.3. Три хука (эталоны: `.claude/hooks/*.sh` memo)
191
+
192
+ | Хук | Событие | Что делает | Ключевые детали |
193
+ |---|---|---|---|
194
+ | `block-dangerous-commands.sh` | PreToolUse (Bash **и** PowerShell) | exit 2 + причина в stderr на необратимое: force-push, reset --hard, `DROP DATABASE`, `TRUNCATE`, `docker volume rm`, `rm -rf /` | fail-safe: не распарсил JSON → грепай сырой ввод. Штатный сброс дев-БД (`compose down -v`) — НЕ блокировать |
195
+ | `auto-format.sh` | PostToolUse (Write\|Edit) | `ruff format` + `ruff check --fix` на изменённый `.py` | агент физически не оставляет неотформатированный код, контекст не тратится |
196
+ | `stop-gate.sh` | Stop | красный `ruff` по прод-путям → exit 2 + хвост ошибок → агент чинит, а не «сдаёт» | обязателен гард `stop_hook_active` (иначе вечный цикл); проверять только СВОЙ домен, не параллельную работу человека |
197
+
198
+ - [ ] ⚠️ **Грабля №1 (Windows): `jq` нет в Git Bash.** Хук с `command -v jq || exit 0` молча
199
+ превращается в no-op — защита «есть», но не работает. Парсить JSON через
200
+ `python -c "import sys,json..."` с fallback-цепочкой jq → python → сырой greп.
201
+ - [ ] **Проверяй хуки функционально**: подай JSON с опасной командой → жди exit 2; безопасной →
202
+ exit 0. Хук, который никогда не тестировали, = хука нет. (Наш сам себя доказал:
203
+ заблокировал мою же тест-команду с `git push --force`.)
204
+ - [ ] Каждому `.sh`: exec-бит через git (§2.1) + LF (§2.1) + shellcheck (§2.3).
205
+ - [ ] Политика хуков: PreToolUse < 100 мс, узкие матчеры (не `.*`), тяжёлое — в PostToolUse; внешние
206
+ вызовы — таймаут 1–2 с и **fail-open с логом** (гейт не должен вешать агента). Проверить, что
207
+ deny-хуки держатся в bypassPermissions-режиме (`permissionDecision: "deny"` — единственный
208
+ несгораемый гейт). Каждый хук-запрет ссылается на rules-файл «почему и как правильно».
209
+ - [ ] **Supply chain самого харнеса:** сторонние MCP-серверы/skills/plugins — только после ручного
210
+ аудита исходников + пин версии; их описания (SKILL.md, tool descriptions) = недоверенный ввод
211
+ (tool poisoning — реальный вектор 2026: 40% MCP-серверов без auth, вредоносные skills в маркетплейсах).
212
+
213
+ ### 4.4. `.claude/rules/` — стандарты по доменам
214
+
215
+ - [ ] 5 файлов: `general.md` (принципы, запреты, лимиты, DoD), `security.md` (главный инвариант
216
+ проекта словами! + секреты/хэши/недоверенный контент), `testing.md` (§7),
217
+ `backend-*.md`, `frontend-*.md` — **под свой реальный стек**, вырезав правила про
218
+ технологии, которых нет (мёртвые правила = шум, агент перестаёт верить остальным).
219
+ - [ ] Формат правила: не «пиши хорошо», а конкретика + почему + чем энфорсится
220
+ («лимит 500 строк — гейт arch-lint»). Правило без гейта — пожелание.
221
+
222
+ ### 4.5. Агенты-критики и память
223
+
224
+ - [ ] Субагенты `gap-finder` / `devils-advocate` (read-only) — самопроверка планов/доков
225
+ **свежим контекстом** (self-review предвзят — confirmation bias).
226
+ - [ ] Память агента: индекс + один факт = один файл; писать туда предпочтения человека и
227
+ процессные уроки, НЕ то, что уже записано в репо/git.
228
+ - [ ] Cross-model / свежая-сессия ревью больших кусков — по возможности.
229
+
230
+ ---
231
+
232
+ ## 5. Docker-first
233
+
234
+ - [ ] compose в **корне** (`compose.yml` — тогда `docker compose` работает без `-f`), Dockerfile — в `infra/`.
235
+ - [ ] Минимум сервисов под текущий срез (web+db+frontend). Celery/Redis/observability — когда
236
+ появится работа для них, не «на вырост» (мы ровно это выпиливали из memo).
237
+ - [ ] healthcheck у stateful; `depends_on: condition: service_healthy`; конфиг только env; логи в stdout JSON.
238
+ - [ ] `.dockerignore`: `node_modules`, `dist`, `*.log`, `.git`.
239
+ - [ ] Windows bind-mount: vite/webpack HMR не видит изменений → `usePolling: true` в конфиге.
240
+ - [ ] Правило людям и агентам: **на хосте dev-серверы не запускать** — артефакты (`dist/`,
241
+ `*.log`) плодятся в рабочей папке. Появились — ищи, КТО запускает мимо Docker.
242
+
243
+ ---
244
+
245
+ ## 6–7. Правила кода и тестов
246
+
247
+ Здесь их нет намеренно. Правила живут в `kit/rules/` и уезжают в проект как `.aqk/rules/`:
248
+
249
+ | Файл | О чём |
250
+ |---|---|
251
+ | `general.md` | принципы, что запрещено в готовом коде, пределы размеров, новая зависимость, разбор входа на границе, коммиты, объяснение диффа, определение «готово» |
252
+ | `testing.md` | поведение важнее реализации, порядок «красный тест первым», как написан сам тест, арбитр нельзя подгонять, живой прогон перед сдачей |
253
+ | `security.md` | секреты, недоверенный ввод, права, необратимое, логи |
254
+
255
+ **ПОЧЕМУ указатель, а не копия.** Правила агент читает всегда, методичку — по надобности. Две
256
+ копии одного правила через месяц врут по-разному, и непонятно, какая настоящая.
257
+
258
+ ---
259
+
260
+ ## 7.1. Как ввести правило в проект, где старый код ему не соответствует
261
+
262
+ Не «большой чисткой» и не необязательным предупреждением. **Храповиком:** файл со списком текущих
263
+ нарушений; гейт разрешает список укорачивать и запрещает удлинять.
264
+
265
+ Чистка откладывается навсегда, потому что она большая. Необязательное предупреждение не блокирует
266
+ ничего, его листают, и правило не действует. Храповик даёт действующее правило **со дня установки**,
267
+ не требуя трогать старый код.
268
+
269
+ Проверка, что это храповик, а не советчик: «может ли новый код добавить нарушение и пройти?»
270
+ Может — значит гейта нет.
271
+
272
+ Механика, поля записи и остальные требования к гейту — в `kit/gates/README.md`. Ступень AQK-2
273
+ требует именно этого: заполненные `samples` и `ratchets` в манифесте.
274
+
275
+ ---
276
+
277
+ ## 8. Отложенный арсенал — внедрить ПО ТРИГГЕРУ (не раньше и не позже)
278
+
279
+ > Это часть методички, а не «может быть»: при наступлении триггера инструмент **обязателен**.
280
+ > На memo зафиксировано в ADR 006 (`spec/decisions/006-quality-gates.md`).
281
+
282
+ | Инструмент | Триггер внедрения | Что даёт |
283
+ |---|---|---|
284
+ | **import-linter** | ≥2 модулей со строгими границами (напр. scoring/runtime) | границы монолита конфигом, не дисциплиной; «ядро скорера не импортирует django» |
285
+ | **django-migration-linter** + `check --deploy` | первые нетривиальные миграции домена | ловит обратно-несовместимые миграции (самая дорогая ошибка) |
286
+ | **mypy strict + django-stubs** | стабилизация моделей/сервисов | типы = feedforward для агента |
287
+ | **schemathesis** (nightly) | появился OpenAPI-контракт | property-фаззинг всех эндпоинтов против схемы бесплатно |
288
+ | **hypothesis** | генератор/скорер (любая чистая детерминированная логика) | property-тесты: seed→байт-в-байт; score детерминирован и в границах |
289
+ | **mutmut** (nightly) | ядро скорера/критическая логика | выжившие мутанты = слепые зоны тестов = завышенный балл |
290
+ | **pre-flight null/random/injection-агенты** | каждая новая задача бенчмарка | тривиальный агент набрал >0 → задача/скорер сломаны |
291
+ | **deny Edit на `tests/e2e/**`** (PreToolUse) | появились залоченные e2e-контракты | агент не переписывает арбитра под свой сломанный код |
292
+ | **radon/xenon, vulture, jscpd** | заметный объём прод-кода | анти-слоп: сложность, мёртвый код, дубли |
293
+ | **semgrep** (кастомные правила) | появились свои инварианты сложнее grep | инварианты как правила с fix-сообщением |
294
+ | **trivy** (nightly) | публикуемые Docker-образы | CVE в образах |
295
+ | **diff-cover** | стабильный test-suite | покрытие только изменённых строк (не роняем, не требуя 100% на легаси) |
296
+ | **testcontainers** | интеграционные тесты вне compose | эфемерный PG на тест |
297
+ | **Renovate/Dependabot** | выход в постоянную разработку | SCA-обновления зависимостей |
298
+ | **markdownlint + lychee** (nightly) | докоцентричный репо стабилизировался | битые ссылки = детектор doc-гниения |
299
+ | **FSD + TypeScript strict** (фронт) | плановая переделка фронта (отдельный срез!) | слои + eslint-boundaries; НЕ болтом поверх живого кода |
300
+ | **React Query + queryKeys-guardian** | фронт начал реально ходить в API за данными | единые ключи кэша, guardian-скрипт в CI |
301
+ | **Stop-хук с тестами** (не только ruff) | тесты стали быстрыми/стабильными в контейнере | полный Ralph-loop: «не завершай, пока не зелёное» |
302
+ | **OTel GenAI semconv** | продукт пишет трейсы агентов | стандартная схема спанов, без вендор-лока |
303
+ | **microVM/egress-allowlist** | публичная фаза платформы с чужим кодом | анти-чит и изоляция; раньше — карго-культ |
304
+ | **aislop** (слоп-линтер) | заметный объём прод-кода; сперва advisory | 50+ детерминированных правил слопа (narrative-комменты, swallowed exceptions, hallucinated imports, дубли); заменяет vulture+jscpd+самописное |
305
+ | **pytest-archon** (вместо/рядом с import-linter) | ≥3 модулей со строгими границами | архитектурные границы как pytest-тесты — в существующий `make test`, без нового CI-шага |
306
+ | **test lock** (deny Edit тестов-верификаторов) | первые e2e ИЛИ первый случай «агент подогнал тест» | SpecBench: o3 хакает 30% прогонов удалением тестов/патчем верификатора |
307
+ | **OTel самого Claude Code** | вопрос «куда уходят токены» / рост правил | встроенная телеметрия агента (tokens/task, cost, retry) — env-переменные, ноль разработки |
308
+ | **cross-model review** (codex-плагин) | security-чувствительные/крупные срезы | чужая модель без сикофантии к своему коду; официальный плагин OpenAI для Claude Code |
309
+ | **ignore-scripts + require-hashes + cooldown версий** | прод-зависимости / переход на pnpm | блок postinstall-атак и свежезарегистрированных пакетов (волны Shai-Hulud) |
310
+ | **CSP (Django 6.0) + django-axes/ratelimit + `check --deploy` в CI** | перед первым внешним доступом | XSS-кража JWT; брутфорс логина; прод-мисконфиг |
311
+ | **credential proxy** (Infisical Agent Vault) | реальные прод-ключи в окружении агента | «агент никогда не видит секрет»: подстановка кред ниже процесса агента |
312
+ | **Code Durability + rework rate** | «хотим мерить прогресс» | доля кода, дожившего N недель (по git); анти-LOC-метрика ценности |
313
+ | **progress-file + одна-фича-за-сессию** (Anthropic long-running) | многосессионные длинные срезы | feature-list JSON + smoke на старте сессии; главный failure mode — one-shot с обрывом контекста |
314
+
315
+ ## 9. Если твой продукт — платформа ДЛЯ агентов (уровень memo)
316
+
317
+ Твой продукт и твой dev-харнес устроены одинаково — это не совпадение (ключ ответов вне
318
+ досягаемости агента = e2e-контракты вне досягаемости кодера):
319
+
320
+ - **The Log Is The Agent**: журнал (TraceEvent) — основа архитектуры; проектируй ЕГО первым.
321
+ - **Record/replay**: полнота трейса такая, что прогон реплеится без LLM; инцидент → тест.
322
+ - **Receipts**: каждое действие — проверяемый чек (что вызвал, с чем, что подтвердилось).
323
+ - **Оси, а не цифра**: исход + процесс + безопасность + стоимость + восстановление; одна цифра —
324
+ произвольная свёртка для лидерборда, истина — разбивка.
325
+ - **Минимум тулов** (3–4); рост → semantic routing, не 100 тулов в промпт.
326
+ - **Анти-гейминг — инженерия, не промпт**: чек-лист BenchJack (8 классов дыр), «баланс сошёлся» —
327
+ необходимое, но НЕ достаточное (AccountingBench: агенты выдумывают транзакции под чек).
328
+ - **Харнес — единица оценки**: балл без записи полной конфигурации харнеса невоспроизводим.
329
+ - **CapCode-принцип**: подмешивай задачи с заведомо недостижимым честным 100% — балл выше потолка =
330
+ статистическое доказательство читерства (детекция по построению, не латание дыр).
331
+ - **Интервалы, не точки**: pass@1 гуляет на 2–6 п.п. даже при temp=0 → 3–5 прогонов на пару
332
+ (агент, задача), pass@k/pass^k как границы, reliability (дисперсия) — отдельная колонка (пивот HAL).
333
+ - **Пул задач**: в зачёте — pass-rate 30–70% (режет объём на 44–70% без потери ранжирования);
334
+ корреляция public/private рангов = метрика здоровья экзамена.
335
+ - **Не изобретать домен**: сперва прочесть соседние бенчмарки (для сверки — FinBalance, AuditFlow),
336
+ переиспользовать таксономии ошибок, дифференцироваться платформой (seed-генерация, арена, трейс).
337
+
338
+ ## 10. Грабли Windows (проверено кровью memo)
339
+
340
+ 1. **`jq` нет в Git Bash** → все «стандартные» примеры хуков молча не работают. Fallback на python (§4.3).
341
+ 2. **CRLF ломает shebang** → `.gitattributes: *.sh text eol=lf` в первом коммите.
342
+ 3. **exec-бит не хранится** → `git update-index --chmod=+x` на каждый новый `.sh`.
343
+ 4. **PowerShell 5.1**: нет `&&`/`||`, `2>&1` на native-exe даёт ложные ошибки, UTF-16 по умолчанию
344
+ в `Out-File` → для файлов, которые читают другие тулы, `-Encoding utf8`.
345
+ 5. **У агента две оболочки** (bash + PowerShell) → permissions и block-хук должны покрывать ОБЕ.
346
+ 6. **vite/HMR через bind-mount не видит изменений** → `usePolling: true`.
347
+ 7. **WSL-файлы читаются с Windows** по UNC: `\\wsl.localhost\<distro>\...` (когда нужен чужой репо-эталон).
348
+ 8. Тяжёлые линтеры (gitleaks/hadolint/actionlint — Go-бинари) → в CI на Linux, не в локальный pre-commit.
349
+
350
+ ## 11. Порядок внедрения — сводка
351
+
352
+ | Когда | Что |
353
+ |---|---|
354
+ | **День 0** (до первой строки кода) | §2 (git, pyproject, pre-commit, arch-lint, Makefile) + §3 CI + §4 .claude + §5 Docker |
355
+ | **Первый кодовый срез** | e2e-арбитр ПЕРВЫМ (красный) → код до зелёного; `makemigrations --check` уже в CI |
356
+ | **Из трения** (баг повторился) | новый гейт того же класса: тест / правило ruff / хук / строка в rules |
357
+ | **По триггерам** | таблица §8 |
358
+ | **Еженедельно** | cleanup-сессия агента: мёртвый код, устаревшие доки, несогласованные паттерны → PR |
359
+ | **Никогда «на вырост»** | сервисы/линтеры/оркестрация без объекта, который они стерегут |
360
+
361
+ ---
362
+
363
+ ## 12. Карта понятий: два слоя над моделью, два материала обвязки
364
+
365
+ **Слой 1 — низкоуровневый агент** (программный код): цикл «подумал → вызвал тул → прочитал →
366
+ подумал», системный промпт, набор тулов, менеджмент контекста/сессии. Claude Code/Codex — ГОТОВЫЕ
367
+ агенты этого слоя. Самописные — на Anthropic API / Agent SDK / pydantic-ai.
368
+
369
+ **Слой 2 — обвязка поверх готового агента.** Внутри неё два РАЗНЫХ материала — не путать:
370
+
371
+ | Материал | Примеры | Гарантия |
372
+ |---|---|---|
373
+ | **Мягкое** (текст в контексте) | CLAUDE.md, rules, skills, память | модель МОЖЕТ проигнорировать (вероятностна) |
374
+ | **Жёсткое** (код в точках расширения) | hooks (exit 2), permissions deny, sandbox, pre-commit, CI | исполняется кодом, минуя волю модели |
375
+
376
+ Главный приём инженерии обвязки: **переносить правила из мягкого в жёсткое** при каждом повторе
377
+ нарушения. SlopCodeBench: промпт-интервенции НЕ замедляют деградацию — только механика. Мягкое
378
+ оставляем для того, что нельзя механизировать: вкус, приоритеты, «почему».
379
+
380
+ ## 13. Skills: когда и как делать
381
+
382
+ - **Когда НЕ делать**: постоянные правила → `rules/` (skills не срабатывают сами в 56% случаев;
383
+ rules с явными указателями — 100%). Skill — для **повторяемой процедуры по запросу** («выпусти
384
+ релиз», «заведи новую задачу бенчмарка»): чек-лист + скрипты, вызывается явно.
385
+ - **Тело по TWI**: шаг → ключевой момент → **ПОЧЕМУ** (без «почему» агент обходит шаг при первом
386
+ неудобстве). Чек-листы — по месту применения, не в общем разделе. Убрать размытое
387
+ («periodically», «typically»).
388
+ - **Git**: skills живут в репо → версии, ревью, история — как код («Skills are the New SDKs»).
389
+
390
+ ### Механика, которую надо знать (иначе строишь неверные ожидания)
391
+
392
+ **Автоподхвата по ключевому слову не существует.** Видны только имя и описание; решение загрузить
393
+ принимает агент. Поэтому «в описании же написано *загружать всегда*» ничего не гарантирует —
394
+ нужен явный указатель в правилах. У нас он стоит в `CLAUDE.md`.
395
+
396
+ **Загруженный скилл остаётся в контексте до конца сессии.** Это не разовая плата, а постоянная.
397
+ Значит `SKILL.md` — короткая процедура, а длинные политики и справочники — в `reference/` рядом,
398
+ подгружаются по требованию.
399
+
400
+ **Бюджет списка скиллов ограничен** (порядка процента от окна). При переполнении описания
401
+ **обрезаются**, начиная с редко вызываемых — и скилл перестаёт находиться именно потому, что им
402
+ редко пользовались.
403
+
404
+ > 📏 **У нас: 23 скилла, 7979 символов описаний.** На окне в миллион токенов это около 80% бюджета
405
+ > листинга. **Запас почти выбран.** Новые скиллы добавляем, укорачивая старые описания, а не
406
+ > дописывая сверху. На модели с окном поменьше мы бы уже были за границей и не знали об этом.
407
+
408
+ **При сжатии контекста скиллы переносятся частично** — старые выпадают. После долгой сессии
409
+ скилл, который «перестал влиять», надо просто перевызвать.
410
+
411
+ ### Структура
412
+
413
+ Папка `<name>/` с `SKILL.md` во frontmatter которого `name` и `description` с конкретным
414
+ триггером «when user asks to…». Внутри: `SKILL.md` (короткая процедура) + `reference/` (почему так устроено) + `scripts/` (инструменты) +
415
+ данные рядом. Одна директория = самодостаточный пакет: инструкция, чем делать и на чём делать.
416
+
417
+ > 📏 У нас подпапки есть у 8 скиллов из 23; полный набор «инструкция + инструменты + данные» —
418
+ > пока только у `nextcloud-webdav`.
419
+
420
+ ### Что куда класть
421
+
422
+ - Постоянное правило, действующее всегда → `rules/` или `CLAUDE.md` (всегда в контексте).
423
+ - Процедура, нужная по теме → скилл.
424
+ - Доступ к внешней системе → **свой** клиент или скрипт внутри скилла. Сторонний MCP — только если
425
+ своего нет и после аудита (см. §7).
426
+
427
+ ---
428
+
429
+ ### Как понять, что скилл работает
430
+
431
+ «Сработал» — не значит «сработал правильно». Проверять надо две разные вещи: **срабатывание** и
432
+ **результат**.
433
+
434
+ - **Явный вызов** — работает, когда позвали по имени.
435
+ - **Неявный** — срабатывает на естественной формулировке задачи, без имени.
436
+ - **Негативный** — НЕ срабатывает там, где не должен.
437
+ - **Прогон без скилла** — если результат тот же, скилл не нужен: **снести**. Модель могла дорасти.
438
+
439
+ Дешёвая рабочая версия: пара реальных задач с ним и без него, в свежей сессии. Свежая — обязательно,
440
+ иначе остатки контекста от написания скилла маскируют дыры в самой инструкции.
441
+
442
+ ---
443
+
444
+ ### Скилл — это тоже цепочка поставки
445
+
446
+ Скилл можно установить, обновить и расшарить — значит его можно атаковать. Опасен **не только код**:
447
+ инструкция на естественном языке в `reference/` для модели тоже исполняемая.
448
+
449
+ Внешние данные для калибровки (не наши замеры): по публичным исследованиям 2026 года заметная доля
450
+ опубликованных скиллов содержит дефекты безопасности, часть — активную полезную нагрузку; публичные
451
+ сканеры обходятся довольно быстро, поэтому сканер — не гарантия, а фильтр.
452
+
453
+ **Наше правило: скиллы пишем сами.** Сторонний — только после чтения всех файлов целиком и с
454
+ фиксацией версии.
455
+
456
+ > 📏 Проверено 2026-07-29: в наших 23 скиллах внешних загрузок нет (единственный `curl` — на
457
+ > localhost, упоминания установки пакетов — документация зависимостей, не исполняемый код).
458
+ > Стороннего происхождения один — `skill-creator`.
459
+
460
+ Что проверять в своём скилле: что делают `scripts/`, куда они ходят по сети, нет ли секретов в
461
+ файлах и аргументах, ограничены ли права, есть ли сухой прогон для разрушающих действий и **явная
462
+ граница записи в коде**.
463
+
464
+ ---
465
+
466
+ ## 14. LLM-ревью кода: как строить и как МЕРИТЬ ревьюера
467
+
468
+ Дизайн (паттерн Cloudflare, 131k ревью/мес): **специализированные ревьюеры** по доменам
469
+ (security/perf/quality), а не один универсал · risk-tiering по размеру диффа · фильтрация
470
+ lock/generated файлов ДО ревью · явный список **«что НЕ флагать»** (главное лекарство от шума) ·
471
+ инкрементальный re-review с памятью прошлых находок и «won't fix» · cross-model или свежая сессия
472
+ (нет сикофантии к своему коду). Честно: LLM-ревью слабо в архитектуре/concurrency — это остаётся
473
+ человеку.
474
+
475
+ **Ревьюер — тоже система, его меряют**: golden-set диффов с подсаженными известными багами →
476
+ recall (сколько поймал) и false-positive rate (сколько шума). Источник кейсов — каждый реальный
477
+ пропущенный баг становится кейсом эвала ревьюера. Ревьюер без эвала = генератор шума.
478
+ Процессная часть (пирамида ревью, роль человека) — `ai-sdlc.md §3` этап 6.
479
+
480
+ ## 15. Рефакторинг, уборка, cron
481
+
482
+ - SOLID/KISS напрямую линтером не проверяются — их прокси: сложность (C90≤10), размер файла
483
+ (500/300/800), дубли (jscpd/aislop), мёртвый код (vulture/aislop) — всё по триггерам §8.
484
+ - **Процесс**: рефакторинг — отдельным PR от фич; маленькие шаги с зелёными тестами между;
485
+ критерий правильных тестов — «переписали реализацию, поведение то же → тест выжил».
486
+ - **Первый скан слоп-линтера = лавина** (Thoughtworks): приоритизировать, не чинить всё сразу.
487
+ - **Cron** двух видов: GitHub Actions `schedule:` — nightly-гейты (mutmut/trivy/lychee, §8);
488
+ планировщик Claude Code — еженедельная cleanup-сессия («найди мёртвый код/дубли/гниющие доки →
489
+ предложи PR»). Триггер обоих: появился объём, который есть смысл убирать.
490
+
491
+ ## 16. Structured output (когда нужен машинный JSON)
492
+
493
+ - **Свой агент / скрипты**: tool use с `input_schema` + форсированный `tool_choice` — модель
494
+ обязана вернуть валидный JSON по схеме. Источник истины схемы — Pydantic (py) / Zod (ts).
495
+ - **Петля**: validate → максимум 1 repair-попытка → ошибка + кейс в evals. Парсить регулярками
496
+ там, где можно schema-first, — запрещено.
497
+ - **В Claude Code интерактивно**: просить JSON + валидировать скриптом-гейтом; субагенты умеют
498
+ форсированную схему.
499
+
500
+ ## 17. Путь в низкоуровневый агент (слой 1)
501
+
502
+ Ядро любого агента — цикл ~20 строк: `system + история + tools → LLM → есть tool_calls? исполни,
503
+ дозапиши результат в историю → повторяй, пока stop_reason != tool_use`. Надстройки добавляются
504
+ **по боли, не заранее**: гардрейлы (workdir, чёрные списки) → loop detection (5 одинаковых вызовов
505
+ → «попробуй иначе») → компактизация контекста → субагенты → скиллы (name+description в промпт,
506
+ тело по load_skill) → память. KV-cache дисциплина: стабильный байт-в-байт префикс, append-only
507
+ история, `sort_keys=True` для тулов (см. reference/context-and-kv-cache).
508
+
509
+ Лучший первый проект — **не учебный, а нужный**: минимальный клиент собственной платформы
510
+ (эталонный агент ~50 строк с 3 тулами). Мост между слоями — Claude Agent SDK (харнес Claude Code
511
+ как библиотека).
512
+
513
+ ## 18. Дисциплина в каждой задаче
514
+
515
+ Переносится из `audit_project`: каждое правило ниже прослеживается до конкретного отказа,
516
+ который там был. Правило без своего инцидента сюда не попадает.
517
+
518
+ ### Знание живёт в репозитории, а не в переписке
519
+
520
+ Всё, что сказано в чате, умирает вместе с окном контекста. Поэтому durable-знание раскладывается
521
+ по трём местам:
522
+
523
+ | Куда | Что там | Когда читать |
524
+ |---|---|---|
525
+ | Очередь работ проекта | что осталось сделать и почему это важно | планирование, «что дальше» |
526
+ | Методички проекта (`.aqk/docs/`) | как и почему устроена тема | любое касание темы |
527
+ | Память агента | предпочтения человека и процессные уроки | автоматически |
528
+
529
+ **Железное правило: прежде чем спрашивать человека — искать в репозитории.**
530
+
531
+ > **Инцидент 2026-07-29.** Задал пять вопросов по дизайну интеграции Nextcloud. Четыре из пяти были
532
+ > согласованы 2026-07-21 и лежали в репозитории готовыми ответами. Человек потратил время на то,
533
+ > чтобы указать мне на мои же записи. Дешевле было потратить одну минуту на поиск.
534
+
535
+ ---
536
+
537
+ ### Документация врёт по умолчанию
538
+
539
+ Документ пишется до кода и расходится с ним молча. Никто не замечает, пока не сверит.
540
+
541
+ - **Сверять при каждом серьёзном касании темы**, а не «когда-нибудь потом».
542
+ - Разделять **CURRENT** (проверено кодом) / **TARGET** (решение принято, не сделано) /
543
+ **HISTORICAL** (датированный замер). Утверждение без даты и способа проверки — не факт.
544
+ - **Историю не переписываем задним числом.** Разошлось — добавляем раздел «фактическое состояние»
545
+ и помечаем расхождение по месту. Иначе теряется, что решение вообще менялось, и почему.
546
+
547
+ > **Инцидент 2026-07-29.** Корпус документов по Nextcloud врал в четырёх местах: обещал иерархию
548
+ > папок, которую платформа не строит; неверно называл момент создания папок; описывал очередь,
549
+ > которой нет; и подавал три несделанных среза так, будто они часть готового.
550
+
551
+ ---
552
+
553
+ ### Не гадать — мерить
554
+
555
+ - **Три источника:** официальная документация × наши правила и интернет × реальная система.
556
+ Расходятся → **СТОП** и сверка. Не выбирать один источник молча и не городить обход.
557
+ - **Не обрезать структурный вывод.** `tail`/`head` при инвентаризации полей прячет поле и даёт
558
+ ложный вывод «его нет». Обрезка — только для шумных логов и прогонов тестов.
559
+ - **Молчание документации — не разрешение решать самому, а повод поставить эксперимент.**
560
+ - «Готово» = зелёный тест **плюс** живой прогон как пользователь **плюс** логи **плюс** метрика.
561
+ «Агент сказал готово» и «сходится» — не доказательства.
562
+
563
+ > **Инцидент 2026-07-29.** Официальные доки Nextcloud не говорят, переживает ли идентификатор файла
564
+ > корзину. Замер на живом инстансе показал: переживает — а наша детекция корзину от настоящей потери
565
+ > **не отличает**. Заложенный необратимый откат принятой задачи сработал бы на штатном действии
566
+ > пользователя, который через минуту достал бы файл обратно. Дока молчала, догадка была бы неверной.
567
+
568
+ ---
569
+
570
+ ### Границы: необратимое и чужое
571
+
572
+ - **Боевые системы:** работаем только в своей песочнице, и **границу переносим в код**. Пример:
573
+ `sandbox()` в `nc_probe.py` падает до обращения к серверу, если путь вне разрешённого корня.
574
+ Память агента границу не держит — код держит.
575
+ - **Деструктивный git запрещён.** В репозитории параллельно работает человек: не `checkout`, не
576
+ `reset`, не `clean`. Стейджим явные пути и проверяем состав индекса перед коммитом.
577
+ - **Обратимое и под гейтом → делай и запиши почему. Необратимое → остановись и спроси.**
578
+ - Секреты не читаем даже когда так удобнее — берём через окружение контейнера.
579
+
580
+ ---
581
+
582
+ ### Против срезания углов
583
+
584
+ Агент срезает углы правдоподобно: «изменение маленькое», «визуально нормально», «полный прогон тут
585
+ не нужен». Лечится не уговорами, а прямыми запретами в инструкции:
586
+
587
+ - не запускал проверку — **не пиши «проверено»**;
588
+ - есть дифф — сперва прочитай, потом резюмируй;
589
+ - меняешь внешнюю систему — сперва сухой прогон;
590
+ - нет данных — верни «нет данных», **не додумывай**;
591
+ - три попытки не помогли — СТОП, к человеку.
592
+
593
+ Сюда же: не сообщать об успехе того, что не сделано, и честно называть пропущенное. Отчёт, в
594
+ котором скрыт незакрытый кусок, хуже отсутствия отчёта.
595
+
596
+ ---